#include "CrashLogger.h" #include #include #include #include #include "Compiler.h" #ifdef WINDOWS // most of this can be removed since Windows uses __try __catch instead of signals #include #include "StackWalker.h" #include #include // The windows analogues to the unix open, close, and write functions // are _open, _close, and _write. These #defines let me use the same name in all cases. #define open _open #define close _close #define write _write #define STDERR_FILENO 2 #else #include #include #include #include #include #endif CrashLogger::CrashLogger() { // Apparently, sigaction should be used instead for Linux // It gives more info. Check if I should bother switching signal(SIGABRT, abortHandler); signal(SIGSEGV, abortHandler); signal(SIGILL, abortHandler); signal(SIGFPE, abortHandler); write(STDERR_FILENO, "Handlers attached\n", 18); } CrashLogger::~CrashLogger() { } static inline void printStackTrace(int fd_out = STDERR_FILENO) { write(fd_out, "stack trace:\n", 13); // storage array for stack trace address data void* addrlist[64]; // retrieve current stack addresses uint32_t addrlen = backtrace(addrlist, sizeof(addrlist) / sizeof(void*)); if (addrlen == 0) { write(fd_out, " \n", 3); return; } // create readable strings to each frame. char** symbollist = backtrace_symbols(addrlist, addrlen); size_t funcnamesize = 1024; char* funcname = (char*)malloc(funcnamesize); // iterate over the returned symbol lines // skip the first few, since those are printStackTrace, abortHandler, // and a couple others called after the crash for (unsigned int i = 4; i < addrlen; i++) { char* begin_name = NULL; char* begin_offset = NULL; char* end_offset = NULL; // Iirc, this is used in the Linux code #ifdef MAC for (char *p = symbollist[i]; *p; p++) { if ((*p == '_') && (*(p-1) == ' ')) { begin_name = p-1; } else if(*p == '+') { begin_offset = p-1; } } if (begin_name && begin_offset && (begin_name < begin_offset )) { *begin_name++ = '\0'; *begin_offset++ = '\0'; // mangled name is now in [begin_name, begin_offset) and caller // offset in [begin_offset, end_offset). now apply // __cxa_demangle(): int status; char* ret = abi::__cxa_demangle(begin_name, funcname, &funcnamesize, &status); if (status == 0) { funcname = ret; // use possibly realloc()-ed string write(fd_out, symbollist[i], strlen(symbollist[i])); write(fd_out, " ", 1); write(fd_out, funcname, strlen(funcname)); write(fd_out, " ", 1); write(fd_out, begin_offset, strlen(begin_offset)); } else { // demangling failed. Output function name as a C function with no arguments. write(fd_out, "Error\n", 6); write(fd_out, symbollist[i], strlen(symbollist[i])); write(fd_out, " ", 1); write(fd_out, begin_name, strlen(begin_name)); write(fd_out, "() ", 3); write(fd_out, begin_offset, strlen(begin_offset)); } } else { // couldn't parse the line? print the whole line. write(fd_out, symbollist[i], strlen(symbollist[i])); } write(fd_out, "\n", 1); #else // Check that this works on Linux Mint write(fd_out, symbollist[i], strlen(symbollist[i])); write(fd_out, "\n", 1); #endif } free(funcname); free(symbollist); write(fd_out, "End of stack trace\n", 19); } // Most of this function could probably be shared between Windows and Linux/Mac void abortHandler(int signum) { #ifdef WINDOWS int crash_log = open("crash.log", O_RDWR | O_CREAT | O_APPEND, _S_IREAD | _S_IWRITE); #else int crash_log = open("crash.log", O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR); #endif if (crash_log == -1) { perror("opening crash.log"); // TODO: Figure out exactly what perror does and if I should use it } printStackTrace(crash_log); close(crash_log); exit(signum); }