3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
23 #include "exceptions.h"
29 #include "threading/mutex.h"
30 #include "threading/mutex_auto_lock.h"
43 #define DEBUGSTREAM_COUNT 2
45 FILE *g_debugstreams[DEBUGSTREAM_COUNT] = {stderr, NULL};
47 #define DEBUGPRINT(...)\
49 for(int i=0; i<DEBUGSTREAM_COUNT; i++)\
51 if(g_debugstreams[i] != NULL){\
52 fprintf(g_debugstreams[i], __VA_ARGS__);\
53 fflush(g_debugstreams[i]);\
58 void debugstreams_init(bool disable_stderr, const char *filename)
61 g_debugstreams[0] = NULL;
63 g_debugstreams[0] = stderr;
66 g_debugstreams[1] = fopen(filename, "a");
70 fprintf(g_debugstreams[1], "\n\n-------------\n");
71 fprintf(g_debugstreams[1], " Separator \n");
72 fprintf(g_debugstreams[1], "-------------\n\n");
76 void debugstreams_deinit()
78 if(g_debugstreams[1] != NULL)
79 fclose(g_debugstreams[1]);
82 class Debugbuf : public std::streambuf
85 Debugbuf(bool disable_stderr)
87 m_disable_stderr = disable_stderr;
92 for(int i=0; i<DEBUGSTREAM_COUNT; i++)
94 if(g_debugstreams[i] == stderr && m_disable_stderr)
96 if(g_debugstreams[i] != NULL)
97 (void)fwrite(&c, 1, 1, g_debugstreams[i]);
99 fflush(g_debugstreams[i]);
104 std::streamsize xsputn(const char *s, std::streamsize n)
107 __android_log_print(ANDROID_LOG_VERBOSE, PROJECT_NAME, "%s", s);
109 for(int i=0; i<DEBUGSTREAM_COUNT; i++)
111 if(g_debugstreams[i] == stderr && m_disable_stderr)
113 if(g_debugstreams[i] != NULL)
114 (void)fwrite(s, 1, n, g_debugstreams[i]);
115 //TODO: Is this slow?
116 fflush(g_debugstreams[i]);
123 bool m_disable_stderr;
126 Debugbuf debugbuf(false);
127 std::ostream dstream(&debugbuf);
128 Debugbuf debugbuf_no_stderr(true);
129 std::ostream dstream_no_stderr(&debugbuf_no_stderr);
136 void sanity_check_fn(const char *assertion, const char *file,
137 unsigned int line, const char *function)
139 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);
144 debug_stacks_print();
146 if(g_debugstreams[1])
147 fclose(g_debugstreams[1]);
152 void fatal_error_fn(const char *msg, const char *file,
153 unsigned int line, const char *function)
155 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);
160 debug_stacks_print();
162 if(g_debugstreams[1])
163 fclose(g_debugstreams[1]);
174 DebugStack(threadid_t id);
175 void print(FILE *file, bool everything);
176 void print(std::ostream &os, bool everything);
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
184 DebugStack::DebugStack(threadid_t id)
189 memset(stack, 0, DEBUG_STACK_SIZE*DEBUG_STACK_TEXT_SIZE);
192 void DebugStack::print(FILE *file, bool everything)
194 fprintf(file, "DEBUG STACK FOR THREAD %lx:\n",
195 (unsigned long)threadid);
197 for(int i=0; i<stack_max_i; i++)
199 if(i == stack_i && everything == false)
203 fprintf(file, "#%d %s\n", i, stack[i]);
205 fprintf(file, "(Leftover data: #%d %s)\n", i, stack[i]);
208 if(stack_i == DEBUG_STACK_SIZE)
209 fprintf(file, "Probably overflown.\n");
212 void DebugStack::print(std::ostream &os, bool everything)
214 os<<"DEBUG STACK FOR THREAD "<<(unsigned long)threadid<<": "<<std::endl;
216 for(int i=0; i<stack_max_i; i++)
218 if(i == stack_i && everything == false)
222 os<<"#"<<i<<" "<<stack[i]<<std::endl;
224 os<<"(Leftover data: #"<<i<<" "<<stack[i]<<")"<<std::endl;
227 if(stack_i == DEBUG_STACK_SIZE)
228 os<<"Probably overflown."<<std::endl;
231 std::map<threadid_t, DebugStack*> g_debug_stacks;
232 Mutex g_debug_stacks_mutex;
234 void debug_stacks_init()
238 void debug_stacks_print_to(std::ostream &os)
240 MutexAutoLock lock(g_debug_stacks_mutex);
242 os<<"Debug stacks:"<<std::endl;
244 for(std::map<threadid_t, DebugStack*>::iterator
245 i = g_debug_stacks.begin();
246 i != g_debug_stacks.end(); ++i)
248 i->second->print(os, false);
252 void debug_stacks_print()
254 MutexAutoLock lock(g_debug_stacks_mutex);
256 DEBUGPRINT("Debug stacks:\n");
258 for(std::map<threadid_t, DebugStack*>::iterator
259 i = g_debug_stacks.begin();
260 i != g_debug_stacks.end(); ++i)
262 DebugStack *stack = i->second;
264 for(int i=0; i<DEBUGSTREAM_COUNT; i++)
266 if(g_debugstreams[i] != NULL)
267 stack->print(g_debugstreams[i], true);
272 DebugStacker::DebugStacker(const char *text)
274 threadid_t threadid = get_current_thread_id();
276 MutexAutoLock lock(g_debug_stacks_mutex);
278 std::map<threadid_t, DebugStack*>::iterator n;
279 n = g_debug_stacks.find(threadid);
280 if(n != g_debug_stacks.end())
286 /*DEBUGPRINT("Creating new debug stack for thread %x\n",
287 (unsigned int)threadid);*/
288 m_stack = new DebugStack(threadid);
289 g_debug_stacks[threadid] = m_stack;
292 if(m_stack->stack_i >= DEBUG_STACK_SIZE)
298 m_overflowed = false;
300 snprintf(m_stack->stack[m_stack->stack_i],
301 DEBUG_STACK_TEXT_SIZE, "%s", text);
303 if(m_stack->stack_i > m_stack->stack_max_i)
304 m_stack->stack_max_i = m_stack->stack_i;
308 DebugStacker::~DebugStacker()
310 MutexAutoLock lock(g_debug_stacks_mutex);
312 if(m_overflowed == true)
317 if(m_stack->stack_i == 0)
319 threadid_t threadid = m_stack->threadid;
320 /*DEBUGPRINT("Deleting debug stack for thread %x\n",
321 (unsigned int)threadid);*/
323 g_debug_stacks.erase(threadid);
329 const char *Win32ExceptionCodeToString(DWORD exception_code)
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";
378 return "Unknown exception";
381 long WINAPI Win32ExceptionHandler(struct _EXCEPTION_POINTERS *pExceptInfo)
384 MINIDUMP_EXCEPTION_INFORMATION mdei;
385 MINIDUMP_USER_STREAM_INFORMATION mdusi;
386 MINIDUMP_USER_STREAM mdus;
387 bool minidump_created = false;
389 std::string dumpfile = porting::path_user + DIR_DELIM PROJECT_NAME ".dmp";
391 std::string version_str(PROJECT_NAME " ");
392 version_str += g_version_hash;
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;
399 if (SetEndOfFile(hFile) == FALSE)
400 goto minidump_failed;
402 mdei.ClientPointers = NULL;
403 mdei.ExceptionPointers = pExceptInfo;
404 mdei.ThreadId = GetCurrentThreadId();
406 mdus.Type = CommentStreamA;
407 mdus.BufferSize = version_str.size();
408 mdus.Buffer = (PVOID)version_str.c_str();
410 mdusi.UserStreamArray = &mdus;
411 mdusi.UserStreamCount = 1;
413 if (MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile,
414 MiniDumpNormal, &mdei, &mdusi, NULL) == FALSE)
415 goto minidump_failed;
417 minidump_created = true;
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);
431 if (minidump_created)
432 dstream << " >> Saved dump to " << dumpfile << std::endl;
434 dstream << " >> Failed to save dump" << std::endl;
436 return EXCEPTION_EXECUTE_HANDLER;
441 void debug_set_exception_handler()
444 SetUnhandledExceptionFilter(Win32ExceptionHandler);