port: add crash handler

you probably won't get symbol names, but you can still use addr2line
This commit is contained in:
fgsfds committed 2023-09-01 22:42:44 +02:00
1 parent e5e685902c
commit 701193e71a
5 files changed
+312 -16

No files matched your search

+1 -1
View File
@@ -130,7 +130,7 @@ G_DIR := src/generated/$(ROMID)
ifneq (,$(findstring windows,$(TARGET_PLATFORM)))
TARGET_CFLAGS := $(shell pkg-config sdl2 --cflags-only-I)
TARGET_LDFLAGS := $(shell pkg-config sdl2 --libs) -lz -lopengl32 -static-libstdc++
TARGET_LDFLAGS := $(shell pkg-config sdl2 --libs) -lz -lopengl32 -static-libstdc++ -ldbghelp
# on windows/mingw we need this to be built with a 32-bit compiler so it finds the correct libs
TOOLCHAIN ?= i686-w64-mingw32-
else # assume *nix
+3
View File
@@ -31,6 +31,9 @@ void *sysMemAlloc(const u32 size);
void *sysMemZeroAlloc(const u32 size);
void sysMemFree(void *ptr);
void crashInit(void);
void crashShutdown(void);
#ifdef __cplusplus
}
#endif
+298 -15
View File
@@ -1,40 +1,323 @@
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <SDL.h>
#include <PR/ultratypes.h>
#include "system.h"
#include "platform.h"
#define CRASH_MAX_MSG 4096
#define CRASH_MAX_SYM 256
#define CRASH_MAX_FRAMES 32
#define CRASH_MSG(...) \
if (msglen < CRASH_MAX_MSG) msglen += snprintf(msg + msglen, CRASH_MAX_MSG - msglen, __VA_ARGS__)
#if defined(PLATFORM_WIN32)
#include <windows.h>
#include <dbghelp.h>
#include <inttypes.h>
#include <excpt.h>
// NOTE: game builds with gcc, which means we have no PDBs for the windows version
// this means that you generally won't get any symbol names in the main executable
static LPTOP_LEVEL_EXCEPTION_FILTER prevExFilter;
static void crashStackTrace(char *msg, PEXCEPTION_POINTERS exinfo)
{
CONTEXT context = *exinfo->ContextRecord;
HANDLE process = GetCurrentProcess();
HANDLE thread = GetCurrentThread();
SymSetOptions(SymGetOptions() | SYMOPT_DEBUG | SYMOPT_LOAD_LINES);
SymInitialize(process, NULL, TRUE);
STACKFRAME64 stackframe;
memset(&stackframe, 0, sizeof(stackframe));
DWORD image;
#ifdef PLATFORM_X86
image = IMAGE_FILE_MACHINE_I386;
stackframe.AddrPC.Offset = context.Eip;
stackframe.AddrPC.Mode = AddrModeFlat;
stackframe.AddrFrame.Offset = context.Ebp;
stackframe.AddrFrame.Mode = AddrModeFlat;
stackframe.AddrStack.Offset = context.Esp;
stackframe.AddrStack.Mode = AddrModeFlat;
#elif defined(PLATFORM_X86_64)
image = IMAGE_FILE_MACHINE_AMD64;
stackframe.AddrPC.Offset = context.Rip;
stackframe.AddrPC.Mode = AddrModeFlat;
stackframe.AddrFrame.Offset = context.Rsp;
stackframe.AddrFrame.Mode = AddrModeFlat;
stackframe.AddrStack.Offset = context.Rsp;
stackframe.AddrStack.Mode = AddrModeFlat;
#else
sysLogPrintf(LOG_ERROR, "no stack trace available on this arch");
return;
#endif
DWORD disp = 0;
DWORD64 disp64 = 0;
IMAGEHLP_LINE64 line;
DWORD msglen = 0;
CRASH_MSG("EXCEPTION: 0x%08lx\n", exinfo->ExceptionRecord->ExceptionCode);
CRASH_MSG("PC: %p", exinfo->ExceptionRecord->ExceptionAddress);
if (SymGetLineFromAddr64(process, (uintptr_t)exinfo->ExceptionRecord->ExceptionAddress, &disp, &line)) {
CRASH_MSG(": %s:%lu+%lu", line.FileName, line.LineNumber, disp);
}
CRASH_MSG("\n\nBACKTRACE:\n");
char symbuf[sizeof(SYMBOL_INFO) + CRASH_MAX_SYM * sizeof(TCHAR)];
PSYMBOL_INFO sym = (PSYMBOL_INFO)symbuf;
sym->SizeOfStruct = sizeof(*sym);
sym->MaxNameLen = CRASH_MAX_SYM;
s32 i;
for (i = 0; i < CRASH_MAX_FRAMES; ++i) {
const BOOL res = StackWalk64(image, process, thread, &stackframe, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL);
if (!res) {
break;
}
CRASH_MSG("#%02d: %p", i, (void *)(uintptr_t)stackframe.AddrPC.Offset);
if (SymFromAddr(process, stackframe.AddrPC.Offset, &disp64, sym)) {
CRASH_MSG(": %s+%llu", sym->Name, disp64);
}
if(SymGetLineFromAddr64(process, stackframe.AddrPC.Offset, &disp, &line)) {
CRASH_MSG(" (%s:%lu+%lu)", line.FileName, line.LineNumber, disp);
}
CRASH_MSG("\n");
}
if (i <= 1) {
CRASH_MSG("no information\n");
} else if (i == CRASH_MAX_FRAMES) {
CRASH_MSG("...\n");
}
SymCleanup(process);
}
static long __stdcall crashHandler(PEXCEPTION_POINTERS exinfo)
{
char msg[CRASH_MAX_MSG + 1] = { 0 };
if (IsDebuggerPresent()) {
if (prevExFilter) {
return prevExFilter(exinfo);
}
return EXCEPTION_CONTINUE_EXECUTION;
}
sysLogPrintf(LOG_ERROR, "FATAL: Crashed: PC=%p CODE=0x%08lx", exinfo->ExceptionRecord->ExceptionAddress, exinfo->ExceptionRecord->ExceptionCode);
fflush(stderr);
fflush(stdout);
crashStackTrace(msg, exinfo);
sysFatalError("Crash!\n\n%s", msg);
return EXCEPTION_CONTINUE_EXECUTION;
}
#elif defined(PLATFORM_LINUX)
#include <ucontext.h>
#include <signal.h>
#include <execinfo.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/fcntl.h>
static struct sigaction prevSigAction;
static s32 crashIsDebuggerPresent(void)
{
static s32 result = -1;
if (result >= 0) {
return result;
}
char buf[4096] = { 0 };
int fd = open("/proc/self/status", O_RDONLY);
if (fd < 0) {
result = 0;
return 0;
}
int rx = read(fd, buf, sizeof(buf) - 1);
close(fd);
if (rx <= 0) {
result = 0;
return 0;
}
buf[rx] = 0;
char *str = strstr(buf, "TracerPid:");
if (!str) {
result = 0;
return 0;
}
str += 10;
while (*str && !isdigit(*str)) {
if (*str == '\n') {
result = 0;
return 0;
}
++str;
}
result = (atoi(str) != 0);
return result;
}
static void crashStackTrace(char *msg, s32 sig, void *pc)
{
u32 msglen = 0;
void *frames[CRASH_MAX_FRAMES] = { NULL };
const s32 nframes = backtrace(frames, CRASH_MAX_FRAMES);
if (nframes <= 0) {
CRASH_MSG("no information\n");
return;
}
char **strings = backtrace_symbols(frames, nframes);
CRASH_MSG("SIGNAL: %d\n", sig);
CRASH_MSG("PC: ");
if (pc) {
CRASH_MSG("%p\n", pc);
} if (strings) {
CRASH_MSG("%s\n", strings[0]);
} else {
CRASH_MSG("%p\n", frames[0]);
}
CRASH_MSG("\nBACKTRACE:\n");
s32 i;
for (i = 0; i < nframes; ++i) {
CRASH_MSG("#%02d: ", i);
if (strings && strings[i]) {
CRASH_MSG("%s\n", strings[i]);
} else {
CRASH_MSG("%p\n", frames[i]);
}
}
if (i == CRASH_MAX_FRAMES) {
CRASH_MSG("...\n");
} else if (i <= 1) {
CRASH_MSG("no information\n");
}
free(strings);
}
static void crashHandler(s32 sig, siginfo_t *siginfo, void *ctx)
{
char msg[CRASH_MAX_MSG + 1] = { 0 };
if (crashIsDebuggerPresent()) {
return;
}
void *pc = NULL;
if (ctx) {
ucontext_t *ucontext = (ucontext_t *)ctx;
#ifdef PLATFORM_X86
pc = (void *)ucontext->uc_mcontext.gregs[REG_EIP];
#elif defined(PLATFORM_X86_64)
pc = (void *)ucontext->uc_mcontext.gregs[REG_RIP];
#elif defined(PLATFORM_ARM) && defined(PLATFORM_64BIT)
pc = (void *)ucontext->uc_mcontext.pc;
#elif defined(PLATFORM_ARM)
pc = (void *)ucontext->uc_mcontext.arm_pc;
#endif
}
sysLogPrintf(LOG_ERROR, "FATAL: Crashed: PC=%p SIGNAL=%d", pc, sig);
fflush(stderr);
fflush(stdout);
crashStackTrace(msg, sig, pc);
sysFatalError("Crash!\n\n%s", msg);
}
#endif
u8 g_CrashHasMessage = 0;
s32 g_CrashEnabled = 0;
static char crashMsg[1024];
void crashInit(void)
{
#ifdef PLATFORM_WIN32
SetErrorMode(SEM_FAILCRITICALERRORS);
prevExFilter = SetUnhandledExceptionFilter(crashHandler);
g_CrashEnabled = 1;
#elif defined(PLATFORM_LINUX)
struct sigaction sigact = { 0 };
sigact.sa_flags = SA_SIGINFO | SA_ONSTACK;
sigact.sa_sigaction = crashHandler;
sigaction(SIGSEGV, &sigact, &prevSigAction);
sigaction(SIGABRT, &sigact, &prevSigAction);
sigaction(SIGBUS, &sigact, &prevSigAction);
sigaction(SIGILL, &sigact, &prevSigAction);
g_CrashEnabled = 1;
#endif
}
void crashShutdown(void)
{
#ifdef PLATFORM_WIN32
if (prevExFilter) {
SetUnhandledExceptionFilter(prevExFilter);
}
#elif defined(PLATFORM_LINUX)
sigaction(SIGSEGV, &prevSigAction, NULL);
sigaction(SIGABRT, &prevSigAction, NULL);
sigaction(SIGBUS, &prevSigAction, NULL);
sigaction(SIGILL, &prevSigAction, NULL);
#endif
g_CrashEnabled = 0;
}
void crashCreateThread(void)
{
// TODO: install a crash handler of some sort
}
void crashSetMessage(char *string)
{
strncpy(crashMsg, string, sizeof(crashMsg) - 1);
sysLogPrintf(LOG_ERROR, "crashSetMessage: %s", crashMsg);
g_CrashHasMessage = 1;
}
void crashReset(void)
{
crashMsg[0] = 0;
g_CrashHasMessage = 0;
}
void crashAppendChar(char c)
{
g_CrashHasMessage = 1;
for (u32 i = 0; i < sizeof(crashMsg) - 1; ++i) {
if (crashMsg[i] == '\0') {
crashMsg[i] = c;
crashMsg[i + 1] = '\0';
break;
}
}
}
+2
View File
@@ -60,6 +60,7 @@ static void cleanup(void)
sysLogPrintf(LOG_NOTE, "shutdown");
inputSaveConfig();
configSave(CONFIG_FNAME);
crashShutdown();
// TODO: actually shut down all subsystems
}
@@ -75,6 +76,7 @@ static void gameLoadConfig(void)
int main(int argc, const char **argv)
{
crashInit();
sysInit(argc, argv);
fsInit();
configInit();
+8
View File
@@ -112,8 +112,16 @@ void sysLogPrintf(s32 level, const char *fmt, ...)
void sysFatalError(const char *fmt, ...)
{
static s32 alreadyCrashed = 0;
if (alreadyCrashed) {
abort();
}
char errmsg[2048] = { 0 };
alreadyCrashed = 1;
va_list ap;
va_start(ap, fmt);
vsnprintf(errmsg, sizeof(errmsg), fmt, ap);