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