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 */ 638