source: opengl-game/CrashLogger.cpp@ b373466

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

Create a log with a stack-trace when the game crashes

  • Property mode set to 100644
File size: 6.5 KB
RevLine 
[d9b6a1c]1#include "CrashLogger.h"
2
3#include <cstdlib>
4#include <cstdio>
5#include <csignal>
[6abfd07]6#include <cstring>
[d9b6a1c]7
8#include <fcntl.h>
9
10#include "Compiler.h"
[6abfd07]11#include "Consts.h"
[d9b6a1c]12
[6abfd07]13// TODO: Double-check which includes are necessary
[d9b6a1c]14
[6abfd07]15#ifdef WINDOWS
[d9b6a1c]16 #include <windows.h>
17 #include <io.h>
18 #include <sys/stat.h>
19
[6abfd07]20 #include "FileStackWalker.h"
21
[d9b6a1c]22 // The windows analogues to the unix open, close, and write functions
23 // are _open, _close, and _write. These #defines let me use the same name in all cases.
24 #define open _open
25 #define close _close
26 #define write _write
27
28 #define STDERR_FILENO 2
[6abfd07]29
30 bool handleException(unsigned int expCode, EXCEPTION_POINTERS* pExp, HANDLE thread);
[d9b6a1c]31#else
32 #include <unistd.h>
33 #include <execinfo.h>
34 #include <errno.h>
35 #include <cxxabi.h>
36 #include <cstring>
[6abfd07]37
38 void abortHandler(int signum);
39 static inline void printStackTrace(int fd_out = STDERR_FILENO);
[d9b6a1c]40#endif
41
[6abfd07]42void printInfo(int fd_out);
43
44CrashLogger::CrashLogger(int(*mainFunc)(int, char*[]), int argc, char* argv[]) {
45 write(STDERR_FILENO, "Calling main\n", 13);
46
47 #ifdef WINDOWS
48 __try {
49 mainFunc(argc, argv);
50 // maybe do this and call a function inside CrashLogger
51 // In that case, also pass GetCurrentThread() as a parameter to then pass to handleException
52 // I could also move almost all of this into CrashLogger by creating a function in CrashLogger that takes a reference
53 // to the effective main function and, for Windows, wraps it in all this error-handling stuff
54 } __except (handleException(
55 GetExceptionCode(),
56 GetExceptionInformation(),
57 GetCurrentThread())
58 ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER) {
59 }
60 #else
61 // Apparently, sigaction should be used instead for Linux
62 // It gives more info. Check if I should bother switching
[d9b6a1c]63
[6abfd07]64 signal(SIGABRT, abortHandler);
65 signal(SIGSEGV, abortHandler);
66 signal(SIGILL, abortHandler);
67 signal(SIGFPE, abortHandler);
[d9b6a1c]68
[6abfd07]69 write(STDERR_FILENO, "Handlers attached\n", 18);
70
71 mainFunc(argc, argv);
72 #endif
[d9b6a1c]73}
74
75CrashLogger::~CrashLogger() {
76}
77
[6abfd07]78#ifdef WINDOWS
79
80bool handleException(unsigned int expCode, EXCEPTION_POINTERS* pExp, HANDLE thread) {
81 int crash_log = open(CRASH_LOG_FILE, O_RDWR | O_CREAT | O_APPEND, _S_IREAD | _S_IWRITE);
82
83 if (crash_log == -1) {
84 // TODO: Figure out exactly what perror does and if I should use it
85 perror("opening crash.log"); // TODO: Figure out exactly what perror does and if I should use it
86 }
87
88 printInfo(crash_log);
89
90 FileStackWalker sw(crash_log == -1 ? STDERR_FILENO : crash_log);
91
92 if (pExp != NULL) {
93 sw.ShowCallstack(thread, pExp->ContextRecord);
94 } else {
95 write(crash_log, "Could not get the stack trace\n", 30);
96 }
97
98 close(crash_log);
99
100 if (expCode == EXCEPTION_ACCESS_VIOLATION) {
101 write(STDERR_FILENO, "ACCESS VIOLATION\n", 17);
102 }
103
104 return true;
105}
106
107#else
108
109void abortHandler(int signum) {
110 int crash_log = open(CRASH_LOG_FILE, O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
111
112 if (crash_log == -1) {
113 // TODO: Figure out exactly what perror does and if I should use it
114 perror("opening crash.log");
115 }
116
117 printInfo(crash_log);
118
119 printStackTrace(crash_log);
120
121 close(crash_log);
122
123 write(STDERR_FILENO, "The game has crashed. Check crash.log for more info\n", 52);
124
125 exit(signum);
126}
127
128static inline void printStackTrace(int fd_out) {
[d9b6a1c]129 write(fd_out, "stack trace:\n", 13);
130
131 // storage array for stack trace address data
132 void* addrlist[64];
133
134 // retrieve current stack addresses
135 uint32_t addrlen = backtrace(addrlist, sizeof(addrlist) / sizeof(void*));
136
137 if (addrlen == 0) {
138 write(fd_out, " \n", 3);
139 return;
140 }
141
142 // create readable strings to each frame.
143 char** symbollist = backtrace_symbols(addrlist, addrlen);
144
145 size_t funcnamesize = 1024;
146 char* funcname = (char*)malloc(funcnamesize);
147
148 // iterate over the returned symbol lines
149 // skip the first few, since those are printStackTrace, abortHandler,
150 // and a couple others called after the crash
151 for (unsigned int i = 4; i < addrlen; i++) {
152 char* begin_name = NULL;
153 char* begin_offset = NULL;
[6abfd07]154 char* end_offset = NULL; // Iirc, this is used in the Linux code (Check what it's used for)
155
156#ifdef MAC
157 for (char *p = symbollist[i]; *p; p++) {
158 if ((*p == '_') && (*(p-1) == ' ')) {
159 begin_name = p-1;
160 } else if(*p == '+') {
161 begin_offset = p-1;
[d9b6a1c]162 }
[6abfd07]163 }
164
165 if (begin_name && begin_offset && (begin_name < begin_offset )) {
166 *begin_name++ = '\0';
167 *begin_offset++ = '\0';
[d9b6a1c]168
[6abfd07]169 // mangled name is now in [begin_name, begin_offset) and caller
170 // offset in [begin_offset, end_offset). now apply
171 // __cxa_demangle():
172 int status;
173 char* ret = abi::__cxa_demangle(begin_name, funcname, &funcnamesize, &status);
174
175 if (status == 0) {
176 funcname = ret; // use possibly realloc()-ed string
177 write(fd_out, symbollist[i], strlen(symbollist[i]));
178 write(fd_out, " ", 1);
179 write(fd_out, funcname, strlen(funcname));
180 write(fd_out, " ", 1);
181 write(fd_out, begin_offset, strlen(begin_offset));
[d9b6a1c]182 } else {
[6abfd07]183 // demangling failed. Output function name as a C function with no arguments.
184 write(fd_out, "Error\n", 6);
[d9b6a1c]185 write(fd_out, symbollist[i], strlen(symbollist[i]));
[6abfd07]186 write(fd_out, " ", 1);
187 write(fd_out, begin_name, strlen(begin_name));
188 write(fd_out, "() ", 3);
189 write(fd_out, begin_offset, strlen(begin_offset));
[d9b6a1c]190 }
[6abfd07]191 } else {
192 // couldn't parse the line? print the whole line.
[d9b6a1c]193 write(fd_out, symbollist[i], strlen(symbollist[i]));
[6abfd07]194 }
195 write(fd_out, "\n", 1);
196#else
197 // Check that this works on Linux Mint
198 write(fd_out, symbollist[i], strlen(symbollist[i]));
199 write(fd_out, "\n", 1);
200#endif
[d9b6a1c]201 }
202
203 free(funcname);
204 free(symbollist);
205
206 write(fd_out, "End of stack trace\n", 19);
207}
208
[6abfd07]209#endif
210
211void printInfo(int fd_out) {
212 write(fd_out, "Game Version: ", 14);
213 write(fd_out, GAME_VERSION, strlen(GAME_VERSION));
214 write(fd_out, "\n", 1);
215
216 write(fd_out, "OS: ", 4);
217 #if defined WINDOWS
218 write(fd_out, "Windows", 7);
219 #elif defined LINUX
220 write(fd_out, "Linux", 5);
221 #elif defined MAC
222 write(fd_out, "Mac", 3);
[d9b6a1c]223 #else
[6abfd07]224 write(fd_out, "Unknown", 7);
[d9b6a1c]225 #endif
[6abfd07]226 write(fd_out, "\n", 1);
[d9b6a1c]227
[6abfd07]228 write(fd_out, "\n", 1);
[d9b6a1c]229}
Note: See TracBrowser for help on using the repository browser.