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