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
142 struct qpCrashHandler_s
144 qpCrashHandlerFunc crashHandlerFunc;
145 void* handlerUserPointer;
147 deMutex crashHandlerLock;
148 qpCrashInfo crashInfo;
149 deUintptr crashAddress;
151 LPTOP_LEVEL_EXCEPTION_FILTER oldExceptionFilter;
154 qpCrashHandler* g_crashHandler = DE_NULL;
156 static LONG WINAPI unhandledExceptionFilter (struct _EXCEPTION_POINTERS* info)
158 qpCrashType crashType = QP_CRASHTYPE_LAST;
159 const char* reason = DE_NULL;
161 /* Skip breakpoints. */
162 if (info->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
164 DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): breakpoint\n"));
165 return EXCEPTION_CONTINUE_SEARCH;
168 /* If no handler present (how could that be?), don't handle. */
169 if (g_crashHandler == DE_NULL)
171 DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): no crash handler registered\n"));
172 return EXCEPTION_CONTINUE_SEARCH;
175 /* If we have a debugger, let it handle the exception. */
176 if (IsDebuggerPresent())
178 DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): debugger present\n"));
179 return EXCEPTION_CONTINUE_SEARCH;
182 /* Acquire crash handler lock. Otherwise we might get strange behavior when multiple threads enter crash handler simultaneously. */
183 deMutex_lock(g_crashHandler->crashHandlerLock);
185 /* Map crash type. */
186 switch (info->ExceptionRecord->ExceptionCode)
188 case EXCEPTION_ACCESS_VIOLATION:
189 crashType = QP_CRASHTYPE_SEGMENTATION_FAULT;
190 reason = "Access violation";
193 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
194 crashType = QP_CRASHTYPE_SEGMENTATION_FAULT;
195 reason = "Array bounds exceeded";
198 case EXCEPTION_ILLEGAL_INSTRUCTION:
199 crashType = QP_CRASHTYPE_OTHER;
200 reason = "Illegal instruction";
203 case EXCEPTION_STACK_OVERFLOW:
204 crashType = QP_CRASHTYPE_OTHER;
205 reason = "Stack overflow";
209 /* \todo [pyry] Others. */
210 crashType = QP_CRASHTYPE_OTHER;
216 qpCrashInfo_set(&g_crashHandler->crashInfo, crashType, reason, __FILE__, __LINE__);
218 /* Store win32-specific crash info. */
219 g_crashHandler->crashAddress = (deUintptr)info->ExceptionRecord->ExceptionAddress;
221 /* Handle the crash. */
222 DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): handled quietly\n"));
223 if (g_crashHandler->crashHandlerFunc != DE_NULL)
224 g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
227 deMutex_unlock(g_crashHandler->crashHandlerLock);
229 return EXCEPTION_EXECUTE_HANDLER;
232 static void assertFailureCallback (const char* expr, const char* file, int line)
234 /* Don't execute crash handler function if debugger is present. */
235 if (IsDebuggerPresent())
237 DBGPRINT(("qpCrashHandler::assertFailureCallback(): debugger present\n"));
241 /* Acquire crash handler lock. */
242 deMutex_lock(g_crashHandler->crashHandlerLock);
245 qpCrashInfo_set(&g_crashHandler->crashInfo, QP_CRASHTYPE_ASSERT, expr, file, line);
246 g_crashHandler->crashAddress = 0;
248 /* Handle the crash. */
249 if (g_crashHandler->crashHandlerFunc != DE_NULL)
250 g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
253 deMutex_unlock(g_crashHandler->crashHandlerLock);
256 qpCrashHandler* qpCrashHandler_create (qpCrashHandlerFunc handlerFunc, void* userPointer)
258 /* Allocate & initialize. */
259 qpCrashHandler* handler = (qpCrashHandler*)deCalloc(sizeof(qpCrashHandler));
260 DBGPRINT(("qpCrashHandler::create() -- Win32\n"));
264 DE_ASSERT(g_crashHandler == DE_NULL);
266 handler->crashHandlerFunc = handlerFunc ? handlerFunc : defaultCrashHandler;
267 handler->handlerUserPointer = userPointer;
269 /* Create lock for crash handler. \note Has to be recursive or otherwise crash in assert failure causes deadlock. */
271 deMutexAttributes attr;
272 attr.flags = DE_MUTEX_RECURSIVE;
273 handler->crashHandlerLock = deMutex_create(&attr);
275 if (!handler->crashHandlerLock)
282 qpCrashInfo_init(&handler->crashInfo);
283 handler->crashAddress = 0;
285 /* Unhandled exception filter. */
286 handler->oldExceptionFilter = SetUnhandledExceptionFilter(unhandledExceptionFilter);
288 /* Prevent nasty error dialog. */
289 SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOGPFAULTERRORBOX);
291 /* DE_ASSERT callback. */
292 deSetAssertFailureCallback(assertFailureCallback);
294 g_crashHandler = handler;
298 void qpCrashHandler_destroy (qpCrashHandler* handler)
300 DBGPRINT(("qpCrashHandler::destroy()\n"));
302 DE_ASSERT(g_crashHandler == handler);
304 deSetAssertFailureCallback(DE_NULL);
305 SetUnhandledExceptionFilter(handler->oldExceptionFilter);
307 g_crashHandler = DE_NULL;
316 void qpCrashHandler_writeCrashInfo (qpCrashHandler* handler, qpWriteCrashInfoFunc writeInfo, void* userPtr)
321 /* Symbol info struct. */
322 deUint8 symInfoStorage[sizeof(SYMBOL_INFO)+MAX_NAME_LENGTH];
323 SYMBOL_INFO* symInfo = (SYMBOL_INFO*)symInfoStorage;
325 DE_STATIC_ASSERT(sizeof(TCHAR) == sizeof(deUint8));
327 /* Write basic info. */
328 qpCrashInfo_write(&handler->crashInfo, writeInfo, userPtr);
330 /* Acquire process handle and initialize symbols. */
331 process = GetCurrentProcess();
333 /* Write backtrace. */
334 if (SymInitialize(process, NULL, TRUE))
337 int globalFrameNdx = 0;
339 /* Initialize symInfo. */
340 deMemset(symInfo, 0, sizeof(symInfoStorage));
341 symInfo->SizeOfStruct = sizeof(SYMBOL_INFO);
342 symInfo->MaxNameLen = MAX_NAME_LENGTH;
344 /* Print address and symbol where crash happened. */
345 if (handler->crashAddress != 0)
347 BOOL symInfoOk = SymFromAddr(process, (DWORD64)handler->crashAddress, 0, symInfo);
349 writeInfoFormat(writeInfo, userPtr, " at %p %s%s\n", handler->crashAddress,
350 symInfoOk ? symInfo->Name : "(unknown)",
351 symInfoOk ? "()" : "");
354 writeInfo(userPtr, "Backtrace:\n");
362 numInBatch = CaptureStackBackTrace(batchStart, DE_LENGTH_OF_ARRAY(addresses), addresses, NULL);
364 for (curFrame = 0; curFrame < numInBatch; curFrame++)
366 BOOL symInfoOk = SymFromAddr(process, (DWORD64)addresses[curFrame], 0, symInfo);
368 writeInfoFormat(writeInfo, userPtr, " %2d: %p %s%s\n", globalFrameNdx++, addresses[curFrame],
369 symInfoOk ? symInfo->Name : "(unknown)",
370 symInfoOk ? "()" : "");
373 batchStart += numInBatch;
375 /* Check if we hit end of stack trace. */
376 if (numInBatch == 0 || numInBatch < DE_LENGTH_OF_ARRAY(addresses))
382 #else /* posix / generic implementation */
384 #if defined(QP_USE_SIGNAL_HANDLER)
388 #if defined(QP_USE_SIGNAL_HANDLER)
390 typedef struct SignalInfo_s
397 static const SignalInfo s_signals[] =
399 { SIGABRT, QP_CRASHTYPE_UNHANDLED_EXCEPTION, "SIGABRT" },
400 { SIGILL, QP_CRASHTYPE_OTHER, "SIGILL" },
401 { SIGSEGV, QP_CRASHTYPE_SEGMENTATION_FAULT, "SIGSEGV" },
402 { SIGFPE, QP_CRASHTYPE_OTHER, "SIGFPE" },
403 { SIGBUS, QP_CRASHTYPE_SEGMENTATION_FAULT, "SIGBUS" },
404 { SIGPIPE, QP_CRASHTYPE_OTHER, "SIGPIPE" }
407 #endif /* QP_USE_SIGNAL_HANDLER */
409 struct qpCrashHandler_s
411 qpCrashHandlerFunc crashHandlerFunc;
412 void* handlerUserPointer;
414 qpCrashInfo crashInfo;
417 #if defined(QP_USE_SIGNAL_HANDLER)
418 struct sigaction oldHandlers[DE_LENGTH_OF_ARRAY(s_signals)];
422 qpCrashHandler* g_crashHandler = DE_NULL;
424 static void assertFailureCallback (const char* expr, const char* file, int line)
427 qpCrashInfo_set(&g_crashHandler->crashInfo, QP_CRASHTYPE_ASSERT, expr, file, line);
429 /* Handle the crash. */
430 if (g_crashHandler->crashHandlerFunc != DE_NULL)
431 g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
434 #if defined(QP_USE_SIGNAL_HANDLER)
436 static const SignalInfo* getSignalInfo (int sigNum)
439 for (ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_signals); ndx++)
441 if (s_signals[ndx].signalNum == sigNum)
442 return &s_signals[ndx];
447 static void signalHandler (int sigNum)
449 const SignalInfo* info = getSignalInfo(sigNum);
450 qpCrashType type = info ? info->type : QP_CRASHTYPE_OTHER;
451 const char* name = info ? info->name : "Unknown signal";
453 qpCrashInfo_set(&g_crashHandler->crashInfo, type, name, DE_NULL, 0);
455 if (g_crashHandler->crashHandlerFunc != DE_NULL)
456 g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
459 #endif /* QP_USE_SIGNAL_HANDLER */
461 qpCrashHandler* qpCrashHandler_create (qpCrashHandlerFunc handlerFunc, void* userPointer)
463 /* Allocate & initialize. */
464 qpCrashHandler* handler = (qpCrashHandler*)deCalloc(sizeof(qpCrashHandler));
465 DBGPRINT(("qpCrashHandler::create()\n"));
469 DE_ASSERT(g_crashHandler == DE_NULL);
471 handler->crashHandlerFunc = handlerFunc ? handlerFunc : defaultCrashHandler;
472 handler->handlerUserPointer = userPointer;
474 qpCrashInfo_init(&handler->crashInfo);
476 g_crashHandler = handler;
478 /* DE_ASSERT callback. */
479 deSetAssertFailureCallback(assertFailureCallback);
481 #if defined(QP_USE_SIGNAL_HANDLER)
482 /* Register signal handlers. */
484 struct sigaction action;
487 sigemptyset(&action.sa_mask);
488 action.sa_handler = signalHandler;
491 for (sigNdx = 0; sigNdx < DE_LENGTH_OF_ARRAY(s_signals); sigNdx++)
492 sigaction(s_signals[sigNdx].signalNum, &action, &handler->oldHandlers[sigNdx]);
499 void qpCrashHandler_destroy (qpCrashHandler* handler)
501 DBGPRINT(("qpCrashHandler::destroy()\n"));
503 DE_ASSERT(g_crashHandler == handler);
505 deSetAssertFailureCallback(DE_NULL);
507 #if defined(QP_USE_SIGNAL_HANDLER)
508 /* Restore old handlers. */
511 for (sigNdx = 0; sigNdx < DE_LENGTH_OF_ARRAY(s_signals); sigNdx++)
512 sigaction(s_signals[sigNdx].signalNum, &handler->oldHandlers[sigNdx], DE_NULL);
516 g_crashHandler = DE_NULL;
521 #if (DE_PTR_SIZE == 8)
522 # define PTR_FMT "0x%016"
523 #elif (DE_PTR_SIZE == 4)
524 # define PTR_FMT "0x%08"
526 # error Unknwon DE_PTR_SIZE
529 void qpCrashHandler_writeCrashInfo (qpCrashHandler* crashHandler, qpWriteCrashInfoFunc writeInfo, void* userPtr)
531 qpCrashInfo_write(&crashHandler->crashInfo, writeInfo, userPtr);
533 #if (DE_OS == DE_OS_UNIX)
535 char tmpFileName[] = "backtrace-XXXXXX";
536 int tmpFile = mkstemp(tmpFileName);
540 writeInfoFormat(writeInfo, userPtr, "Failed to create tmpfile '%s' for the backtrace %s.", tmpFileName, strerror(errno));
549 /* Remove file from filesystem. */
552 symbolCount = backtrace(symbols, DE_LENGTH_OF_ARRAY(symbols));
553 backtrace_symbols_fd(symbols, symbolCount, tmpFile);
555 if (lseek(tmpFile, 0, SEEK_SET) < 0)
557 writeInfoFormat(writeInfo, userPtr, "Failed to seek to the beginning of the trace file %s.", strerror(errno));
562 for (symbolNdx = 0; symbolNdx < symbolCount; symbolNdx++)
564 char nameBuffer[256];
565 size_t symbolNameLength = 0;
569 const int ret = snprintf(nameBuffer, DE_LENGTH_OF_ARRAY(nameBuffer), PTR_FMT PRIXPTR " : ", (uintptr_t)symbols[symbolNdx]);
573 writeInfoFormat(writeInfo, userPtr, "Failed to print symbol pointer.");
574 symbolNameLength = 0;
576 else if (ret >= DE_LENGTH_OF_ARRAY(nameBuffer))
578 symbolNameLength = DE_LENGTH_OF_ARRAY(nameBuffer) - 1;
579 nameBuffer[DE_LENGTH_OF_ARRAY(nameBuffer) - 1] = '\0';
582 symbolNameLength = ret;
587 if (read(tmpFile, &c, 1) == 1)
591 /* Flush nameBuffer and move to next symbol. */
592 nameBuffer[symbolNameLength] = '\0';
593 writeInfo(userPtr, nameBuffer);
598 /* Add character to buffer if there is still space left. */
599 if (symbolNameLength+1 < DE_LENGTH_OF_ARRAY(nameBuffer))
601 nameBuffer[symbolNameLength] = c;
608 /* Flush nameBuffer. */
609 nameBuffer[symbolNameLength] = '\0';
610 writeInfo(userPtr, nameBuffer);
612 /* Temp file ended unexpectedly? */
613 writeInfoFormat(writeInfo, userPtr, "Unexpected EOF reading backtrace file '%s'", tmpFileName);