Line data Source code
1 : /*
2 : Minetest
3 : Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 :
5 : This program is free software; you can redistribute it and/or modify
6 : it under the terms of the GNU Lesser General Public License as published by
7 : the Free Software Foundation; either version 2.1 of the License, or
8 : (at your option) any later version.
9 :
10 : This program is distributed in the hope that it will be useful,
11 : but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU Lesser General Public License for more details.
14 :
15 : You should have received a copy of the GNU Lesser General Public License along
16 : with this program; if not, write to the Free Software Foundation, Inc.,
17 : 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 : */
19 :
20 :
21 : #include "porting.h"
22 : #include "debug.h"
23 : #include "exceptions.h"
24 : #include "threads.h"
25 : #include <stdio.h>
26 : #include <stdlib.h>
27 : #include <cstring>
28 : #include <map>
29 : #include "jthread/jmutex.h"
30 : #include "jthread/jmutexautolock.h"
31 : #include "config.h"
32 :
33 : #ifdef _MSC_VER
34 : #include <dbghelp.h>
35 : #include "version.h"
36 : #include "filesys.h"
37 : #endif
38 :
39 : /*
40 : Debug output
41 : */
42 :
43 : #define DEBUGSTREAM_COUNT 2
44 :
45 1 : FILE *g_debugstreams[DEBUGSTREAM_COUNT] = {stderr, NULL};
46 :
47 : #define DEBUGPRINT(...)\
48 : {\
49 : for(int i=0; i<DEBUGSTREAM_COUNT; i++)\
50 : {\
51 : if(g_debugstreams[i] != NULL){\
52 : fprintf(g_debugstreams[i], __VA_ARGS__);\
53 : fflush(g_debugstreams[i]);\
54 : }\
55 : }\
56 : }
57 :
58 1 : void debugstreams_init(bool disable_stderr, const char *filename)
59 : {
60 1 : if(disable_stderr)
61 0 : g_debugstreams[0] = NULL;
62 : else
63 1 : g_debugstreams[0] = stderr;
64 :
65 1 : if(filename)
66 1 : g_debugstreams[1] = fopen(filename, "a");
67 :
68 1 : if(g_debugstreams[1])
69 : {
70 1 : fprintf(g_debugstreams[1], "\n\n-------------\n");
71 1 : fprintf(g_debugstreams[1], " Separator \n");
72 1 : fprintf(g_debugstreams[1], "-------------\n\n");
73 : }
74 1 : }
75 :
76 1 : void debugstreams_deinit()
77 : {
78 1 : if(g_debugstreams[1] != NULL)
79 1 : fclose(g_debugstreams[1]);
80 1 : }
81 :
82 2 : class Debugbuf : public std::streambuf
83 : {
84 : public:
85 2 : Debugbuf(bool disable_stderr)
86 2 : {
87 2 : m_disable_stderr = disable_stderr;
88 2 : }
89 :
90 18 : int overflow(int c)
91 : {
92 54 : for(int i=0; i<DEBUGSTREAM_COUNT; i++)
93 : {
94 36 : if(g_debugstreams[i] == stderr && m_disable_stderr)
95 6 : continue;
96 30 : if(g_debugstreams[i] != NULL)
97 21 : (void)fwrite(&c, 1, 1, g_debugstreams[i]);
98 : //TODO: Is this slow?
99 30 : fflush(g_debugstreams[i]);
100 : }
101 :
102 18 : return c;
103 : }
104 27 : std::streamsize xsputn(const char *s, std::streamsize n)
105 : {
106 : #ifdef __ANDROID__
107 : __android_log_print(ANDROID_LOG_VERBOSE, PROJECT_NAME, "%s", s);
108 : #endif
109 81 : for(int i=0; i<DEBUGSTREAM_COUNT; i++)
110 : {
111 54 : if(g_debugstreams[i] == stderr && m_disable_stderr)
112 6 : continue;
113 48 : if(g_debugstreams[i] != NULL)
114 33 : (void)fwrite(s, 1, n, g_debugstreams[i]);
115 : //TODO: Is this slow?
116 48 : fflush(g_debugstreams[i]);
117 : }
118 :
119 27 : return n;
120 : }
121 :
122 : private:
123 : bool m_disable_stderr;
124 : };
125 :
126 1 : Debugbuf debugbuf(false);
127 1 : std::ostream dstream(&debugbuf);
128 1 : Debugbuf debugbuf_no_stderr(true);
129 1 : std::ostream dstream_no_stderr(&debugbuf_no_stderr);
130 1 : Nullstream dummyout;
131 :
132 : /*
133 : Assert
134 : */
135 :
136 0 : void sanity_check_fn(const char *assertion, const char *file,
137 : unsigned int line, const char *function)
138 : {
139 0 : DEBUGPRINT("\nIn thread %lx:\n"
140 : "%s:%u: %s: An engine assumption '%s' failed.\n",
141 : (unsigned long)get_current_thread_id(),
142 : file, line, function, assertion);
143 :
144 0 : debug_stacks_print();
145 :
146 0 : if(g_debugstreams[1])
147 0 : fclose(g_debugstreams[1]);
148 :
149 0 : abort();
150 : }
151 :
152 0 : void fatal_error_fn(const char *msg, const char *file,
153 : unsigned int line, const char *function)
154 : {
155 0 : DEBUGPRINT("\nIn thread %lx:\n"
156 : "%s:%u: %s: A fatal error occurred: %s\n",
157 : (unsigned long)get_current_thread_id(),
158 : file, line, function, msg);
159 :
160 0 : debug_stacks_print();
161 :
162 0 : if(g_debugstreams[1])
163 0 : fclose(g_debugstreams[1]);
164 :
165 0 : abort();
166 : }
167 :
168 : /*
169 : DebugStack
170 : */
171 :
172 : struct DebugStack
173 : {
174 : DebugStack(threadid_t id);
175 : void print(FILE *file, bool everything);
176 : void print(std::ostream &os, bool everything);
177 :
178 : threadid_t threadid;
179 : char stack[DEBUG_STACK_SIZE][DEBUG_STACK_TEXT_SIZE];
180 : int stack_i; // Points to the lowest empty position
181 : int stack_max_i; // Highest i that was seen
182 : };
183 :
184 3 : DebugStack::DebugStack(threadid_t id)
185 : {
186 3 : threadid = id;
187 3 : stack_i = 0;
188 3 : stack_max_i = 0;
189 3 : memset(stack, 0, DEBUG_STACK_SIZE*DEBUG_STACK_TEXT_SIZE);
190 3 : }
191 :
192 0 : void DebugStack::print(FILE *file, bool everything)
193 : {
194 : fprintf(file, "DEBUG STACK FOR THREAD %lx:\n",
195 0 : (unsigned long)threadid);
196 :
197 0 : for(int i=0; i<stack_max_i; i++)
198 : {
199 0 : if(i == stack_i && everything == false)
200 0 : break;
201 :
202 0 : if(i < stack_i)
203 0 : fprintf(file, "#%d %s\n", i, stack[i]);
204 : else
205 0 : fprintf(file, "(Leftover data: #%d %s)\n", i, stack[i]);
206 : }
207 :
208 0 : if(stack_i == DEBUG_STACK_SIZE)
209 0 : fprintf(file, "Probably overflown.\n");
210 0 : }
211 :
212 0 : void DebugStack::print(std::ostream &os, bool everything)
213 : {
214 0 : os<<"DEBUG STACK FOR THREAD "<<(unsigned long)threadid<<": "<<std::endl;
215 :
216 0 : for(int i=0; i<stack_max_i; i++)
217 : {
218 0 : if(i == stack_i && everything == false)
219 0 : break;
220 :
221 0 : if(i < stack_i)
222 0 : os<<"#"<<i<<" "<<stack[i]<<std::endl;
223 : else
224 0 : os<<"(Leftover data: #"<<i<<" "<<stack[i]<<")"<<std::endl;
225 : }
226 :
227 0 : if(stack_i == DEBUG_STACK_SIZE)
228 0 : os<<"Probably overflown."<<std::endl;
229 0 : }
230 :
231 1 : std::map<threadid_t, DebugStack*> g_debug_stacks;
232 1 : JMutex g_debug_stacks_mutex;
233 :
234 1 : void debug_stacks_init()
235 : {
236 1 : }
237 :
238 0 : void debug_stacks_print_to(std::ostream &os)
239 : {
240 0 : JMutexAutoLock lock(g_debug_stacks_mutex);
241 :
242 0 : os<<"Debug stacks:"<<std::endl;
243 :
244 0 : for(std::map<threadid_t, DebugStack*>::iterator
245 0 : i = g_debug_stacks.begin();
246 0 : i != g_debug_stacks.end(); ++i)
247 : {
248 0 : i->second->print(os, false);
249 : }
250 0 : }
251 :
252 0 : void debug_stacks_print()
253 : {
254 0 : JMutexAutoLock lock(g_debug_stacks_mutex);
255 :
256 0 : DEBUGPRINT("Debug stacks:\n");
257 :
258 0 : for(std::map<threadid_t, DebugStack*>::iterator
259 0 : i = g_debug_stacks.begin();
260 0 : i != g_debug_stacks.end(); ++i)
261 : {
262 0 : DebugStack *stack = i->second;
263 :
264 0 : for(int i=0; i<DEBUGSTREAM_COUNT; i++)
265 : {
266 0 : if(g_debugstreams[i] != NULL)
267 0 : stack->print(g_debugstreams[i], true);
268 : }
269 : }
270 0 : }
271 :
272 17788 : DebugStacker::DebugStacker(const char *text)
273 : {
274 17788 : threadid_t threadid = get_current_thread_id();
275 :
276 35576 : JMutexAutoLock lock(g_debug_stacks_mutex);
277 :
278 17788 : std::map<threadid_t, DebugStack*>::iterator n;
279 17788 : n = g_debug_stacks.find(threadid);
280 17788 : if(n != g_debug_stacks.end())
281 : {
282 17785 : m_stack = n->second;
283 : }
284 : else
285 : {
286 : /*DEBUGPRINT("Creating new debug stack for thread %x\n",
287 : (unsigned int)threadid);*/
288 3 : m_stack = new DebugStack(threadid);
289 3 : g_debug_stacks[threadid] = m_stack;
290 : }
291 :
292 17788 : if(m_stack->stack_i >= DEBUG_STACK_SIZE)
293 : {
294 0 : m_overflowed = true;
295 : }
296 : else
297 : {
298 17788 : m_overflowed = false;
299 :
300 17788 : snprintf(m_stack->stack[m_stack->stack_i],
301 17788 : DEBUG_STACK_TEXT_SIZE, "%s", text);
302 17788 : m_stack->stack_i++;
303 17788 : if(m_stack->stack_i > m_stack->stack_max_i)
304 8 : m_stack->stack_max_i = m_stack->stack_i;
305 : }
306 17788 : }
307 :
308 35574 : DebugStacker::~DebugStacker()
309 : {
310 35574 : JMutexAutoLock lock(g_debug_stacks_mutex);
311 :
312 17787 : if(m_overflowed == true)
313 0 : return;
314 :
315 17787 : m_stack->stack_i--;
316 :
317 17787 : if(m_stack->stack_i == 0)
318 : {
319 2 : threadid_t threadid = m_stack->threadid;
320 : /*DEBUGPRINT("Deleting debug stack for thread %x\n",
321 : (unsigned int)threadid);*/
322 2 : delete m_stack;
323 2 : g_debug_stacks.erase(threadid);
324 : }
325 17787 : }
326 :
327 : #ifdef _MSC_VER
328 :
329 : const char *Win32ExceptionCodeToString(DWORD exception_code)
330 : {
331 : switch (exception_code) {
332 : case EXCEPTION_ACCESS_VIOLATION:
333 : return "Access violation";
334 : case EXCEPTION_DATATYPE_MISALIGNMENT:
335 : return "Misaligned data access";
336 : case EXCEPTION_BREAKPOINT:
337 : return "Breakpoint reached";
338 : case EXCEPTION_SINGLE_STEP:
339 : return "Single debug step";
340 : case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
341 : return "Array access out of bounds";
342 : case EXCEPTION_FLT_DENORMAL_OPERAND:
343 : return "Denormal floating point operand";
344 : case EXCEPTION_FLT_DIVIDE_BY_ZERO:
345 : return "Floating point division by zero";
346 : case EXCEPTION_FLT_INEXACT_RESULT:
347 : return "Inaccurate floating point result";
348 : case EXCEPTION_FLT_INVALID_OPERATION:
349 : return "Invalid floating point operation";
350 : case EXCEPTION_FLT_OVERFLOW:
351 : return "Floating point exponent overflow";
352 : case EXCEPTION_FLT_STACK_CHECK:
353 : return "Floating point stack overflow or underflow";
354 : case EXCEPTION_FLT_UNDERFLOW:
355 : return "Floating point exponent underflow";
356 : case EXCEPTION_INT_DIVIDE_BY_ZERO:
357 : return "Integer division by zero";
358 : case EXCEPTION_INT_OVERFLOW:
359 : return "Integer overflow";
360 : case EXCEPTION_PRIV_INSTRUCTION:
361 : return "Privileged instruction executed";
362 : case EXCEPTION_IN_PAGE_ERROR:
363 : return "Could not access or load page";
364 : case EXCEPTION_ILLEGAL_INSTRUCTION:
365 : return "Illegal instruction encountered";
366 : case EXCEPTION_NONCONTINUABLE_EXCEPTION:
367 : return "Attempted to continue after fatal exception";
368 : case EXCEPTION_STACK_OVERFLOW:
369 : return "Stack overflow";
370 : case EXCEPTION_INVALID_DISPOSITION:
371 : return "Invalid disposition returned to the exception dispatcher";
372 : case EXCEPTION_GUARD_PAGE:
373 : return "Attempted guard page access";
374 : case EXCEPTION_INVALID_HANDLE:
375 : return "Invalid handle";
376 : }
377 :
378 : return "Unknown exception";
379 : }
380 :
381 : long WINAPI Win32ExceptionHandler(struct _EXCEPTION_POINTERS *pExceptInfo)
382 : {
383 : char buf[512];
384 : MINIDUMP_EXCEPTION_INFORMATION mdei;
385 : MINIDUMP_USER_STREAM_INFORMATION mdusi;
386 : MINIDUMP_USER_STREAM mdus;
387 : bool minidump_created = false;
388 :
389 : std::string dumpfile = porting::path_user + DIR_DELIM PROJECT_NAME ".dmp";
390 :
391 : std::string version_str(PROJECT_NAME " ");
392 : version_str += g_version_hash;
393 :
394 : HANDLE hFile = CreateFileA(dumpfile.c_str(), GENERIC_WRITE,
395 : FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
396 : if (hFile == INVALID_HANDLE_VALUE)
397 : goto minidump_failed;
398 :
399 : if (SetEndOfFile(hFile) == FALSE)
400 : goto minidump_failed;
401 :
402 : mdei.ClientPointers = NULL;
403 : mdei.ExceptionPointers = pExceptInfo;
404 : mdei.ThreadId = GetCurrentThreadId();
405 :
406 : mdus.Type = CommentStreamA;
407 : mdus.BufferSize = version_str.size();
408 : mdus.Buffer = (PVOID)version_str.c_str();
409 :
410 : mdusi.UserStreamArray = &mdus;
411 : mdusi.UserStreamCount = 1;
412 :
413 : if (MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile,
414 : MiniDumpNormal, &mdei, &mdusi, NULL) == FALSE)
415 : goto minidump_failed;
416 :
417 : minidump_created = true;
418 :
419 : minidump_failed:
420 :
421 : CloseHandle(hFile);
422 :
423 : DWORD excode = pExceptInfo->ExceptionRecord->ExceptionCode;
424 : _snprintf(buf, sizeof(buf),
425 : " >> === FATAL ERROR ===\n"
426 : " >> %s (Exception 0x%08X) at 0x%p\n",
427 : Win32ExceptionCodeToString(excode), excode,
428 : pExceptInfo->ExceptionRecord->ExceptionAddress);
429 : dstream << buf;
430 :
431 : if (minidump_created)
432 : dstream << " >> Saved dump to " << dumpfile << std::endl;
433 : else
434 : dstream << " >> Failed to save dump" << std::endl;
435 :
436 : return EXCEPTION_EXECUTE_HANDLER;
437 : }
438 :
439 : #endif
440 :
441 1 : void debug_set_exception_handler()
442 : {
443 : #ifdef _MSC_VER
444 : SetUnhandledExceptionFilter(Win32ExceptionHandler);
445 : #endif
446 4 : }
447 :
|