1/*-------------------------------------------------------------------------
2 * drawElements Quality Program Helper Library
3 * -------------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief System handler handler override
22 *//*--------------------------------------------------------------------*/
23
24#include "qpCrashHandler.h"
25#include "qpDebugOut.h"
26
27#include "deThread.h"
28#include "deMemory.h"
29#include "deString.h"
30#include "deMutex.h"
31
32#include <stdio.h>
33#include <stdarg.h>
34
35#if 0
36#	define DBGPRINT(X) qpPrintf X
37#else
38#	define DBGPRINT(X)
39#endif
40
41/* Crash info write helper. */
42static void writeInfoFormat (qpWriteCrashInfoFunc writeFunc, void* userPtr, const char* format, ...)
43{
44	char		buf[256];
45	va_list		ap;
46
47	va_start(ap, format);
48	vsnprintf(buf, sizeof(buf), format, ap);
49	va_end(ap);
50
51	writeFunc(userPtr, buf);
52}
53
54/* Shared crash info. */
55typedef enum qpCrashType_e
56{
57	QP_CRASHTYPE_SEGMENTATION_FAULT = 0,
58	QP_CRASHTYPE_ASSERT,
59	QP_CRASHTYPE_UNHANDLED_EXCEPTION,
60	QP_CRASHTYPE_OTHER,
61
62	QP_CRASHTYPE_LAST
63} qpCrashType;
64
65typedef struct qpCrashInfo_s
66{
67	qpCrashType						type;
68	const char*						message;
69	const char*						file;
70	int								line;
71} qpCrashInfo;
72
73static void qpCrashInfo_init (qpCrashInfo* info)
74{
75	info->type		= QP_CRASHTYPE_LAST;
76	info->message	= DE_NULL;
77	info->file		= DE_NULL;
78	info->line		= 0;
79}
80
81static void qpCrashInfo_set (qpCrashInfo* info, qpCrashType type, const char* message, const char* file, int line)
82{
83	info->type		= type;
84	info->message	= message;
85	info->file		= file;
86	info->line		= line;
87}
88
89static void qpCrashInfo_write (qpCrashInfo* info, qpWriteCrashInfoFunc writeInfo, void* userPtr)
90{
91	switch (info->type)
92	{
93		case QP_CRASHTYPE_SEGMENTATION_FAULT:
94			writeInfoFormat(writeInfo, userPtr, "Segmentation fault: '%s'\n", info->message);
95			break;
96
97		case QP_CRASHTYPE_UNHANDLED_EXCEPTION:
98			writeInfoFormat(writeInfo, userPtr, "Unhandled exception: '%s'\n", info->message);
99			break;
100
101		case QP_CRASHTYPE_ASSERT:
102			writeInfoFormat(writeInfo, userPtr, "Assertion '%s' failed at %s:%d\n",
103							info->message,
104							info->file,
105							info->line);
106			break;
107
108		case QP_CRASHTYPE_OTHER:
109		default:
110			writeInfoFormat(writeInfo, userPtr, "Crash: '%s'\n", info->message);
111			break;
112	}
113}
114
115static void defaultWriteInfo (void* userPtr, const char* infoString)
116{
117	DE_UNREF(userPtr);
118	qpPrintf("%s", infoString);
119}
120
121static void defaultCrashHandler (qpCrashHandler* crashHandler, void* userPtr)
122{
123	DE_UNREF(userPtr);
124	qpCrashHandler_writeCrashInfo(crashHandler, defaultWriteInfo, DE_NULL);
125	qpDief("Test process crashed");
126}
127
128#if (DE_OS == DE_OS_WIN32) && (DE_COMPILER == DE_COMPILER_MSC)
129
130#define WIN32_LEAN_AND_MEAN
131#include <windows.h>
132#include <DbgHelp.h>
133
134struct qpCrashHandler_s
135{
136	qpCrashHandlerFunc				crashHandlerFunc;
137	void*							handlerUserPointer;
138
139	deMutex							crashHandlerLock;
140	qpCrashInfo						crashInfo;
141	deUintptr						crashAddress;
142
143	LPTOP_LEVEL_EXCEPTION_FILTER	oldExceptionFilter;
144};
145
146qpCrashHandler*		g_crashHandler = DE_NULL;
147
148static LONG WINAPI unhandledExceptionFilter (struct _EXCEPTION_POINTERS* info)
149{
150	qpCrashType crashType	= QP_CRASHTYPE_LAST;
151	const char*	reason		= DE_NULL;
152
153	/* Skip breakpoints. */
154	if (info->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
155	{
156		DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): breakpoint\n"));
157		return EXCEPTION_CONTINUE_SEARCH;
158	}
159
160	/* If no handler present (how could that be?), don't handle. */
161	if (g_crashHandler == DE_NULL)
162	{
163		DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): no crash handler registered\n"));
164		return EXCEPTION_CONTINUE_SEARCH;
165	}
166
167	/* If we have a debugger, let it handle the exception. */
168	if (IsDebuggerPresent())
169	{
170		DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): debugger present\n"));
171		return EXCEPTION_CONTINUE_SEARCH;
172	}
173
174	/* Acquire crash handler lock. Otherwise we might get strange behavior when multiple threads enter crash handler simultaneously. */
175	deMutex_lock(g_crashHandler->crashHandlerLock);
176
177	/* Map crash type. */
178	switch (info->ExceptionRecord->ExceptionCode)
179	{
180		case EXCEPTION_ACCESS_VIOLATION:
181			crashType	= QP_CRASHTYPE_SEGMENTATION_FAULT;
182			reason		= "Access violation";
183			break;
184
185		case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
186			crashType	= QP_CRASHTYPE_SEGMENTATION_FAULT;
187			reason		= "Array bounds exceeded";
188			break;
189
190		case EXCEPTION_ILLEGAL_INSTRUCTION:
191			crashType	= QP_CRASHTYPE_OTHER;
192			reason		= "Illegal instruction";
193			break;
194
195		case EXCEPTION_STACK_OVERFLOW:
196			crashType	= QP_CRASHTYPE_OTHER;
197			reason		= "Stack overflow";
198			break;
199
200		default:
201			/* \todo [pyry] Others. */
202			crashType	= QP_CRASHTYPE_OTHER;
203			reason		= "";
204			break;
205	}
206
207	/* Store reason. */
208	qpCrashInfo_set(&g_crashHandler->crashInfo, crashType, reason, __FILE__, __LINE__);
209
210	/* Store win32-specific crash info. */
211	g_crashHandler->crashAddress = (deUintptr)info->ExceptionRecord->ExceptionAddress;
212
213	/* Handle the crash. */
214	DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): handled quietly\n"));
215	if (g_crashHandler->crashHandlerFunc != DE_NULL)
216		g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
217
218	/* Release lock. */
219	deMutex_unlock(g_crashHandler->crashHandlerLock);
220
221	return EXCEPTION_EXECUTE_HANDLER;
222}
223
224static void assertFailureCallback (const char* expr, const char* file, int line)
225{
226	/* Don't execute crash handler function if debugger is present. */
227	if (IsDebuggerPresent())
228	{
229		DBGPRINT(("qpCrashHandler::assertFailureCallback(): debugger present\n"));
230		return;
231	}
232
233	/* Acquire crash handler lock. */
234	deMutex_lock(g_crashHandler->crashHandlerLock);
235
236	/* Store info. */
237	qpCrashInfo_set(&g_crashHandler->crashInfo, QP_CRASHTYPE_ASSERT, expr, file, line);
238	g_crashHandler->crashAddress = 0;
239
240	/* Handle the crash. */
241	if (g_crashHandler->crashHandlerFunc != DE_NULL)
242		g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
243
244	/* Release lock. */
245	deMutex_unlock(g_crashHandler->crashHandlerLock);
246}
247
248qpCrashHandler* qpCrashHandler_create (qpCrashHandlerFunc handlerFunc, void* userPointer)
249{
250	/* Allocate & initialize. */
251	qpCrashHandler* handler = (qpCrashHandler*)deCalloc(sizeof(qpCrashHandler));
252	DBGPRINT(("qpCrashHandler::create() -- Win32\n"));
253	if (!handler)
254		return handler;
255
256	DE_ASSERT(g_crashHandler == DE_NULL);
257
258	handler->crashHandlerFunc	= handlerFunc ? handlerFunc : defaultCrashHandler;
259	handler->handlerUserPointer	= userPointer;
260
261	/* Create lock for crash handler. \note Has to be recursive or otherwise crash in assert failure causes deadlock. */
262	{
263		deMutexAttributes attr;
264		attr.flags = DE_MUTEX_RECURSIVE;
265		handler->crashHandlerLock = deMutex_create(&attr);
266
267		if (!handler->crashHandlerLock)
268		{
269			deFree(handler);
270			return DE_NULL;
271		}
272	}
273
274	qpCrashInfo_init(&handler->crashInfo);
275	handler->crashAddress		= 0;
276
277	/* Unhandled exception filter. */
278	handler->oldExceptionFilter = SetUnhandledExceptionFilter(unhandledExceptionFilter);
279
280	/* Prevent nasty error dialog. */
281	SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX);
282
283	/* DE_ASSERT callback. */
284	deSetAssertFailureCallback(assertFailureCallback);
285
286	g_crashHandler = handler;
287	return handler;
288}
289
290void qpCrashHandler_destroy (qpCrashHandler* handler)
291{
292	DBGPRINT(("qpCrashHandler::destroy()\n"));
293
294	DE_ASSERT(g_crashHandler == handler);
295
296	deSetAssertFailureCallback(DE_NULL);
297	SetUnhandledExceptionFilter(handler->oldExceptionFilter);
298
299	g_crashHandler = DE_NULL;
300	deFree(handler);
301}
302
303enum
304{
305	MAX_NAME_LENGTH = 64
306};
307
308void qpCrashHandler_writeCrashInfo (qpCrashHandler* handler, qpWriteCrashInfoFunc writeInfo, void* userPtr)
309{
310	void*			addresses[32];
311	HANDLE			process;
312
313	/* Symbol info struct. */
314	deUint8			symInfoStorage[sizeof(SYMBOL_INFO)+MAX_NAME_LENGTH];
315	SYMBOL_INFO*	symInfo			= (SYMBOL_INFO*)symInfoStorage;
316
317	DE_STATIC_ASSERT(sizeof(TCHAR) == sizeof(deUint8));
318
319	/* Write basic info. */
320	qpCrashInfo_write(&handler->crashInfo, writeInfo, userPtr);
321
322	/* Acquire process handle and initialize symbols. */
323	process = GetCurrentProcess();
324
325	/* Write backtrace. */
326	if (SymInitialize(process, NULL, TRUE))
327	{
328		int batchStart		= 0;
329		int globalFrameNdx	= 0;
330
331		/* Initialize symInfo. */
332		deMemset(symInfo, 0, sizeof(symInfoStorage));
333		symInfo->SizeOfStruct	= sizeof(SYMBOL_INFO);
334		symInfo->MaxNameLen		= MAX_NAME_LENGTH;
335
336		/* Print address and symbol where crash happened. */
337		if (handler->crashAddress != 0)
338		{
339			BOOL symInfoOk = SymFromAddr(process, (DWORD64)handler->crashAddress, 0, symInfo);
340
341			writeInfoFormat(writeInfo, userPtr, "  at %p %s%s\n", handler->crashAddress,
342							symInfoOk ? symInfo->Name : "(unknown)",
343							symInfoOk ? "()" : "");
344		}
345
346		writeInfo(userPtr, "Backtrace:\n");
347
348		for (;;)
349		{
350			int curFrame;
351			int numInBatch;
352
353			/* Get one batch. */
354			numInBatch = CaptureStackBackTrace(batchStart, DE_LENGTH_OF_ARRAY(addresses), addresses, NULL);
355
356			for (curFrame = 0; curFrame < numInBatch; curFrame++)
357			{
358				BOOL symInfoOk = SymFromAddr(process, (DWORD64)addresses[curFrame], 0, symInfo);
359
360				writeInfoFormat(writeInfo, userPtr, "  %2d: %p %s%s\n", globalFrameNdx++, addresses[curFrame],
361								symInfoOk ? symInfo->Name : "(unknown)",
362								symInfoOk ? "()" : "");
363			}
364
365			batchStart += numInBatch;
366
367			/* Check if we hit end of stack trace. */
368			if (numInBatch == 0 || numInBatch < DE_LENGTH_OF_ARRAY(addresses))
369				break;
370		}
371	}
372}
373
374#else /* posix / generic implementation */
375
376#if defined(QP_USE_SIGNAL_HANDLER)
377#	include <signal.h>
378#endif
379
380#if defined(QP_USE_SIGNAL_HANDLER)
381
382typedef struct SignalInfo_s
383{
384	int				signalNum;
385	qpCrashType		type;
386	const char*		name;
387} SignalInfo;
388
389static const SignalInfo s_signals[] =
390{
391	{ SIGABRT,		QP_CRASHTYPE_UNHANDLED_EXCEPTION,	"SIGABRT"	},
392	{ SIGILL,		QP_CRASHTYPE_OTHER,					"SIGILL"	},
393	{ SIGSEGV,		QP_CRASHTYPE_SEGMENTATION_FAULT,	"SIGSEGV"	},
394	{ SIGFPE,		QP_CRASHTYPE_OTHER,					"SIGFPE"	},
395	{ SIGBUS,		QP_CRASHTYPE_SEGMENTATION_FAULT,	"SIGBUS"	},
396	{ SIGPIPE,		QP_CRASHTYPE_OTHER,					"SIGPIPE"	}
397};
398
399#endif /* QP_USE_SIGNAL_HANDLER */
400
401struct qpCrashHandler_s
402{
403	qpCrashHandlerFunc		crashHandlerFunc;
404	void*					handlerUserPointer;
405
406	qpCrashInfo				crashInfo;
407	int						crashSignal;
408
409#if defined(QP_USE_SIGNAL_HANDLER)
410	struct sigaction		oldHandlers[DE_LENGTH_OF_ARRAY(s_signals)];
411#endif
412};
413
414qpCrashHandler* g_crashHandler = DE_NULL;
415
416static void assertFailureCallback (const char* expr, const char* file, int line)
417{
418	/* Store info. */
419	qpCrashInfo_set(&g_crashHandler->crashInfo, QP_CRASHTYPE_ASSERT, expr, file, line);
420
421	/* Handle the crash. */
422	if (g_crashHandler->crashHandlerFunc != DE_NULL)
423		g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
424}
425
426#if defined(QP_USE_SIGNAL_HANDLER)
427
428static const SignalInfo* getSignalInfo (int sigNum)
429{
430	int ndx;
431	for (ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_signals); ndx++)
432	{
433		if (s_signals[ndx].signalNum == sigNum)
434			return &s_signals[ndx];
435	}
436	return DE_NULL;
437}
438
439static void signalHandler (int sigNum)
440{
441	const SignalInfo*	info	= getSignalInfo(sigNum);
442	qpCrashType			type	= info ? info->type : QP_CRASHTYPE_OTHER;
443	const char*			name	= info ? info->name : "Unknown signal";
444
445	qpCrashInfo_set(&g_crashHandler->crashInfo, type, name, DE_NULL, 0);
446
447	if (g_crashHandler->crashHandlerFunc != DE_NULL)
448		g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
449}
450
451#endif /* QP_USE_SIGNAL_HANDLER */
452
453qpCrashHandler* qpCrashHandler_create (qpCrashHandlerFunc handlerFunc, void* userPointer)
454{
455	/* Allocate & initialize. */
456	qpCrashHandler* handler = (qpCrashHandler*)deCalloc(sizeof(qpCrashHandler));
457	DBGPRINT(("qpCrashHandler::create()\n"));
458	if (!handler)
459		return handler;
460
461	DE_ASSERT(g_crashHandler == DE_NULL);
462
463	handler->crashHandlerFunc	= handlerFunc ? handlerFunc : defaultCrashHandler;
464	handler->handlerUserPointer	= userPointer;
465
466	qpCrashInfo_init(&handler->crashInfo);
467
468	g_crashHandler = handler;
469
470	/* DE_ASSERT callback. */
471	deSetAssertFailureCallback(assertFailureCallback);
472
473#if defined(QP_USE_SIGNAL_HANDLER)
474	/* Register signal handlers. */
475	{
476		struct sigaction	action;
477		int					sigNdx;
478
479		sigemptyset(&action.sa_mask);
480		action.sa_handler	= signalHandler;
481		action.sa_flags		= 0;
482
483		for (sigNdx = 0; sigNdx < DE_LENGTH_OF_ARRAY(s_signals); sigNdx++)
484			sigaction(s_signals[sigNdx].signalNum, &action, &handler->oldHandlers[sigNdx]);
485	}
486#endif
487
488	return handler;
489}
490
491void qpCrashHandler_destroy (qpCrashHandler* handler)
492{
493	DBGPRINT(("qpCrashHandler::destroy()\n"));
494
495	DE_ASSERT(g_crashHandler == handler);
496
497	deSetAssertFailureCallback(DE_NULL);
498
499#if defined(QP_USE_SIGNAL_HANDLER)
500	/* Restore old handlers. */
501	{
502		int sigNdx;
503		for (sigNdx = 0; sigNdx < DE_LENGTH_OF_ARRAY(s_signals); sigNdx++)
504			sigaction(s_signals[sigNdx].signalNum, &handler->oldHandlers[sigNdx], DE_NULL);
505	}
506#endif
507
508	g_crashHandler = DE_NULL;
509
510	deFree(handler);
511}
512
513void qpCrashHandler_writeCrashInfo (qpCrashHandler* crashHandler, qpWriteCrashInfoFunc writeInfo, void* userPtr)
514{
515	qpCrashInfo_write(&crashHandler->crashInfo, writeInfo, userPtr);
516}
517
518#endif /* generic */
519