/* * 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 "tools/CrashHandler.h" #include "src/core/SkLeanWindows.h" #include #if defined(SK_BUILD_FOR_GOOGLE3) #include "base/config.h" // May define GOOGLE_ENABLE_SIGNAL_HANDLERS. #endif #if defined(GOOGLE_ENABLE_SIGNAL_HANDLERS) #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 #include 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 #include #include #if defined(__Fuchsia__) #include // syslog crash reporting from Fuchsia's backtrace_request.h // // Special value we put in the first register to let the exception handler know // that we are just requesting a backtrace and we should resume the thread. #define BACKTRACE_REQUEST_MAGIC ((uint64_t)0xee726573756d65ee) // Prints a backtrace, resuming the thread without killing the process. __attribute__((always_inline)) static inline void backtrace_request(void) { // Two instructions: one that sets a software breakpoint ("int3" on x64, // "brk" on arm64) and one that writes the "magic" value in the first // register ("a" on x64, "x0" on arm64). // // We set a software breakpoint to trigger the exception handling in // crashsvc, which will print the debug info, including the backtrace. // // We write the "magic" value in the first register so that the exception // handler can check for it and resume the thread if present. #ifdef __x86_64__ __asm__("int3" : : "a"(BACKTRACE_REQUEST_MAGIC)); #endif #ifdef __aarch64__ // This is what gdb uses. __asm__( "mov x0, %0\n" "\tbrk 0" : : "r"(BACKTRACE_REQUEST_MAGIC) : "x0"); #endif } #else #include #endif static void handler(int sig) { #if defined(__Fuchsia__) backtrace_request(); #else void* stack[64]; const int count = backtrace(stack, SK_ARRAY_COUNT(stack)); char** symbols = backtrace_symbols(stack, count); SkDebugf("\nSignal %d [%s]:\n", sig, strsignal(sig)); for (int i = 0; i < count; i++) { Dl_info info; if (dladdr(stack[i], &info) && info.dli_sname) { char demangled[256]; size_t len = SK_ARRAY_COUNT(demangled); int ok; abi::__cxa_demangle(info.dli_sname, demangled, &len, &ok); if (ok == 0) { SkDebugf(" %s\n", demangled); continue; } } SkDebugf(" %s\n", symbols[i]); } #endif // 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 void SetupCrashHandler() { static const int kSignals[] = { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP, }; 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 #include "include/private/SkMalloc.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; #elif defined(_M_ARM64) frame.AddrPC.Offset = c->Pc; frame.AddrStack.Offset = c->Sp; frame.AddrFrame.Offset = c->Fp; const DWORD machineType = IMAGE_FILE_MACHINE_ARM64; #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(&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 #endif // SK_BUILD_FOR_GOOGLE3?