Make backtrace work on windows
This commit is contained in:
parent
2299867320
commit
efbe606961
@ -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
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
127
src/cpp-utils/assert/backtrace_windows.cpp
Normal file
127
src/cpp-utils/assert/backtrace_windows.cpp
Normal 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
|
Loading…
Reference in New Issue
Block a user