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