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 #include <stdlib.h>
35
36 #if (DE_OS == DE_OS_UNIX)
37 #include <unistd.h>
38 #include <errno.h>
39 #include <inttypes.h>
40 #if defined(__GLIBC__) || defined(__FreeBSD__)
41 #include <execinfo.h>
42 #endif
43 #endif
44
45 #if 0
46 #define DBGPRINT(X) qpPrintf X
47 #else
48 #define DBGPRINT(X)
49 #endif
50
51 /* Crash info write helper. */
writeInfoFormat(qpWriteCrashInfoFunc writeFunc,void * userPtr,const char * format,...)52 static void writeInfoFormat(qpWriteCrashInfoFunc writeFunc, void *userPtr, const char *format, ...)
53 {
54 char buf[256];
55 va_list ap;
56
57 va_start(ap, format);
58 vsnprintf(buf, sizeof(buf), format, ap);
59 va_end(ap);
60
61 writeFunc(userPtr, buf);
62 }
63
64 /* Shared crash info. */
65 typedef enum qpCrashType_e
66 {
67 QP_CRASHTYPE_SEGMENTATION_FAULT = 0,
68 QP_CRASHTYPE_ASSERT,
69 QP_CRASHTYPE_UNHANDLED_EXCEPTION,
70 QP_CRASHTYPE_OTHER,
71
72 QP_CRASHTYPE_LAST
73 } qpCrashType;
74
75 typedef struct qpCrashInfo_s
76 {
77 qpCrashType type;
78 const char *message;
79 const char *file;
80 int line;
81 } qpCrashInfo;
82
qpCrashInfo_init(qpCrashInfo * info)83 static void qpCrashInfo_init(qpCrashInfo *info)
84 {
85 info->type = QP_CRASHTYPE_LAST;
86 info->message = DE_NULL;
87 info->file = DE_NULL;
88 info->line = 0;
89 }
90
qpCrashInfo_set(qpCrashInfo * info,qpCrashType type,const char * message,const char * file,int line)91 static void qpCrashInfo_set(qpCrashInfo *info, qpCrashType type, const char *message, const char *file, int line)
92 {
93 info->type = type;
94 info->message = message;
95 info->file = file;
96 info->line = line;
97 }
98
qpCrashInfo_write(qpCrashInfo * info,qpWriteCrashInfoFunc writeInfo,void * userPtr)99 static void qpCrashInfo_write(qpCrashInfo *info, qpWriteCrashInfoFunc writeInfo, void *userPtr)
100 {
101 switch (info->type)
102 {
103 case QP_CRASHTYPE_SEGMENTATION_FAULT:
104 writeInfoFormat(writeInfo, userPtr, "Segmentation fault: '%s'\n", info->message);
105 break;
106
107 case QP_CRASHTYPE_UNHANDLED_EXCEPTION:
108 writeInfoFormat(writeInfo, userPtr, "Unhandled exception: '%s'\n", info->message);
109 break;
110
111 case QP_CRASHTYPE_ASSERT:
112 writeInfoFormat(writeInfo, userPtr, "Assertion '%s' failed at %s:%d\n", info->message, info->file, info->line);
113 break;
114
115 case QP_CRASHTYPE_OTHER:
116 default:
117 writeInfoFormat(writeInfo, userPtr, "Crash: '%s'\n", info->message);
118 break;
119 }
120 }
121
defaultWriteInfo(void * userPtr,const char * infoString)122 static void defaultWriteInfo(void *userPtr, const char *infoString)
123 {
124 DE_UNREF(userPtr);
125 qpPrintf("%s", infoString);
126 }
127
defaultCrashHandler(qpCrashHandler * crashHandler,void * userPtr)128 static void defaultCrashHandler(qpCrashHandler *crashHandler, void *userPtr)
129 {
130 DE_UNREF(userPtr);
131 qpCrashHandler_writeCrashInfo(crashHandler, defaultWriteInfo, DE_NULL);
132 qpDief("Test process crashed");
133 }
134
135 #if (DE_OS == DE_OS_WIN32) && (DE_COMPILER == DE_COMPILER_MSC)
136
137 #define WIN32_LEAN_AND_MEAN
138 #include <windows.h>
139
140 /* DbgHelp.h generates C4091 */
141 #pragma warning(push)
142 #pragma warning(disable : 4091)
143 #include <DbgHelp.h>
144 #pragma warning(pop)
145
146 struct qpCrashHandler_s
147 {
148 qpCrashHandlerFunc crashHandlerFunc;
149 void *handlerUserPointer;
150
151 deMutex crashHandlerLock;
152 qpCrashInfo crashInfo;
153 uintptr_t crashAddress;
154
155 LPTOP_LEVEL_EXCEPTION_FILTER oldExceptionFilter;
156 };
157
158 qpCrashHandler *g_crashHandler = DE_NULL;
159
unhandledExceptionFilter(struct _EXCEPTION_POINTERS * info)160 static LONG WINAPI unhandledExceptionFilter(struct _EXCEPTION_POINTERS *info)
161 {
162 qpCrashType crashType = QP_CRASHTYPE_LAST;
163 const char *reason = DE_NULL;
164
165 /* Skip breakpoints. */
166 if (info->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
167 {
168 DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): breakpoint\n"));
169 return EXCEPTION_CONTINUE_SEARCH;
170 }
171
172 /* If no handler present (how could that be?), don't handle. */
173 if (g_crashHandler == DE_NULL)
174 {
175 DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): no crash handler registered\n"));
176 return EXCEPTION_CONTINUE_SEARCH;
177 }
178
179 /* If we have a debugger, let it handle the exception. */
180 if (IsDebuggerPresent())
181 {
182 DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): debugger present\n"));
183 return EXCEPTION_CONTINUE_SEARCH;
184 }
185
186 /* Acquire crash handler lock. Otherwise we might get strange behavior when multiple threads enter crash handler simultaneously. */
187 deMutex_lock(g_crashHandler->crashHandlerLock);
188
189 /* Map crash type. */
190 switch (info->ExceptionRecord->ExceptionCode)
191 {
192 case EXCEPTION_ACCESS_VIOLATION:
193 crashType = QP_CRASHTYPE_SEGMENTATION_FAULT;
194 reason = "Access violation";
195 break;
196
197 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
198 crashType = QP_CRASHTYPE_SEGMENTATION_FAULT;
199 reason = "Array bounds exceeded";
200 break;
201
202 case EXCEPTION_ILLEGAL_INSTRUCTION:
203 crashType = QP_CRASHTYPE_OTHER;
204 reason = "Illegal instruction";
205 break;
206
207 case EXCEPTION_STACK_OVERFLOW:
208 crashType = QP_CRASHTYPE_OTHER;
209 reason = "Stack overflow";
210 break;
211
212 default:
213 /* \todo [pyry] Others. */
214 crashType = QP_CRASHTYPE_OTHER;
215 reason = "";
216 break;
217 }
218
219 /* Store reason. */
220 qpCrashInfo_set(&g_crashHandler->crashInfo, crashType, reason, __FILE__, __LINE__);
221
222 /* Store win32-specific crash info. */
223 g_crashHandler->crashAddress = (uintptr_t)info->ExceptionRecord->ExceptionAddress;
224
225 /* Handle the crash. */
226 DBGPRINT(("qpCrashHandler::unhandledExceptionFilter(): handled quietly\n"));
227 if (g_crashHandler->crashHandlerFunc != DE_NULL)
228 g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
229
230 /* Release lock. */
231 deMutex_unlock(g_crashHandler->crashHandlerLock);
232
233 return EXCEPTION_EXECUTE_HANDLER;
234 }
235
assertFailureCallback(const char * expr,const char * file,int line)236 static void assertFailureCallback(const char *expr, const char *file, int line)
237 {
238 /* Don't execute crash handler function if debugger is present. */
239 if (IsDebuggerPresent())
240 {
241 DBGPRINT(("qpCrashHandler::assertFailureCallback(): debugger present\n"));
242 return;
243 }
244
245 /* Acquire crash handler lock. */
246 deMutex_lock(g_crashHandler->crashHandlerLock);
247
248 /* Store info. */
249 qpCrashInfo_set(&g_crashHandler->crashInfo, QP_CRASHTYPE_ASSERT, expr, file, line);
250 g_crashHandler->crashAddress = 0;
251
252 /* Handle the crash. */
253 if (g_crashHandler->crashHandlerFunc != DE_NULL)
254 g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
255
256 /* Release lock. */
257 deMutex_unlock(g_crashHandler->crashHandlerLock);
258 }
259
qpCrashHandler_create(qpCrashHandlerFunc handlerFunc,void * userPointer)260 qpCrashHandler *qpCrashHandler_create(qpCrashHandlerFunc handlerFunc, void *userPointer)
261 {
262 /* Allocate & initialize. */
263 qpCrashHandler *handler = (qpCrashHandler *)deCalloc(sizeof(qpCrashHandler));
264 DBGPRINT(("qpCrashHandler::create() -- Win32\n"));
265 if (!handler)
266 return handler;
267
268 DE_ASSERT(g_crashHandler == DE_NULL);
269
270 handler->crashHandlerFunc = handlerFunc ? handlerFunc : defaultCrashHandler;
271 handler->handlerUserPointer = userPointer;
272
273 /* Create lock for crash handler. \note Has to be recursive or otherwise crash in assert failure causes deadlock. */
274 {
275 deMutexAttributes attr;
276 attr.flags = DE_MUTEX_RECURSIVE;
277 handler->crashHandlerLock = deMutex_create(&attr);
278
279 if (!handler->crashHandlerLock)
280 {
281 deFree(handler);
282 return DE_NULL;
283 }
284 }
285
286 qpCrashInfo_init(&handler->crashInfo);
287 handler->crashAddress = 0;
288
289 /* Unhandled exception filter. */
290 handler->oldExceptionFilter = SetUnhandledExceptionFilter(unhandledExceptionFilter);
291
292 /* Prevent nasty error dialog. */
293 SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
294
295 /* DE_ASSERT callback. */
296 deSetAssertFailureCallback(assertFailureCallback);
297
298 g_crashHandler = handler;
299 return handler;
300 }
301
qpCrashHandler_destroy(qpCrashHandler * handler)302 void qpCrashHandler_destroy(qpCrashHandler *handler)
303 {
304 DBGPRINT(("qpCrashHandler::destroy()\n"));
305
306 DE_ASSERT(g_crashHandler == handler);
307
308 deSetAssertFailureCallback(DE_NULL);
309 SetUnhandledExceptionFilter(handler->oldExceptionFilter);
310
311 g_crashHandler = DE_NULL;
312 deFree(handler);
313 }
314
315 enum
316 {
317 MAX_NAME_LENGTH = 64
318 };
319
qpCrashHandler_writeCrashInfo(qpCrashHandler * handler,qpWriteCrashInfoFunc writeInfo,void * userPtr)320 void qpCrashHandler_writeCrashInfo(qpCrashHandler *handler, qpWriteCrashInfoFunc writeInfo, void *userPtr)
321 {
322 void *addresses[32];
323 HANDLE process;
324
325 /* Symbol info struct. */
326 uint8_t symInfoStorage[sizeof(SYMBOL_INFO) + MAX_NAME_LENGTH];
327 SYMBOL_INFO *symInfo = (SYMBOL_INFO *)symInfoStorage;
328
329 DE_STATIC_ASSERT(sizeof(TCHAR) == sizeof(uint8_t));
330
331 /* Write basic info. */
332 qpCrashInfo_write(&handler->crashInfo, writeInfo, userPtr);
333
334 /* Acquire process handle and initialize symbols. */
335 process = GetCurrentProcess();
336
337 /* Write backtrace. */
338 if (SymInitialize(process, NULL, TRUE))
339 {
340 int batchStart = 0;
341 int globalFrameNdx = 0;
342
343 /* Initialize symInfo. */
344 deMemset(symInfo, 0, sizeof(symInfoStorage));
345 symInfo->SizeOfStruct = sizeof(SYMBOL_INFO);
346 symInfo->MaxNameLen = MAX_NAME_LENGTH;
347
348 /* Print address and symbol where crash happened. */
349 if (handler->crashAddress != 0)
350 {
351 BOOL symInfoOk = SymFromAddr(process, (DWORD64)handler->crashAddress, 0, symInfo);
352
353 writeInfoFormat(writeInfo, userPtr, " at %p %s%s\n", handler->crashAddress,
354 symInfoOk ? symInfo->Name : "(unknown)", symInfoOk ? "()" : "");
355 }
356
357 writeInfo(userPtr, "Backtrace:\n");
358
359 for (;;)
360 {
361 int curFrame;
362 int numInBatch;
363
364 /* Get one batch. */
365 numInBatch = CaptureStackBackTrace(batchStart, DE_LENGTH_OF_ARRAY(addresses), addresses, NULL);
366
367 for (curFrame = 0; curFrame < numInBatch; curFrame++)
368 {
369 BOOL symInfoOk = SymFromAddr(process, (DWORD64)addresses[curFrame], 0, symInfo);
370
371 writeInfoFormat(writeInfo, userPtr, " %2d: %p %s%s\n", globalFrameNdx++, addresses[curFrame],
372 symInfoOk ? symInfo->Name : "(unknown)", symInfoOk ? "()" : "");
373 }
374
375 batchStart += numInBatch;
376
377 /* Check if we hit end of stack trace. */
378 if (numInBatch == 0 || numInBatch < DE_LENGTH_OF_ARRAY(addresses))
379 break;
380 }
381 }
382 }
383
384 #else /* posix / generic implementation */
385
386 #if defined(QP_USE_SIGNAL_HANDLER)
387 #include <signal.h>
388 #endif
389
390 #if defined(QP_USE_SIGNAL_HANDLER)
391
392 typedef struct SignalInfo_s
393 {
394 int signalNum;
395 qpCrashType type;
396 const char *name;
397 } SignalInfo;
398
399 static const SignalInfo s_signals[] = {
400 {SIGABRT, QP_CRASHTYPE_UNHANDLED_EXCEPTION, "SIGABRT"}, {SIGILL, QP_CRASHTYPE_OTHER, "SIGILL"},
401 {SIGSEGV, QP_CRASHTYPE_SEGMENTATION_FAULT, "SIGSEGV"}, {SIGFPE, QP_CRASHTYPE_OTHER, "SIGFPE"},
402 {SIGBUS, QP_CRASHTYPE_SEGMENTATION_FAULT, "SIGBUS"}, {SIGPIPE, QP_CRASHTYPE_OTHER, "SIGPIPE"}};
403
404 #endif /* QP_USE_SIGNAL_HANDLER */
405
406 struct qpCrashHandler_s
407 {
408 qpCrashHandlerFunc crashHandlerFunc;
409 void *handlerUserPointer;
410
411 qpCrashInfo crashInfo;
412 int crashSignal;
413
414 #if defined(QP_USE_SIGNAL_HANDLER)
415 struct sigaction oldHandlers[DE_LENGTH_OF_ARRAY(s_signals)];
416 #endif
417 };
418
419 qpCrashHandler *g_crashHandler = DE_NULL;
420
assertFailureCallback(const char * expr,const char * file,int line)421 static void assertFailureCallback(const char *expr, const char *file, int line)
422 {
423 /* Store info. */
424 qpCrashInfo_set(&g_crashHandler->crashInfo, QP_CRASHTYPE_ASSERT, expr, file, line);
425
426 /* Handle the crash. */
427 if (g_crashHandler->crashHandlerFunc != DE_NULL)
428 g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
429 }
430
431 #if defined(QP_USE_SIGNAL_HANDLER)
432
getSignalInfo(int sigNum)433 static const SignalInfo *getSignalInfo(int sigNum)
434 {
435 int ndx;
436 for (ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_signals); ndx++)
437 {
438 if (s_signals[ndx].signalNum == sigNum)
439 return &s_signals[ndx];
440 }
441 return DE_NULL;
442 }
443
signalHandler(int sigNum)444 static void signalHandler(int sigNum)
445 {
446 const SignalInfo *info = getSignalInfo(sigNum);
447 qpCrashType type = info ? info->type : QP_CRASHTYPE_OTHER;
448 const char *name = info ? info->name : "Unknown signal";
449
450 qpCrashInfo_set(&g_crashHandler->crashInfo, type, name, DE_NULL, 0);
451
452 if (g_crashHandler->crashHandlerFunc != DE_NULL)
453 g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer);
454 }
455
456 #endif /* QP_USE_SIGNAL_HANDLER */
457
qpCrashHandler_create(qpCrashHandlerFunc handlerFunc,void * userPointer)458 qpCrashHandler *qpCrashHandler_create(qpCrashHandlerFunc handlerFunc, void *userPointer)
459 {
460 /* Allocate & initialize. */
461 qpCrashHandler *handler = (qpCrashHandler *)deCalloc(sizeof(qpCrashHandler));
462 DBGPRINT(("qpCrashHandler::create()\n"));
463 if (!handler)
464 return handler;
465
466 DE_ASSERT(g_crashHandler == DE_NULL);
467
468 handler->crashHandlerFunc = handlerFunc ? handlerFunc : defaultCrashHandler;
469 handler->handlerUserPointer = userPointer;
470
471 qpCrashInfo_init(&handler->crashInfo);
472
473 g_crashHandler = handler;
474
475 /* DE_ASSERT callback. */
476 deSetAssertFailureCallback(assertFailureCallback);
477
478 #if defined(QP_USE_SIGNAL_HANDLER)
479 /* Register signal handlers. */
480 {
481 struct sigaction action;
482 int sigNdx;
483
484 sigemptyset(&action.sa_mask);
485 action.sa_handler = signalHandler;
486 action.sa_flags = 0;
487
488 for (sigNdx = 0; sigNdx < DE_LENGTH_OF_ARRAY(s_signals); sigNdx++)
489 sigaction(s_signals[sigNdx].signalNum, &action, &handler->oldHandlers[sigNdx]);
490 }
491 #endif
492
493 return handler;
494 }
495
qpCrashHandler_destroy(qpCrashHandler * handler)496 void qpCrashHandler_destroy(qpCrashHandler *handler)
497 {
498 DBGPRINT(("qpCrashHandler::destroy()\n"));
499
500 DE_ASSERT(g_crashHandler == handler);
501
502 deSetAssertFailureCallback(DE_NULL);
503
504 #if defined(QP_USE_SIGNAL_HANDLER)
505 /* Restore old handlers. */
506 {
507 int sigNdx;
508 for (sigNdx = 0; sigNdx < DE_LENGTH_OF_ARRAY(s_signals); sigNdx++)
509 sigaction(s_signals[sigNdx].signalNum, &handler->oldHandlers[sigNdx], DE_NULL);
510 }
511 #endif
512
513 g_crashHandler = DE_NULL;
514
515 deFree(handler);
516 }
517
518 #if (DE_PTR_SIZE == 8)
519 #define PTR_FMT "0x%016"
520 #elif (DE_PTR_SIZE == 4)
521 #define PTR_FMT "0x%08"
522 #else
523 #error Unknwon DE_PTR_SIZE
524 #endif
525
qpCrashHandler_writeCrashInfo(qpCrashHandler * crashHandler,qpWriteCrashInfoFunc writeInfo,void * userPtr)526 void qpCrashHandler_writeCrashInfo(qpCrashHandler *crashHandler, qpWriteCrashInfoFunc writeInfo, void *userPtr)
527 {
528 qpCrashInfo_write(&crashHandler->crashInfo, writeInfo, userPtr);
529
530 #if (DE_OS == DE_OS_UNIX && (defined(__GLIBC__) || defined(__FreeBSD__)))
531 {
532 char tmpFileName[] = "backtrace-XXXXXX";
533 int tmpFile = mkstemp(tmpFileName);
534
535 if (tmpFile == -1)
536 {
537 writeInfoFormat(writeInfo, userPtr, "Failed to create tmpfile '%s' for the backtrace %s.", tmpFileName,
538 strerror(errno));
539 return;
540 }
541 else
542 {
543 void *symbols[32];
544 int symbolCount;
545 int symbolNdx;
546
547 /* Remove file from filesystem. */
548 remove(tmpFileName);
549
550 symbolCount = backtrace(symbols, DE_LENGTH_OF_ARRAY(symbols));
551 backtrace_symbols_fd(symbols, symbolCount, tmpFile);
552
553 if (lseek(tmpFile, 0, SEEK_SET) < 0)
554 {
555 writeInfoFormat(writeInfo, userPtr, "Failed to seek to the beginning of the trace file %s.",
556 strerror(errno));
557 close(tmpFile);
558 return;
559 }
560
561 for (symbolNdx = 0; symbolNdx < symbolCount; symbolNdx++)
562 {
563 char nameBuffer[256];
564 size_t symbolNameLength = 0;
565 char c;
566
567 {
568 const int ret = snprintf(nameBuffer, DE_LENGTH_OF_ARRAY(nameBuffer), PTR_FMT PRIXPTR " : ",
569 (uintptr_t)symbols[symbolNdx]);
570
571 if (ret < 0)
572 {
573 writeInfoFormat(writeInfo, userPtr, "Failed to print symbol pointer.");
574 symbolNameLength = 0;
575 }
576 else if (ret >= DE_LENGTH_OF_ARRAY(nameBuffer))
577 {
578 symbolNameLength = DE_LENGTH_OF_ARRAY(nameBuffer) - 1;
579 nameBuffer[DE_LENGTH_OF_ARRAY(nameBuffer) - 1] = '\0';
580 }
581 else
582 symbolNameLength = ret;
583 }
584
585 for (;;)
586 {
587 if (read(tmpFile, &c, 1) == 1)
588 {
589 if (c == '\n')
590 {
591 /* Flush nameBuffer and move to next symbol. */
592 nameBuffer[symbolNameLength] = '\0';
593 writeInfo(userPtr, nameBuffer);
594 break;
595 }
596 else
597 {
598 /* Add character to buffer if there is still space left. */
599 if (symbolNameLength + 1 < DE_LENGTH_OF_ARRAY(nameBuffer))
600 {
601 nameBuffer[symbolNameLength] = c;
602 symbolNameLength++;
603 }
604 }
605 }
606 else
607 {
608 /* Flush nameBuffer. */
609 nameBuffer[symbolNameLength] = '\0';
610 writeInfo(userPtr, nameBuffer);
611
612 /* Temp file ended unexpectedly? */
613 writeInfoFormat(writeInfo, userPtr, "Unexpected EOF reading backtrace file '%s'", tmpFileName);
614 close(tmpFile);
615 tmpFile = -1;
616
617 break;
618 }
619 }
620
621 if (tmpFile == -1)
622 break;
623 }
624
625 if (tmpFile != -1)
626 close(tmpFile);
627 }
628 }
629 #endif
630 }
631
632 #endif /* generic */
633