1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program Helper Library
3 * -------------------------------------------
5 * Copyright 2014 The Android Open Source Project
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 * \brief System handler handler override
22 *//*--------------------------------------------------------------------*/
24 #include "qpCrashHandler.h"
25 #include "qpDebugOut.h"
36 #if (DE_OS == DE_OS_UNIX)
38 # include <execinfo.h>
40 # include <inttypes.h>
44 # define DBGPRINT(X) qpPrintf X
49 /* Crash info write helper. */
50 static void writeInfoFormat (qpWriteCrashInfoFunc writeFunc, void* userPtr, const char* format, ...)
56 vsnprintf(buf, sizeof(buf), format, ap);
59 writeFunc(userPtr, buf);
62 /* Shared crash info. */
63 typedef enum qpCrashType_e
65 QP_CRASHTYPE_SEGMENTATION_FAULT = 0,
67 QP_CRASHTYPE_UNHANDLED_EXCEPTION,
73 typedef struct qpCrashInfo_s
81 static void qpCrashInfo_init (qpCrashInfo* info)
83 info->type = QP_CRASHTYPE_LAST;
84 info->message = DE_NULL;
89 static void qpCrashInfo_set (qpCrashInfo* info, qpCrashType type, const char* message, const char* file, int line)
92 info->message = message;
97 static void qpCrashInfo_write (qpCrashInfo* info, qpWriteCrashInfoFunc writeInfo, void* userPtr)
101 case QP_CRASHTYPE_SEGMENTATION_FAULT:
102 writeInfoFormat(writeInfo, userPtr, "Segmentation fault: '%s'\n", info->message);
105 case QP_CRASHTYPE_UNHANDLED_EXCEPTION:
106 writeInfoFormat(writeInfo, userPtr, "Unhandled exception: '%s'\n", info->message);
109 case QP_CRASHTYPE_ASSERT:
110 writeInfoFormat(writeInfo, userPtr, "Assertion '%s' failed at %s:%d\n",
116 case QP_CRASHTYPE_OTHER:
118 writeInfoFormat(writeInfo, userPtr, "Crash: '%s'\n", info->message);
123 static void defaultWriteInfo (void* userPtr, const char* infoString)
126 qpPrintf("%s", infoString);
129 static void defaultCrashHandler (qpCrashHandler* crashHandler, void* userPtr)
132 qpCrashHandler_writeCrashInfo(crashHandler, defaultWriteInfo, DE_NULL);
133 qpDief("Test process crashed");
136 #if (DE_OS == DE_OS_WIN32) && (DE_COMPILER == DE_COMPILER_MSC)
138 #define WIN32_LEAN_AND_MEAN
141 /* DbgHelp.h generates C4091 */
142 #pragma warning (push)
143 #pragma warning (disable: 4091)
145 #pragma warning (pop)
147 struct qpCrashHandler_s
149 qpCrashHandlerFunc crashHandlerFunc;
150 void* handlerUserPointer;
152 deMutex crashHandlerLock;
153 qpCrashInfo crashInfo;
154 deUintptr crashAddress;
156 LPTOP_LEVEL_EXCEPTION_FILTER oldExceptionFilter;
159 qpCrashHandler* g_crashHandler = DE_NULL;
161 static LONG WINAPI unhandledExceptionFilter (struct _EXCEPTION_POINTERS* info)
163 qpCrashType crashType = QP_CRASHTYPE_LAST;
164 const char* reason = DE_NULL;
166 /* Skip breakpoints. */
167 if (info->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
169 DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): breakpoint\n"));
170 return EXCEPTION_CONTINUE_SEARCH;
173 /* If no handler present (how could that be?), don't handle. */
174 if (g_crashHandler == DE_NULL)
176 DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): no crash handler registered\n"));
177 return EXCEPTION_CONTINUE_SEARCH;
180 /* If we have a debugger, let it handle the exception. */
181 if (IsDebuggerPresent())
183 DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): debugger present\n"));
184 return EXCEPTION_CONTINUE_SEARCH;
187 /* Acquire crash handler lock. Otherwise we might get strange behavior when multiple threads enter crash handler simultaneously. */
188 deMutex_lock(g_crashHandler->crashHandlerLock);
190 /* Map crash type. */
191 switch (info->ExceptionRecord->ExceptionCode)
193 case EXCEPTION_ACCESS_VIOLATION:
194 crashType = QP_CRASHTYPE_SEGMENTATION_FAULT;
195 reason = "Access violation";
198 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
199 crashType = QP_CRASHTYPE_SEGMENTATION_FAULT;
200 reason = "Array bounds exceeded";
203 case EXCEPTION_ILLEGAL_INSTRUCTION:
204 crashType = QP_CRASHTYPE_OTHER;
205 reason = "Illegal instruction";
208 case EXCEPTION_STACK_OVERFLOW:
209 crashType = QP_CRASHTYPE_OTHER;
210 reason = "Stack overflow";
214 /* \todo [pyry] Others. */
215 crashType = QP_CRASHTYPE_OTHER;
221 qpCrashInfo_set(&g_crashHandler->crashInfo, crashType, reason, __FILE__, __LINE__);
223 /* Store win32-specific crash info. */
224 g_crashHandler->crashAddress = (deUintptr)info->ExceptionRecord->ExceptionAddress;
226 /* Handle the crash. */
227 DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): handled quietly\n"));
228 if (g_crashHandler->crashHandlerFunc != DE_NULL)
229 g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
232 deMutex_unlock(g_crashHandler->crashHandlerLock);
234 return EXCEPTION_EXECUTE_HANDLER;
237 static void assertFailureCallback (const char* expr, const char* file, int line)
239 /* Don't execute crash handler function if debugger is present. */
240 if (IsDebuggerPresent())
242 DBGPRINT(("qpCrashHandler::assertFailureCallback(): debugger present\n"));
246 /* Acquire crash handler lock. */
247 deMutex_lock(g_crashHandler->crashHandlerLock);
250 qpCrashInfo_set(&g_crashHandler->crashInfo, QP_CRASHTYPE_ASSERT, expr, file, line);
251 g_crashHandler->crashAddress = 0;
253 /* Handle the crash. */
254 if (g_crashHandler->crashHandlerFunc != DE_NULL)
255 g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
258 deMutex_unlock(g_crashHandler->crashHandlerLock);
261 qpCrashHandler* qpCrashHandler_create (qpCrashHandlerFunc handlerFunc, void* userPointer)
263 /* Allocate & initialize. */
264 qpCrashHandler* handler = (qpCrashHandler*)deCalloc(sizeof(qpCrashHandler));
265 DBGPRINT(("qpCrashHandler::create() -- Win32\n"));
269 DE_ASSERT(g_crashHandler == DE_NULL);
271 handler->crashHandlerFunc = handlerFunc ? handlerFunc : defaultCrashHandler;
272 handler->handlerUserPointer = userPointer;
274 /* Create lock for crash handler. \note Has to be recursive or otherwise crash in assert failure causes deadlock. */
276 deMutexAttributes attr;
277 attr.flags = DE_MUTEX_RECURSIVE;
278 handler->crashHandlerLock = deMutex_create(&attr);
280 if (!handler->crashHandlerLock)
287 qpCrashInfo_init(&handler->crashInfo);
288 handler->crashAddress = 0;
290 /* Unhandled exception filter. */
291 handler->oldExceptionFilter = SetUnhandledExceptionFilter(unhandledExceptionFilter);
293 /* Prevent nasty error dialog. */
294 SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX);
296 /* DE_ASSERT callback. */
297 deSetAssertFailureCallback(assertFailureCallback);
299 g_crashHandler = handler;
303 void qpCrashHandler_destroy (qpCrashHandler* handler)
305 DBGPRINT(("qpCrashHandler::destroy()\n"));
307 DE_ASSERT(g_crashHandler == handler);
309 deSetAssertFailureCallback(DE_NULL);
310 SetUnhandledExceptionFilter(handler->oldExceptionFilter);
312 g_crashHandler = DE_NULL;
321 void qpCrashHandler_writeCrashInfo (qpCrashHandler* handler, qpWriteCrashInfoFunc writeInfo, void* userPtr)
326 /* Symbol info struct. */
327 deUint8 symInfoStorage[sizeof(SYMBOL_INFO)+MAX_NAME_LENGTH];
328 SYMBOL_INFO* symInfo = (SYMBOL_INFO*)symInfoStorage;
330 DE_STATIC_ASSERT(sizeof(TCHAR) == sizeof(deUint8));
332 /* Write basic info. */
333 qpCrashInfo_write(&handler->crashInfo, writeInfo, userPtr);
335 /* Acquire process handle and initialize symbols. */
336 process = GetCurrentProcess();
338 /* Write backtrace. */
339 if (SymInitialize(process, NULL, TRUE))
342 int globalFrameNdx = 0;
344 /* Initialize symInfo. */
345 deMemset(symInfo, 0, sizeof(symInfoStorage));
346 symInfo->SizeOfStruct = sizeof(SYMBOL_INFO);
347 symInfo->MaxNameLen = MAX_NAME_LENGTH;
349 /* Print address and symbol where crash happened. */
350 if (handler->crashAddress != 0)
352 BOOL symInfoOk = SymFromAddr(process, (DWORD64)handler->crashAddress, 0, symInfo);
354 writeInfoFormat(writeInfo, userPtr, " at %p %s%s\n", handler->crashAddress,
355 symInfoOk ? symInfo->Name : "(unknown)",
356 symInfoOk ? "()" : "");
359 writeInfo(userPtr, "Backtrace:\n");
367 numInBatch = CaptureStackBackTrace(batchStart, DE_LENGTH_OF_ARRAY(addresses), addresses, NULL);
369 for (curFrame = 0; curFrame < numInBatch; curFrame++)
371 BOOL symInfoOk = SymFromAddr(process, (DWORD64)addresses[curFrame], 0, symInfo);
373 writeInfoFormat(writeInfo, userPtr, " %2d: %p %s%s\n", globalFrameNdx++, addresses[curFrame],
374 symInfoOk ? symInfo->Name : "(unknown)",
375 symInfoOk ? "()" : "");
378 batchStart += numInBatch;
380 /* Check if we hit end of stack trace. */
381 if (numInBatch == 0 || numInBatch < DE_LENGTH_OF_ARRAY(addresses))
387 #else /* posix / generic implementation */
389 #if defined(QP_USE_SIGNAL_HANDLER)
393 #if defined(QP_USE_SIGNAL_HANDLER)
395 typedef struct SignalInfo_s
402 static const SignalInfo s_signals[] =
404 { SIGABRT, QP_CRASHTYPE_UNHANDLED_EXCEPTION, "SIGABRT" },
405 { SIGILL, QP_CRASHTYPE_OTHER, "SIGILL" },
406 { SIGSEGV, QP_CRASHTYPE_SEGMENTATION_FAULT, "SIGSEGV" },
407 { SIGFPE, QP_CRASHTYPE_OTHER, "SIGFPE" },
408 { SIGBUS, QP_CRASHTYPE_SEGMENTATION_FAULT, "SIGBUS" },
409 { SIGPIPE, QP_CRASHTYPE_OTHER, "SIGPIPE" }
412 #endif /* QP_USE_SIGNAL_HANDLER */
414 struct qpCrashHandler_s
416 qpCrashHandlerFunc crashHandlerFunc;
417 void* handlerUserPointer;
419 qpCrashInfo crashInfo;
422 #if defined(QP_USE_SIGNAL_HANDLER)
423 struct sigaction oldHandlers[DE_LENGTH_OF_ARRAY(s_signals)];
427 qpCrashHandler* g_crashHandler = DE_NULL;
429 static void assertFailureCallback (const char* expr, const char* file, int line)
432 qpCrashInfo_set(&g_crashHandler->crashInfo, QP_CRASHTYPE_ASSERT, expr, file, line);
434 /* Handle the crash. */
435 if (g_crashHandler->crashHandlerFunc != DE_NULL)
436 g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
439 #if defined(QP_USE_SIGNAL_HANDLER)
441 static const SignalInfo* getSignalInfo (int sigNum)
444 for (ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_signals); ndx++)
446 if (s_signals[ndx].signalNum == sigNum)
447 return &s_signals[ndx];
452 static void signalHandler (int sigNum)
454 const SignalInfo* info = getSignalInfo(sigNum);
455 qpCrashType type = info ? info->type : QP_CRASHTYPE_OTHER;
456 const char* name = info ? info->name : "Unknown signal";
458 qpCrashInfo_set(&g_crashHandler->crashInfo, type, name, DE_NULL, 0);
460 if (g_crashHandler->crashHandlerFunc != DE_NULL)
461 g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
464 #endif /* QP_USE_SIGNAL_HANDLER */
466 qpCrashHandler* qpCrashHandler_create (qpCrashHandlerFunc handlerFunc, void* userPointer)
468 /* Allocate & initialize. */
469 qpCrashHandler* handler = (qpCrashHandler*)deCalloc(sizeof(qpCrashHandler));
470 DBGPRINT(("qpCrashHandler::create()\n"));
474 DE_ASSERT(g_crashHandler == DE_NULL);
476 handler->crashHandlerFunc = handlerFunc ? handlerFunc : defaultCrashHandler;
477 handler->handlerUserPointer = userPointer;
479 qpCrashInfo_init(&handler->crashInfo);
481 g_crashHandler = handler;
483 /* DE_ASSERT callback. */
484 deSetAssertFailureCallback(assertFailureCallback);
486 #if defined(QP_USE_SIGNAL_HANDLER)
487 /* Register signal handlers. */
489 struct sigaction action;
492 sigemptyset(&action.sa_mask);
493 action.sa_handler = signalHandler;
496 for (sigNdx = 0; sigNdx < DE_LENGTH_OF_ARRAY(s_signals); sigNdx++)
497 sigaction(s_signals[sigNdx].signalNum, &action, &handler->oldHandlers[sigNdx]);
504 void qpCrashHandler_destroy (qpCrashHandler* handler)
506 DBGPRINT(("qpCrashHandler::destroy()\n"));
508 DE_ASSERT(g_crashHandler == handler);
510 deSetAssertFailureCallback(DE_NULL);
512 #if defined(QP_USE_SIGNAL_HANDLER)
513 /* Restore old handlers. */
516 for (sigNdx = 0; sigNdx < DE_LENGTH_OF_ARRAY(s_signals); sigNdx++)
517 sigaction(s_signals[sigNdx].signalNum, &handler->oldHandlers[sigNdx], DE_NULL);
521 g_crashHandler = DE_NULL;
526 #if (DE_PTR_SIZE == 8)
527 # define PTR_FMT "0x%016"
528 #elif (DE_PTR_SIZE == 4)
529 # define PTR_FMT "0x%08"
531 # error Unknwon DE_PTR_SIZE
534 void qpCrashHandler_writeCrashInfo (qpCrashHandler* crashHandler, qpWriteCrashInfoFunc writeInfo, void* userPtr)
536 qpCrashInfo_write(&crashHandler->crashInfo, writeInfo, userPtr);
538 #if (DE_OS == DE_OS_UNIX)
540 char tmpFileName[] = "backtrace-XXXXXX";
541 int tmpFile = mkstemp(tmpFileName);
545 writeInfoFormat(writeInfo, userPtr, "Failed to create tmpfile '%s' for the backtrace %s.", tmpFileName, strerror(errno));
554 /* Remove file from filesystem. */
557 symbolCount = backtrace(symbols, DE_LENGTH_OF_ARRAY(symbols));
558 backtrace_symbols_fd(symbols, symbolCount, tmpFile);
560 if (lseek(tmpFile, 0, SEEK_SET) < 0)
562 writeInfoFormat(writeInfo, userPtr, "Failed to seek to the beginning of the trace file %s.", strerror(errno));
567 for (symbolNdx = 0; symbolNdx < symbolCount; symbolNdx++)
569 char nameBuffer[256];
570 size_t symbolNameLength = 0;
574 const int ret = snprintf(nameBuffer, DE_LENGTH_OF_ARRAY(nameBuffer), PTR_FMT PRIXPTR " : ", (uintptr_t)symbols[symbolNdx]);
578 writeInfoFormat(writeInfo, userPtr, "Failed to print symbol pointer.");
579 symbolNameLength = 0;
581 else if (ret >= DE_LENGTH_OF_ARRAY(nameBuffer))
583 symbolNameLength = DE_LENGTH_OF_ARRAY(nameBuffer) - 1;
584 nameBuffer[DE_LENGTH_OF_ARRAY(nameBuffer) - 1] = '\0';
587 symbolNameLength = ret;
592 if (read(tmpFile, &c, 1) == 1)
596 /* Flush nameBuffer and move to next symbol. */
597 nameBuffer[symbolNameLength] = '\0';
598 writeInfo(userPtr, nameBuffer);
603 /* Add character to buffer if there is still space left. */
604 if (symbolNameLength+1 < DE_LENGTH_OF_ARRAY(nameBuffer))
606 nameBuffer[symbolNameLength] = c;
613 /* Flush nameBuffer. */
614 nameBuffer[symbolNameLength] = '\0';
615 writeInfo(userPtr, nameBuffer);
617 /* Temp file ended unexpectedly? */
618 writeInfoFormat(writeInfo, userPtr, "Unexpected EOF reading backtrace file '%s'", tmpFileName);