am 881b21d9: (-s ours) am 3bca299a: Remove infeasible derivative.fwidth cases am...
[platform/upstream/VK-GL-CTS.git] / framework / qphelper / qpCrashHandler.c
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. */
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
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
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
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
123 static void defaultWriteInfo (void* userPtr, const char* infoString)
124 {
125         DE_UNREF(userPtr);
126         qpPrintf("%s", infoString);
127 }
128
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
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
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
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
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
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
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
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
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
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
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
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 */