win crash printer

This commit is contained in:
otavepto 2024-01-07 09:20:26 +02:00
parent 57e7dbebde
commit fcfda3ddd2
2 changed files with 166 additions and 0 deletions

View File

@ -0,0 +1,15 @@
#ifndef _CRASH_PRINTER_WIN
#define _CRASH_PRINTER_WIN
#include <string>
namespace crash_printer {
bool init(const std::wstring &log_file);
void deinit();
}
#endif // _CRASH_PRINTER_WIN

151
crash_printer/win.cpp Normal file
View File

@ -0,0 +1,151 @@
#include "crash_printer/common.hpp"
#include "crash_printer/win.hpp"
#include <sstream>
#include <chrono>
#include <ctime>
#include <vector>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <DbgHelp.h>
static LPTOP_LEVEL_EXCEPTION_FILTER originalExceptionFilter = nullptr;
static std::wstring logs_filepath{};
static void print_stacktrace(std::ofstream &file, CONTEXT* context) {
auto this_proc = GetCurrentProcess();
auto this_thread = GetCurrentThread();
if (!SymInitialize(this_proc, NULL, TRUE)) {
return;
}
STACKFRAME stack_frame{};
#ifdef _WIN64
constexpr DWORD machine_type = IMAGE_FILE_MACHINE_AMD64;
DWORD64 symbol_displacement = 0;
stack_frame.AddrPC.Offset = context->Rip;
stack_frame.AddrFrame.Offset = context->Rsp;
stack_frame.AddrStack.Offset = context->Rsp;
#else
constexpr DWORD machine_type = IMAGE_FILE_MACHINE_I386;
DWORD symbol_displacement = 0;
stack_frame.AddrPC.Offset = context->Eip;
stack_frame.AddrFrame.Offset = context->Esp; // EBP may not be used by module
stack_frame.AddrStack.Offset = context->Esp;
#endif
stack_frame.AddrPC.Mode = ADDRESS_MODE::AddrModeFlat;
stack_frame.AddrFrame.Mode = ADDRESS_MODE::AddrModeFlat;
stack_frame.AddrStack.Mode = ADDRESS_MODE::AddrModeFlat;
crash_printer::write(file, "*********** Stack trace ***********");
std::vector<std::string> symbols{};
while (true) {
BOOL res = StackWalk(
machine_type,
this_proc, this_thread,
&stack_frame,
context,
NULL, SymFunctionTableAccess, SymGetModuleBase, NULL);
if (!res) {
break;
}
bool print_symbol = false;
IMAGEHLP_SYMBOL *symbol = (IMAGEHLP_SYMBOL *)_malloca(sizeof(IMAGEHLP_SYMBOL) + MAX_PATH);
if (symbol) {
symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
symbol->MaxNameLength = MAX_PATH - 1; // "in characters, not including the null-terminating character"
symbol_displacement = 0;
if (SymGetSymFromAddr(this_proc, stack_frame.AddrPC.Offset, &symbol_displacement, symbol)) {
print_symbol = true;
}
}
std::stringstream ss{};
ss << "0x" << std::hex << (void *)stack_frame.AddrPC.Offset << " | ";
if (print_symbol) {
ss << symbol->Name;
} else {
ss << "<unknown function>";
}
symbols.push_back(ss.str());
if (symbol) {
_freea(symbol);
}
}
SymCleanup(this_proc);
auto idx = symbols.size();
for (const auto &s : symbols) {
std::stringstream ss{};
ss << "[frame " << std::dec << (idx - 1) << "]: "
<< s;
crash_printer::write(file, ss.str());
idx--;
}
}
static void log_exception(LPEXCEPTION_POINTERS ex_pointers)
{
if (!crash_printer::create_dir(logs_filepath)) {
return;
}
std::ofstream file(logs_filepath, std::ios::app);
auto now = std::chrono::system_clock::now();
auto t_now = std::chrono::system_clock::to_time_t(now);
auto gm_time = std::gmtime(&t_now);
auto time = std::string(std::asctime(gm_time));
time.pop_back(); // remove the trailing '\n' added by asctime
crash_printer::write(file, "[" + time + "]");
{
std::stringstream ss{};
ss << "Unhandled exception:" << std::endl
<< " code: 0x" << std::hex << ex_pointers->ExceptionRecord->ExceptionCode << std::endl
<< " @address = 0x" << std::hex << ex_pointers->ExceptionRecord->ExceptionAddress;
crash_printer::write(file, ss.str());
}
print_stacktrace(file, ex_pointers->ContextRecord);
crash_printer::write(file, "**********************************\n");
file.close();
}
static LONG WINAPI exception_handler(LPEXCEPTION_POINTERS ex_pointers)
{
log_exception(ex_pointers);
if (originalExceptionFilter) {
return originalExceptionFilter(ex_pointers);
}
return EXCEPTION_CONTINUE_SEARCH;
}
bool crash_printer::init(const std::wstring &log_file)
{
logs_filepath = log_file;
originalExceptionFilter = SetUnhandledExceptionFilter(exception_handler);
return true;
}
void crash_printer::deinit()
{
}