File indexing completed on 2024-05-12 05:26:20

0001 /*
0002  *   Copyright (C) 2016 The Qt Company Ltd.
0003  *   Copyright (C) 2016 Intel Corporation.
0004  *   Copyright (C) 2019 Christian Mollekopf <mollekopf@kolabsys.com>
0005  *
0006  *   This program is free software; you can redistribute it and/or modify
0007  *   it under the terms of the GNU General Public License as published by
0008  *   the Free Software Foundation; either version 2 of the License, or
0009  *   (at your option) any later version.
0010  *
0011  *   This program is distributed in the hope that it will be useful,
0012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014  *   GNU General Public License for more details.
0015  *
0016  *   You should have received a copy of the GNU General Public License
0017  *   along with this program; if not, write to the
0018  *   Free Software Foundation, Inc.,
0019  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
0020  */
0021 #include "backtrace.h"
0022 
0023 #include <QtGlobal> //For the OS ifdefs
0024 #include <signal.h>
0025 #include <csignal>
0026 #include <iostream>
0027 #include <cstdlib>
0028 #include <ostream>
0029 #include <sstream>
0030 #include <thread>
0031 #include <chrono>
0032 
0033 #if defined(Q_OS_WIN)
0034 #include <io.h>
0035 #include <process.h>
0036 #include <windows.h>
0037 # if !defined(Q_CC_MINGW) || (defined(Q_CC_MINGW) && defined(__MINGW64_VERSION_MAJOR))
0038 #include <crtdbg.h>
0039 # endif
0040 #else //Linux/Mac/...
0041 #include <unistd.h>
0042 # if defined(__GLIBC__) || defined(Q_OS_DARWIN) //musl lacks backtrace/backtrace_symbols
0043 #include <execinfo.h>
0044 #include <cxxabi.h>
0045 #include <dlfcn.h>
0046 # endif
0047 #endif
0048 
0049 #include "listener.h"
0050 
0051 using namespace Sink;
0052 
0053 #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
0054 // Helper class for resolving symbol names by dynamically loading "dbghelp.dll".
0055 class DebugSymbolResolver
0056 {
0057     Q_DISABLE_COPY(DebugSymbolResolver)
0058 public:
0059     struct Symbol {
0060         Symbol() : name(Q_NULLPTR), address(0) {}
0061 
0062         const char *name; // Must be freed by caller.
0063         DWORD64 address;
0064     };
0065 
0066     explicit DebugSymbolResolver(HANDLE process)
0067         : m_process(process), m_dbgHelpLib(0), m_symFromAddr(Q_NULLPTR)
0068     {
0069         bool success = false;
0070         m_dbgHelpLib = LoadLibraryW(L"dbghelp.dll");
0071         if (m_dbgHelpLib) {
0072             SymInitializeType symInitialize = (SymInitializeType)(GetProcAddress(m_dbgHelpLib, "SymInitialize"));
0073             m_symFromAddr = (SymFromAddrType)(GetProcAddress(m_dbgHelpLib, "SymFromAddr"));
0074             success = symInitialize && m_symFromAddr && symInitialize(process, NULL, TRUE);
0075         }
0076         if (!success) {
0077             cleanup();
0078         }
0079     }
0080 
0081 
0082     ~DebugSymbolResolver() { cleanup(); }
0083 
0084     bool isValid() const { return m_symFromAddr; }
0085 
0086     Symbol resolveSymbol(DWORD64 address) const {
0087         // reserve additional buffer where SymFromAddr() will store the name
0088         struct NamedSymbolInfo : public DBGHELP_SYMBOL_INFO {
0089             enum { symbolNameLength = 255 };
0090 
0091             char name[symbolNameLength + 1];
0092         };
0093 
0094         Symbol result;
0095         if (!isValid())
0096             return result;
0097         NamedSymbolInfo symbolBuffer;
0098         memset(&symbolBuffer, 0, sizeof(NamedSymbolInfo));
0099         symbolBuffer.MaxNameLen = NamedSymbolInfo::symbolNameLength;
0100         symbolBuffer.SizeOfStruct = sizeof(DBGHELP_SYMBOL_INFO);
0101         if (!m_symFromAddr(m_process, address, 0, &symbolBuffer))
0102             return result;
0103         result.name = qstrdup(symbolBuffer.Name);
0104         result.address = symbolBuffer.Address;
0105         return result;
0106     }
0107 
0108 private:
0109     // typedefs from DbgHelp.h/.dll
0110     struct DBGHELP_SYMBOL_INFO { // SYMBOL_INFO
0111         ULONG       SizeOfStruct;
0112         ULONG       TypeIndex;        // Type Index of symbol
0113         ULONG64     Reserved[2];
0114         ULONG       Index;
0115         ULONG       Size;
0116         ULONG64     ModBase;          // Base Address of module comtaining this symbol
0117         ULONG       Flags;
0118         ULONG64     Value;            // Value of symbol, ValuePresent should be 1
0119         ULONG64     Address;          // Address of symbol including base address of module
0120         ULONG       Register;         // register holding value or pointer to value
0121         ULONG       Scope;            // scope of the symbol
0122         ULONG       Tag;              // pdb classification
0123         ULONG       NameLen;          // Actual length of name
0124         ULONG       MaxNameLen;
0125         CHAR        Name[1];          // Name of symbol
0126     };
0127 
0128     typedef BOOL (__stdcall *SymInitializeType)(HANDLE, PCSTR, BOOL);
0129     typedef BOOL (__stdcall *SymFromAddrType)(HANDLE, DWORD64, PDWORD64, DBGHELP_SYMBOL_INFO *);
0130 
0131     void cleanup() {
0132         if (m_dbgHelpLib) {
0133             FreeLibrary(m_dbgHelpLib);
0134         }
0135         m_dbgHelpLib = 0;
0136         m_symFromAddr = Q_NULLPTR;
0137     }
0138 
0139     const HANDLE m_process;
0140     HMODULE m_dbgHelpLib;
0141     SymFromAddrType m_symFromAddr;
0142 };
0143 #endif
0144 
0145 //Print a demangled stacktrace
0146 static void printStacktrace()
0147 {
0148 #if defined(__GLIBC__) || defined(Q_OS_DARWIN)
0149     int skip = 1;
0150     void *callstack[128];
0151     const int nMaxFrames = sizeof(callstack) / sizeof(callstack[0]);
0152     char buf[1024];
0153     int nFrames = backtrace(callstack, nMaxFrames);
0154     char **symbols = backtrace_symbols(callstack, nFrames);
0155 
0156     std::ostringstream trace_buf;
0157     for (int i = skip; i < nFrames; i++) {
0158         // printf("%s\n", symbols[i]);
0159         Dl_info info;
0160         if (dladdr(callstack[i], &info) && info.dli_sname) {
0161             char *demangled = NULL;
0162             int status = -1;
0163             if (info.dli_sname[0] == '_') {
0164                 demangled = abi::__cxa_demangle(info.dli_sname, NULL, 0, &status);
0165             }
0166             snprintf(buf, sizeof(buf), "%-3d %*p %s + %zd\n",
0167                      i, int(2 + sizeof(void*) * 2), callstack[i],
0168                      status == 0 ? demangled :
0169                      info.dli_sname == 0 ? symbols[i] : info.dli_sname,
0170                      (char *)callstack[i] - (char *)info.dli_saddr);
0171             free(demangled);
0172         } else {
0173             snprintf(buf, sizeof(buf), "%-3d %*p %s\n",
0174                      i, int(2 + sizeof(void*) * 2), callstack[i], symbols[i]);
0175         }
0176         trace_buf << buf;
0177     }
0178     free(symbols);
0179     if (nFrames == nMaxFrames) {
0180         trace_buf << "[truncated]\n";
0181     }
0182     std::cerr << trace_buf.str();
0183 #elif defined(Q_OS_WIN)
0184     enum { maxStackFrames = 100 };
0185     DebugSymbolResolver resolver(GetCurrentProcess());
0186     if (resolver.isValid()) {
0187         void *stack[maxStackFrames];
0188         fputs("\nStack:\n", stdout);
0189         const unsigned frameCount = CaptureStackBackTrace(0, DWORD(maxStackFrames), stack, NULL);
0190         for (unsigned f = 0; f < frameCount; ++f)     {
0191             DebugSymbolResolver::Symbol symbol = resolver.resolveSymbol(DWORD64(stack[f]));
0192             if (symbol.name) {
0193                 printf("#%3u: %s() - 0x%p\n", f + 1, symbol.name, (const void *)symbol.address);
0194                 delete [] symbol.name;
0195             } else {
0196                 printf("#%3u: Unable to obtain symbol\n", f + 1);
0197             }
0198         }
0199     }
0200 
0201     fputc('\n', stdout);
0202     fflush(stdout);
0203 
0204 #endif
0205 }
0206 
0207 #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
0208 static LONG WINAPI windowsFaultHandler(struct _EXCEPTION_POINTERS *exInfo)
0209 {
0210     char appName[MAX_PATH];
0211     if (!GetModuleFileNameA(NULL, appName, MAX_PATH)) {
0212         appName[0] = 0;
0213     }
0214     const void *exceptionAddress = exInfo->ExceptionRecord->ExceptionAddress;
0215     printf("A crash occurred in %s.\n"
0216            "Exception address: 0x%p\n"
0217            "Exception code   : 0x%lx\n",
0218            appName, exceptionAddress, exInfo->ExceptionRecord->ExceptionCode);
0219 
0220     DebugSymbolResolver resolver(GetCurrentProcess());
0221     if (resolver.isValid()) {
0222         DebugSymbolResolver::Symbol exceptionSymbol = resolver.resolveSymbol(DWORD64(exceptionAddress));
0223         if (exceptionSymbol.name) {
0224             printf("Nearby symbol    : %s\n", exceptionSymbol.name);
0225             delete [] exceptionSymbol.name;
0226         }
0227     }
0228 
0229     printStacktrace();
0230     return EXCEPTION_EXECUTE_HANDLER;
0231 }
0232 #endif // Q_OS_WIN) && !Q_OS_WINRT
0233 
0234 
0235 
0236 static int sCounter = 0;
0237 static Listener *sListener = nullptr;
0238 
0239 static void crashHandler(int signal)
0240 {
0241     //Guard against crashing in here
0242     if (sCounter > 1) {
0243         std::_Exit(EXIT_FAILURE);
0244     }
0245     sCounter++;
0246 
0247     if (signal == SIGABRT) {
0248         std::cerr << "SIGABRT received\n";
0249     } else if (signal == SIGSEGV) {
0250         std::cerr << "SIGSEV received\n";
0251     } else {
0252         std::cerr << "Unexpected signal " << signal << " received\n";
0253     }
0254 
0255     printStacktrace();
0256 
0257     //Get the word out that we're going down
0258     if (sListener) {
0259         sListener->emergencyAbortAllConnections();
0260     }
0261 
0262     std::fprintf(stdout, "Sleeping for 10s to attach a debugger: gdb attach %i\n", getpid());
0263     std::this_thread::sleep_for(std::chrono::seconds(10));
0264 
0265     // std::system("exec gdb -p \"$PPID\" -ex \"thread apply all bt\"");
0266     // This only works if we actually have xterm and X11 available
0267     // std::system("exec xterm -e gdb -p \"$PPID\"");
0268 
0269     std::_Exit(EXIT_FAILURE);
0270 }
0271 
0272 static void terminateHandler()
0273 {
0274     std::exception_ptr exptr = std::current_exception();
0275     if (exptr != 0)
0276     {
0277         // the only useful feature of std::exception_ptr is that it can be rethrown...
0278         try {
0279             std::rethrow_exception(exptr);
0280         } catch (std::exception &ex) {
0281             std::fprintf(stderr, "Terminated due to exception: %s\n", ex.what());
0282         } catch (...) {
0283             std::fprintf(stderr, "Terminated due to unknown exception\n");
0284         }
0285     } else {
0286         std::fprintf(stderr, "Terminated due to unknown reason :(\n");
0287     }
0288     std::abort();
0289 }
0290 
0291 void Sink::setListener(Listener *listener)
0292 {
0293     sListener = listener;
0294 }
0295 
0296 void Sink::installCrashHandler()
0297 {
0298 #ifndef Q_OS_WIN
0299     std::signal(SIGSEGV, crashHandler);
0300     std::signal(SIGABRT, crashHandler);
0301     std::set_terminate(terminateHandler);
0302 #else
0303 # ifndef Q_CC_MINGW
0304     _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
0305 # endif
0306 # ifndef Q_OS_WINRT
0307     SetErrorMode(SetErrorMode(0) | SEM_NOGPFAULTERRORBOX);
0308     SetUnhandledExceptionFilter(windowsFaultHandler);
0309 # endif
0310 #endif
0311 }