diff --git a/src/core/error/callstack.h b/src/core/error/callstack.h index d4f6cf5b6..d9b8b7616 100644 --- a/src/core/error/callstack.h +++ b/src/core/error/callstack.h @@ -12,6 +12,12 @@ namespace crown { namespace error { + /// Initializes the callstack subsystem. + s32 callstack_init(); + + /// Shutdowns the callstack subsystem. + void callstack_shutdown(); + /// Logs the current call stack. void callstack(log_internal::System system, LogSeverity::Enum severity = LogSeverity::LOG_INFO); diff --git a/src/core/error/callstack_emscripten.cpp b/src/core/error/callstack_emscripten.cpp index fada10b76..4c51aeed2 100644 --- a/src/core/error/callstack_emscripten.cpp +++ b/src/core/error/callstack_emscripten.cpp @@ -15,6 +15,16 @@ namespace crown { namespace error { + s32 callstack_init() + { + return 0; + } + + void callstack_shutdown() + { + CE_NOOP(); + } + void callstack(log_internal::System system, LogSeverity::Enum severity) { int size = emscripten_get_callstack(EM_LOG_C_STACK | EM_LOG_JS_STACK, NULL, 0); diff --git a/src/core/error/callstack_linux.cpp b/src/core/error/callstack_linux.cpp index 76d04b069..246e8c8f3 100644 --- a/src/core/error/callstack_linux.cpp +++ b/src/core/error/callstack_linux.cpp @@ -9,17 +9,24 @@ #include "core/error/callstack.h" #include "core/process.h" #include "core/strings/string.inl" -#include -#include +#include // __cxa_demangle +#include // backtrace, backtrace_symbols_fd +#include // fdopen, fgets +#include // free +#include // strchr +#include // waitpid +#include // getpid #include -#include // free -#include // strchr -#include // getpid namespace crown { namespace error { + static int symbol_fds[2]; + static int demangled_fds[2]; + static int exit_fds[2]; + static pid_t demangler; + static const char *addr2line(char *line, int len, const char *addr) { char process_exe[256]; @@ -46,50 +53,148 @@ namespace error return ""; } + static void demangler_main() + { + FILE *fp = fdopen(symbol_fds[0], "r"); + if (fp == NULL) + return; + + while (true) { + fd_set fdset; + + FD_ZERO(&fdset); + FD_SET(symbol_fds[0], &fdset); + FD_SET(exit_fds[0], &fdset); + int maxfd = max(symbol_fds[0], exit_fds[0]); + + if (select(maxfd + 1, &fdset, NULL, NULL, NULL) <= 0) + continue; + + if (FD_ISSET(exit_fds[0], &fdset)) { + break; + } else if (FD_ISSET(symbol_fds[0], &fdset)) { + char msg[512]; + char buf[512]; + + while (fgets(msg, sizeof(msg), fp) != NULL) { + u32 len = strlen32(msg); + if (len <= 1) + break; + msg[len - 1] = '\0'; + + char *mangled_name = strchr(msg, '('); + char *offset_begin = strchr(msg, '+'); + char *offset_end = strchr(msg, ')'); + char *addr_begin = strchr(msg, '['); + char *addr_end = strchr(msg, ']'); + + // Attempt to demangle the symbol. + if (mangled_name && offset_begin && offset_end && mangled_name < offset_begin) { + *mangled_name++ = '\0'; + *offset_begin++ = '\0'; + *offset_end++ = '\0'; + *addr_begin++ = '\0'; + *addr_end++ = '\0'; + + int demangle_ok; + char *real_name = abi::__cxa_demangle(mangled_name, 0, 0, &demangle_ok); + char line[256]; + memset(line, 0, sizeof(line)); + + stbsp_snprintf(buf + , sizeof(buf) + , "%s(%s+%s) in %s" + , msg + , (demangle_ok == 0 ? real_name : mangled_name) + , offset_begin + , addr2line(line, sizeof(line), addr_begin) + ); + + free(real_name); + } else { + stbsp_snprintf(buf, sizeof(buf), "%s", msg); + } + + write(demangled_fds[1], buf, sizeof(buf)); + } + } + } + + fclose(fp); + } + + void callstack_shutdown() + { + int wstatus; + + if (demangler <= 0) + return; + + write(exit_fds[1], "q", 1); + waitpid(demangler, &wstatus, 0); + demangler = 0; + } + + s32 callstack_init() + { + s32 ret = -1; + + if (pipe(symbol_fds) < 0) + goto close_0; + if (pipe(demangled_fds) < 0) + goto close_1; + if (pipe(exit_fds) < 0) + goto close_2; + + demangler = fork(); + if (demangler == -1) { + close(symbol_fds[0]); + close(symbol_fds[1]); + close(demangled_fds[0]); + close(demangled_fds[1]); + close(exit_fds[0]); + close(exit_fds[1]); + return -1; + } else if (demangler == 0) { + close(symbol_fds[1]); + close(demangled_fds[0]); + close(exit_fds[1]); + demangler_main(); + } else { + ret = 0; + } + + close(exit_fds[0]); + close_2: + close(demangled_fds[1]); + close_1: + close(symbol_fds[0]); + close_0: + return ret; + } + void callstack(log_internal::System system, LogSeverity::Enum severity) { + char msg[512] = {}; void *array[64]; + + if (demangler <= 0) { + log_internal::logx(severity, system, "Callstack unavailable."); + return; + } + + // Get symbols and write them to demangler process. int size = backtrace(array, countof(array)); - char **messages = backtrace_symbols(array, size); - - // skip first stack frame (points here) - for (int i = 1; i < size && messages != NULL; ++i) { - char *msg = messages[i]; - char *mangled_name = strchr(msg, '('); - char *offset_begin = strchr(msg, '+'); - char *offset_end = strchr(msg, ')'); - char *addr_begin = strchr(msg, '['); - char *addr_end = strchr(msg, ']'); - - // Attempt to demangle the symbol - if (mangled_name && offset_begin && offset_end && mangled_name < offset_begin) { - *mangled_name++ = '\0'; - *offset_begin++ = '\0'; - *offset_end++ = '\0'; - *addr_begin++ = '\0'; - *addr_end++ = '\0'; - - int demangle_ok; - char *real_name = abi::__cxa_demangle(mangled_name, 0, 0, &demangle_ok); - char line[256]; - memset(line, 0, sizeof(line)); - - log_internal::logx(severity - , system - , "[%2d] %s: (%s)+%s in %s" - , i - , msg - , (demangle_ok == 0 ? real_name : mangled_name) - , offset_begin - , addr2line(line, sizeof(line), addr_begin) - ); - - free(real_name); - } else { + backtrace_symbols_fd(array, size, symbol_fds[1]); + write(symbol_fds[1], "\n", 1); + + // Log demangled symbols. + for (int i = 0; i < size; ++i) { + if (read(demangled_fds[0], msg, sizeof(msg)) <= 0) + break; + if (i >= 1) // Skip this very function. log_internal::logx(severity, system, "[%2d] %s", i, msg); - } } - free(messages); } } // namespace error diff --git a/src/core/error/callstack_noop.cpp b/src/core/error/callstack_noop.cpp index 35e0688f9..ba1cacddd 100644 --- a/src/core/error/callstack_noop.cpp +++ b/src/core/error/callstack_noop.cpp @@ -12,13 +12,23 @@ namespace crown { namespace error { + s32 callstack_init() + { + return 0; + } + + void callstack_shutdown() + { + CE_NOOP(); + } + void callstack(log_internal::System system, LogSeverity::Enum severity) { - log_internal::logx(severity, system, "Not supported"); + log_internal::logx(severity, system, "Callstack not supported on this platform."); } } // namespace error } // namespace crown -#endif +#endif // if CROWN_PLATFORM_ANDROID diff --git a/src/core/error/callstack_windows.cpp b/src/core/error/callstack_windows.cpp index 2ec0cb002..88cbeb95b 100644 --- a/src/core/error/callstack_windows.cpp +++ b/src/core/error/callstack_windows.cpp @@ -21,6 +21,16 @@ namespace crown { namespace error { + s32 callstack_init() + { + return 0; + } + + void callstack_shutdown() + { + CE_NOOP(); + } + void callstack(log_internal::System system, LogSeverity::Enum severity) { SymInitialize(GetCurrentProcess(), NULL, TRUE); diff --git a/src/device/main_linux.cpp b/src/device/main_linux.cpp index 78042ff4b..c2b02c14a 100644 --- a/src/device/main_linux.cpp +++ b/src/device/main_linux.cpp @@ -8,6 +8,7 @@ #if CROWN_PLATFORM_LINUX #include "core/command_line.h" #include "core/containers/array.inl" +#include "core/error/callstack.h" #include "core/guid.h" #include "core/memory/globals.h" #include "core/memory/memory.inl" @@ -1031,12 +1032,24 @@ struct InitGlobals } }; +void at_exit() +{ + error::callstack_shutdown(); +} + } // namespace crown int main(int argc, char **argv) { using namespace crown; + if (error::callstack_init() != 0) + return EXIT_FAILURE; + if (atexit(at_exit) != 0) { + error::callstack_shutdown(); + return EXIT_FAILURE; + } + struct sigaction act; // code-format off act.sa_handler = [](int signum) { @@ -1055,12 +1068,10 @@ int main(int argc, char **argv) case SIGPIPE: case SIGSEGV: case SIGSYS: - // FIXME: only use signal-safe functions. error::abort("Signal %d", signum); break; default: - // FIXME: only use signal-safe functions. error::abort("Unhandled signal %d", signum); break; }