Home | History | Annotate | Download | only in qphelper
      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. */
     42 static 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. */
     55 typedef 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 
     65 typedef struct qpCrashInfo_s
     66 {
     67 	qpCrashType						type;
     68 	const char*						message;
     69 	const char*						file;
     70 	int								line;
     71 } qpCrashInfo;
     72 
     73 static 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 
     81 static 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 
     89 static 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 
    115 static void defaultWriteInfo (void* userPtr, const char* infoString)
    116 {
    117 	DE_UNREF(userPtr);
    118 	qpPrintf("%s", infoString);
    119 }
    120 
    121 static 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 
    134 struct 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 
    146 qpCrashHandler*		g_crashHandler = DE_NULL;
    147 
    148 static 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 
    224 static 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 
    248 qpCrashHandler* 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 
    290 void 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 
    303 enum
    304 {
    305 	MAX_NAME_LENGTH = 64
    306 };
    307 
    308 void 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 (DE_OS == DE_OS_UNIX) || (DE_OS == DE_OS_ANDROID) || (DE_OS == DE_OS_OSX) || (DE_OS == DE_OS_IOS)
    377 #	define USE_SIGNAL_HANDLER 1
    378 #	include <signal.h>
    379 #endif
    380 
    381 #if defined(USE_SIGNAL_HANDLER)
    382 
    383 typedef struct SignalInfo_s
    384 {
    385 	int				signalNum;
    386 	qpCrashType		type;
    387 	const char*		name;
    388 } SignalInfo;
    389 
    390 static const SignalInfo s_signals[] =
    391 {
    392 	{ SIGABRT,		QP_CRASHTYPE_UNHANDLED_EXCEPTION,	"SIGABRT"	},
    393 	{ SIGILL,		QP_CRASHTYPE_OTHER,					"SIGILL"	},
    394 	{ SIGSEGV,		QP_CRASHTYPE_SEGMENTATION_FAULT,	"SIGSEGV"	},
    395 	{ SIGFPE,		QP_CRASHTYPE_OTHER,					"SIGFPE"	},
    396 	{ SIGBUS,		QP_CRASHTYPE_SEGMENTATION_FAULT,	"SIGBUS"	},
    397 	{ SIGPIPE,		QP_CRASHTYPE_OTHER,					"SIGPIPE"	}
    398 };
    399 
    400 #endif /* USE_SIGNAL_HANDLER */
    401 
    402 struct qpCrashHandler_s
    403 {
    404 	qpCrashHandlerFunc		crashHandlerFunc;
    405 	void*					handlerUserPointer;
    406 
    407 	qpCrashInfo				crashInfo;
    408 	int						crashSignal;
    409 
    410 #if defined(USE_SIGNAL_HANDLER)
    411 	struct sigaction		oldHandlers[DE_LENGTH_OF_ARRAY(s_signals)];
    412 #endif
    413 };
    414 
    415 qpCrashHandler* g_crashHandler = DE_NULL;
    416 
    417 static void assertFailureCallback (const char* expr, const char* file, int line)
    418 {
    419 	/* Store info. */
    420 	qpCrashInfo_set(&g_crashHandler->crashInfo, QP_CRASHTYPE_ASSERT, expr, file, line);
    421 
    422 	/* Handle the crash. */
    423 	if (g_crashHandler->crashHandlerFunc != DE_NULL)
    424 		g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
    425 }
    426 
    427 #if defined(USE_SIGNAL_HANDLER)
    428 
    429 static const SignalInfo* getSignalInfo (int sigNum)
    430 {
    431 	int ndx;
    432 	for (ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_signals); ndx++)
    433 	{
    434 		if (s_signals[ndx].signalNum == sigNum)
    435 			return &s_signals[ndx];
    436 	}
    437 	return DE_NULL;
    438 }
    439 
    440 static void signalHandler (int sigNum)
    441 {
    442 	const SignalInfo*	info	= getSignalInfo(sigNum);
    443 	qpCrashType			type	= info ? info->type : QP_CRASHTYPE_OTHER;
    444 	const char*			name	= info ? info->name : "Unknown signal";
    445 
    446 	qpCrashInfo_set(&g_crashHandler->crashInfo, type, name, DE_NULL, 0);
    447 
    448 	if (g_crashHandler->crashHandlerFunc != DE_NULL)
    449 		g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
    450 }
    451 
    452 #endif /* USE_SIGNAL_HANDLER */
    453 
    454 qpCrashHandler* qpCrashHandler_create (qpCrashHandlerFunc handlerFunc, void* userPointer)
    455 {
    456 	/* Allocate & initialize. */
    457 	qpCrashHandler* handler = (qpCrashHandler*)deCalloc(sizeof(qpCrashHandler));
    458 	DBGPRINT(("qpCrashHandler::create()\n"));
    459 	if (!handler)
    460 		return handler;
    461 
    462 	DE_ASSERT(g_crashHandler == DE_NULL);
    463 
    464 	handler->crashHandlerFunc	= handlerFunc ? handlerFunc : defaultCrashHandler;
    465 	handler->handlerUserPointer	= userPointer;
    466 
    467 	qpCrashInfo_init(&handler->crashInfo);
    468 
    469 	g_crashHandler = handler;
    470 
    471 	/* DE_ASSERT callback. */
    472 	deSetAssertFailureCallback(assertFailureCallback);
    473 
    474 #if defined(USE_SIGNAL_HANDLER)
    475 	/* Register signal handlers. */
    476 	{
    477 		struct sigaction	action;
    478 		int					sigNdx;
    479 
    480 		sigemptyset(&action.sa_mask);
    481 		action.sa_handler	= signalHandler;
    482 		action.sa_flags		= 0;
    483 
    484 		for (sigNdx = 0; sigNdx < DE_LENGTH_OF_ARRAY(s_signals); sigNdx++)
    485 			sigaction(s_signals[sigNdx].signalNum, &action, &handler->oldHandlers[sigNdx]);
    486 	}
    487 #endif
    488 
    489 	return handler;
    490 }
    491 
    492 void qpCrashHandler_destroy (qpCrashHandler* handler)
    493 {
    494 	DBGPRINT(("qpCrashHandler::destroy()\n"));
    495 
    496 	DE_ASSERT(g_crashHandler == handler);
    497 
    498 	deSetAssertFailureCallback(DE_NULL);
    499 
    500 #if defined(USE_SIGNAL_HANDLER)
    501 	/* Restore old handlers. */
    502 	{
    503 		int sigNdx;
    504 		for (sigNdx = 0; sigNdx < DE_LENGTH_OF_ARRAY(s_signals); sigNdx++)
    505 			sigaction(s_signals[sigNdx].signalNum, &handler->oldHandlers[sigNdx], DE_NULL);
    506 	}
    507 #endif
    508 
    509 	g_crashHandler = DE_NULL;
    510 
    511 	deFree(handler);
    512 }
    513 
    514 void qpCrashHandler_writeCrashInfo (qpCrashHandler* crashHandler, qpWriteCrashInfoFunc writeInfo, void* userPtr)
    515 {
    516 	qpCrashInfo_write(&crashHandler->crashInfo, writeInfo, userPtr);
    517 }
    518 
    519 #endif /* generic */
    520