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 }