#include "CrashHandler.h" #include "SkTypes.h" #include <stdlib.h> #if defined(SK_BUILD_FOR_MAC) // We only use local unwinding, so we can define this to select a faster implementation. #define UNW_LOCAL_ONLY #include <libunwind.h> #include <cxxabi.h> static void handler(int sig) { unw_context_t context; unw_getcontext(&context); unw_cursor_t cursor; unw_init_local(&cursor, &context); SkDebugf("\nSignal %d:\n", sig); while (unw_step(&cursor) > 0) { static const size_t kMax = 256; char mangled[kMax], demangled[kMax]; unw_word_t offset; unw_get_proc_name(&cursor, mangled, kMax, &offset); int ok; size_t len = kMax; abi::__cxa_demangle(mangled, demangled, &len, &ok); SkDebugf("%s (+0x%zx)\n", ok == 0 ? demangled : mangled, (size_t)offset); } SkDebugf("\n"); // Exit NOW. Don't notify other threads, don't call anything registered with atexit(). _Exit(sig); } #elif defined(SK_BUILD_FOR_UNIX) && !defined(SK_BUILD_FOR_NACL) // NACL doesn't have backtrace(). // We'd use libunwind here too, but it's a pain to get installed for both 32 and 64 bit on bots. // Doesn't matter much: catchsegv is best anyway. #include <execinfo.h> static void handler(int sig) { static const int kMax = 64; void* stack[kMax]; const int count = backtrace(stack, kMax); SkDebugf("\nSignal %d:\n", sig); backtrace_symbols_fd(stack, count, 2/*stderr*/); // Exit NOW. Don't notify other threads, don't call anything registered with atexit(). _Exit(sig); } #endif #if defined(SK_BUILD_FOR_MAC) || (defined(SK_BUILD_FOR_UNIX) && !defined(SK_BUILD_FOR_NACL)) #include <signal.h> void SetupCrashHandler() { static const int kSignals[] = { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, }; for (size_t i = 0; i < sizeof(kSignals) / sizeof(kSignals[0]); i++) { // Register our signal handler unless something's already done so (e.g. catchsegv). void (*prev)(int) = signal(kSignals[i], handler); if (prev != SIG_DFL) { signal(kSignals[i], prev); } } } #elif defined(SK_BUILD_FOR_WIN) #include <DbgHelp.h> static const struct { const char* name; int code; } kExceptions[] = { #define _(E) {#E, E} _(EXCEPTION_ACCESS_VIOLATION), _(EXCEPTION_BREAKPOINT), _(EXCEPTION_INT_DIVIDE_BY_ZERO), _(EXCEPTION_STACK_OVERFLOW), // TODO: more? #undef _ }; static LONG WINAPI handler(EXCEPTION_POINTERS* e) { const DWORD code = e->ExceptionRecord->ExceptionCode; SkDebugf("\nCaught exception %u", code); for (size_t i = 0; i < SK_ARRAY_COUNT(kExceptions); i++) { if (kExceptions[i].code == code) { SkDebugf(" %s", kExceptions[i].name); } } SkDebugf("\n"); // We need to run SymInitialize before doing any of the stack walking below. HANDLE hProcess = GetCurrentProcess(); SymInitialize(hProcess, 0, true); STACKFRAME64 frame; sk_bzero(&frame, sizeof(frame)); // Start frame off from the frame that triggered the exception. CONTEXT* c = e->ContextRecord; frame.AddrPC.Mode = AddrModeFlat; frame.AddrStack.Mode = AddrModeFlat; frame.AddrFrame.Mode = AddrModeFlat; #if defined(_X86_) frame.AddrPC.Offset = c->Eip; frame.AddrStack.Offset = c->Esp; frame.AddrFrame.Offset = c->Ebp; const DWORD machineType = IMAGE_FILE_MACHINE_I386; #elif defined(_AMD64_) frame.AddrPC.Offset = c->Rip; frame.AddrStack.Offset = c->Rsp; frame.AddrFrame.Offset = c->Rbp; const DWORD machineType = IMAGE_FILE_MACHINE_AMD64; #endif while (StackWalk64(machineType, GetCurrentProcess(), GetCurrentThread(), &frame, c, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) { // Buffer to store symbol name in. static const int kMaxNameLength = 1024; uint8_t buffer[sizeof(IMAGEHLP_SYMBOL64) + kMaxNameLength]; sk_bzero(buffer, sizeof(buffer)); // We have to place IMAGEHLP_SYMBOL64 at the front, and fill in how much space it can use. IMAGEHLP_SYMBOL64* symbol = reinterpret_cast<IMAGEHLP_SYMBOL64*>(&buffer); symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); symbol->MaxNameLength = kMaxNameLength - 1; // Translate the current PC into a symbol and byte offset from the symbol. DWORD64 offset; SymGetSymFromAddr64(hProcess, frame.AddrPC.Offset, &offset, symbol); SkDebugf("%s +%x\n", symbol->Name, offset); } // Exit NOW. Don't notify other threads, don't call anything registered with atexit(). _exit(1); // The compiler wants us to return something. This is what we'd do if we didn't _exit(). return EXCEPTION_EXECUTE_HANDLER; } void SetupCrashHandler() { SetUnhandledExceptionFilter(handler); } #else void SetupCrashHandler() { } #endif