LCOV - code coverage report
Current view: top level - src - stacktraces.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 26 239 10.9 %
Date: 2026-06-25 07:23:43 Functions: 6 59 10.2 %

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

Generated by: LCOV version 1.16