Fix bone-attached entities (#10015)
[oweals/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 <cstdio>
25 #include <cstdlib>
26 #include <cstring>
27 #include <map>
28 #include <sstream>
29 #include <thread>
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                 << std::this_thread::get_id() << ":" << std::endl;
56         errorstream << file << ":" << line << ": " << function
57                 << ": An engine assumption '" << assertion << "' failed." << std::endl;
58
59         abort();
60 }
61
62 void fatal_error_fn(const char *msg, const char *file,
63                 unsigned int line, const char *function)
64 {
65 #if USE_CURSES
66         g_term_console.stopAndWaitforThread();
67 #endif
68
69         errorstream << std::endl << "In thread " << std::hex
70                 << std::this_thread::get_id() << ":" << std::endl;
71         errorstream << file << ":" << line << ": " << function
72                 << ": A fatal error occurred: " << msg << std::endl;
73
74         abort();
75 }
76
77 #ifdef _MSC_VER
78
79 const char *Win32ExceptionCodeToString(DWORD exception_code)
80 {
81         switch (exception_code) {
82         case EXCEPTION_ACCESS_VIOLATION:
83                 return "Access violation";
84         case EXCEPTION_DATATYPE_MISALIGNMENT:
85                 return "Misaligned data access";
86         case EXCEPTION_BREAKPOINT:
87                 return "Breakpoint reached";
88         case EXCEPTION_SINGLE_STEP:
89                 return "Single debug step";
90         case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
91                 return "Array access out of bounds";
92         case EXCEPTION_FLT_DENORMAL_OPERAND:
93                 return "Denormal floating point operand";
94         case EXCEPTION_FLT_DIVIDE_BY_ZERO:
95                 return "Floating point division by zero";
96         case EXCEPTION_FLT_INEXACT_RESULT:
97                 return "Inaccurate floating point result";
98         case EXCEPTION_FLT_INVALID_OPERATION:
99                 return "Invalid floating point operation";
100         case EXCEPTION_FLT_OVERFLOW:
101                 return "Floating point exponent overflow";
102         case EXCEPTION_FLT_STACK_CHECK:
103                 return "Floating point stack overflow or underflow";
104         case EXCEPTION_FLT_UNDERFLOW:
105                 return "Floating point exponent underflow";
106         case EXCEPTION_INT_DIVIDE_BY_ZERO:
107                 return "Integer division by zero";
108         case EXCEPTION_INT_OVERFLOW:
109                 return "Integer overflow";
110         case EXCEPTION_PRIV_INSTRUCTION:
111                 return "Privileged instruction executed";
112         case EXCEPTION_IN_PAGE_ERROR:
113                 return "Could not access or load page";
114         case EXCEPTION_ILLEGAL_INSTRUCTION:
115                 return "Illegal instruction encountered";
116         case EXCEPTION_NONCONTINUABLE_EXCEPTION:
117                 return "Attempted to continue after fatal exception";
118         case EXCEPTION_STACK_OVERFLOW:
119                 return "Stack overflow";
120         case EXCEPTION_INVALID_DISPOSITION:
121                 return "Invalid disposition returned to the exception dispatcher";
122         case EXCEPTION_GUARD_PAGE:
123                 return "Attempted guard page access";
124         case EXCEPTION_INVALID_HANDLE:
125                 return "Invalid handle";
126         }
127
128         return "Unknown exception";
129 }
130
131 long WINAPI Win32ExceptionHandler(struct _EXCEPTION_POINTERS *pExceptInfo)
132 {
133         char buf[512];
134         MINIDUMP_EXCEPTION_INFORMATION mdei;
135         MINIDUMP_USER_STREAM_INFORMATION mdusi;
136         MINIDUMP_USER_STREAM mdus;
137         bool minidump_created = false;
138
139         std::string dumpfile = porting::path_user + DIR_DELIM PROJECT_NAME ".dmp";
140
141         std::string version_str(PROJECT_NAME " ");
142         version_str += g_version_hash;
143
144         HANDLE hFile = CreateFileA(dumpfile.c_str(), GENERIC_WRITE,
145                 FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
146         if (hFile == INVALID_HANDLE_VALUE)
147                 goto minidump_failed;
148
149         if (SetEndOfFile(hFile) == FALSE)
150                 goto minidump_failed;
151
152         mdei.ClientPointers        = NULL;
153         mdei.ExceptionPointers = pExceptInfo;
154         mdei.ThreadId              = GetCurrentThreadId();
155
156         mdus.Type       = CommentStreamA;
157         mdus.BufferSize = version_str.size();
158         mdus.Buffer     = (PVOID)version_str.c_str();
159
160         mdusi.UserStreamArray = &mdus;
161         mdusi.UserStreamCount = 1;
162
163         if (MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile,
164                         MiniDumpNormal, &mdei, &mdusi, NULL) == FALSE)
165                 goto minidump_failed;
166
167         minidump_created = true;
168
169 minidump_failed:
170
171         CloseHandle(hFile);
172
173         DWORD excode = pExceptInfo->ExceptionRecord->ExceptionCode;
174         _snprintf(buf, sizeof(buf),
175                 " >> === FATAL ERROR ===\n"
176                 " >> %s (Exception 0x%08X) at 0x%p\n",
177                 Win32ExceptionCodeToString(excode), excode,
178                 pExceptInfo->ExceptionRecord->ExceptionAddress);
179         dstream << buf;
180
181         if (minidump_created)
182                 dstream << " >> Saved dump to " << dumpfile << std::endl;
183         else
184                 dstream << " >> Failed to save dump" << std::endl;
185
186         return EXCEPTION_EXECUTE_HANDLER;
187 }
188
189 #endif
190
191 void debug_set_exception_handler()
192 {
193 #ifdef _MSC_VER
194         SetUnhandledExceptionFilter(Win32ExceptionHandler);
195 #endif
196 }
197