From c20e4dd01047e28fc20074810ad65d55683a1d51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pa=CC=84vels=20Nadtoc=CC=8Cajevs?= <7645683+bruvzg@users.noreply.github.com> Date: Thu, 5 Feb 2026 09:41:30 +0200 Subject: [PATCH] [CrashHandler] Always print frame PCs and main module load address for retrace. --- platform/linuxbsd/crash_handler_linuxbsd.cpp | 5 +- platform/macos/crash_handler_macos.mm | 6 +- .../windows/crash_handler_windows_seh.cpp | 23 ++- .../windows/crash_handler_windows_signal.cpp | 144 +++++++++++++++--- 4 files changed, 154 insertions(+), 24 deletions(-) diff --git a/platform/linuxbsd/crash_handler_linuxbsd.cpp b/platform/linuxbsd/crash_handler_linuxbsd.cpp index 610225cfa4..ca7969ae38 100644 --- a/platform/linuxbsd/crash_handler_linuxbsd.cpp +++ b/platform/linuxbsd/crash_handler_linuxbsd.cpp @@ -104,6 +104,9 @@ static void handle_crash(int sig) { // Non glibc systems apparently don't give PIE relocation info. uintptr_t relocation = 0; #endif //__GLIBC__ + + print_error(vformat("Load address: %x\n", (uint64_t)relocation)); + if (strings) { int ret; @@ -170,7 +173,7 @@ static void handle_crash(int sig) { } // Simplify printed file paths to remove redundant `/./` sections (e.g. `/opt/godot/./core` -> `/opt/godot/core`). - print_error(vformat("[%d] %s (%s)", (int64_t)i, fname, err == OK ? addr2line_results[i].replace("/./", "/") : "")); + print_error(vformat("[%d] %x - %s (%s)", (int64_t)i, (uint64_t)bt_buffer[i], fname, err == OK ? addr2line_results[i].replace("/./", "/") : "")); } free(strings); diff --git a/platform/macos/crash_handler_macos.mm b/platform/macos/crash_handler_macos.mm index 837edd783f..a072c1a322 100644 --- a/platform/macos/crash_handler_macos.mm +++ b/platform/macos/crash_handler_macos.mm @@ -137,6 +137,8 @@ static void handle_crash(int sig) { args.push_back(str); } + print_error(vformat("Load address: %x\n", (uint64_t)load_addr)); + // Single execution of atos with all addresses. String out; int ret; @@ -176,7 +178,7 @@ static void handle_crash(int sig) { output = fname; } - print_error(vformat("[%d] %s", (int64_t)i, output)); + print_error(vformat("[%d] %x - %s", (int64_t)i, (uint64_t)bt_buffer[i], output)); } if (strings) { @@ -187,7 +189,7 @@ static void handle_crash(int sig) { char **strings = backtrace_symbols(bt_buffer, size); if (strings) { for (size_t i = 0; i < size; i++) { - print_error(vformat("[%d] %s", (int64_t)i, strings[i])); + print_error(vformat("[%d] %x - %s", (int64_t)i, (uint64_t)bt_buffer[i], strings[i])); } free(strings); } diff --git a/platform/windows/crash_handler_windows_seh.cpp b/platform/windows/crash_handler_windows_seh.cpp index e44e7a1807..3e3ac6bce9 100644 --- a/platform/windows/crash_handler_windows_seh.cpp +++ b/platform/windows/crash_handler_windows_seh.cpp @@ -166,6 +166,8 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) { std::transform(module_handles.begin(), module_handles.end(), std::back_inserter(modules), get_mod_info(process)); void *base = modules[0].base_address; + print_error(vformat("Load address: %x\n", (uint64_t)base)); + // Setup stuff: CONTEXT *context = ep->ContextRecord; STACKFRAME64 frame; @@ -208,10 +210,27 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) { if (frame.AddrPC.Offset != 0) { std::string fnName = symbol(process, frame.AddrPC.Offset).undecorated_name(); + IMAGEHLP_MODULE64 mod_info; + memset(&mod_info, 0, sizeof(IMAGEHLP_MODULE64)); + mod_info.SizeOfStruct = sizeof(IMAGEHLP_MODULE64); + uint64_t offset = (uint64_t)base; + String mod_name = "main"; + if (SymGetModuleInfo64(process, frame.AddrPC.Offset, &mod_info)) { + offset = mod_info.BaseOfImage; + if (offset != (uint64_t)base) { + if (mod_info.ImageName[0] != 0) { + mod_name = String((const char *)mod_info.ImageName).to_lower().get_file(); + } else if (mod_info.ModuleName[0] != 0) { + mod_name = String((const char *)mod_info.ModuleName).to_lower(); + } else { + mod_name = ""; + } + } + } if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &offset_from_symbol, &line)) { - print_error(vformat("[%d] %s (%s:%d)", n, fnName.c_str(), (char *)line.FileName, (int)line.LineNumber)); + print_error(vformat("[%d] %x (%s+%x) - %s (%s:%d)", n, (uint64_t)frame.AddrPC.Offset, mod_name, (uint64_t)frame.AddrPC.Offset - offset, fnName.c_str(), (char *)line.FileName, (int)line.LineNumber)); } else { - print_error(vformat("[%d] %s", n, fnName.c_str())); + print_error(vformat("[%d] %x (%s+%x) - %s", n, (uint64_t)frame.AddrPC.Offset, mod_name, (uint64_t)frame.AddrPC.Offset - offset, fnName.c_str())); } } else { print_error(vformat("[%d] ???", n)); diff --git a/platform/windows/crash_handler_windows_signal.cpp b/platform/windows/crash_handler_windows_signal.cpp index 7eac3d27f8..0a018e4731 100644 --- a/platform/windows/crash_handler_windows_signal.cpp +++ b/platform/windows/crash_handler_windows_signal.cpp @@ -45,38 +45,106 @@ #include #include +#include #include #include +#include +#include + +// Some versions of imagehlp.dll lack the proper packing directives themselves +// so we need to do it. +#pragma pack(push, before_imagehlp, 8) +#include +#pragma pack(pop, before_imagehlp) struct CrashHandlerData { int64_t index = 0; backtrace_state *state = nullptr; int64_t offset = 0; + int64_t base = 0; + uint64_t pc = 0; + HANDLE process = nullptr; + bool sym_ok = false; +}; + +struct module_data { + std::string image_name; + std::string module_name; + void *base_address = nullptr; + DWORD load_size; +}; + +class get_mod_info { + HANDLE process; + +public: + get_mod_info(HANDLE h) : + process(h) {} + + module_data operator()(HMODULE module) { + module_data ret; + char temp[4096]; + MODULEINFO mi; + + GetModuleInformation(process, module, &mi, sizeof(mi)); + ret.base_address = mi.lpBaseOfDll; + ret.load_size = mi.SizeOfImage; + + GetModuleFileNameEx(process, module, temp, sizeof(temp)); + ret.image_name = temp; + GetModuleBaseName(process, module, temp, sizeof(temp)); + ret.module_name = temp; + std::vector img(ret.image_name.begin(), ret.image_name.end()); + std::vector mod(ret.module_name.begin(), ret.module_name.end()); + SymLoadModule64(process, nullptr, &img[0], &mod[0], (DWORD64)ret.base_address, ret.load_size); + return ret; + } }; int symbol_callback(void *data, uintptr_t pc, const char *filename, int lineno, const char *function) { CrashHandlerData *ch_data = reinterpret_cast(data); - if (!function) { - return 0; - } - - char fname[1024]; - snprintf(fname, 1024, "%s", function); - - if (function[0] == '_') { - int status; - char *demangled = abi::__cxa_demangle(function, nullptr, nullptr, &status); - - if (status == 0 && demangled) { - snprintf(fname, 1024, "%s", demangled); - } - - if (demangled) { - free(demangled); + uint64_t offset = (uint64_t)ch_data->base; + String mod_name = "main"; + if (ch_data->sym_ok) { + IMAGEHLP_MODULE64 mod_info; + memset(&mod_info, 0, sizeof(IMAGEHLP_MODULE64)); + mod_info.SizeOfStruct = sizeof(IMAGEHLP_MODULE64); + if (SymGetModuleInfo64(ch_data->process, ch_data->pc, &mod_info)) { + offset = mod_info.BaseOfImage; + if (offset != (uint64_t)ch_data->base) { + if (mod_info.ImageName[0] != 0) { + mod_name = String((const char *)mod_info.ImageName).to_lower().get_file(); + } else if (mod_info.ModuleName[0] != 0) { + mod_name = String((const char *)mod_info.ModuleName).to_lower(); + } else { + mod_name = ""; + } + } } } - print_error(vformat("[%d] %s (%s:%d)", ch_data->index++, String::utf8(fname), String::utf8(filename), lineno)); + if (function) { + char fname[1024]; + snprintf(fname, 1024, "%s", function); + + if (function[0] == '_') { + int status; + char *demangled = abi::__cxa_demangle(function, nullptr, nullptr, &status); + + if (status == 0 && demangled) { + snprintf(fname, 1024, "%s", demangled); + } + + if (demangled) { + free(demangled); + } + } + print_error(vformat("[%d] %x (%s+%x) - %s (%s:%d)", ch_data->index++, ch_data->pc, mod_name, ch_data->pc - offset, String::utf8(fname), String::utf8(filename), lineno)); + } else if ((int64_t)ch_data->pc > 0) { + print_error(vformat("[%d] %x (%s+%x) - ", ch_data->index++, ch_data->pc, mod_name, ch_data->pc - offset)); + } else { + print_error(vformat("[%d] ???", ch_data->index++)); + } return 0; } @@ -85,12 +153,32 @@ void error_callback(void *data, const char *msg, int errnum) { if (ch_data->index == 0) { print_error(vformat("Error(%d): %s", errnum, String::utf8(msg))); } else { - print_error(vformat("[%d] error(%d): %s", ch_data->index++, errnum, String::utf8(msg))); + uint64_t offset = (uint64_t)ch_data->base; + String mod_name = "main"; + if (ch_data->sym_ok) { + IMAGEHLP_MODULE64 mod_info; + memset(&mod_info, 0, sizeof(IMAGEHLP_MODULE64)); + mod_info.SizeOfStruct = sizeof(IMAGEHLP_MODULE64); + if (SymGetModuleInfo64(ch_data->process, ch_data->pc, &mod_info)) { + offset = mod_info.BaseOfImage; + if (offset != (uint64_t)ch_data->base) { + if (mod_info.ImageName[0] != 0) { + mod_name = String((const char *)mod_info.ImageName).to_lower().get_file(); + } else if (mod_info.ModuleName[0] != 0) { + mod_name = String((const char *)mod_info.ModuleName).to_lower(); + } else { + mod_name = ""; + } + } + } + } + print_error(vformat("[%d] %x (%s+%x) - %s", ch_data->index++, ch_data->pc, mod_name, ch_data->pc - offset, String::utf8(msg))); } } int trace_callback(void *data, uintptr_t pc) { CrashHandlerData *ch_data = reinterpret_cast(data); + ch_data->pc = (uint64_t)pc; backtrace_pcinfo(ch_data->state, pc - ch_data->offset, &symbol_callback, &error_callback, data); return 0; } @@ -166,6 +254,24 @@ extern void CrashHandlerException(int signal) { int64_t image_file_base = get_image_base(_execpath); data.offset = image_mem_base - image_file_base; + std::vector modules; + DWORD cbNeeded; + std::vector module_handles(1); + + data.process = GetCurrentProcess(); + data.sym_ok = SymInitialize(data.process, nullptr, false); + + if (data.sym_ok) { + SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME | SYMOPT_EXACT_SYMBOLS); + EnumProcessModules(data.process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cbNeeded); + module_handles.resize(cbNeeded / sizeof(HMODULE)); + EnumProcessModules(data.process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cbNeeded); + std::transform(module_handles.begin(), module_handles.end(), std::back_inserter(modules), get_mod_info(data.process)); + data.base = (uint64_t)modules[0].base_address; + } + + print_error(vformat("Load address: %x\n", (uint64_t)data.offset)); + if (FileAccess::exists(_execpath + ".debugsymbols")) { _execpath = _execpath + ".debugsymbols"; }