mirror of
https://github.com/djhackersdev/bemanitools.git
synced 2025-03-01 16:10:30 +01:00
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:
parent
31fa84e0ef
commit
d3c293de45
BIN
dist/dwarfstack/32/dwarfstack.dll
vendored
Normal file
BIN
dist/dwarfstack/32/dwarfstack.dll
vendored
Normal file
Binary file not shown.
BIN
dist/dwarfstack/64/dwarfstack.dll
vendored
Normal file
BIN
dist/dwarfstack/64/dwarfstack.dll
vendored
Normal file
Binary file not shown.
1
dist/dwarfstack/readme.md
vendored
Normal file
1
dist/dwarfstack/readme.md
vendored
Normal file
@ -0,0 +1 @@
|
||||
Version/release: https://github.com/ssbssa/dwarfstack/releases/tag/2.2
|
162
src/imports/dwarfstack.h
Normal file
162
src/imports/dwarfstack.h
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2019 Hannes Domani
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __DWARFSTACK_H__
|
||||
#define __DWARFSTACK_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#if defined(DWST_STATIC)
|
||||
#define EXPORT
|
||||
#elif defined(DWST_SHARED)
|
||||
#define EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define EXPORT __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
// dwstCallback(): callback function
|
||||
// addr: stack address
|
||||
// filename: source file location
|
||||
// lineno: line number
|
||||
// funcname: function name
|
||||
// context: user-provided pointer (callbackContext)
|
||||
// columnno: column number
|
||||
typedef void dwstCallback(
|
||||
uint64_t addr,const char *filename,int lineno,const char *funcname,
|
||||
void *context,int columnno );
|
||||
|
||||
typedef void dwstCallbackW(
|
||||
uint64_t addr,const wchar_t *filename,int lineno,const char *funcname,
|
||||
void *context,int columnno );
|
||||
|
||||
// special values for lineno:
|
||||
|
||||
// DWST_BASE_ADDR: inform about the used image base address
|
||||
// (important in case it's not the same as the preferred image base address)
|
||||
// addr: used image base address
|
||||
// filename: executable location
|
||||
#define DWST_BASE_ADDR 0
|
||||
|
||||
// DWST_NO_DBG_SYM: no debug information available
|
||||
// addr: stack address
|
||||
// filename: executable location
|
||||
#define DWST_NO_DBG_SYM -1
|
||||
|
||||
// DWST_NO_SRC_FILE: no source file information available
|
||||
// addr: stack address
|
||||
// filename: executable location
|
||||
#define DWST_NO_SRC_FILE -2
|
||||
|
||||
// DWST_NOT_FOUND: no information available (invalid address?)
|
||||
// addr: stack address
|
||||
// filename: executable location
|
||||
#define DWST_NOT_FOUND -3
|
||||
|
||||
|
||||
// dwstOfFile(): stack information of file
|
||||
// name: executable location
|
||||
// imageBase: used image base address
|
||||
// addr: stack addresses
|
||||
// count: number of addresses
|
||||
// callbackFunc: callback function
|
||||
// callbackContext: user-provided pointer (context)
|
||||
// (for example see examples/addr2line/)
|
||||
EXPORT int dwstOfFile(
|
||||
const char *name,uint64_t imageBase,
|
||||
uint64_t *addr,int count,
|
||||
dwstCallback *callbackFunc,void *callbackContext );
|
||||
|
||||
EXPORT int dwstOfFileW(
|
||||
const wchar_t *name,uint64_t imageBase,
|
||||
uint64_t *addr,int count,
|
||||
dwstCallbackW *callbackFunc,void *callbackContext );
|
||||
|
||||
|
||||
// dwstOfProcess(): stack information of current process
|
||||
// addr: stack addresses
|
||||
// count: number of addresses
|
||||
// callbackFunc: callback function
|
||||
// callbackContext: user-provided pointer (context)
|
||||
EXPORT int dwstOfProcess(
|
||||
uintptr_t *addr,int count,
|
||||
dwstCallback *callbackFunc,void *callbackContext );
|
||||
|
||||
EXPORT int dwstOfProcessW(
|
||||
uintptr_t *addr,int count,
|
||||
dwstCallbackW *callbackFunc,void *callbackContext );
|
||||
|
||||
|
||||
// dwstOfLocation(): stack information of current location
|
||||
// callbackFunc: callback function
|
||||
// callbackContext: user-provided pointer (context)
|
||||
// (for example see examples/location/)
|
||||
EXPORT int dwstOfLocation(
|
||||
dwstCallback *callbackFunc,void *callbackContext );
|
||||
|
||||
EXPORT int dwstOfLocationW(
|
||||
dwstCallbackW *callbackFunc,void *callbackContext );
|
||||
|
||||
|
||||
// dwstOfException(): stack information of exception
|
||||
// context: ContextRecord of exception
|
||||
// callbackFunc: callback function
|
||||
// callbackContext: user-provided pointer (context)
|
||||
// (for example see examples/exception/)
|
||||
EXPORT int dwstOfException(
|
||||
void *context,
|
||||
dwstCallback *callbackFunc,void *callbackContext );
|
||||
|
||||
EXPORT int dwstOfExceptionW(
|
||||
void *context,
|
||||
dwstCallbackW *callbackFunc,void *callbackContext );
|
||||
|
||||
|
||||
// dwstExceptionDialog(): show dialog on unhandled exception
|
||||
// extraInfo: extra information shown in dialog
|
||||
// (for example see examples/exception-dialog/)
|
||||
EXPORT void dwstExceptionDialog(
|
||||
const char *extraInfo );
|
||||
|
||||
EXPORT void dwstExceptionDialogW(
|
||||
const wchar_t *extraInfo );
|
||||
|
||||
|
||||
#ifndef DWST_STATIC
|
||||
// dwstDemangle(): demangle gcc style c++ symbols
|
||||
// mangled: mangled name
|
||||
// demangled: demangled name
|
||||
// length: size of demangled buffer
|
||||
EXPORT size_t dwstDemangle(
|
||||
const char *mangled,
|
||||
char *demangled,size_t length );
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
13
src/imports/import_32_indep_dwarfstack.def
Normal file
13
src/imports/import_32_indep_dwarfstack.def
Normal file
@ -0,0 +1,13 @@
|
||||
LIBRARY dwarfstack
|
||||
|
||||
EXPORTS
|
||||
dwstOfFile @ 441
|
||||
dwstOfFileW @ 443
|
||||
dwstOfProcess @ 446
|
||||
dwstOfProcessW @ 448
|
||||
dwstOfLocation @ 444
|
||||
dwstOfLocationW @ 445
|
||||
dwstOfException @ 438
|
||||
dwstOfExceptionW @ 440
|
||||
dwstExceptionDialog @ 436
|
||||
dwstExceptionDialogW @ 437
|
13
src/imports/import_64_indep_dwarfstack.def
Normal file
13
src/imports/import_64_indep_dwarfstack.def
Normal file
@ -0,0 +1,13 @@
|
||||
LIBRARY dwarfstack
|
||||
|
||||
EXPORTS
|
||||
dwstOfFile @ 441
|
||||
dwstOfFileW @ 443
|
||||
dwstOfProcess @ 446
|
||||
dwstOfProcessW @ 448
|
||||
dwstOfLocation @ 444
|
||||
dwstOfLocationW @ 445
|
||||
dwstOfException @ 438
|
||||
dwstOfExceptionW @ 440
|
||||
dwstExceptionDialog @ 436
|
||||
dwstExceptionDialogW @ 437
|
@ -5,6 +5,7 @@ src_util := \
|
||||
cmdline.c \
|
||||
crc.c \
|
||||
crypto.c \
|
||||
debug.c \
|
||||
fs.c \
|
||||
hex.c \
|
||||
iobuf.c \
|
||||
|
193
src/main/util/debug.c
Normal file
193
src/main/util/debug.c
Normal 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
src/main/util/debug.h
Normal file
12
src/main/util/debug.h
Normal 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
|
@ -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";
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user