Skip to content

Commit

Permalink
core: use backtrace_symbols_fd()
Browse files Browse the repository at this point in the history
Demangle on a separate process because of
dynamic memory allocations involved.

Fixes: #255
  • Loading branch information
dbartolini committed Jan 18, 2025
1 parent 1c9651d commit 2dc9f6a
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 47 deletions.
6 changes: 6 additions & 0 deletions src/core/error/callstack.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
10 changes: 10 additions & 0 deletions src/core/error/callstack_emscripten.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
192 changes: 149 additions & 43 deletions src/core/error/callstack_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,24 @@
#include "core/error/callstack.h"
#include "core/process.h"
#include "core/strings/string.inl"
#include <cxxabi.h>
#include <execinfo.h>
#include <cxxabi.h> // __cxa_demangle
#include <execinfo.h> // backtrace, backtrace_symbols_fd
#include <stdio.h> // fdopen, fgets
#include <stdlib.h> // free
#include <string.h> // strchr
#include <sys/wait.h> // waitpid
#include <unistd.h> // getpid
#include <stb_sprintf.h>
#include <stdlib.h> // free
#include <string.h> // strchr
#include <unistd.h> // 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];
Expand All @@ -46,50 +53,149 @@ namespace error
return "<addr2line missing>";
}

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) {
char *nl = (char *)strnl(msg);
if (*(nl - 1) == '\n')
*(nl - 1) = '\0';
if (strlen32(msg) == 0)
break;

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
Expand Down
14 changes: 12 additions & 2 deletions src/core/error/callstack_noop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
10 changes: 10 additions & 0 deletions src/core/error/callstack_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
15 changes: 13 additions & 2 deletions src/device/main_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
}
Expand Down

0 comments on commit 2dc9f6a

Please sign in to comment.