feat(util): Separate debug module with proper stacktrace printing

Because we are using mingw, we can't just use window's
dbghelp library as the symbols created are in dwarf format.

Fortunately, the dwarfstack library already provides all the
facilities to easily print very descriptive stacktraces,
including function names, file names and line numbers,
when dwarf symbols are available.

This moves the incomplete exception handling portion from
signal to a separate module as well to improve scoping.
This commit is contained in:
icex2
2024-08-14 17:53:52 +02:00
parent 4b352ed080
commit 1b8bf7b245
10 changed files with 395 additions and 83 deletions
+1
View File
@@ -5,6 +5,7 @@ src_util := \
cmdline.c \
crc.c \
crypto.c \
debug.c \
fs.c \
hex.c \
iobuf.c \
+193
View File
@@ -0,0 +1,193 @@
#define LOG_MODULE "util-debug"
#include <windows.h>
#include <stdio.h>
#include "core/log.h"
#include "imports/dwarfstack.h"
#include "util/debug.h"
#define EX_DESC(name) \
case EXCEPTION_##name: \
desc = " (" #name ")"; \
break
#define log_exception(...) _debug_exception_msg("exception", __VA_ARGS__)
static core_log_message_t _debug_exception_msg;
static void _debug_stacktrace_printer(
uint64_t addr,
const char *filename,
int lineno,
const char *funcname,
void *context,
int columnno)
{
int *count;
const char *delim;
void *ptr;
char buffer[512];
char *buffer_ptr;
count = context;
delim = strrchr(filename, '/');
if (delim) {
filename = delim + 1;
}
delim = strrchr(filename, '\\');
if (delim) {
filename = delim + 1;
}
ptr = (void *) (uintptr_t) addr;
switch (lineno) {
case DWST_BASE_ADDR:
log_exception("base address: 0x%p (%s)", ptr, filename);
break;
case DWST_NOT_FOUND:
case DWST_NO_DBG_SYM:
case DWST_NO_SRC_FILE:
log_exception(
" stack %02d: 0x%p (%s)", (*count)++, ptr, filename);
break;
default:
buffer_ptr = buffer;
memset(buffer, 0, sizeof(buffer));
if (ptr) {
buffer_ptr += sprintf(
buffer_ptr, " stack %02d: 0x%p", (*count)++, ptr);
} else {
buffer_ptr += sprintf(
buffer_ptr,
" %*s",
(int) sizeof(void *) * 2,
"");
}
buffer_ptr += sprintf(buffer_ptr, " (%s:%d", filename, lineno);
if (columnno > 0) {
buffer_ptr += sprintf(buffer_ptr, ":%d", columnno);
}
buffer_ptr += sprintf(buffer_ptr, ")");
if (funcname) {
buffer_ptr += sprintf(buffer_ptr, " [%s]", funcname);
}
log_exception(buffer);
break;
}
}
static LONG WINAPI _debug_unhandled_exception_filter(LPEXCEPTION_POINTERS ep)
{
DWORD code;
const char *desc;
ULONG_PTR flag;
ULONG_PTR addr;
int count;
log_exception("==========================================================");
log_exception("The application has crashed due to an unhandled exception!");
code = ep->ExceptionRecord->ExceptionCode;
desc = debug_exception_code_to_str(code);
log_exception("code: 0x%08lX", code);
log_exception("desc: %s", desc);
if (code == EXCEPTION_ACCESS_VIOLATION &&
ep->ExceptionRecord->NumberParameters == 2) {
flag = ep->ExceptionRecord->ExceptionInformation[0];
addr = ep->ExceptionRecord->ExceptionInformation[1];
log_exception(
"%s violation at 0x%p",
flag == 8 ? "data execution prevention" :
(flag ? "write access" : "read access"),
(void *) addr);
}
log_exception("stacktrace:");
count = 0;
dwstOfException(ep->ContextRecord, &_debug_stacktrace_printer, &count);
log_exception("End of stacktrace");
log_exception("==========================================================");
return EXCEPTION_EXECUTE_HANDLER;
}
void debug_init(core_log_message_t exception_msg)
{
_debug_exception_msg = exception_msg;
SetUnhandledExceptionFilter(_debug_unhandled_exception_filter);
log_info("Initialized");
}
void debug_print_stacktrace()
{
int count;
count = 0;
log_exception("==========================================================");
log_exception("Debug stacktrace");
dwstOfLocation(&_debug_stacktrace_printer, &count);
log_exception("End of debug stacktrace");
log_exception("==========================================================");
}
const char *debug_exception_code_to_str(DWORD code)
{
const char *desc = "";
switch (code) {
EX_DESC(ACCESS_VIOLATION);
EX_DESC(ARRAY_BOUNDS_EXCEEDED);
EX_DESC(BREAKPOINT);
EX_DESC(DATATYPE_MISALIGNMENT);
EX_DESC(FLT_DENORMAL_OPERAND);
EX_DESC(FLT_DIVIDE_BY_ZERO);
EX_DESC(FLT_INEXACT_RESULT);
EX_DESC(FLT_INVALID_OPERATION);
EX_DESC(FLT_OVERFLOW);
EX_DESC(FLT_STACK_CHECK);
EX_DESC(FLT_UNDERFLOW);
EX_DESC(ILLEGAL_INSTRUCTION);
EX_DESC(IN_PAGE_ERROR);
EX_DESC(INT_DIVIDE_BY_ZERO);
EX_DESC(INT_OVERFLOW);
EX_DESC(INVALID_DISPOSITION);
EX_DESC(NONCONTINUABLE_EXCEPTION);
EX_DESC(PRIV_INSTRUCTION);
EX_DESC(SINGLE_STEP);
EX_DESC(STACK_OVERFLOW);
case DBG_CONTROL_C:
return "DBG_CONTROL_C";
default:
log_warning("Unknown exception code: %lX", code);
return "Unknown";
}
return desc;
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef UTIL_DEBUG_H
#define UTIL_DEBUG_H
#include <windows.h>
#include "core/log.h"
void debug_init(core_log_message_t exception_msg);
void debug_print_stacktrace();
const char *debug_exception_code_to_str(DWORD code);
#endif
-83
View File
@@ -1,5 +1,4 @@
#include <unistd.h>
#include <windows.h>
#include "core/log.h"
@@ -46,40 +45,9 @@ static BOOL WINAPI console_ctrl_handler(DWORD dwCtrlType)
return FALSE;
}
static LONG WINAPI
unhandled_exception_filter(struct _EXCEPTION_POINTERS *ExceptionInfo)
{
// no exception info provided
if (ExceptionInfo != NULL) {
struct _EXCEPTION_RECORD *ExceptionRecord =
ExceptionInfo->ExceptionRecord;
log_warning(
"Exception raised: %s",
signal_exception_code_to_str(ExceptionRecord->ExceptionCode));
struct _EXCEPTION_RECORD *record_cause =
ExceptionRecord->ExceptionRecord;
while (record_cause != NULL) {
log_warning(
"Caused by: %s",
signal_exception_code_to_str(record_cause->ExceptionCode));
record_cause = record_cause->ExceptionRecord;
}
// TODO print stacktrace
log_fatal("End exception handler");
}
return EXCEPTION_CONTINUE_SEARCH;
}
void signal_exception_handler_init()
{
SetConsoleCtrlHandler(console_ctrl_handler, TRUE);
SetUnhandledExceptionFilter(unhandled_exception_filter);
log_info("Initialized");
}
@@ -89,55 +57,4 @@ void signal_register_shutdown_handler(signal_shutdown_handler_t handler)
shutdown_handler = handler;
log_misc("Registered shutdown handler");
}
const char *signal_exception_code_to_str(DWORD code)
{
switch (code) {
case EXCEPTION_ACCESS_VIOLATION:
return "EXCEPTION_ACCESS_VIOLATION";
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
case EXCEPTION_BREAKPOINT:
return "EXCEPTION_BREAKPOINT";
case EXCEPTION_DATATYPE_MISALIGNMENT:
return "EXCEPTION_DATATYPE_MISALIGNMENT";
case EXCEPTION_FLT_DENORMAL_OPERAND:
return "EXCEPTION_FLT_DENORMAL_OPERAND";
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
case EXCEPTION_FLT_INEXACT_RESULT:
return "EXCEPTION_FLT_INEXACT_RESULT";
case EXCEPTION_FLT_INVALID_OPERATION:
return "EXCEPTION_FLT_INVALID_OPERATION";
case EXCEPTION_FLT_OVERFLOW:
return "EXCEPTION_FLT_OVERFLOW";
case EXCEPTION_FLT_STACK_CHECK:
return "EXCEPTION_FLT_STACK_CHECK";
case EXCEPTION_FLT_UNDERFLOW:
return "EXCEPTION_FLT_UNDERFLOW";
case EXCEPTION_ILLEGAL_INSTRUCTION:
return "EXCEPTION_ILLEGAL_INSTRUCTION";
case EXCEPTION_IN_PAGE_ERROR:
return "EXCEPTION_IN_PAGE_ERROR";
case EXCEPTION_INT_DIVIDE_BY_ZERO:
return "EXCEPTION_INT_DIVIDE_BY_ZERO";
case EXCEPTION_INT_OVERFLOW:
return "EXCEPTION_INT_OVERFLOW";
case EXCEPTION_INVALID_DISPOSITION:
return "EXCEPTION_INVALID_DISPOSITION";
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
return "EXCEPTION_NONCONTINUABLE_EXCEPTION";
case EXCEPTION_PRIV_INSTRUCTION:
return "EXCEPTION_PRIV_INSTRUCTION";
case EXCEPTION_SINGLE_STEP:
return "EXCEPTION_SINGLE_STEP";
case EXCEPTION_STACK_OVERFLOW:
return "EXCEPTION_STACK_OVERFLOW";
case DBG_CONTROL_C:
return "DBG_CONTROL_C";
default:
log_warning("Unknown exception code: %lX", code);
return "Unknown";
}
}