|  | /* | 
|  | * Copyright 2014 Google Inc. | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include "CrashHandler.h" | 
|  |  | 
|  | #include "../private/SkLeanWindows.h" | 
|  |  | 
|  | #include <stdlib.h> | 
|  |  | 
|  | // Disable SetupCrashHandler() unless SK_CRASH_HANDLER is defined. | 
|  | #ifndef SK_CRASH_HANDLER | 
|  | void SetupCrashHandler() { } | 
|  |  | 
|  | #elif defined(SK_BUILD_FOR_GOOGLE3) | 
|  | #include "base/process_state.h" | 
|  | void SetupCrashHandler() { InstallSignalHandlers(); } | 
|  |  | 
|  | #else | 
|  |  | 
|  | #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) | 
|  |  | 
|  | // 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 [%s]:\n", sig, strsignal(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) | 
|  | #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_CRASH_HANDLER) && defined(SK_BUILD_FOR_WIN) | 
|  |  | 
|  | #include <DbgHelp.h> | 
|  |  | 
|  | static const struct { | 
|  | const char* name; | 
|  | const DWORD 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, | 
|  | nullptr, | 
|  | SymFunctionTableAccess64, | 
|  | SymGetModuleBase64, | 
|  | nullptr)) { | 
|  | // 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  // We asked for SK_CRASH_HANDLER, but it's not Mac, Linux, or Windows.  Sorry! | 
|  |  | 
|  | void SetupCrashHandler() { } | 
|  |  | 
|  | #endif | 
|  | #endif // SK_CRASH_HANDLER |