Dash Core Source Documentation (0.16.0.1)

Find detailed information regarding the Dash Core source code.

stacktraces.cpp
Go to the documentation of this file.
1 // Copyright (c) 2014-2019 The Dash Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include <stacktraces.h>
6 #include <fs.h>
7 #include <tinyformat.h>
8 #include <random.h>
9 #include <streams.h>
10 #include <util.h>
11 #include <utilstrencodings.h>
12 
13 #include <dash-config.h>
14 
15 #include <mutex>
16 #include <map>
17 #include <string>
18 #include <vector>
19 #include <memory>
20 #include <thread>
21 #include <atomic>
22 
23 #include <cxxabi.h>
24 
25 #if WIN32
26 #include <windows.h>
27 #include <dbghelp.h>
28 #else
29 #include <execinfo.h>
30 #include <unistd.h>
31 #include <signal.h>
32 #endif
33 
34 #if !WIN32
35 #include <dlfcn.h>
36 #if !__APPLE__
37 #include <link.h>
38 #endif
39 #endif
40 
41 #if __APPLE__
42 #include <mach-o/dyld.h>
43 #include <mach/mach_init.h>
44 #include <sys/sysctl.h>
45 #include <mach/mach_vm.h>
46 #endif
47 
48 #include <backtrace.h>
49 #include <string.h>
50 
51 std::string DemangleSymbol(const std::string& name)
52 {
53 #if __GNUC__ || __clang__
54  int status = -4; // some arbitrary value to eliminate the compiler warning
55  char* str = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status);
56  if (status != 0) {
57  free(str);
58  return name;
59  }
60  std::string ret = str;
61  free(str);
62  return ret;
63 #else
64  // TODO other platforms/compilers
65  return name;
66 #endif
67 }
68 
69 // set to true when the abort signal should not handled
70 // this is the case when the terminate handler or an assert already handled the exception
71 static std::atomic<bool> skipAbortSignal(false);
72 
73 static ssize_t GetExeFileNameImpl(char* buf, size_t bufSize)
74 {
75 #if WIN32
76  std::vector<TCHAR> tmp(bufSize);
77  DWORD len = GetModuleFileName(nullptr, tmp.data(), bufSize);
78  if (len >= bufSize) {
79  return len;
80  }
81  for (size_t i = 0; i < len; i++) {
82  buf[i] = (char)tmp[i];
83  }
84  return len;
85 #elif __APPLE__
86  uint32_t bufSize2 = (uint32_t)bufSize;
87  if (_NSGetExecutablePath(buf, &bufSize2) != 0) {
88  // it's not entirely clear if the value returned by _NSGetExecutablePath includes the null character
89  return bufSize2 + 1;
90  }
91  // TODO do we have to call realpath()? The path returned by _NSGetExecutablePath may include ., .. symbolic links
92  return strlen(buf);
93 #else
94  ssize_t len = readlink("/proc/self/exe", buf, bufSize - 1);
95  if (len == -1) {
96  return -1;
97  }
98  return len;
99 #endif
100 }
101 
102 static std::string GetExeFileName()
103 {
104  std::vector<char> buf(1024);
105  while (true) {
106  ssize_t len = GetExeFileNameImpl(buf.data(), buf.size());
107  if (len < 0) {
108  return "";
109  }
110  if (len < buf.size()) {
111  return std::string(buf.begin(), buf.begin() + len);
112  }
113  buf.resize(buf.size() * 2);
114  }
115 }
116 
117 static std::string g_exeFileName = GetExeFileName();
118 static std::string g_exeFileBaseName = fs::path(g_exeFileName).filename().string();
119 
120 static void my_backtrace_error_callback (void *data, const char *msg,
121  int errnum)
122 {
123 }
124 
125 static backtrace_state* GetLibBacktraceState()
126 {
127 #if WIN32
128  // libbacktrace is not able to handle the DWARF debuglink in the .exe
129  // but luckily we can just specify the .dbg file here as it's a valid PE/XCOFF file
130  static std::string debugFileName = g_exeFileName + ".dbg";
131  static const char* exeFileNamePtr = fs::exists(debugFileName) ? debugFileName.c_str() : g_exeFileName.c_str();
132 #else
133  static const char* exeFileNamePtr = g_exeFileName.empty() ? nullptr : g_exeFileName.c_str();
134 #endif
135  static backtrace_state* st = backtrace_create_state(exeFileNamePtr, 1, my_backtrace_error_callback, nullptr);
136  return st;
137 }
138 
139 #if WIN32
140 static uint64_t GetBaseAddress()
141 {
142  return 0;
143 }
144 
145 // PC addresses returned by StackWalk64 are in the real mapped space, while libbacktrace expects them to be in the
146 // default mapped space starting at 0x400000. This method converts the address.
147 // TODO this is probably the same reason libbacktrace is not able to gather the stacktrace on Windows (returns pointers like 0x1 or 0xfffffff)
148 // If they ever fix this problem, we might end up converting to invalid addresses here
149 static uint64_t ConvertAddress(uint64_t addr)
150 {
151  MEMORY_BASIC_INFORMATION mbi;
152 
153  if (!VirtualQuery((PVOID)addr, &mbi, sizeof(mbi)))
154  return 0;
155 
156  uint64_t hMod = (uint64_t)mbi.AllocationBase;
157  uint64_t offset = addr - hMod;
158  return 0x400000 + offset;
159 }
160 
161 static __attribute__((noinline)) std::vector<uint64_t> GetStackFrames(size_t skip, size_t max_frames, const CONTEXT* pContext = nullptr)
162 {
163  // We can't use libbacktrace for stack unwinding on Windows as it returns invalid addresses (like 0x1 or 0xffffffff)
164  static BOOL symInitialized = SymInitialize(GetCurrentProcess(), nullptr, TRUE);
165 
166  // dbghelp is not thread safe
167  static std::mutex m;
168  std::lock_guard<std::mutex> l(m);
169 
170  HANDLE process = GetCurrentProcess();
171  HANDLE thread = GetCurrentThread();
172 
173  CONTEXT context;
174  if (!pContext) {
175  memset(&context, 0, sizeof(CONTEXT));
176  context.ContextFlags = CONTEXT_FULL;
177  RtlCaptureContext(&context);
178  } else {
179  memcpy(&context, pContext, sizeof(CONTEXT));
180  }
181 
182  DWORD image;
183  STACKFRAME64 stackframe;
184  ZeroMemory(&stackframe, sizeof(STACKFRAME64));
185 
186 #ifdef __i386__
187  image = IMAGE_FILE_MACHINE_I386;
188  stackframe.AddrPC.Offset = context.Eip;
189  stackframe.AddrPC.Mode = AddrModeFlat;
190  stackframe.AddrFrame.Offset = context.Ebp;
191  stackframe.AddrFrame.Mode = AddrModeFlat;
192  stackframe.AddrStack.Offset = context.Esp;
193  stackframe.AddrStack.Mode = AddrModeFlat;
194 #elif __x86_64__
195  image = IMAGE_FILE_MACHINE_AMD64;
196  stackframe.AddrPC.Offset = context.Rip;
197  stackframe.AddrPC.Mode = AddrModeFlat;
198  stackframe.AddrFrame.Offset = context.Rbp;
199  stackframe.AddrFrame.Mode = AddrModeFlat;
200  stackframe.AddrStack.Offset = context.Rsp;
201  stackframe.AddrStack.Mode = AddrModeFlat;
202  if (!pContext) {
203  skip++; // skip this method
204  }
205 #else
206 #error unsupported architecture
207 #endif
208 
209  std::vector<uint64_t> ret;
210 
211  size_t i = 0;
212  while (ret.size() < max_frames) {
213  BOOL result = StackWalk64(
214  image, process, thread,
215  &stackframe, &context, nullptr,
216  SymFunctionTableAccess64, SymGetModuleBase64, nullptr);
217 
218  if (!result) {
219  break;
220  }
221  if (i >= skip) {
222  uint64_t pc = ConvertAddress(stackframe.AddrPC.Offset);
223  if (pc == 0) {
224  pc = stackframe.AddrPC.Offset;
225  }
226  ret.emplace_back(pc);
227  }
228  i++;
229  }
230 
231  return ret;
232 }
233 #else
234 
235 #if __APPLE__
236 static uint64_t GetBaseAddress()
237 {
238  mach_port_name_t target_task;
239  vm_map_offset_t vmoffset;
240  vm_map_size_t vmsize;
241  uint32_t nesting_depth = 0;
242  struct vm_region_submap_info_64 vbr;
243  mach_msg_type_number_t vbrcount = 16;
244  kern_return_t kr;
245 
246  kr = task_for_pid(mach_task_self(), getpid(), &target_task);
247  if (kr != KERN_SUCCESS) {
248  return 0;
249  }
250 
251  kr = mach_vm_region_recurse(target_task, &vmoffset, &vmsize, &nesting_depth, (vm_region_recurse_info_t)&vbr, &vbrcount);
252  if (kr != KERN_SUCCESS) {
253  return 0;
254  }
255 
256  return vmoffset;
257 }
258 #else
259 static int dl_iterate_callback(struct dl_phdr_info* info, size_t s, void* data)
260 {
261  uint64_t* p = (uint64_t*)data;
262  if (info->dlpi_name == nullptr || info->dlpi_name[0] == '\0') {
263  *p = info->dlpi_addr;
264  }
265  return 0;
266 }
267 
268 static uint64_t GetBaseAddress()
269 {
270  uint64_t basePtr = 0;
271  dl_iterate_phdr(dl_iterate_callback, &basePtr);
272  return basePtr;
273 }
274 #endif
275 
276 static __attribute__((noinline)) std::vector<uint64_t> GetStackFrames(size_t skip, size_t max_frames)
277 {
278  // FYI, this is not using libbacktrace, but "backtrace()" from <execinfo.h>
279  std::vector<void*> buf(max_frames);
280  int count = backtrace(buf.data(), (int)buf.size());
281  if (count == 0) {
282  return {};
283  }
284  buf.resize((size_t)count);
285 
286  std::vector<uint64_t> ret;
287  ret.reserve(count);
288  for (size_t i = skip + 1; i < buf.size(); i++) {
289  ret.emplace_back((uint64_t) buf[i]);
290  }
291  return ret;
292 }
293 #endif
294 
296  uint64_t pc{0};
297  std::string filename;
298  int lineno{-1};
299  std::string function;
300 
302 
303  template <typename Stream, typename Operation>
304  inline void SerializationOp(Stream& s, Operation ser_action)
305  {
306  READWRITE(pc);
308  READWRITE(lineno);
309  READWRITE(function);
310  }
311 };
312 
313 static int my_backtrace_full_callback (void *data, uintptr_t pc, const char *filename, int lineno, const char *function)
314 {
315  auto sis = (std::vector<stackframe_info>*)data;
316  stackframe_info si;
317  si.pc = pc;
318  si.lineno = lineno;
319  if (filename) {
320  si.filename = filename;
321  }
322  if (function) {
323  si.function = DemangleSymbol(function);
324  }
325  sis->emplace_back(si);
326  if (sis->size() >= 128) {
327  // abort
328  return 1;
329  }
330  if (si.function == "mainCRTStartup" ||
331  si.function == "pthread_create_wrapper" ||
332  si.function == "__tmainCRTStartup") {
333  // on Windows, stack frames are unwinded into invalid PCs after entry points
334  // this doesn't catch all cases unfortunately
335  return 1;
336  }
337  return 0;
338 }
339 
340 static std::vector<stackframe_info> GetStackFrameInfos(const std::vector<uint64_t>& stackframes)
341 {
342  std::vector<stackframe_info> infos;
343  infos.reserve(stackframes.size());
344 
345  for (size_t i = 0; i < stackframes.size(); i++) {
346  if (backtrace_pcinfo(GetLibBacktraceState(), stackframes[i], my_backtrace_full_callback, my_backtrace_error_callback, &infos)) {
347  break;
348  }
349  }
350 
351  return infos;
352 }
353 
355 {
356  std::string magic;
357  uint16_t version;
358  std::string exeFileName;
359 
361 
362  template <typename Stream, typename Operation>
363  inline void SerializationOp(Stream& s, Operation ser_action)
364  {
365  READWRITE(magic);
368  }
369 };
370 
372 {
373  std::string crashDescription;
374  std::vector<uint64_t> stackframes;
375  std::vector<stackframe_info> stackframeInfos;
376 
378 
379  template <typename Stream, typename Operation>
380  inline void SerializationOp(Stream& s, Operation ser_action)
381  {
385  }
386 
387  void ConvertAddresses(int64_t offset)
388  {
389  for (auto& sf : stackframes) {
390  sf += offset;
391  }
392  for (auto& sfi : stackframeInfos) {
393  sfi.pc += offset;
394  }
395  }
396 };
397 
399 {
400  static uint64_t basePtr = GetBaseAddress();
401 
402  CDataStream ds(SER_DISK, 0);
403 
404  crash_info_header hdr;
405  hdr.magic = "DashCrashInfo";
406  hdr.version = 1;
408  ds << hdr;
409 
410  ci.ConvertAddresses(-(int64_t)basePtr);
411  ds << ci;
412 
413  auto ciStr = EncodeBase32((const unsigned char*)ds.data(), ds.size());
414  std::string s = ci.crashDescription + "\n";
415  s += strprintf("No debug information available for stacktrace. You should add debug information and then run:\n"
416  "%s -printcrashinfo=%s\n", g_exeFileBaseName, ciStr);
417  return s;
418 }
419 
420 static std::string GetCrashInfoStr(const crash_info& ci, size_t spaces = 2);
421 
422 std::string GetCrashInfoStrFromSerializedStr(const std::string& ciStr)
423 {
424  static uint64_t basePtr = GetBaseAddress();
425 
426  bool dataInvalid = false;
427  auto buf = DecodeBase32(ciStr.c_str(), &dataInvalid);
428  if (buf.empty() || dataInvalid) {
429  return "Error while deserializing crash info";
430  }
431 
432  CDataStream ds(buf, SER_DISK, 0);
433 
434  crash_info_header hdr;
435  try {
436  ds >> hdr;
437  } catch (...) {
438  return "Error while deserializing crash info header";
439  }
440 
441  if (hdr.magic != "DashCrashInfo") {
442  return "Invalid magic string";
443  }
444  if (hdr.version != 1) {
445  return "Unsupported version";
446  }
447  if (hdr.exeFileName != g_exeFileBaseName) {
448  return "Crash info is not for this executable";
449  }
450 
451  crash_info ci;
452  try {
453  ds >> ci;
454  } catch (...) {
455  return "Error while deserializing crash info";
456  }
457 
458  ci.ConvertAddresses(basePtr);
459 
460  if (ci.stackframeInfos.empty()) {
461  std::vector<uint64_t> stackframes(ci.stackframes.begin(), ci.stackframes.end());
462  ci.stackframeInfos = GetStackFrameInfos(stackframes);
463  }
464 
465  return GetCrashInfoStr(ci);
466 }
467 
468 static std::string GetCrashInfoStr(const crash_info& ci, size_t spaces)
469 {
470  if (ci.stackframeInfos.empty()) {
471  return GetCrashInfoStrNoDebugInfo(ci);
472  }
473 
474  std::string sp;
475  for (size_t i = 0; i < spaces; i++) {
476  sp += " ";
477  }
478 
479  std::vector<std::string> lstrs;
480  lstrs.reserve(ci.stackframeInfos.size());
481 
482  for (size_t i = 0; i < ci.stackframeInfos.size(); i++) {
483  auto& si = ci.stackframeInfos[i];
484 
485  std::string lstr;
486  if (!si.filename.empty()) {
487  lstr += fs::path(si.filename).filename().string();
488  } else {
489  lstr += "<unknown-file>";
490  }
491  if (si.lineno != 0) {
492  lstr += strprintf(":%d", si.lineno);
493  }
494 
495  lstrs.emplace_back(lstr);
496  }
497 
498  // get max "filename:line" length so we can better format it
499  size_t lstrlen = std::max_element(lstrs.begin(), lstrs.end(), [](const std::string& a, const std::string& b) { return a.size() < b.size(); })->size();
500 
501  std::string fmtStr = strprintf("%%2d#: (0x%%08X) %%-%ds - %%s\n", lstrlen);
502 
503  std::string s = ci.crashDescription + "\n";
504  for (size_t i = 0; i < ci.stackframeInfos.size(); i++) {
505  auto& si = ci.stackframeInfos[i];
506 
507  auto& lstr = lstrs[i];
508 
509  std::string fstr;
510  if (!si.function.empty()) {
511  fstr = si.function;
512  } else {
513  fstr = "???";
514  }
515 
516  std::string s2 = strprintf(fmtStr, i, si.pc, lstr, fstr);
517 
518  s += sp;
519  s += s2;
520  }
521  return s;
522 }
523 
524 static void PrintCrashInfo(const crash_info& ci)
525 {
526  auto str = GetCrashInfoStr(ci);
527  LogPrintf("%s", str); /* Continued */
528  fprintf(stderr, "%s", str.c_str());
529  fflush(stderr);
530 }
531 
532 #ifdef ENABLE_CRASH_HOOKS
533 static std::mutex g_stacktraces_mutex;
534 static std::map<void*, std::shared_ptr<std::vector<uint64_t>>> g_stacktraces;
535 
536 #if CRASH_HOOKS_WRAPPED_CXX_ABI
537 // These come in through -Wl,-wrap
538 // It only works on GCC
539 extern "C" void* __real___cxa_allocate_exception(size_t thrown_size);
540 extern "C" void __real___cxa_free_exception(void * thrown_exception);
541 #if __clang__
542 #error not supported on WIN32 (no dlsym support)
543 #elif WIN32
544 extern "C" void __real__assert(const char *assertion, const char *file, unsigned int line);
545 extern "C" void __real__wassert(const wchar_t *assertion, const wchar_t *file, unsigned int line);
546 #else
547 extern "C" void __real___assert_fail(const char *assertion, const char *file, unsigned int line, const char *function);
548 #endif
549 #else
550 // Clang does not support -Wl,-wrap, so we must use dlsym
551 // This is ok because at the same time Clang only supports dynamic linking to libc/libc++
552 extern "C" void* __real___cxa_allocate_exception(size_t thrown_size)
553 {
554  static auto f = (void*(*)(size_t))dlsym(RTLD_NEXT, "__cxa_allocate_exception");
555  return f(thrown_size);
556 }
557 extern "C" void __real___cxa_free_exception(void * thrown_exception)
558 {
559  static auto f = (void(*)(void*))dlsym(RTLD_NEXT, "__cxa_free_exception");
560  return f(thrown_exception);
561 }
562 #if __clang__
563 extern "C" void __attribute__((noreturn)) __real___assert_rtn(const char *function, const char *file, int line, const char *assertion)
564 {
565  static auto f = (void(__attribute__((noreturn)) *) (const char*, const char*, int, const char*))dlsym(RTLD_NEXT, "__assert_rtn");
566  f(function, file, line, assertion);
567 }
568 #elif WIN32
569 #error not supported on WIN32 (no dlsym support)
570 #else
571 extern "C" void __real___assert_fail(const char *assertion, const char *file, unsigned int line, const char *function)
572 {
573  static auto f = (void(*)(const char*, const char*, unsigned int, const char*))dlsym(RTLD_NEXT, "__assert_fail");
574  f(assertion, file, line, function);
575 }
576 #endif
577 #endif
578 
579 #if CRASH_HOOKS_WRAPPED_CXX_ABI
580 #define WRAPPED_NAME(x) __wrap_##x
581 #else
582 #define WRAPPED_NAME(x) x
583 #endif
584 
585 extern "C" void* __attribute__((noinline)) WRAPPED_NAME(__cxa_allocate_exception)(size_t thrown_size)
586 {
587  // grab the current stack trace and store it in the global exception->stacktrace map
588  auto localSt = GetStackFrames(1, 16);
589 
590  // WARNING keep this as it is and don't try to optimize it (no std::move, no std::make_shared, ...)
591  // trying to optimize this will cause the optimizer to move the GetStackFrames() call deep into the stl libs
592  std::shared_ptr<std::vector<uint64_t>> st(new std::vector<uint64_t>(localSt));
593 
594  void* p = __real___cxa_allocate_exception(thrown_size);
595 
596  std::lock_guard<std::mutex> l(g_stacktraces_mutex);
597  g_stacktraces.emplace(p, st);
598  return p;
599 }
600 
601 extern "C" void __attribute__((noinline)) WRAPPED_NAME(__cxa_free_exception)(void * thrown_exception)
602 {
603  __real___cxa_free_exception(thrown_exception);
604 
605  std::lock_guard<std::mutex> l(g_stacktraces_mutex);
606  g_stacktraces.erase(thrown_exception);
607 }
608 
609 static __attribute__((noinline)) crash_info GetCrashInfoFromAssertion(const char* assertion, const char* file, int line, const char* function)
610 {
611  crash_info ci;
612  ci.stackframes = GetStackFrames(1, 16);
613  ci.crashDescription = "Assertion failure:";
614  if (assertion) {
615  ci.crashDescription += strprintf("\n assertion: %s", assertion);
616  }
617  if (file) {
618  ci.crashDescription += strprintf("\n file: %s, line: %d", file, line);
619  }
620  if (function) {
621  ci.crashDescription += strprintf("\n function: %s", function);
622  }
624  return ci;
625 }
626 
627 #if __clang__
628 extern "C" void __attribute__((noinline)) WRAPPED_NAME(__assert_rtn)(const char *function, const char *file, int line, const char *assertion)
629 {
630  auto ci = GetCrashInfoFromAssertion(assertion, file, line, function);
631  PrintCrashInfo(ci);
632  skipAbortSignal = true;
633  __real___assert_rtn(function, file, line, assertion);
634 }
635 #elif WIN32
636 extern "C" void __attribute__((noinline)) WRAPPED_NAME(_assert)(const char *assertion, const char *file, unsigned int line)
637 {
638  auto ci = GetCrashInfoFromAssertion(assertion, file, line, nullptr);
639  PrintCrashInfo(ci);
640  skipAbortSignal = true;
641  __real__assert(assertion, file, line);
642 }
643 extern "C" void __attribute__((noinline)) WRAPPED_NAME(_wassert)(const wchar_t *assertion, const wchar_t *file, unsigned int line)
644 {
645  auto ci = GetCrashInfoFromAssertion(
646  assertion ? std::string(assertion, assertion + wcslen(assertion)).c_str() : nullptr,
647  file ? std::string(file, file + wcslen(file)).c_str() : nullptr,
648  line, nullptr);
649  PrintCrashInfo(ci);
650  skipAbortSignal = true;
651  __real__wassert(assertion, file, line);
652 }
653 #else
654 extern "C" void __attribute__((noinline)) WRAPPED_NAME(__assert_fail)(const char *assertion, const char *file, unsigned int line, const char *function)
655 {
656  auto ci = GetCrashInfoFromAssertion(assertion, file, line, function);
657  PrintCrashInfo(ci);
658  skipAbortSignal = true;
659  __real___assert_fail(assertion, file, line, function);
660 }
661 #endif
662 #endif //ENABLE_CRASH_HOOKS
663 
664 static std::shared_ptr<std::vector<uint64_t>> GetExceptionStacktrace(const std::exception_ptr& e)
665 {
666 #ifdef ENABLE_CRASH_HOOKS
667  void* p = *(void**)&e;
668 
669  std::lock_guard<std::mutex> l(g_stacktraces_mutex);
670  auto it = g_stacktraces.find(p);
671  if (it == g_stacktraces.end()) {
672  return nullptr;
673  }
674  return it->second;
675 #else
676  return {};
677 #endif
678 }
679 
680 crash_info GetCrashInfoFromException(const std::exception_ptr& e)
681 {
682  crash_info ci;
683  ci.crashDescription = "Exception: ";
684 
685  if (!e) {
686  ci.crashDescription += "<null>";
687  return ci;
688  }
689 
690  std::string type;
691  std::string what;
692 
693  auto getExceptionType = [&]() -> std::string {
694  auto type = abi::__cxa_current_exception_type();
695  if (type && (strlen(type->name()) > 0)) {
696  return DemangleSymbol(type->name());
697  }
698  return "<unknown>";
699  };
700 
701  try {
702  // rethrow and catch the exception as there is no other way to reliably cast to the real type (not possible with RTTI)
703  std::rethrow_exception(e);
704  } catch (const std::exception& e) {
705  type = getExceptionType();
706  what = GetExceptionWhat(e);
707  } catch (const std::string& e) {
708  type = getExceptionType();
709  what = GetExceptionWhat(e);
710  } catch (const char* e) {
711  type = getExceptionType();
712  what = GetExceptionWhat(e);
713  } catch (int e) {
714  type = getExceptionType();
715  what = GetExceptionWhat(e);
716  } catch (...) {
717  type = getExceptionType();
718  what = "<unknown>";
719  }
720 
721  ci.crashDescription += strprintf("type=%s, what=\"%s\"", type, what);
722 
723  auto stackframes = GetExceptionStacktrace(e);
724  if (stackframes) {
725  ci.stackframes = *stackframes;
727  }
728 
729  return ci;
730 }
731 
732 std::string GetPrettyExceptionStr(const std::exception_ptr& e)
733 {
735 }
736 
737 static void terminate_handler()
738 {
739  auto exc = std::current_exception();
740 
741  crash_info ci;
742  ci.crashDescription = "std::terminate() called, aborting";
743 
744  if (exc) {
745  auto ci2 = GetCrashInfoFromException(exc);
746  ci.crashDescription = strprintf("std::terminate() called due to unhandled exception\n%s", ci2.crashDescription);
747  ci.stackframes = std::move(ci2.stackframes);
748  ci.stackframeInfos = std::move(ci2.stackframeInfos);
749  } else {
750  ci.crashDescription = "std::terminate() called due unknown reason";
751  ci.stackframes = GetStackFrames(0, 16);
752  }
753 
755 
756  PrintCrashInfo(ci);
757 
758  skipAbortSignal = true;
759  std::abort();
760 }
761 
763 {
764  std::set_terminate(terminate_handler);
765 }
766 
767 #if !WIN32
768 static void HandlePosixSignal(int s)
769 {
770  if (s == SIGABRT && skipAbortSignal) {
771  return;
772  }
773 
774  const char* name = strsignal(s);
775  if (!name) {
776  name = "UNKNOWN";
777  }
778 
779  crash_info ci;
780  ci.crashDescription = strprintf("Posix Signal: %s", name);
781  ci.stackframes = GetStackFrames(0, 16);
783  PrintCrashInfo(ci);
784 
785  // avoid a signal loop
786  skipAbortSignal = true;
787  std::abort();
788 }
789 #else
790 static void DoHandleWindowsException(EXCEPTION_POINTERS * ExceptionInfo)
791 {
792  std::string excType;
793  switch(ExceptionInfo->ExceptionRecord->ExceptionCode)
794  {
795  case EXCEPTION_ACCESS_VIOLATION: excType = "EXCEPTION_ACCESS_VIOLATION"; break;
796  case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: excType = "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"; break;
797  case EXCEPTION_BREAKPOINT: excType = "EXCEPTION_BREAKPOINT"; break;
798  case EXCEPTION_DATATYPE_MISALIGNMENT: excType = "EXCEPTION_DATATYPE_MISALIGNMENT"; break;
799  case EXCEPTION_FLT_DENORMAL_OPERAND: excType = "EXCEPTION_FLT_DENORMAL_OPERAND"; break;
800  case EXCEPTION_FLT_DIVIDE_BY_ZERO: excType = "EXCEPTION_FLT_DIVIDE_BY_ZERO"; break;
801  case EXCEPTION_FLT_INEXACT_RESULT: excType = "EXCEPTION_FLT_INEXACT_RESULT"; break;
802  case EXCEPTION_FLT_INVALID_OPERATION: excType = "EXCEPTION_FLT_INVALID_OPERATION"; break;
803  case EXCEPTION_FLT_OVERFLOW: excType = "EXCEPTION_FLT_OVERFLOW"; break;
804  case EXCEPTION_FLT_STACK_CHECK: excType = "EXCEPTION_FLT_STACK_CHECK"; break;
805  case EXCEPTION_FLT_UNDERFLOW: excType = "EXCEPTION_FLT_UNDERFLOW"; break;
806  case EXCEPTION_ILLEGAL_INSTRUCTION: excType = "EXCEPTION_ILLEGAL_INSTRUCTION"; break;
807  case EXCEPTION_IN_PAGE_ERROR: excType = "EXCEPTION_IN_PAGE_ERROR"; break;
808  case EXCEPTION_INT_DIVIDE_BY_ZERO: excType = "EXCEPTION_INT_DIVIDE_BY_ZERO"; break;
809  case EXCEPTION_INT_OVERFLOW: excType = "EXCEPTION_INT_OVERFLOW"; break;
810  case EXCEPTION_INVALID_DISPOSITION: excType = "EXCEPTION_INVALID_DISPOSITION"; break;
811  case EXCEPTION_NONCONTINUABLE_EXCEPTION: excType = "EXCEPTION_NONCONTINUABLE_EXCEPTION"; break;
812  case EXCEPTION_PRIV_INSTRUCTION: excType = "EXCEPTION_PRIV_INSTRUCTION"; break;
813  case EXCEPTION_SINGLE_STEP: excType = "EXCEPTION_SINGLE_STEP"; break;
814  case EXCEPTION_STACK_OVERFLOW: excType = "EXCEPTION_STACK_OVERFLOW"; break;
815  default: excType = "UNKNOWN"; break;
816  }
817 
818  crash_info ci;
819  ci.crashDescription = strprintf("Windows Exception: %s", excType);
820  ci.stackframes = GetStackFrames(0, 16, ExceptionInfo->ContextRecord);
822 
823  PrintCrashInfo(ci);
824 }
825 
826 LONG WINAPI HandleWindowsException(EXCEPTION_POINTERS * ExceptionInfo)
827 {
828  if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) {
829  // We can't directly do the exception handling in this thread anymore as we need stack space for this
830  // So let's do the handling in another thread
831  // On Wine, the exception handler is not called at all
832  std::thread([&]() {
833  DoHandleWindowsException(ExceptionInfo);
834  }).join();
835  } else {
836  DoHandleWindowsException(ExceptionInfo);
837  }
838  return EXCEPTION_CONTINUE_SEARCH;
839 }
840 #endif
841 
843 {
844 #if WIN32
845  SetUnhandledExceptionFilter(HandleWindowsException);
846 #else
847  const std::vector<int> posix_signals = {
848  // Signals for which the default action is "Core".
849  SIGABRT, // Abort signal from abort(3)
850  SIGBUS, // Bus error (bad memory access)
851  SIGFPE, // Floating point exception
852  SIGILL, // Illegal Instruction
853  SIGIOT, // IOT trap. A synonym for SIGABRT
854  SIGQUIT, // Quit from keyboard
855  SIGSEGV, // Invalid memory reference
856  SIGSYS, // Bad argument to routine (SVr4)
857  SIGTRAP, // Trace/breakpoint trap
858  SIGXCPU, // CPU time limit exceeded (4.2BSD)
859  SIGXFSZ, // File size limit exceeded (4.2BSD)
860 #if __APPLE__
861  SIGEMT, // emulation instruction executed
862 #endif
863  };
864 
865  for (auto s : posix_signals) {
866  struct sigaction sa_segv;
867  sa_segv.sa_handler = HandlePosixSignal;
868  sigemptyset(&sa_segv.sa_mask);
869  sa_segv.sa_flags = 0;
870  sigaction(s, &sa_segv, nullptr);
871  }
872 #endif
873 }
void SerializationOp(Stream &s, Operation ser_action)
std::string magic
static int dl_iterate_callback(struct dl_phdr_info *info, size_t s, void *data)
#define READWRITE(obj)
Definition: serialize.h:165
std::string exeFileName
std::string function
#define strprintf
Definition: tinyformat.h:1066
static __attribute__((noinline)) std
std::string GetCrashInfoStrFromSerializedStr(const std::string &ciStr)
value_type * data()
Definition: streams.h:203
std::vector< uint64_t > stackframes
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:103
static void terminate_handler()
static backtrace_state * GetLibBacktraceState()
void ConvertAddresses(int64_t offset)
static void PrintCrashInfo(const crash_info &ci)
static std::string GetCrashInfoStr(const crash_info &ci, size_t spaces=2)
std::string DemangleSymbol(const std::string &name)
Definition: stacktraces.cpp:51
static std::string GetExeFileName()
std::string filename
std::size_t size_t
Definition: bits.hpp:21
#define LogPrintf(...)
Definition: util.h:203
static uint64_t GetBaseAddress()
size_type size() const
Definition: streams.h:194
static std::string g_exeFileBaseName
const char * name
Definition: rest.cpp:36
std::string crashDescription
std::vector< unsigned char > DecodeBase32(const char *p, bool *pfInvalid)
static int my_backtrace_full_callback(void *data, uintptr_t pc, const char *filename, int lineno, const char *function)
static ssize_t GetExeFileNameImpl(char *buf, size_t bufSize)
Definition: stacktraces.cpp:73
void RegisterPrettyTerminateHander()
static std::vector< stackframe_info > GetStackFrameInfos(const std::vector< uint64_t > &stackframes)
std::string GetPrettyExceptionStr(const std::exception_ptr &e)
void SerializationOp(Stream &s, Operation ser_action)
static std::atomic< bool > skipAbortSignal(false)
void SerializationOp(Stream &s, Operation ser_action)
void * memcpy(void *a, const void *b, size_t c)
static void HandlePosixSignal(int s)
std::string EncodeBase32(const unsigned char *pch, size_t len)
static int count
Definition: tests.c:45
void RegisterPrettySignalHandlers()
static void my_backtrace_error_callback(void *data, const char *msg, int errnum)
static std::shared_ptr< std::vector< uint64_t > > GetExceptionStacktrace(const std::exception_ptr &e)
std::vector< stackframe_info > stackframeInfos
std::string GetExceptionWhat(const T &e)
Definition: stacktraces.h:32
static std::string g_exeFileName
static std::string GetCrashInfoStrNoDebugInfo(crash_info ci)
crash_info GetCrashInfoFromException(const std::exception_ptr &e)
Released under the MIT license