]> git.lizzy.rs Git - minetest.git/blob - src/debug.cpp
Refactor logging
[minetest.git] / src / debug.cpp
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 <sstream>
30 #include "threading/mutex.h"
31 #include "threading/mutex_auto_lock.h"
32 #include "config.h"
33
34 #ifdef _MSC_VER
35         #include <dbghelp.h>
36         #include "version.h"
37         #include "filesys.h"
38 #endif
39
40 /*
41         Assert
42 */
43
44 void sanity_check_fn(const char *assertion, const char *file,
45                 unsigned int line, const char *function)
46 {
47         errorstream << std::endl << "In thread " << std::hex
48                 << (unsigned long)get_current_thread_id() << ":" << std::endl;
49         errorstream << file << ":" << line << ": " << function
50                 << ": An engine assumption '" << assertion << "' failed." << std::endl;
51
52         debug_stacks_print_to(errorstream);
53
54         abort();
55 }
56
57 void fatal_error_fn(const char *msg, const char *file,
58                 unsigned int line, const char *function)
59 {
60         errorstream << std::endl << "In thread " << std::hex
61                 << (unsigned long)get_current_thread_id() << ":" << std::endl;
62         errorstream << file << ":" << line << ": " << function
63                 << ": A fatal error occured: " << msg << std::endl;
64
65         debug_stacks_print_to(errorstream);
66
67         abort();
68 }
69
70 /*
71         DebugStack
72 */
73
74 struct DebugStack
75 {
76         DebugStack(threadid_t id);
77         void print(FILE *file, bool everything);
78         void print(std::ostream &os, bool everything);
79
80         threadid_t threadid;
81         char stack[DEBUG_STACK_SIZE][DEBUG_STACK_TEXT_SIZE];
82         int stack_i; // Points to the lowest empty position
83         int stack_max_i; // Highest i that was seen
84 };
85
86 DebugStack::DebugStack(threadid_t id)
87 {
88         threadid = id;
89         stack_i = 0;
90         stack_max_i = 0;
91         memset(stack, 0, DEBUG_STACK_SIZE*DEBUG_STACK_TEXT_SIZE);
92 }
93
94 void DebugStack::print(FILE *file, bool everything)
95 {
96         fprintf(file, "DEBUG STACK FOR THREAD %lx:\n",
97                         (unsigned long)threadid);
98
99         for(int i=0; i<stack_max_i; i++)
100         {
101                 if(i == stack_i && everything == false)
102                         break;
103
104                 if(i < stack_i)
105                         fprintf(file, "#%d  %s\n", i, stack[i]);
106                 else
107                         fprintf(file, "(Leftover data: #%d  %s)\n", i, stack[i]);
108         }
109
110         if(stack_i == DEBUG_STACK_SIZE)
111                 fprintf(file, "Probably overflown.\n");
112 }
113
114 void DebugStack::print(std::ostream &os, bool everything)
115 {
116         os<<"DEBUG STACK FOR THREAD "<<(unsigned long)threadid<<": "<<std::endl;
117
118         for(int i=0; i<stack_max_i; i++)
119         {
120                 if(i == stack_i && everything == false)
121                         break;
122
123                 if(i < stack_i)
124                         os<<"#"<<i<<"  "<<stack[i]<<std::endl;
125                 else
126                         os<<"(Leftover data: #"<<i<<"  "<<stack[i]<<")"<<std::endl;
127         }
128
129         if(stack_i == DEBUG_STACK_SIZE)
130                 os<<"Probably overflown."<<std::endl;
131 }
132
133 std::map<threadid_t, DebugStack*> g_debug_stacks;
134 Mutex g_debug_stacks_mutex;
135
136 void debug_stacks_init()
137 {
138 }
139
140 void debug_stacks_print_to(std::ostream &os)
141 {
142         MutexAutoLock lock(g_debug_stacks_mutex);
143
144         os<<"Debug stacks:"<<std::endl;
145
146         for(std::map<threadid_t, DebugStack*>::iterator
147                         i = g_debug_stacks.begin();
148                         i != g_debug_stacks.end(); ++i)
149         {
150                 i->second->print(os, false);
151         }
152 }
153
154 void debug_stacks_print()
155 {
156         debug_stacks_print_to(errorstream);
157 }
158
159 DebugStacker::DebugStacker(const char *text)
160 {
161         threadid_t threadid = get_current_thread_id();
162
163         MutexAutoLock lock(g_debug_stacks_mutex);
164
165         std::map<threadid_t, DebugStack*>::iterator n;
166         n = g_debug_stacks.find(threadid);
167         if(n != g_debug_stacks.end())
168         {
169                 m_stack = n->second;
170         }
171         else
172         {
173                 /*DEBUGPRINT("Creating new debug stack for thread %x\n",
174                                 (unsigned int)threadid);*/
175                 m_stack = new DebugStack(threadid);
176                 g_debug_stacks[threadid] = m_stack;
177         }
178
179         if(m_stack->stack_i >= DEBUG_STACK_SIZE)
180         {
181                 m_overflowed = true;
182         }
183         else
184         {
185                 m_overflowed = false;
186
187                 snprintf(m_stack->stack[m_stack->stack_i],
188                                 DEBUG_STACK_TEXT_SIZE, "%s", text);
189                 m_stack->stack_i++;
190                 if(m_stack->stack_i > m_stack->stack_max_i)
191                         m_stack->stack_max_i = m_stack->stack_i;
192         }
193 }
194
195 DebugStacker::~DebugStacker()
196 {
197         MutexAutoLock lock(g_debug_stacks_mutex);
198
199         if(m_overflowed == true)
200                 return;
201
202         m_stack->stack_i--;
203
204         if(m_stack->stack_i == 0)
205         {
206                 threadid_t threadid = m_stack->threadid;
207                 /*DEBUGPRINT("Deleting debug stack for thread %x\n",
208                                 (unsigned int)threadid);*/
209                 delete m_stack;
210                 g_debug_stacks.erase(threadid);
211         }
212 }
213
214 #ifdef _MSC_VER
215
216 const char *Win32ExceptionCodeToString(DWORD exception_code)
217 {
218         switch (exception_code) {
219         case EXCEPTION_ACCESS_VIOLATION:
220                 return "Access violation";
221         case EXCEPTION_DATATYPE_MISALIGNMENT:
222                 return "Misaligned data access";
223         case EXCEPTION_BREAKPOINT:
224                 return "Breakpoint reached";
225         case EXCEPTION_SINGLE_STEP:
226                 return "Single debug step";
227         case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
228                 return "Array access out of bounds";
229         case EXCEPTION_FLT_DENORMAL_OPERAND:
230                 return "Denormal floating point operand";
231         case EXCEPTION_FLT_DIVIDE_BY_ZERO:
232                 return "Floating point division by zero";
233         case EXCEPTION_FLT_INEXACT_RESULT:
234                 return "Inaccurate floating point result";
235         case EXCEPTION_FLT_INVALID_OPERATION:
236                 return "Invalid floating point operation";
237         case EXCEPTION_FLT_OVERFLOW:
238                 return "Floating point exponent overflow";
239         case EXCEPTION_FLT_STACK_CHECK:
240                 return "Floating point stack overflow or underflow";
241         case EXCEPTION_FLT_UNDERFLOW:
242                 return "Floating point exponent underflow";
243         case EXCEPTION_INT_DIVIDE_BY_ZERO:
244                 return "Integer division by zero";
245         case EXCEPTION_INT_OVERFLOW:
246                 return "Integer overflow";
247         case EXCEPTION_PRIV_INSTRUCTION:
248                 return "Privileged instruction executed";
249         case EXCEPTION_IN_PAGE_ERROR:
250                 return "Could not access or load page";
251         case EXCEPTION_ILLEGAL_INSTRUCTION:
252                 return "Illegal instruction encountered";
253         case EXCEPTION_NONCONTINUABLE_EXCEPTION:
254                 return "Attempted to continue after fatal exception";
255         case EXCEPTION_STACK_OVERFLOW:
256                 return "Stack overflow";
257         case EXCEPTION_INVALID_DISPOSITION:
258                 return "Invalid disposition returned to the exception dispatcher";
259         case EXCEPTION_GUARD_PAGE:
260                 return "Attempted guard page access";
261         case EXCEPTION_INVALID_HANDLE:
262                 return "Invalid handle";
263         }
264
265         return "Unknown exception";
266 }
267
268 long WINAPI Win32ExceptionHandler(struct _EXCEPTION_POINTERS *pExceptInfo)
269 {
270         char buf[512];
271         MINIDUMP_EXCEPTION_INFORMATION mdei;
272         MINIDUMP_USER_STREAM_INFORMATION mdusi;
273         MINIDUMP_USER_STREAM mdus;
274         bool minidump_created = false;
275
276         std::string dumpfile = porting::path_user + DIR_DELIM PROJECT_NAME ".dmp";
277
278         std::string version_str(PROJECT_NAME " ");
279         version_str += g_version_hash;
280
281         HANDLE hFile = CreateFileA(dumpfile.c_str(), GENERIC_WRITE,
282                 FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
283         if (hFile == INVALID_HANDLE_VALUE)
284                 goto minidump_failed;
285
286         if (SetEndOfFile(hFile) == FALSE)
287                 goto minidump_failed;
288
289         mdei.ClientPointers        = NULL;
290         mdei.ExceptionPointers = pExceptInfo;
291         mdei.ThreadId              = GetCurrentThreadId();
292
293         mdus.Type       = CommentStreamA;
294         mdus.BufferSize = version_str.size();
295         mdus.Buffer     = (PVOID)version_str.c_str();
296
297         mdusi.UserStreamArray = &mdus;
298         mdusi.UserStreamCount = 1;
299
300         if (MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile,
301                         MiniDumpNormal, &mdei, &mdusi, NULL) == FALSE)
302                 goto minidump_failed;
303
304         minidump_created = true;
305
306 minidump_failed:
307
308         CloseHandle(hFile);
309
310         DWORD excode = pExceptInfo->ExceptionRecord->ExceptionCode;
311         _snprintf(buf, sizeof(buf),
312                 " >> === FATAL ERROR ===\n"
313                 " >> %s (Exception 0x%08X) at 0x%p\n",
314                 Win32ExceptionCodeToString(excode), excode,
315                 pExceptInfo->ExceptionRecord->ExceptionAddress);
316         dstream << buf;
317
318         if (minidump_created)
319                 dstream << " >> Saved dump to " << dumpfile << std::endl;
320         else
321                 dstream << " >> Failed to save dump" << std::endl;
322
323         return EXCEPTION_EXECUTE_HANDLER;
324 }
325
326 #endif
327
328 void debug_set_exception_handler()
329 {
330 #ifdef _MSC_VER
331         SetUnhandledExceptionFilter(Win32ExceptionHandler);
332 #endif
333 }
334