From efbe606961a743eee6d963e2036a569e2835591f Mon Sep 17 00:00:00 2001 From: Sebastian Messmer Date: Wed, 16 May 2018 19:47:51 -0700 Subject: [PATCH] Make backtrace work on windows --- src/cpp-utils/CMakeLists.txt | 3 +- src/cpp-utils/assert/backtrace.h | 3 + ...backtrace.cpp => backtrace_nonwindows.cpp} | 39 +++--- src/cpp-utils/assert/backtrace_windows.cpp | 127 ++++++++++++++++++ 4 files changed, 156 insertions(+), 16 deletions(-) rename src/cpp-utils/assert/{backtrace.cpp => backtrace_nonwindows.cpp} (68%) create mode 100644 src/cpp-utils/assert/backtrace_windows.cpp diff --git a/src/cpp-utils/CMakeLists.txt b/src/cpp-utils/CMakeLists.txt index bb287015..df5d2de3 100644 --- a/src/cpp-utils/CMakeLists.txt +++ b/src/cpp-utils/CMakeLists.txt @@ -37,7 +37,8 @@ set(SOURCES data/DataFixture.cpp data/DataUtils.cpp data/Data.cpp - assert/backtrace.cpp + assert/backtrace_nonwindows.cpp + assert/backtrace_windows.cpp assert/AssertFailed.cpp system/get_total_memory.cpp system/homedir.cpp diff --git a/src/cpp-utils/assert/backtrace.h b/src/cpp-utils/assert/backtrace.h index 4d7ca1a7..66defcf1 100644 --- a/src/cpp-utils/assert/backtrace.h +++ b/src/cpp-utils/assert/backtrace.h @@ -6,6 +6,9 @@ namespace cpputils { std::string backtrace(); + + //TODO Refactor (for example: RAII or at least try{}finally{} instead of free()) + //TODO Use the following? https://github.com/bombela/backward-cpp void showBacktraceOnSigSegv(); } diff --git a/src/cpp-utils/assert/backtrace.cpp b/src/cpp-utils/assert/backtrace_nonwindows.cpp similarity index 68% rename from src/cpp-utils/assert/backtrace.cpp rename to src/cpp-utils/assert/backtrace_nonwindows.cpp index b2f030fa..767294e2 100644 --- a/src/cpp-utils/assert/backtrace.cpp +++ b/src/cpp-utils/assert/backtrace_nonwindows.cpp @@ -1,3 +1,5 @@ +#if !defined(_MSC_VER) + #include "backtrace.h" #include #include @@ -12,12 +14,9 @@ using std::string; using std::ostringstream; using namespace cpputils::logging; -//TODO Use the following? https://github.com/bombela/backward-cpp - namespace cpputils { - //TODO Refactor (for example: RAII or at least try{}finally{} instead of free()) - +namespace { std::string demangle(const string &mangledName) { string result; int status = -10; @@ -37,7 +36,8 @@ namespace cpputils { if (startMangledName == string::npos || endMangledName == string::npos) { return backtraceLine; } - return demangle(backtraceLine.substr(startMangledName+1, endMangledName-startMangledName-1)) + ": (" + backtraceLine.substr(0, startMangledName) + backtraceLine.substr(endMangledName); + return demangle(backtraceLine.substr(startMangledName + 1, endMangledName - startMangledName - 1)) + ": (" + + backtraceLine.substr(0, startMangledName) + backtraceLine.substr(endMangledName); } string backtrace_to_string(void *array[], size_t size) { @@ -49,20 +49,29 @@ namespace cpputils { free(ptr); return result.str(); } +} - string backtrace() { - constexpr unsigned int MAX_SIZE = 100; - void *array[MAX_SIZE]; - size_t size = ::backtrace(array, MAX_SIZE); - return backtrace_to_string(array, size); - } + string backtrace() { + constexpr unsigned int MAX_SIZE = 100; + void *array[MAX_SIZE]; + size_t size = ::backtrace(array, MAX_SIZE); + return backtrace_to_string(array, size); + } +namespace { void sigsegv_handler(int) { LOG(ERROR, "SIGSEGV\n{}", backtrace()); exit(1); } - - void showBacktraceOnSigSegv() { - signal(SIGSEGV, sigsegv_handler); - } } + + void showBacktraceOnSigSegv() { + auto result = signal(SIGSEGV, sigsegv_handler); + if (SIG_ERR == result) { + LOG(ERROR, "Failed to set sigsegv signal handler. Errno: {}", errno); + } + } + +} + +#endif diff --git a/src/cpp-utils/assert/backtrace_windows.cpp b/src/cpp-utils/assert/backtrace_windows.cpp new file mode 100644 index 00000000..5d7e4f68 --- /dev/null +++ b/src/cpp-utils/assert/backtrace_windows.cpp @@ -0,0 +1,127 @@ +#if defined(_MSC_VER) + +#include "backtrace.h" +#include +#include +#include +#include +#include "../logging/logging.h" +#include +#include + +using std::string; +using std::ostringstream; +using namespace cpputils::logging; + +namespace cpputils { + + namespace { + std::string backtrace_to_string(CONTEXT* context_record) { + std::ostringstream backtrace; + + HANDLE process = GetCurrentProcess(); + if (!::SymInitialize(process, NULL, TRUE)) { + DWORD error = GetLastError(); + backtrace << "[Can't get backtrace. SymInitialize failed with error code " << std::dec << error << "]\n"; + } + else { + // Initialize stack walking. + STACKFRAME64 stack_frame; + memset(&stack_frame, 0, sizeof(stack_frame)); +#if defined(_WIN64) + int machine_type = IMAGE_FILE_MACHINE_AMD64; + stack_frame.AddrPC.Offset = context_record->Rip; + stack_frame.AddrFrame.Offset = context_record->Rbp; + stack_frame.AddrStack.Offset = context_record->Rsp; +#else + int machine_type = IMAGE_FILE_MACHINE_I386; + stack_frame.AddrPC.Offset = context_record->Eip; + stack_frame.AddrFrame.Offset = context_record->Ebp; + stack_frame.AddrStack.Offset = context_record->Esp; +#endif + stack_frame.AddrPC.Mode = AddrModeFlat; + stack_frame.AddrFrame.Mode = AddrModeFlat; + stack_frame.AddrStack.Mode = AddrModeFlat; + + auto symbol_storage = std::make_unique(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)); + PSYMBOL_INFO symbol = (PSYMBOL_INFO)symbol_storage.get(); + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + + int i = 0; + + while (StackWalk64(machine_type, + GetCurrentProcess(), + GetCurrentThread(), + &stack_frame, + context_record, + nullptr, + &SymFunctionTableAccess64, + &SymGetModuleBase64, + nullptr)) { + + backtrace << "#" << (i++) << " "; + + DWORD64 displacement = 0; + + if (SymFromAddr(process, (DWORD64)stack_frame.AddrPC.Offset, &displacement, symbol)) + { + IMAGEHLP_MODULE64 moduleInfo; + std::memset(&moduleInfo, 0, sizeof(IMAGEHLP_MODULE64)); + moduleInfo.SizeOfStruct = sizeof(moduleInfo); + + if (::SymGetModuleInfo64(process, symbol->ModBase, &moduleInfo)) { + backtrace << moduleInfo.ModuleName << ":"; + } + backtrace << "0x" << std::hex << (DWORD64)stack_frame.AddrPC.Offset << ": "; + + backtrace << symbol->Name << " + 0x" << std::hex << static_cast(displacement); + } + else { + DWORD error = GetLastError(); + backtrace << std::hex << (DWORD64)stack_frame.AddrPC.Offset << ": [can't get symbol. SymFromAddr failed with error code " << std::dec << error << "]"; + } + + DWORD dwDisplacement; + IMAGEHLP_LINE64 line; + SymSetOptions(SYMOPT_LOAD_LINES); + line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + if (::SymGetLineFromAddr64(process, (DWORD64)stack_frame.AddrPC.Offset, &dwDisplacement, &line)) { + backtrace << " at " << line.FileName << ":" << std::dec << line.LineNumber; + } + else { + DWORD error = GetLastError(); + backtrace << " at [file/line unavailable, SymGetLineFromAddr64 failed with error code " << std::dec << error << "]"; + } + backtrace << "\n"; + } + } + + return backtrace.str(); + } + + + LONG WINAPI TopLevelExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) + { + std::string backtrace = backtrace_to_string(pExceptionInfo->ContextRecord); + LOG(ERROR, "Top level exception. Backtrace:\n{}", backtrace); + + return EXCEPTION_CONTINUE_SEARCH; + } + } + + std::string backtrace() { + CONTEXT context; + memset(&context, 0, sizeof(CONTEXT)); + context.ContextFlags = CONTEXT_FULL; + RtlCaptureContext(&context); + return backtrace_to_string(&context); + } + + void showBacktraceOnSigSegv() { + SetUnhandledExceptionFilter(TopLevelExceptionHandler); + } + +} + +#endif