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