source: opengl-game/CrashLogger.cpp@ d9b6a1c

feature/imgui-sdl points-test
Last change on this file since d9b6a1c was d9b6a1c, checked in by Dmitry Portnoy <dmitry.portnoy@…>, 5 years ago

Print a stack trace in the event of a crash. Currently, the code for this on Windows outputs the stack trace to stderr, not to a log file.

  • Property mode set to 100644
File size: 4.4 KB
Line 
1#include "CrashLogger.h"
2
3#include <cstdlib>
4#include <cstdio>
5#include <csignal>
6
7#include <fcntl.h>
8
9#include "Compiler.h"
10
11#ifdef WINDOWS
12 // most of this can be removed since Windows uses __try __catch instead of signals
13
14 #include <windows.h>
15 #include "StackWalker.h"
16
17 #include <io.h>
18 #include <sys/stat.h>
19
20 // The windows analogues to the unix open, close, and write functions
21 // are _open, _close, and _write. These #defines let me use the same name in all cases.
22 #define open _open
23 #define close _close
24 #define write _write
25
26 #define STDERR_FILENO 2
27#else
28 #include <unistd.h>
29 #include <execinfo.h>
30 #include <errno.h>
31 #include <cxxabi.h>
32 #include <cstring>
33#endif
34
35CrashLogger::CrashLogger() {
36 // Apparently, sigaction should be used instead for Linux
37 // It gives more info. Check if I should bother switching
38
39 signal(SIGABRT, abortHandler);
40 signal(SIGSEGV, abortHandler);
41 signal(SIGILL, abortHandler);
42 signal(SIGFPE, abortHandler);
43
44 write(STDERR_FILENO, "Handlers attached\n", 18);
45}
46
47CrashLogger::~CrashLogger() {
48}
49
50static inline void printStackTrace(int fd_out = STDERR_FILENO) {
51 write(fd_out, "stack trace:\n", 13);
52
53 // storage array for stack trace address data
54 void* addrlist[64];
55
56 // retrieve current stack addresses
57 uint32_t addrlen = backtrace(addrlist, sizeof(addrlist) / sizeof(void*));
58
59 if (addrlen == 0) {
60 write(fd_out, " \n", 3);
61 return;
62 }
63
64 // create readable strings to each frame.
65 char** symbollist = backtrace_symbols(addrlist, addrlen);
66
67 size_t funcnamesize = 1024;
68 char* funcname = (char*)malloc(funcnamesize);
69
70 // iterate over the returned symbol lines
71 // skip the first few, since those are printStackTrace, abortHandler,
72 // and a couple others called after the crash
73 for (unsigned int i = 4; i < addrlen; i++) {
74 char* begin_name = NULL;
75 char* begin_offset = NULL;
76 char* end_offset = NULL; // Iirc, this is used in the Linux code
77
78 #ifdef MAC
79 for (char *p = symbollist[i]; *p; p++) {
80 if ((*p == '_') && (*(p-1) == ' ')) {
81 begin_name = p-1;
82 } else if(*p == '+') {
83 begin_offset = p-1;
84 }
85 }
86
87 if (begin_name && begin_offset && (begin_name < begin_offset )) {
88 *begin_name++ = '\0';
89 *begin_offset++ = '\0';
90
91 // mangled name is now in [begin_name, begin_offset) and caller
92 // offset in [begin_offset, end_offset). now apply
93 // __cxa_demangle():
94 int status;
95 char* ret = abi::__cxa_demangle(begin_name, funcname, &funcnamesize, &status);
96
97 if (status == 0) {
98 funcname = ret; // use possibly realloc()-ed string
99 write(fd_out, symbollist[i], strlen(symbollist[i]));
100 write(fd_out, " ", 1);
101 write(fd_out, funcname, strlen(funcname));
102 write(fd_out, " ", 1);
103 write(fd_out, begin_offset, strlen(begin_offset));
104 } else {
105 // demangling failed. Output function name as a C function with no arguments.
106 write(fd_out, "Error\n", 6);
107 write(fd_out, symbollist[i], strlen(symbollist[i]));
108 write(fd_out, " ", 1);
109 write(fd_out, begin_name, strlen(begin_name));
110 write(fd_out, "() ", 3);
111 write(fd_out, begin_offset, strlen(begin_offset));
112 }
113 } else {
114 // couldn't parse the line? print the whole line.
115 write(fd_out, symbollist[i], strlen(symbollist[i]));
116 }
117 write(fd_out, "\n", 1);
118 #else
119 // Check that this works on Linux Mint
120 write(fd_out, symbollist[i], strlen(symbollist[i]));
121 write(fd_out, "\n", 1);
122 #endif
123 }
124
125 free(funcname);
126 free(symbollist);
127
128 write(fd_out, "End of stack trace\n", 19);
129}
130
131// Most of this function could probably be shared between Windows and Linux/Mac
132void abortHandler(int signum) {
133 #ifdef WINDOWS
134 int crash_log = open("crash.log", O_RDWR | O_CREAT | O_APPEND, _S_IREAD | _S_IWRITE);
135 #else
136 int crash_log = open("crash.log", O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
137 #endif
138
139 if (crash_log == -1) {
140 perror("opening crash.log"); // TODO: Figure out exactly what perror does and if I should use it
141 }
142
143 printStackTrace(crash_log);
144
145 close(crash_log);
146
147 exit(signum);
148}
Note: See TracBrowser for help on using the repository browser.