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. */ 42static 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. */ 55typedef 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 65typedef struct qpCrashInfo_s 66{ 67 qpCrashType type; 68 const char* message; 69 const char* file; 70 int line; 71} qpCrashInfo; 72 73static 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 81static 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 89static 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 115static void defaultWriteInfo (void* userPtr, const char* infoString) 116{ 117 DE_UNREF(userPtr); 118 qpPrintf("%s", infoString); 119} 120 121static 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 134struct 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 146qpCrashHandler* g_crashHandler = DE_NULL; 147 148static 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 224static 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 248qpCrashHandler* 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 290void 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 303enum 304{ 305 MAX_NAME_LENGTH = 64 306}; 307 308void 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 defined(QP_USE_SIGNAL_HANDLER) 377# include <signal.h> 378#endif 379 380#if defined(QP_USE_SIGNAL_HANDLER) 381 382typedef struct SignalInfo_s 383{ 384 int signalNum; 385 qpCrashType type; 386 const char* name; 387} SignalInfo; 388 389static const SignalInfo s_signals[] = 390{ 391 { SIGABRT, QP_CRASHTYPE_UNHANDLED_EXCEPTION, "SIGABRT" }, 392 { SIGILL, QP_CRASHTYPE_OTHER, "SIGILL" }, 393 { SIGSEGV, QP_CRASHTYPE_SEGMENTATION_FAULT, "SIGSEGV" }, 394 { SIGFPE, QP_CRASHTYPE_OTHER, "SIGFPE" }, 395 { SIGBUS, QP_CRASHTYPE_SEGMENTATION_FAULT, "SIGBUS" }, 396 { SIGPIPE, QP_CRASHTYPE_OTHER, "SIGPIPE" } 397}; 398 399#endif /* QP_USE_SIGNAL_HANDLER */ 400 401struct qpCrashHandler_s 402{ 403 qpCrashHandlerFunc crashHandlerFunc; 404 void* handlerUserPointer; 405 406 qpCrashInfo crashInfo; 407 int crashSignal; 408 409#if defined(QP_USE_SIGNAL_HANDLER) 410 struct sigaction oldHandlers[DE_LENGTH_OF_ARRAY(s_signals)]; 411#endif 412}; 413 414qpCrashHandler* g_crashHandler = DE_NULL; 415 416static void assertFailureCallback (const char* expr, const char* file, int line) 417{ 418 /* Store info. */ 419 qpCrashInfo_set(&g_crashHandler->crashInfo, QP_CRASHTYPE_ASSERT, expr, file, line); 420 421 /* Handle the crash. */ 422 if (g_crashHandler->crashHandlerFunc != DE_NULL) 423 g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer); 424} 425 426#if defined(QP_USE_SIGNAL_HANDLER) 427 428static const SignalInfo* getSignalInfo (int sigNum) 429{ 430 int ndx; 431 for (ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_signals); ndx++) 432 { 433 if (s_signals[ndx].signalNum == sigNum) 434 return &s_signals[ndx]; 435 } 436 return DE_NULL; 437} 438 439static void signalHandler (int sigNum) 440{ 441 const SignalInfo* info = getSignalInfo(sigNum); 442 qpCrashType type = info ? info->type : QP_CRASHTYPE_OTHER; 443 const char* name = info ? info->name : "Unknown signal"; 444 445 qpCrashInfo_set(&g_crashHandler->crashInfo, type, name, DE_NULL, 0); 446 447 if (g_crashHandler->crashHandlerFunc != DE_NULL) 448 g_crashHandler->crashHandlerFunc(g_crashHandler, g_crashHandler->handlerUserPointer); 449} 450 451#endif /* QP_USE_SIGNAL_HANDLER */ 452 453qpCrashHandler* qpCrashHandler_create (qpCrashHandlerFunc handlerFunc, void* userPointer) 454{ 455 /* Allocate & initialize. */ 456 qpCrashHandler* handler = (qpCrashHandler*)deCalloc(sizeof(qpCrashHandler)); 457 DBGPRINT(("qpCrashHandler::create()\n")); 458 if (!handler) 459 return handler; 460 461 DE_ASSERT(g_crashHandler == DE_NULL); 462 463 handler->crashHandlerFunc = handlerFunc ? handlerFunc : defaultCrashHandler; 464 handler->handlerUserPointer = userPointer; 465 466 qpCrashInfo_init(&handler->crashInfo); 467 468 g_crashHandler = handler; 469 470 /* DE_ASSERT callback. */ 471 deSetAssertFailureCallback(assertFailureCallback); 472 473#if defined(QP_USE_SIGNAL_HANDLER) 474 /* Register signal handlers. */ 475 { 476 struct sigaction action; 477 int sigNdx; 478 479 sigemptyset(&action.sa_mask); 480 action.sa_handler = signalHandler; 481 action.sa_flags = 0; 482 483 for (sigNdx = 0; sigNdx < DE_LENGTH_OF_ARRAY(s_signals); sigNdx++) 484 sigaction(s_signals[sigNdx].signalNum, &action, &handler->oldHandlers[sigNdx]); 485 } 486#endif 487 488 return handler; 489} 490 491void qpCrashHandler_destroy (qpCrashHandler* handler) 492{ 493 DBGPRINT(("qpCrashHandler::destroy()\n")); 494 495 DE_ASSERT(g_crashHandler == handler); 496 497 deSetAssertFailureCallback(DE_NULL); 498 499#if defined(QP_USE_SIGNAL_HANDLER) 500 /* Restore old handlers. */ 501 { 502 int sigNdx; 503 for (sigNdx = 0; sigNdx < DE_LENGTH_OF_ARRAY(s_signals); sigNdx++) 504 sigaction(s_signals[sigNdx].signalNum, &handler->oldHandlers[sigNdx], DE_NULL); 505 } 506#endif 507 508 g_crashHandler = DE_NULL; 509 510 deFree(handler); 511} 512 513void qpCrashHandler_writeCrashInfo (qpCrashHandler* crashHandler, qpWriteCrashInfoFunc writeInfo, void* userPtr) 514{ 515 qpCrashInfo_write(&crashHandler->crashInfo, writeInfo, userPtr); 516} 517 518#endif /* generic */ 519