// Copyright (c) 2014-2022 The Dash Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) #include #endif // HAVE_CONFIG_H #include #include #include #include #include #include #include #include #include #include #if defined(WIN32) #include #include #include #else #ifdef ENABLE_STACKTRACES #include #endif #include #include #endif #if !defined(WIN32) #include #if !defined(__APPLE__) #include #endif #endif #if defined(__APPLE__) #include #include #include #include #endif #ifdef ENABLE_STACKTRACES #include #endif #include std::string DemangleSymbol(const std::string& name) { #if defined(__GNUC__) || defined(__clang__) int status = -4; // some arbitrary value to eliminate the compiler warning char* str = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status); if (status != 0) { free(str); return name; } std::string ret = str; free(str); return ret; #else // TODO other platforms/compilers return name; #endif } // set to true when the abort signal should not handled // this is the case when the terminate handler or an assert already handled the exception static std::atomic skipAbortSignal(false); static ssize_t GetExeFileNameImpl(char* buf, size_t bufSize) { #if defined(WIN32) std::vector tmp(bufSize); DWORD len = GetModuleFileName(nullptr, tmp.data(), bufSize); if (len >= bufSize) { return len; } for (size_t i = 0; i < len; i++) { buf[i] = (char)tmp[i]; } return len; #elif defined(__APPLE__) uint32_t bufSize2 = (uint32_t)bufSize; if (_NSGetExecutablePath(buf, &bufSize2) != 0) { // it's not entirely clear if the value returned by _NSGetExecutablePath includes the null character return bufSize2 + 1; } // TODO do we have to call realpath()? The path returned by _NSGetExecutablePath may include ., .. symbolic links return strlen(buf); #else ssize_t len = readlink("/proc/self/exe", buf, bufSize - 1); if (len == -1) { return -1; } return len; #endif } static std::string GetExeFileName() { std::vector buf(1024); while (true) { ssize_t len = GetExeFileNameImpl(buf.data(), buf.size()); if (len < 0) { return ""; } if (len < int64_t(buf.size())) { return std::string(buf.begin(), buf.begin() + len); } buf.resize(buf.size() * 2); } } static std::string g_exeFileName = GetExeFileName(); static std::string g_exeFileBaseName = fs::path(g_exeFileName).filename().string(); #ifdef ENABLE_STACKTRACES static void my_backtrace_error_callback (void *data, const char *msg, int errnum) { } static backtrace_state* GetLibBacktraceState() { #if defined(WIN32) // libbacktrace is not able to handle the DWARF debuglink in the .exe // but luckily we can just specify the .dbg file here as it's a valid PE/XCOFF file static std::string debugFileName = g_exeFileName + ".dbg"; static const char* exeFileNamePtr = fs::exists(debugFileName) ? debugFileName.c_str() : g_exeFileName.c_str(); #else static const char* exeFileNamePtr = g_exeFileName.empty() ? nullptr : g_exeFileName.c_str(); #endif static backtrace_state* st = backtrace_create_state(exeFileNamePtr, 1, my_backtrace_error_callback, nullptr); return st; } #endif // ENABLE_STACKTRACES #if defined(WIN32) static uint64_t GetBaseAddress() { return 0; } // PC addresses returned by StackWalk64 are in the real mapped space, while libbacktrace expects them to be in the // default mapped space starting at 0x400000. This method converts the address. // TODO this is probably the same reason libbacktrace is not able to gather the stacktrace on Windows (returns pointers like 0x1 or 0xfffffff) // If they ever fix this problem, we might end up converting to invalid addresses here static uint64_t ConvertAddress(uint64_t addr) { MEMORY_BASIC_INFORMATION mbi; if (!VirtualQuery((PVOID)addr, &mbi, sizeof(mbi))) return 0; uint64_t hMod = (uint64_t)mbi.AllocationBase; uint64_t offset = addr - hMod; return 0x400000 + offset; } static __attribute__((noinline)) std::vector GetStackFrames(size_t skip, size_t max_frames, const CONTEXT* pContext = nullptr) { #ifdef ENABLE_STACKTRACES // We can't use libbacktrace for stack unwinding on Windows as it returns invalid addresses (like 0x1 or 0xffffffff) static BOOL symInitialized = SymInitialize(GetCurrentProcess(), nullptr, TRUE); // dbghelp is not thread safe static StdMutex m; StdLockGuard l(m); HANDLE process = GetCurrentProcess(); HANDLE thread = GetCurrentThread(); CONTEXT context; if (!pContext) { memset(&context, 0, sizeof(CONTEXT)); context.ContextFlags = CONTEXT_FULL; RtlCaptureContext(&context); } else { memcpy(&context, pContext, sizeof(CONTEXT)); } DWORD image; STACKFRAME64 stackframe; ZeroMemory(&stackframe, sizeof(STACKFRAME64)); #if defined(__i386__) image = IMAGE_FILE_MACHINE_I386; stackframe.AddrPC.Offset = context.Eip; stackframe.AddrPC.Mode = AddrModeFlat; stackframe.AddrFrame.Offset = context.Ebp; stackframe.AddrFrame.Mode = AddrModeFlat; stackframe.AddrStack.Offset = context.Esp; stackframe.AddrStack.Mode = AddrModeFlat; #elif defined(__x86_64__) image = IMAGE_FILE_MACHINE_AMD64; stackframe.AddrPC.Offset = context.Rip; stackframe.AddrPC.Mode = AddrModeFlat; stackframe.AddrFrame.Offset = context.Rbp; stackframe.AddrFrame.Mode = AddrModeFlat; stackframe.AddrStack.Offset = context.Rsp; stackframe.AddrStack.Mode = AddrModeFlat; if (!pContext) { skip++; // skip this method } #else #error unsupported architecture #endif std::vector ret; size_t i = 0; while (ret.size() < max_frames) { BOOL result = StackWalk64( image, process, thread, &stackframe, &context, nullptr, SymFunctionTableAccess64, SymGetModuleBase64, nullptr); if (!result) { break; } if (i >= skip) { uint64_t pc = ConvertAddress(stackframe.AddrPC.Offset); if (pc == 0) { pc = stackframe.AddrPC.Offset; } ret.emplace_back(pc); } i++; } return ret; #else return {}; #endif // ENABLE_STACKTRACES } #else #if defined(__APPLE__) static uint64_t GetBaseAddress() { mach_port_name_t target_task; vm_map_offset_t vmoffset; vm_map_size_t vmsize; uint32_t nesting_depth = 0; struct vm_region_submap_info_64 vbr; mach_msg_type_number_t vbrcount = 16; kern_return_t kr; kr = task_for_pid(mach_task_self(), getpid(), &target_task); if (kr != KERN_SUCCESS) { return 0; } kr = mach_vm_region_recurse(target_task, &vmoffset, &vmsize, &nesting_depth, (vm_region_recurse_info_t)&vbr, &vbrcount); if (kr != KERN_SUCCESS) { return 0; } return vmoffset; } #else static int dl_iterate_callback(struct dl_phdr_info* info, size_t s, void* data) { uint64_t* p = reinterpret_cast(data); if (info->dlpi_name == nullptr || info->dlpi_name[0] == '\0') { *p = info->dlpi_addr; } return 0; } static uint64_t GetBaseAddress() { uint64_t basePtr = 0; dl_iterate_phdr(dl_iterate_callback, &basePtr); return basePtr; } #endif static __attribute__((noinline)) std::vector GetStackFrames(size_t skip, size_t max_frames) { #ifdef ENABLE_STACKTRACES // FYI, this is not using libbacktrace, but "backtrace()" from std::vector buf(max_frames); int count = backtrace(buf.data(), (int)buf.size()); if (count == 0) { return {}; } buf.resize((size_t)count); std::vector ret; ret.reserve(count); for (size_t i = skip + 1; i < buf.size(); i++) { ret.emplace_back((uint64_t) buf[i]); } return ret; #else return {}; #endif // ENABLE_STACKTRACES } #endif struct stackframe_info { uint64_t pc{0}; std::string filename; int lineno{-1}; std::string function; SERIALIZE_METHODS(stackframe_info, obj) { READWRITE(obj.pc, obj.filename, obj.lineno, obj.function); } }; #ifdef ENABLE_STACKTRACES static int my_backtrace_full_callback (void *data, uintptr_t pc, const char *filename, int lineno, const char *function) { auto sis = (std::vector*)data; stackframe_info si; si.pc = pc; si.lineno = lineno; if (filename) { si.filename = filename; } if (function) { si.function = DemangleSymbol(function); } sis->emplace_back(si); if (sis->size() >= 128) { // abort return 1; } if (si.function == "mainCRTStartup" || si.function == "pthread_create_wrapper" || si.function == "__tmainCRTStartup") { // on Windows, stack frames are unwinded into invalid PCs after entry points // this doesn't catch all cases unfortunately return 1; } return 0; } static std::vector GetStackFrameInfos(const std::vector& stackframes) { std::vector infos; infos.reserve(stackframes.size()); for (uint64_t stackframe : stackframes) { if (backtrace_pcinfo(GetLibBacktraceState(), stackframe, my_backtrace_full_callback, my_backtrace_error_callback, &infos)) { break; } } return infos; } #else static std::vector GetStackFrameInfos(const std::vector& stackframes) { return {}; } #endif // ENABLE_STACKTRACES struct crash_info_header { std::string magic; uint16_t version{0}; std::string exeFileName; SERIALIZE_METHODS(crash_info_header, obj) { READWRITE(obj.magic, obj.version, obj.exeFileName); } }; struct crash_info { std::string crashDescription; std::vector stackframes; std::vector stackframeInfos; SERIALIZE_METHODS(crash_info, obj) { READWRITE(obj.crashDescription, obj.stackframes, obj.stackframeInfos); } void ConvertAddresses(int64_t offset) { for (auto& sf : stackframes) { sf += offset; } for (auto& sfi : stackframeInfos) { sfi.pc += offset; } } }; static std::string GetCrashInfoStrNoDebugInfo(crash_info ci) { static uint64_t basePtr = GetBaseAddress(); CDataStream ds(SER_DISK, 0); crash_info_header hdr; hdr.magic = "DashCrashInfo"; hdr.version = 1; hdr.exeFileName = g_exeFileBaseName; ds << hdr; ci.ConvertAddresses(-(int64_t)basePtr); ds << ci; auto ciStr = EncodeBase32(ds.str()); std::string s = ci.crashDescription + "\n"; s += strprintf("No debug information available for stacktrace. You should add debug information and then run:\n" "%s -printcrashinfo=%s\n", g_exeFileBaseName, ciStr); return s; } static std::string GetCrashInfoStr(const crash_info& ci, size_t spaces = 2); std::string GetCrashInfoStrFromSerializedStr(const std::string& ciStr) { static uint64_t basePtr = GetBaseAddress(); bool dataInvalid = false; auto buf = DecodeBase32(ciStr.c_str(), &dataInvalid); if (buf.empty() || dataInvalid) { return "Error while deserializing crash info"; } CDataStream ds(buf, SER_DISK, 0); crash_info_header hdr; try { ds >> hdr; } catch (...) { return "Error while deserializing crash info header"; } if (hdr.magic != "DashCrashInfo") { return "Invalid magic string"; } if (hdr.version != 1) { return "Unsupported version"; } if (hdr.exeFileName != g_exeFileBaseName) { return "Crash info is not for this executable"; } crash_info ci; try { ds >> ci; } catch (...) { return "Error while deserializing crash info"; } ci.ConvertAddresses(basePtr); if (ci.stackframeInfos.empty()) { std::vector stackframes(ci.stackframes.begin(), ci.stackframes.end()); ci.stackframeInfos = GetStackFrameInfos(stackframes); } return GetCrashInfoStr(ci); } static std::string GetCrashInfoStr(const crash_info& ci, size_t spaces) { if (ci.stackframeInfos.empty()) { return GetCrashInfoStrNoDebugInfo(ci); } std::string sp; for (size_t i = 0; i < spaces; i++) { sp += " "; } std::vector lstrs; lstrs.reserve(ci.stackframeInfos.size()); for (const auto& si : ci.stackframeInfos) { std::string lstr; if (!si.filename.empty()) { lstr += fs::path(si.filename).filename().string(); } else { lstr += ""; } if (si.lineno != 0) { lstr += strprintf(":%d", si.lineno); } lstrs.emplace_back(lstr); } // get max "filename:line" length so we can better format it size_t lstrlen = std::max_element(lstrs.begin(), lstrs.end(), [](const std::string& a, const std::string& b) { return a.size() < b.size(); })->size(); std::string fmtStr = strprintf("%%2d#: (0x%%08X) %%-%ds - %%s\n", lstrlen); std::string s = ci.crashDescription + "\n"; for (size_t i = 0; i < ci.stackframeInfos.size(); i++) { auto& si = ci.stackframeInfos[i]; auto& lstr = lstrs[i]; std::string fstr; if (!si.function.empty()) { fstr = si.function; } else { fstr = "???"; } std::string s2 = strprintf(fmtStr, i, si.pc, lstr, fstr); s += sp; s += s2; } return s; } static void PrintCrashInfo(const crash_info& ci) { auto str = GetCrashInfoStr(ci); LogPrintf("%s", str); /* Continued */ tfm::format(std::cerr, "%s", str); fflush(stderr); } #ifdef ENABLE_CRASH_HOOKS static StdMutex g_stacktraces_mutex; static std::map>> g_stacktraces; #ifdef CRASH_HOOKS_WRAPPED_CXX_ABI // These come in through -Wl,-wrap extern "C" void* __real___cxa_allocate_exception(size_t thrown_size); extern "C" void __real___cxa_free_exception(void * thrown_exception); #if defined(WIN32) extern "C" void __real__assert(const char *assertion, const char *file, unsigned int line); extern "C" void __real__wassert(const wchar_t *assertion, const wchar_t *file, unsigned int line); #else extern "C" void __real___assert_fail(const char *assertion, const char *file, unsigned int line, const char *function); #endif #else // Clang does not support -Wl,-wrap, so we must use dlsym // This is ok because at the same time Clang only supports dynamic linking to libc/libc++ extern "C" void* __real___cxa_allocate_exception(size_t thrown_size) { static auto f = (void*(*)(size_t))dlsym(RTLD_NEXT, "__cxa_allocate_exception"); return f(thrown_size); } extern "C" void __real___cxa_free_exception(void * thrown_exception) { static auto f = (void(*)(void*))dlsym(RTLD_NEXT, "__cxa_free_exception"); return f(thrown_exception); } #if defined(__clang__) && defined(__APPLE__) extern "C" void __attribute__((noreturn)) __real___assert_rtn(const char *function, const char *file, int line, const char *assertion) { static auto f = (void(__attribute__((noreturn)) *) (const char*, const char*, int, const char*))dlsym(RTLD_NEXT, "__assert_rtn"); f(function, file, line, assertion); } #elif defined(WIN32) #error not supported on WIN32 (no dlsym support) #else extern "C" void __real___assert_fail(const char *assertion, const char *file, unsigned int line, const char *function) { static auto f = (void(*)(const char*, const char*, unsigned int, const char*))dlsym(RTLD_NEXT, "__assert_fail"); f(assertion, file, line, function); } #endif #endif #ifdef CRASH_HOOKS_WRAPPED_CXX_ABI #define WRAPPED_NAME(x) __wrap_##x #else #define WRAPPED_NAME(x) x #endif extern "C" void* __attribute__((noinline)) WRAPPED_NAME(__cxa_allocate_exception)(size_t thrown_size) { // grab the current stack trace and store it in the global exception->stacktrace map auto localSt = GetStackFrames(1, 16); // WARNING keep this as it is and don't try to optimize it (no std::move, no std::make_shared, ...) // trying to optimize this will cause the optimizer to move the GetStackFrames() call deep into the stl libs std::shared_ptr> st(new std::vector(localSt)); void* p = __real___cxa_allocate_exception(thrown_size); StdLockGuard l(g_stacktraces_mutex); g_stacktraces.emplace(p, st); return p; } extern "C" void __attribute__((noinline)) WRAPPED_NAME(__cxa_free_exception)(void * thrown_exception) { __real___cxa_free_exception(thrown_exception); StdLockGuard l(g_stacktraces_mutex); g_stacktraces.erase(thrown_exception); } static __attribute__((noinline)) crash_info GetCrashInfoFromAssertion(const char* assertion, const char* file, int line, const char* function) { crash_info ci; ci.stackframes = GetStackFrames(1, 16); ci.crashDescription = "Assertion failure:"; if (assertion) { ci.crashDescription += strprintf("\n assertion: %s", assertion); } if (file) { ci.crashDescription += strprintf("\n file: %s, line: %d", file, line); } if (function) { ci.crashDescription += strprintf("\n function: %s", function); } ci.stackframeInfos = GetStackFrameInfos(ci.stackframes); return ci; } #if defined(__clang__) && defined(__APPLE__) extern "C" void __attribute__((noinline)) WRAPPED_NAME(__assert_rtn)(const char *function, const char *file, int line, const char *assertion) { auto ci = GetCrashInfoFromAssertion(assertion, file, line, function); PrintCrashInfo(ci); skipAbortSignal = true; __real___assert_rtn(function, file, line, assertion); } #elif defined(WIN32) extern "C" void __attribute__((noinline)) WRAPPED_NAME(_assert)(const char *assertion, const char *file, unsigned int line) { auto ci = GetCrashInfoFromAssertion(assertion, file, line, nullptr); PrintCrashInfo(ci); skipAbortSignal = true; __real__assert(assertion, file, line); } extern "C" void __attribute__((noinline)) WRAPPED_NAME(_wassert)(const wchar_t *assertion, const wchar_t *file, unsigned int line) { auto ci = GetCrashInfoFromAssertion( assertion ? std::string(assertion, assertion + wcslen(assertion)).c_str() : nullptr, file ? std::string(file, file + wcslen(file)).c_str() : nullptr, line, nullptr); PrintCrashInfo(ci); skipAbortSignal = true; __real__wassert(assertion, file, line); } #else extern "C" void __attribute__((noinline)) WRAPPED_NAME(__assert_fail)(const char *assertion, const char *file, unsigned int line, const char *function) { auto ci = GetCrashInfoFromAssertion(assertion, file, line, function); PrintCrashInfo(ci); skipAbortSignal = true; __real___assert_fail(assertion, file, line, function); } #endif #endif //ENABLE_CRASH_HOOKS static std::shared_ptr> GetExceptionStacktrace(const std::exception_ptr& e) { #ifdef ENABLE_CRASH_HOOKS void* p = *(void**)&e; StdLockGuard l(g_stacktraces_mutex); auto it = g_stacktraces.find(p); if (it == g_stacktraces.end()) { return nullptr; } return it->second; #else return {}; #endif } crash_info GetCrashInfoFromException(const std::exception_ptr& e) { crash_info ci; ci.crashDescription = "Exception: "; if (!e) { ci.crashDescription += ""; return ci; } std::string type; std::string what; auto getExceptionType = [&]() -> std::string { auto type = abi::__cxa_current_exception_type(); if (type && (strlen(type->name()) > 0)) { return DemangleSymbol(type->name()); } return ""; }; try { // rethrow and catch the exception as there is no other way to reliably cast to the real type (not possible with RTTI) std::rethrow_exception(e); } catch (const std::exception& e2) { type = getExceptionType(); what = GetExceptionWhat(e2); } catch (const std::string& e2) { type = getExceptionType(); what = GetExceptionWhat(e2); } catch (const char* e2) { type = getExceptionType(); what = GetExceptionWhat(e2); } catch (int e2) { type = getExceptionType(); what = GetExceptionWhat(e2); } catch (...) { type = getExceptionType(); what = ""; } ci.crashDescription += strprintf("type=%s, what=\"%s\"", type, what); auto stackframes = GetExceptionStacktrace(e); if (stackframes) { ci.stackframes = *stackframes; ci.stackframeInfos = GetStackFrameInfos(ci.stackframes); } return ci; } std::string GetPrettyExceptionStr(const std::exception_ptr& e) { return GetCrashInfoStr(GetCrashInfoFromException(e)); } static void terminate_handler() { auto exc = std::current_exception(); crash_info ci; if (exc) { auto ci2 = GetCrashInfoFromException(exc); ci.crashDescription = strprintf("std::terminate() called due to unhandled exception\n%s", ci2.crashDescription); ci.stackframes = std::move(ci2.stackframes); ci.stackframeInfos = std::move(ci2.stackframeInfos); } else { ci.crashDescription = "std::terminate() called due unknown reason"; ci.stackframes = GetStackFrames(0, 16); ci.stackframeInfos = GetStackFrameInfos(ci.stackframes); } PrintCrashInfo(ci); skipAbortSignal = true; std::abort(); } void RegisterPrettyTerminateHander() { std::set_terminate(terminate_handler); } #if !defined(WIN32) static void HandlePosixSignal(int s) { if (s == SIGABRT && skipAbortSignal) { return; } const char* name = strsignal(s); if (!name) { name = "UNKNOWN"; } crash_info ci; ci.crashDescription = strprintf("Posix Signal: %s", name); ci.stackframes = GetStackFrames(0, 16); ci.stackframeInfos = GetStackFrameInfos(ci.stackframes); PrintCrashInfo(ci); // avoid a signal loop skipAbortSignal = true; std::abort(); } #else static void DoHandleWindowsException(EXCEPTION_POINTERS * ExceptionInfo) { std::string excType; switch(ExceptionInfo->ExceptionRecord->ExceptionCode) { case EXCEPTION_ACCESS_VIOLATION: excType = "EXCEPTION_ACCESS_VIOLATION"; break; case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: excType = "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"; break; case EXCEPTION_BREAKPOINT: excType = "EXCEPTION_BREAKPOINT"; break; case EXCEPTION_DATATYPE_MISALIGNMENT: excType = "EXCEPTION_DATATYPE_MISALIGNMENT"; break; case EXCEPTION_FLT_DENORMAL_OPERAND: excType = "EXCEPTION_FLT_DENORMAL_OPERAND"; break; case EXCEPTION_FLT_DIVIDE_BY_ZERO: excType = "EXCEPTION_FLT_DIVIDE_BY_ZERO"; break; case EXCEPTION_FLT_INEXACT_RESULT: excType = "EXCEPTION_FLT_INEXACT_RESULT"; break; case EXCEPTION_FLT_INVALID_OPERATION: excType = "EXCEPTION_FLT_INVALID_OPERATION"; break; case EXCEPTION_FLT_OVERFLOW: excType = "EXCEPTION_FLT_OVERFLOW"; break; case EXCEPTION_FLT_STACK_CHECK: excType = "EXCEPTION_FLT_STACK_CHECK"; break; case EXCEPTION_FLT_UNDERFLOW: excType = "EXCEPTION_FLT_UNDERFLOW"; break; case EXCEPTION_ILLEGAL_INSTRUCTION: excType = "EXCEPTION_ILLEGAL_INSTRUCTION"; break; case EXCEPTION_IN_PAGE_ERROR: excType = "EXCEPTION_IN_PAGE_ERROR"; break; case EXCEPTION_INT_DIVIDE_BY_ZERO: excType = "EXCEPTION_INT_DIVIDE_BY_ZERO"; break; case EXCEPTION_INT_OVERFLOW: excType = "EXCEPTION_INT_OVERFLOW"; break; case EXCEPTION_INVALID_DISPOSITION: excType = "EXCEPTION_INVALID_DISPOSITION"; break; case EXCEPTION_NONCONTINUABLE_EXCEPTION: excType = "EXCEPTION_NONCONTINUABLE_EXCEPTION"; break; case EXCEPTION_PRIV_INSTRUCTION: excType = "EXCEPTION_PRIV_INSTRUCTION"; break; case EXCEPTION_SINGLE_STEP: excType = "EXCEPTION_SINGLE_STEP"; break; case EXCEPTION_STACK_OVERFLOW: excType = "EXCEPTION_STACK_OVERFLOW"; break; default: excType = "UNKNOWN"; break; } crash_info ci; ci.crashDescription = strprintf("Windows Exception: %s", excType); ci.stackframes = GetStackFrames(0, 16, ExceptionInfo->ContextRecord); ci.stackframeInfos = GetStackFrameInfos(ci.stackframes); PrintCrashInfo(ci); } LONG WINAPI HandleWindowsException(EXCEPTION_POINTERS * ExceptionInfo) { if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) { // We can't directly do the exception handling in this thread anymore as we need stack space for this // So let's do the handling in another thread // On Wine, the exception handler is not called at all std::thread([&]() { DoHandleWindowsException(ExceptionInfo); }).join(); } else { DoHandleWindowsException(ExceptionInfo); } return EXCEPTION_CONTINUE_SEARCH; } #endif void RegisterPrettySignalHandlers() { #if defined(WIN32) SetUnhandledExceptionFilter(HandleWindowsException); #else const std::vector posix_signals = { // Signals for which the default action is "Core". SIGABRT, // Abort signal from abort(3) SIGBUS, // Bus error (bad memory access) SIGFPE, // Floating point exception SIGILL, // Illegal Instruction SIGIOT, // IOT trap. A synonym for SIGABRT SIGQUIT, // Quit from keyboard SIGSEGV, // Invalid memory reference SIGSYS, // Bad argument to routine (SVr4) SIGTRAP, // Trace/breakpoint trap SIGXCPU, // CPU time limit exceeded (4.2BSD) SIGXFSZ, // File size limit exceeded (4.2BSD) #if defined(__APPLE__) SIGEMT, // emulation instruction executed #endif }; for (auto s : posix_signals) { struct sigaction sa_segv; sa_segv.sa_handler = HandlePosixSignal; sigemptyset(&sa_segv.sa_mask); sa_segv.sa_flags = 0; sigaction(s, &sa_segv, nullptr); } #endif }