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