CP: Non-compressed copy image target size (64,64,8 am: 7aff1a044f am: 8afffd6f14...
[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
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
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
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
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
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
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
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
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
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
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
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
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 */