1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2025-06-08 01:22:02 +00:00

attempt to generate a stacktrace on crash. only tested on windows so far.

This commit is contained in:
fgenesis 2025-05-30 06:17:55 +02:00
parent 3ac8b54afd
commit 1077849746
3 changed files with 525 additions and 0 deletions

View file

@ -24,9 +24,87 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <SDL_main.h> #include <SDL_main.h>
#include "Randomness.h" #include "Randomness.h"
#define B_STACKTRACE_IMPL
#include "b_stacktrace.h"
static void onCrash()
{
char *bt = b_stacktrace_get_string();
fputs(bt, stderr);
time_t rawtime;
time(&rawtime);
struct tm *timeinfo = localtime(&rawtime);
char buf[128];
strftime(buf, sizeof(buf), "aquaria-crash-%Y-%m-%d_%H-%M-%S.txt", timeinfo);
FILE *f = fopen(buf, "w");
if(f)
{
fwrite(bt, 1, strlen(bt), f);
fflush(f);
fclose(f);
openURL(buf);
}
else
messageBox("Aquaria crash!", bt);
free(bt);
}
#ifdef _WIN32
#include <DbgHelp.h>
#pragma comment(lib, "DbgHelp.lib")
static LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* ep)
{
DWORD code = ep->ExceptionRecord->ExceptionCode;
if (code == DBG_PRINTEXCEPTION_C || code == DBG_CONTROL_C)
return EXCEPTION_CONTINUE_SEARCH;
HANDLE hFile = CreateFileA("aquaria-minidump.dmp", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
MINIDUMP_EXCEPTION_INFORMATION dumpInfo = { GetCurrentThreadId(), ep, FALSE };
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpWithDataSegs, &dumpInfo, NULL, NULL);
CloseHandle(hFile);
}
onCrash();
return EXCEPTION_CONTINUE_SEARCH;
}
#else
#include <signal.h>
static void signalHandler(int signal, siginfo_t* info, void* context)
{
onCrash();
_exit(-1);
}
#endif
void setupCrashHandler()
{
#ifdef _WIN32
SetUnhandledExceptionFilter(UnhandledExceptionHandler);
#else
struct sigaction sa;
sa.sa_sigaction = my_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGABRT, &sa, NULL);
sigaction(SIGFPE, &sa, NULL);
sigaction(SIGILL, &sa, NULL);
sigaction(SIGBUS, &sa, NULL);
#endif
}
extern "C" int main(int argc,char *argv[]) extern "C" int main(int argc,char *argv[])
{ {
setupCrashHandler();
std::string dsqParam = ""; // fileSystem std::string dsqParam = ""; // fileSystem
std::string extraDataDir = ""; std::string extraDataDir = "";

View file

@ -49,7 +49,10 @@ if(MSVC)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /ZI") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /ZI")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /SAFESEH:NO") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /SAFESEH:NO")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO")
else()
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Zi")
endif() endif()
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEBUG /OPT:REF /OPT:ICF")
elseif(GCC OR CLANG) elseif(GCC OR CLANG)
set(_FNO_STACK_PROTECTOR "") set(_FNO_STACK_PROTECTOR "")
# See if -fno-stack-protector is available to us. # See if -fno-stack-protector is available to us.

444
ExternalLibs/b_stacktrace.h Normal file
View file

@ -0,0 +1,444 @@
#if !defined(B_STACKTRACE_INCLUDED)
#define B_STACKTRACE_INCLUDED (1)
/*
b_stacktrace v0.23 -- a cross-platform stack-trace generator
SPDX-License-Identifier: MIT
URL: https://github.com/iboB/b_stacktrace
Usage
=====
#define B_STACKTRACE_IMPL before including b_stacktrace.h in *one* C or C++
file to create the implementation
#include "b_stacktrace.h" to get access to the following functions:
char* b_stacktrace_get_string();
Returns a human-readable stack-trace string from the point of view of the
caller.
The string is allocated with `malloc` and needs to be freed with `free`
b_stacktrace_handle b_stacktrace_get();
Returns a stack-trace handle from the point of view of the caller which
can be expanded to a string via b_stacktrace_to_string.
The handle is allocated with `malloc` and needs to be freed with `free`
b_stacktrace_to_string(b_stacktrace_handle stacktrace);
Converts a stack-trace handle to a human-readable string.
The string is allocated with `malloc` and needs to be freed with `free`
B_STACKTRACE_API int b_stacktrace_depth(b_stacktrace_handle stacktrace);
Returns the number of entries (frames) in the stack-trace handle.
Config
======
#define B_STACKTRACE_API to custom export symbols to export the library
functions from a shared lib
Revision History
================
* 0.23 (2022-12-20) Add b_stacktrace_depth
* 0.21 (2022-12-20) Fixed typo
* 0.20 (2022-12-18) Beta.
Expanded interface
Minor fixes
* 0.10 (2020-12-07) Initial public release. Alpha version
MIT License
===========
Copyright (c) 2020-2025 Borislav Stanimirov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#if !defined(B_STACKTRACE_API)
#define B_STACKTRACE_API extern
#endif
#ifdef __cplusplus
extern "C" {
#endif
/*
A stacktrace handle
*/
typedef struct b_stacktrace_tag* b_stacktrace_handle;
/*
Returns a stack-trace handle from the point of view of the caller which
can be expanded to a string via b_stacktrace_to_string.
The handle is allocated with `malloc` and needs to be freed with `free`
*/
B_STACKTRACE_API b_stacktrace_handle b_stacktrace_get();
/*
Returns the number of entries (frames) in the stack-trace handle.
*/
B_STACKTRACE_API int b_stacktrace_depth(b_stacktrace_handle stacktrace);
/*
Converts a stack-trace handle to a human-readable string.
The string is allocated with `malloc` and needs to be freed with `free`
*/
B_STACKTRACE_API char* b_stacktrace_to_string(b_stacktrace_handle stacktrace);
/*
Returns a human-readable stack-trace string from the point of view of the
caller.
The string is allocated with `malloc` and needs to be freed with `free`
*/
B_STACKTRACE_API char* b_stacktrace_get_string(void);
/* version */
#define B_STACKTRACE_VER_MAJOR 0
#define B_STACKTRACE_VER_MINOR 20
#ifdef __cplusplus
}
#endif
#if defined(B_STACKTRACE_IMPL)
#if defined(__linux__) && !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
typedef struct print_buf {
char* buf;
int pos;
int size;
} print_buf;
static print_buf buf_init(void) {
print_buf ret = {
(char*) malloc(1024),
0,
1024
};
return ret;
}
static void buf_printf(print_buf* b, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
const int len = vsnprintf(NULL, 0, fmt, args) + 1;
va_end(args);
const int new_end = b->pos + len;
if (new_end > b->size) {
while (new_end > b->size) b->size *= 2;
b->buf = (char*)realloc(b->buf, b->size);
}
va_start(args, fmt);
b->pos += vsnprintf(b->buf + b->pos, len, fmt, args);
va_end(args);
}
char* b_stacktrace_get_string(void) {
b_stacktrace_handle h = b_stacktrace_get();
char* ret = b_stacktrace_to_string(h);
free(h);
return ret;
}
#define B_STACKTRACE_MAX_DEPTH 1024
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <TlHelp32.h>
#include <DbgHelp.h>
#pragma comment(lib, "DbgHelp.lib")
#define B_STACKTRACE_ERROR_FLAG ((DWORD64)1 << 63)
typedef struct b_stacktrace_entry {
DWORD64 AddrPC_Offset;
DWORD64 AddrReturn_Offset;
} b_stacktrace_entry;
static int SymInitialize_called = 0;
b_stacktrace_handle b_stacktrace_get(void) {
HANDLE process = GetCurrentProcess();
HANDLE thread = GetCurrentThread();
CONTEXT context;
STACKFRAME64 frame; /* in/out stackframe */
DWORD imageType;
b_stacktrace_entry* ret = (b_stacktrace_entry*)malloc(B_STACKTRACE_MAX_DEPTH * sizeof(b_stacktrace_entry));
int i = 0;
if (!SymInitialize_called) {
SymInitialize(process, NULL, TRUE);
SymInitialize_called = 1;
}
RtlCaptureContext(&context);
memset(&frame, 0, sizeof(frame));
#ifdef _M_IX86
imageType = IMAGE_FILE_MACHINE_I386;
frame.AddrPC.Offset = context.Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Ebp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Esp;
frame.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
imageType = IMAGE_FILE_MACHINE_AMD64;
frame.AddrPC.Offset = context.Rip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Rsp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Rsp;
frame.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
imageType = IMAGE_FILE_MACHINE_IA64;
frame.AddrPC.Offset = context.StIIP;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.IntSp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrBStore.Offset = context.RsBSP;
frame.AddrBStore.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.IntSp;
frame.AddrStack.Mode = AddrModeFlat;
#else
#error "Platform not supported!"
#endif
while (1) {
b_stacktrace_entry* cur = ret + i++;
if (i == B_STACKTRACE_MAX_DEPTH) {
cur->AddrPC_Offset = 0;
cur->AddrReturn_Offset = 0;
break;
}
if (!StackWalk64(imageType, process, thread, &frame, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) {
cur->AddrPC_Offset = frame.AddrPC.Offset;
cur->AddrReturn_Offset = B_STACKTRACE_ERROR_FLAG; /* mark error */
cur->AddrReturn_Offset |= GetLastError();
break;
}
cur->AddrPC_Offset = frame.AddrPC.Offset;
cur->AddrReturn_Offset = frame.AddrReturn.Offset;
if (frame.AddrReturn.Offset == 0) {
break;
}
}
return (b_stacktrace_handle)(ret);
}
int b_stacktrace_depth(b_stacktrace_handle h) {
const b_stacktrace_entry* entries = (b_stacktrace_entry*)h;
int i = 0;
while (1) {
const b_stacktrace_entry* cur = entries + i++;
if (cur->AddrReturn_Offset == 0) {
break;
}
}
return i;
}
char* b_stacktrace_to_string(b_stacktrace_handle h) {
const b_stacktrace_entry* entries = (b_stacktrace_entry*)h;
int i = 0;
HANDLE process = GetCurrentProcess();
print_buf out = buf_init();
IMAGEHLP_SYMBOL64* symbol = (IMAGEHLP_SYMBOL64*)malloc(sizeof(IMAGEHLP_SYMBOL64) + 1024);
symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
symbol->MaxNameLength = 1024;
while (1) {
IMAGEHLP_LINE64 lineData;
DWORD lineOffset = 0;
DWORD64 symOffset = 0;
const b_stacktrace_entry* cur = entries + i++;
if (cur->AddrReturn_Offset & B_STACKTRACE_ERROR_FLAG) {
DWORD error = cur->AddrReturn_Offset & 0xFFFFFFFF;
buf_printf(&out, "StackWalk64 error: %d @ %p\n", error, cur->AddrPC_Offset);
break;
}
if (cur->AddrPC_Offset == cur->AddrReturn_Offset) {
buf_printf(&out, "Stack overflow @ %p\n", cur->AddrPC_Offset);
break;
}
SymGetLineFromAddr64(process, cur->AddrPC_Offset, &lineOffset, &lineData);
buf_printf(&out, "%s(%d): ", lineData.FileName, lineData.LineNumber);
if (SymGetSymFromAddr64(process, cur->AddrPC_Offset, &symOffset, symbol)) {
buf_printf(&out, "%s\n", symbol->Name);
}
else {
buf_printf(&out, " Unkown symbol @ %p\n", cur->AddrPC_Offset);
}
if (cur->AddrReturn_Offset == 0) {
break;
}
}
free(symbol);
return out.buf;
}
#elif defined(__APPLE__)
#include <execinfo.h>
#include <unistd.h>
#include <dlfcn.h>
typedef struct b_stacktrace {
void* trace[B_STACKTRACE_MAX_DEPTH];
int trace_size;
} b_stacktrace;
b_stacktrace_handle b_stacktrace_get(void) {
b_stacktrace* ret = (b_stacktrace*)malloc(sizeof(b_stacktrace));
ret->trace_size = backtrace(ret->trace, B_STACKTRACE_MAX_DEPTH);
return (b_stacktrace_handle)(ret);
}
int b_stacktrace_depth(b_stacktrace_handle h) {
const b_stacktrace* stacktrace = (b_stacktrace*)h;
return stacktrace->trace_size;
}
char* b_stacktrace_to_string(b_stacktrace_handle h) {
const b_stacktrace* stacktrace = (b_stacktrace*)h;
char** messages = backtrace_symbols(stacktrace->trace, stacktrace->trace_size);
print_buf out = buf_init();
*out.buf = 0;
for (int i = 0; i < stacktrace->trace_size; ++i) {
buf_printf(&out, "%s\n", messages[i]);
}
free(messages);
return out.buf;
}
#elif defined(__linux__)
#include <execinfo.h>
#include <ucontext.h>
#include <unistd.h>
#include <dlfcn.h>
#include <string.h>
typedef struct b_stacktrace {
void* trace[B_STACKTRACE_MAX_DEPTH];
int trace_size;
} b_stacktrace;
b_stacktrace_handle b_stacktrace_get(void) {
b_stacktrace* ret = (b_stacktrace*)malloc(sizeof(b_stacktrace));
ret->trace_size = backtrace(ret->trace, B_STACKTRACE_MAX_DEPTH);
return (b_stacktrace_handle)(ret);
}
int b_stacktrace_depth(b_stacktrace_handle h) {
const b_stacktrace* stacktrace = (b_stacktrace*)h;
return stacktrace->trace_size;
}
char* b_stacktrace_to_string(b_stacktrace_handle h) {
const b_stacktrace* stacktrace = (b_stacktrace*)h;
char** messages = backtrace_symbols(stacktrace->trace, stacktrace->trace_size);
print_buf out = buf_init();
int i;
for (i = 0; i < stacktrace->trace_size; ++i) {
void* tracei = stacktrace->trace[i];
char* msg = messages[i];
/* calculate load offset */
Dl_info info;
dladdr(tracei, &info);
if (info.dli_fbase == (void*)0x400000) {
/* address from executable, so don't offset */
info.dli_fbase = NULL;
}
while (*msg && *msg != '(') ++msg;
*msg = 0;
{
char cmd[1024];
char line[2048];
FILE* fp;
snprintf(cmd, 1024, "addr2line -e %s -f -C -p %p 2>/dev/null", messages[i], (void*)((char*)tracei - (char*)info.dli_fbase));
fp = popen(cmd, "r");
if (!fp) {
buf_printf(&out, "Failed to generate trace further...\n");
break;
}
while (fgets(line, sizeof(line), fp)) {
buf_printf(&out, "%s: ", messages[i]);
if (strstr(line, "?? ")) {
/* just output address if nothing can be found */
buf_printf(&out, "%p\n", tracei);
}
else {
buf_printf(&out, "%s", line);
}
}
pclose(fp);
}
}
free(messages);
return out.buf;
}
#else
/* noop implementation */
char* b_stacktrace_get_string(void) {
print_buf out = buf_init();
buf_printf("b_stacktrace: unsupported platform\n");
return out.buf;
}
#endif /* platform */
#endif /* B_STACKTRACE_IMPL */
#endif /* B_STACKTRACE_INCLUDED */