From 701193e71ad57d62453a553318853df7da8fdb00 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Fri, 1 Sep 2023 22:42:23 +0200 Subject: [PATCH 1/5] port: add crash handler you probably won't get symbol names, but you can still use addr2line --- Makefile.port | 2 +- port/include/system.h | 3 + port/src/crash.c | 313 ++++++++++++++++++++++++++++++++++++++++-- port/src/main.c | 2 + port/src/system.c | 8 ++ 5 files changed, 312 insertions(+), 16 deletions(-) diff --git a/Makefile.port b/Makefile.port index ecf4e9bed..0ca2bd254 100644 --- a/Makefile.port +++ b/Makefile.port @@ -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 diff --git a/port/include/system.h b/port/include/system.h index b744122a7..cc9d4277e 100644 --- a/port/include/system.h +++ b/port/include/system.h @@ -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 diff --git a/port/src/crash.c b/port/src/crash.c index abe1c3e25..d41baa4a0 100644 --- a/port/src/crash.c +++ b/port/src/crash.c @@ -1,40 +1,323 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif + #include #include #include +#include #include #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 +#include +#include +#include + +// 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 +#include +#include +#include +#include +#include + +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; - } - } + } diff --git a/port/src/main.c b/port/src/main.c index ae6abc84f..6e7d04dd4 100644 --- a/port/src/main.c +++ b/port/src/main.c @@ -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(); diff --git a/port/src/system.c b/port/src/system.c index df874b8a7..73cd1727c 100644 --- a/port/src/system.c +++ b/port/src/system.c @@ -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); From 37907dccfc2ed8dd34a14a099eb78e406cb274f8 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Fri, 1 Sep 2023 22:49:45 +0200 Subject: [PATCH 2/5] port: small crashhandler fix --- port/src/crash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/src/crash.c b/port/src/crash.c index d41baa4a0..fec0a9793 100644 --- a/port/src/crash.c +++ b/port/src/crash.c @@ -58,7 +58,7 @@ static void crashStackTrace(char *msg, PEXCEPTION_POINTERS exinfo) stackframe.AddrStack.Offset = context.Rsp; stackframe.AddrStack.Mode = AddrModeFlat; #else - sysLogPrintf(LOG_ERROR, "no stack trace available on this arch"); + snprintf(msg, CRASH_MAX_MSG, "no stack trace available on this arch\n"); return; #endif From cc079e842bac9bb2f0ff8345dd0791261e9b4476 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Fri, 1 Sep 2023 22:58:45 +0200 Subject: [PATCH 3/5] port: small crashhandler fix 2 --- port/src/crash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/src/crash.c b/port/src/crash.c index fec0a9793..c87a2b05c 100644 --- a/port/src/crash.c +++ b/port/src/crash.c @@ -204,7 +204,7 @@ static void crashStackTrace(char *msg, s32 sig, void *pc) CRASH_MSG("PC: "); if (pc) { CRASH_MSG("%p\n", pc); - } if (strings) { + } else if (strings) { CRASH_MSG("%s\n", strings[0]); } else { CRASH_MSG("%p\n", frames[0]); From 56df5b4054f93dc5e849621a8ddc6fb463b3e84a Mon Sep 17 00:00:00 2001 From: fgsfds Date: Fri, 1 Sep 2023 23:08:52 +0200 Subject: [PATCH 4/5] port: ignore invert pitch setting when mouse aiming --- src/game/bondmove.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/game/bondmove.c b/src/game/bondmove.c index 62d42ee54..949dcecbd 100644 --- a/src/game/bondmove.c +++ b/src/game/bondmove.c @@ -1295,12 +1295,19 @@ void bmoveProcessInput(bool allowc1x, bool allowc1y, bool allowc1buttons, bool i movedata.aimturnleftspeed = (g_Vars.currentplayer->swivelpos[0] - -0.9f) / -0.1f; movedata.aimturnrightspeed = 0.f; } + f32 vertaup = 0.f, vertadown = 0.f; if (g_Vars.currentplayer->swivelpos[1] > 0.9f) { - movedata.speedvertaup = (g_Vars.currentplayer->swivelpos[1] - 0.9f) / 0.1f; - movedata.speedvertadown = 0.f; + vertaup = (g_Vars.currentplayer->swivelpos[1] - 0.9f) / 0.1f; } else if (g_Vars.currentplayer->swivelpos[1] < -0.9f) { - movedata.speedvertadown = (g_Vars.currentplayer->swivelpos[1] - -0.9f) / -0.1f; - movedata.speedvertaup = 0.f; + vertadown = (g_Vars.currentplayer->swivelpos[1] - -0.9f) / -0.1f; + } + // Uninvert pitch if needed + if (movedata.invertpitch) { + movedata.speedvertaup = vertadown; + movedata.speedvertadown = vertaup; + } else { + movedata.speedvertaup = vertaup; + movedata.speedvertadown = vertadown; } } else { // Reset mouse aim position when not mouse aiming From 0942f61affc6fe19a3cc45a81ac29f73fc778a06 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sat, 2 Sep 2023 16:18:06 +0200 Subject: [PATCH 5/5] port: possibly fix skyboxes by just rendering them with normal triangles --- src/game/sky.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/game/sky.c b/src/game/sky.c index 77f443941..588c7209c 100644 --- a/src/game/sky.c +++ b/src/game/sky.c @@ -15,6 +15,7 @@ #include "data.h" #include "types.h" #ifndef PLATFORM_N64 +#include "game/gfxmemory.h" #include "game/game_13c510.h" #include "game/player.h" #endif @@ -826,6 +827,7 @@ Gfx *skyRender(Gfx *gdl) gDPSetRenderMode(gdl++, G_RM_OPA_SURF, G_RM_OPA_SURF2); +#ifdef PLATFORM_N64 if (numvertices == 4) { gdl = skyRenderTri(gdl, &watervertices2d[0], &watervertices2d[1], &watervertices2d[3], 130.0f, true); @@ -846,6 +848,40 @@ Gfx *skyRender(Gfx *gdl) // 1 corner is on the ground gdl = skyRenderTri(gdl, &watervertices2d[0], &watervertices2d[1], &watervertices2d[2], 130.0f, true); } +#else + Vtx *verts = gfxAllocateVertices(5); + Col *cols = gfxAllocateColours(5); + Mtxf *mtx = gfxAllocateMatrix(); + mtx4MultMtx4(camGetWorldToScreenMtxf(), &g_SkyMtx, mtx); + mtxF2L(mtx, mtx); + + gSPMatrix(gdl++, osVirtualToPhysical(mtx), G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_PUSH); + gSPColor(gdl++, osVirtualToPhysical(cols), numvertices); + gSPVertex(gdl++, osVirtualToPhysical(verts), numvertices, 0); + + for (s32 i = 0; i < numvertices; ++i) { + verts[i].x = watervertices3d[i].x; + verts[i].y = watervertices3d[i].y; + verts[i].z = watervertices3d[i].z; + verts[i].s = watervertices3d[i].s; + verts[i].t = watervertices3d[i].t; + verts[i].colour = i * 4; + cols[i].r = watervertices3d[i].r; + cols[i].g = watervertices3d[i].g; + cols[i].b = watervertices3d[i].b; + cols[i].a = watervertices3d[i].a; + } + + if (numvertices == 4) { + gSPTri2(gdl++, 0, 1, 3, 3, 2, 0); + } else if (numvertices == 5) { + gSPTri3(gdl++, 0, 1, 2, 0, 2, 3, 0, 3, 4); + } else if (numvertices == 3) { + gSPTri1(gdl++, 0, 1, 2); + } + + gSPPopMatrix(gdl++, G_MTX_MODELVIEW); +#endif } } @@ -1252,6 +1288,7 @@ Gfx *skyRender(Gfx *gdl) skyvertices2d[i].y = skyClamp(skyvertices2d[i].y, camGetScreenTop() * 4.0f, (camGetScreenTop() + camGetScreenHeight()) * 4.0f - 1.0f); } +#ifdef PLATFORM_N64 if (numvertices == 4) { if (cornerstate == CORNERSTATE_TOP) { if (sp548 < sp54c) { @@ -1292,6 +1329,41 @@ Gfx *skyRender(Gfx *gdl) // One corner is in the sky gdl = skyRenderTri(gdl, &skyvertices2d[0], &skyvertices2d[1], &skyvertices2d[2], 130.0f, true); } +#else + Vtx *verts = gfxAllocateVertices(5); + Col *cols = gfxAllocateColours(5); + Mtxf *mtx = gfxAllocateMatrix(); + mtx4MultMtx4(camGetWorldToScreenMtxf(), &g_SkyMtx, mtx); + mtxF2L(mtx, mtx); + + gSPMatrix(gdl++, osVirtualToPhysical(mtx), G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_PUSH); + gSPColor(gdl++, osVirtualToPhysical(cols), numvertices); + gSPVertex(gdl++, osVirtualToPhysical(verts), numvertices, 0); + + const f32 iscale = 1.f / scale; + for (s32 i = 0; i < numvertices; ++i) { + verts[i].x = skyvertices3d[i].x; + verts[i].y = skyvertices3d[i].y; + verts[i].z = skyvertices3d[i].z; + verts[i].s = skyvertices3d[i].s; + verts[i].t = skyvertices3d[i].t; + verts[i].colour = i * 4; + cols[i].r = skyvertices3d[i].r; + cols[i].g = skyvertices3d[i].g; + cols[i].b = skyvertices3d[i].b; + cols[i].a = skyvertices3d[i].a; + } + + if (numvertices == 4) { + gSPTri2(gdl++, 0, 1, 3, 3, 2, 0); + } else if (numvertices == 5) { + gSPTri3(gdl++, 0, 1, 2, 0, 2, 3, 0, 3, 4); + } else if (numvertices == 3) { + gSPTri1(gdl++, 0, 1, 2); + } + + gSPPopMatrix(gdl++, G_MTX_MODELVIEW); +#endif } return gdl;