Make backtrace work on windows

This commit is contained in:
Sebastian Messmer 2018-05-16 19:47:51 -07:00
parent 2299867320
commit efbe606961
4 changed files with 156 additions and 16 deletions

View File

@ -37,7 +37,8 @@ set(SOURCES
data/DataFixture.cpp data/DataFixture.cpp
data/DataUtils.cpp data/DataUtils.cpp
data/Data.cpp data/Data.cpp
assert/backtrace.cpp assert/backtrace_nonwindows.cpp
assert/backtrace_windows.cpp
assert/AssertFailed.cpp assert/AssertFailed.cpp
system/get_total_memory.cpp system/get_total_memory.cpp
system/homedir.cpp system/homedir.cpp

View File

@ -6,6 +6,9 @@
namespace cpputils { namespace cpputils {
std::string backtrace(); 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(); void showBacktraceOnSigSegv();
} }

View File

@ -1,3 +1,5 @@
#if !defined(_MSC_VER)
#include "backtrace.h" #include "backtrace.h"
#include <execinfo.h> #include <execinfo.h>
#include <signal.h> #include <signal.h>
@ -12,12 +14,9 @@ using std::string;
using std::ostringstream; using std::ostringstream;
using namespace cpputils::logging; using namespace cpputils::logging;
//TODO Use the following? https://github.com/bombela/backward-cpp
namespace cpputils { namespace cpputils {
//TODO Refactor (for example: RAII or at least try{}finally{} instead of free()) namespace {
std::string demangle(const string &mangledName) { std::string demangle(const string &mangledName) {
string result; string result;
int status = -10; int status = -10;
@ -37,7 +36,8 @@ namespace cpputils {
if (startMangledName == string::npos || endMangledName == string::npos) { if (startMangledName == string::npos || endMangledName == string::npos) {
return backtraceLine; 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) { string backtrace_to_string(void *array[], size_t size) {
@ -49,20 +49,29 @@ namespace cpputils {
free(ptr); free(ptr);
return result.str(); return result.str();
} }
}
string backtrace() { string backtrace() {
constexpr unsigned int MAX_SIZE = 100; constexpr unsigned int MAX_SIZE = 100;
void *array[MAX_SIZE]; void *array[MAX_SIZE];
size_t size = ::backtrace(array, MAX_SIZE); size_t size = ::backtrace(array, MAX_SIZE);
return backtrace_to_string(array, size); return backtrace_to_string(array, size);
} }
namespace {
void sigsegv_handler(int) { void sigsegv_handler(int) {
LOG(ERROR, "SIGSEGV\n{}", backtrace()); LOG(ERROR, "SIGSEGV\n{}", backtrace());
exit(1); 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

View File

@ -0,0 +1,127 @@
#if defined(_MSC_VER)
#include "backtrace.h"
#include <signal.h>
#include <iostream>
#include <string>
#include <sstream>
#include "../logging/logging.h"
#include <Windows.h>
#include <Dbghelp.h>
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<char[]>(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<int64_t>(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