remote/CrashHandler.cpp (199 lines of code) (raw):

#include "CrashHandler.h" #include "log/Log.h" #ifdef WIN32 #include <windows.h> #include <dbghelp.h> #include <fstream> #include <iomanip> #include <iostream> void CrashLog(const std::string& message, std::ofstream * logFile = nullptr) { std::cerr << message << std::endl; if (!Log::isStdStreamLogger()) Log::error(message.c_str()); if (logFile != nullptr && logFile->is_open()) { *logFile << message << std::endl; logFile->flush(); } } void PrintStackTrace(std::stringstream & os, EXCEPTION_POINTERS* pExceptionPtrs = nullptr) { HANDLE hProcess = GetCurrentProcess(); HANDLE hThread = GetCurrentThread(); CONTEXT context; if (!pExceptionPtrs) // Always available during crash handling return; // Use exception context — this is reliable and always available context = *pExceptionPtrs->ContextRecord; SymInitialize(hProcess, NULL, TRUE); STACKFRAME64 stackFrame = {}; DWORD imageType = 0; #ifdef _M_X64 imageType = 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; #elif _M_ARM64 imageType = IMAGE_FILE_MACHINE_ARM64; stackFrame.AddrPC.Offset = context.Pc; stackFrame.AddrPC.Mode = AddrModeFlat; stackFrame.AddrFrame.Offset = context.Fp; stackFrame.AddrFrame.Mode = AddrModeFlat; stackFrame.AddrStack.Offset = context.Sp; stackFrame.AddrStack.Mode = AddrModeFlat; #else #error "Unsupported architecture" #endif os << "Stack trace:\n"; for (int frame = 0; frame < 64; frame++) { BOOL result = StackWalk64( imageType, hProcess, hThread, &stackFrame, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL ); if (!result) break; DWORD64 address = stackFrame.AddrPC.Offset; if (address == 0) break; // Get symbol info char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer; symbol->SizeOfStruct = sizeof(SYMBOL_INFO); symbol->MaxNameLen = MAX_SYM_NAME; DWORD64 displacement = 0; if (SymFromAddr(hProcess, address, &displacement, symbol)) { os << " [" << std::setw(2) << frame << "] " << symbol->Name << " + 0x" << std::hex << displacement << " (0x" << address << ")" << std::dec << std::endl; } else { os << " [" << std::setw(2) << frame << "] 0x" << std::hex << address << std::dec << std::endl; } // Get source file and line info IMAGEHLP_LINE64 line = {}; line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); DWORD lineDisplacement = 0; if (SymGetLineFromAddr64(hProcess, address, &lineDisplacement, &line)) { os << " File: " << line.FileName << ", Line: " << line.LineNumber << std::endl; } } SymCleanup(hProcess); } std::string GetExceptionDesc(DWORD exceptionCode) { switch (exceptionCode) { case EXCEPTION_ACCESS_VIOLATION: return "Access violation"; case EXCEPTION_DATATYPE_MISALIGNMENT: return "Datatype misalignment"; case EXCEPTION_BREAKPOINT: return "Breakpoint"; case EXCEPTION_SINGLE_STEP: return "Single step"; case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "Array bounds exceeded"; case EXCEPTION_FLT_DENORMAL_OPERAND: return "Floating point denormal operand"; case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "Floating point divide by zero"; case EXCEPTION_FLT_INEXACT_RESULT: return "Floating point inexact result"; case EXCEPTION_FLT_INVALID_OPERATION: return "Floating point invalid operation"; case EXCEPTION_FLT_OVERFLOW: return "Floating point overflow"; case EXCEPTION_FLT_STACK_CHECK: return "Floating point stack check"; case EXCEPTION_FLT_UNDERFLOW: return "Floating point underflow"; case EXCEPTION_INT_DIVIDE_BY_ZERO: return "Integer divide by zero"; case EXCEPTION_INT_OVERFLOW: return "Integer overflow"; case EXCEPTION_PRIV_INSTRUCTION: return "Privileged instruction"; case EXCEPTION_IN_PAGE_ERROR: return "In page error"; case EXCEPTION_ILLEGAL_INSTRUCTION: return "Illegal instruction"; case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "Noncontinuable exception"; case EXCEPTION_STACK_OVERFLOW: return "Stack overflow"; case EXCEPTION_INVALID_DISPOSITION: return "Invalid disposition"; case EXCEPTION_GUARD_PAGE: return "Guard page violation"; case EXCEPTION_INVALID_HANDLE: return "Invalid handle"; default: return "Exception Code: " + std::to_string(exceptionCode); } } LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS* pExceptionPtrs) { const DWORD exceptionCode = pExceptionPtrs->ExceptionRecord->ExceptionCode; if (exceptionCode == EXCEPTION_BREAKPOINT) return EXCEPTION_CONTINUE_EXECUTION; std::ofstream logFile; std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); char buf[64] = {0}; std::strftime(buf, sizeof(buf), "%Y-%m-%d_%H-%M-%S", std::localtime(&now)); logFile.open("crash_stacktrace_" + std::string(buf) + ".txt", std::ios::app); CrashLog("*** APPLICATION CRASHED ***\n", &logFile); CrashLog("Exception: " + GetExceptionDesc(exceptionCode), &logFile); std::stringstream os; PrintStackTrace(os, pExceptionPtrs); CrashLog(os.str(), &logFile); return EXCEPTION_EXECUTE_HANDLER; // Terminate the application } void setupCrashHandler() { Log::info("Enable debug privileges and SetUnhandledExceptionFilter."); // optional, for better symbol resolution HANDLE hToken; if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { TOKEN_PRIVILEGES tp; LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid); tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL); CloseHandle(hToken); } // Setup crash handler LPTOP_LEVEL_EXCEPTION_FILTER prevHandler = SetUnhandledExceptionFilter(ExceptionHandler); if (prevHandler != nullptr) Log::info("Previous unhandled exception filter was %p.", prevHandler); } #else // WIN32 #include <execinfo.h> #include <signal.h> #include <stdlib.h> #include <unistd.h> void signalHandler(int signum) { Log::error("Received SIGNAL %d.", signum); void *array[64]; size_t size = backtrace(array, 64); // get void*'s for all entries on the stack // print out all the frames to stderr backtrace_symbols_fd(array, size, STDERR_FILENO); Log::error("Stacktrace (size %d) was printed to stderr.", size); if (!Log::isStdStreamLogger()) { char** symbols = backtrace_symbols(array, size); if (symbols == NULL) { Log::error("Can't get stacktrace: backtrace_symbols returns null."); } else { Log::error("Stack trace:"); for (int i = 0; i < size; i++) Log::error("%s", symbols[i]); free(symbols); } } std::exit(signum); } void setupCrashHandler() { Log::info("Install SIGNAL handlers."); signal(SIGSEGV, signalHandler); signal(SIGABRT, signalHandler); signal(SIGFPE, signalHandler); signal(SIGILL, signalHandler); signal(SIGBUS, signalHandler); } #endif // WIN32