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 
sanity_check_fn(const char * assertion,const char * file,unsigned int line,const char * function)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 
fatal_error_fn(const char * msg,const char * file,unsigned int line,const char * function)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 
Win32ExceptionCodeToString(DWORD exception_code)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 
Win32ExceptionHandler(struct _EXCEPTION_POINTERS * pExceptInfo)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 
debug_set_exception_handler()191 void debug_set_exception_handler()
192 {
193 #ifdef _MSC_VER
194 	SetUnhandledExceptionFilter(Win32ExceptionHandler);
195 #endif
196 }
197 
198