From dfe2abbf26dced09def65aec86a0ff5e43f52a1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pa=CC=84vels=20Nadtoc=CC=8Cajevs?= <7645683+bruvzg@users.noreply.github.com> Date: Fri, 17 Apr 2026 09:41:21 +0300 Subject: [PATCH] [macOS/Linux] Add module information to the crash handler. --- platform/linuxbsd/crash_handler_linuxbsd.cpp | 82 ++++++++++++++----- platform/macos/crash_handler_macos.mm | 59 ++++++++++--- .../windows/crash_handler_windows_seh.cpp | 10 +-- .../windows/crash_handler_windows_signal.cpp | 12 +-- 4 files changed, 121 insertions(+), 42 deletions(-) diff --git a/platform/linuxbsd/crash_handler_linuxbsd.cpp b/platform/linuxbsd/crash_handler_linuxbsd.cpp index a7a13ab67d..8fedfa5797 100644 --- a/platform/linuxbsd/crash_handler_linuxbsd.cpp +++ b/platform/linuxbsd/crash_handler_linuxbsd.cpp @@ -105,7 +105,15 @@ static void handle_crash(int sig) { uintptr_t relocation = 0; #endif //__GLIBC__ - print_error(vformat("Load address: %x\n", (uint64_t)relocation)); + void *load_addr = nullptr; + { + Dl_info info; + if (dladdr(bt_buffer[size - 1], &info)) { + load_addr = info.dli_fbase; + } + } + + print_error(vformat("Load address: %x\n", (uint64_t)load_addr)); if (strings) { int ret; @@ -146,40 +154,76 @@ static void handle_crash(int sig) { args.push_back("-C"); // Try to get the file/line number using addr2line - String output; - Error err = OS::get_singleton()->execute(exe_name, args, &output, &ret); + String addr2line_output; + Error err = OS::get_singleton()->execute(exe_name, args, &addr2line_output, &ret); if (err == OK) { - Vector addr2line_results = output.substr(0, output.length() - 1).split("\n", false); + Vector addr2line_results = addr2line_output.substr(0, addr2line_output.length() - 1).split("\n", false); for (size_t i = 1; i < size; i++) { - // Simplify printed file paths to remove redundant `/./` sections (e.g. `/opt/godot/./core` -> `/opt/godot/core`). - print_error(vformat("[%d] %x - %s", (int64_t)i, (uint64_t)bt_buffer[i], addr2line_results[i].replace("/./", "/"))); - } - } else { - // Otherwise fall back to trace symbols. - for (size_t i = 1; i < size; i++) { - char fname[1024]; + String output = addr2line_results[i].replace("/./", "/"); + String mod_name = "main"; + uint64_t mod_off = (uint64_t)load_addr; + bool addr2line_fail = output.strip_edges().ends_with("??:0"); + if (addr2line_fail) { + output = String(strings[i]); + } + Dl_info info; - - snprintf(fname, 1024, "%s", strings[i]); - - // Try to demangle the function name to provide a more readable one. - if (dladdr(bt_buffer[i], &info) && info.dli_sname) { - if (info.dli_sname[0] == '_') { + if (dladdr(bt_buffer[i], &info)) { + mod_off = (uint64_t)info.dli_fbase; + if (mod_off != (uint64_t)load_addr) { + mod_name = String(info.dli_fname).get_file(); + } + if (addr2line_fail && info.dli_sname && info.dli_sname[0] == '_') { int status = 0; char *demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status); if (status == 0 && demangled) { - snprintf(fname, 1024, "%s", demangled); + output = String(demangled); } if (demangled) { free(demangled); } } + } else { + mod_name = ""; } - print_error(vformat("[%d] %x - %s", (int64_t)i, (uint64_t)bt_buffer[i], fname)); + // Simplify printed file paths to remove redundant `/./` sections (e.g. `/opt/godot/./core` -> `/opt/godot/core`). + print_error(vformat("[%d] %x (%s+%x) - %s", (int64_t)i, (uint64_t)bt_buffer[i], mod_name, (uint64_t)bt_buffer[i] - mod_off, output)); + } + } else { + // Otherwise fall back to trace symbols. + for (size_t i = 1; i < size; i++) { + String output = String(strings[i]); + String mod_name = "main"; + uint64_t mod_off = (uint64_t)load_addr; + + Dl_info info; + // Try to demangle the function name to provide a more readable one. + if (dladdr(bt_buffer[i], &info)) { + mod_off = (uint64_t)info.dli_fbase; + if (mod_off != (uint64_t)load_addr) { + mod_name = String(info.dli_fname).get_file(); + } + if (info.dli_sname && info.dli_sname[0] == '_') { + int status = 0; + char *demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status); + + if (status == 0 && demangled) { + output = String(demangled); + } + + if (demangled) { + free(demangled); + } + } + } else { + mod_name = ""; + } + + print_error(vformat("[%d] %x (%s+%x) - %s", (int64_t)i, (uint64_t)bt_buffer[i], mod_name, (uint64_t)bt_buffer[i] - mod_off, output)); } } diff --git a/platform/macos/crash_handler_macos.mm b/platform/macos/crash_handler_macos.mm index a072c1a322..9fa88d7bc6 100644 --- a/platform/macos/crash_handler_macos.mm +++ b/platform/macos/crash_handler_macos.mm @@ -153,32 +153,41 @@ static void handle_crash(int sig) { for (int i = 1; i < lines.size() && i < (int)size; i++) { String output = lines[i]; + String mod_name = "main"; + uint64_t mod_off = (uint64_t)load_addr; // If atos failed for this address, fall back to dladdr. - if (output.substr(0, 2) == "0x" && strings) { - char fname[1024]; + if (strings) { + bool atos_fail = output.substr(0, 2) == "0x"; + if (atos_fail) { + Vector fname_spl = String(strings[i]).split(" ", false, 3); + output = fname_spl[fname_spl.size() - 1]; + } + Dl_info info; - - snprintf(fname, 1024, "%s", strings[i]); - - if (dladdr(bt_buffer[i], &info) && info.dli_sname) { - if (info.dli_sname[0] == '_') { + if (dladdr(bt_buffer[i], &info)) { + mod_off = (uint64_t)info.dli_fbase; + if (mod_off != (uint64_t)load_addr) { + mod_name = String(info.dli_fname).get_file(); + } + if (atos_fail && info.dli_sname && info.dli_sname[0] == '_') { int status; char *demangled = abi::__cxa_demangle(info.dli_sname, nullptr, 0, &status); if (status == 0 && demangled) { - snprintf(fname, 1024, "%s", demangled); + output = String(demangled); } if (demangled) { free(demangled); } } + } else { + mod_name = ""; } - output = fname; } - print_error(vformat("[%d] %x - %s", (int64_t)i, (uint64_t)bt_buffer[i], output)); + print_error(vformat("[%d] %x (%s+%x) - %s", (int64_t)i, (uint64_t)bt_buffer[i], mod_name, (uint64_t)bt_buffer[i] - mod_off, output)); } if (strings) { @@ -189,7 +198,35 @@ 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] %x - %s", (int64_t)i, (uint64_t)bt_buffer[i], strings[i])); + Vector fname_spl = String(strings[i]).split(" ", false, 3); + String output = fname_spl[fname_spl.size() - 1]; + + String mod_name = "main"; + uint64_t mod_off = (uint64_t)load_addr; + + Dl_info info; + if (dladdr(bt_buffer[i], &info)) { + mod_off = (uint64_t)info.dli_fbase; + if (mod_off != (uint64_t)load_addr) { + mod_name = String(info.dli_fname).get_file(); + } + if (info.dli_sname && info.dli_sname[0] == '_') { + int status; + char *demangled = abi::__cxa_demangle(info.dli_sname, nullptr, 0, &status); + + if (status == 0 && demangled) { + output = String(demangled); + } + + if (demangled) { + free(demangled); + } + } + } else { + mod_name = ""; + } + + print_error(vformat("[%d] %x (%s+%x) - %s", (int64_t)i, (uint64_t)bt_buffer[i], mod_name, (uint64_t)bt_buffer[i] - mod_off, output)); } free(strings); } diff --git a/platform/windows/crash_handler_windows_seh.cpp b/platform/windows/crash_handler_windows_seh.cpp index 3e3ac6bce9..13bf9eb266 100644 --- a/platform/windows/crash_handler_windows_seh.cpp +++ b/platform/windows/crash_handler_windows_seh.cpp @@ -109,9 +109,7 @@ public: 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); + SymLoadModule64(process, nullptr, ret.image_name.c_str(), ret.module_name.c_str(), (DWORD64)ret.base_address, ret.load_size); return ret; } }; @@ -229,11 +227,11 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) { } if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &offset_from_symbol, &line)) { 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 { + } else if (!fnName.empty()) { 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] %x (%s+%x) - ???", n, (uint64_t)frame.AddrPC.Offset, mod_name, (uint64_t)frame.AddrPC.Offset - offset)); } - } else { - print_error(vformat("[%d] ???", n)); } n++; diff --git a/platform/windows/crash_handler_windows_signal.cpp b/platform/windows/crash_handler_windows_signal.cpp index 0a018e4731..d0f6231411 100644 --- a/platform/windows/crash_handler_windows_signal.cpp +++ b/platform/windows/crash_handler_windows_signal.cpp @@ -94,9 +94,7 @@ public: 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); + SymLoadModule64(process, nullptr, ret.image_name.c_str(), ret.module_name.c_str(), (DWORD64)ret.base_address, ret.load_size); return ret; } }; @@ -141,9 +139,7 @@ int symbol_callback(void *data, uintptr_t pc, const char *filename, int lineno, } 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++)); + print_error(vformat("[%d] %x (%s+%x) - ???", ch_data->index++, ch_data->pc, mod_name, ch_data->pc - offset)); } return 0; } @@ -287,6 +283,10 @@ extern void CrashHandlerException(int signal) { print_error("-- END OF C++ BACKTRACE --"); print_error("================================================================"); + if (data.sym_ok) { + SymCleanup(data.process); + } + for (const Ref &backtrace : ScriptServer::capture_script_backtraces(false)) { if (!backtrace->is_empty()) { print_error(backtrace->format());