| /* |
| * 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 "include/private/base/SkDebug.h" |
| #include "src/base/SkLeanWindows.h" |
| |
| #include <array> // for std::size |
| #include <stdlib.h> |
| |
| #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 <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 <cxxabi.h> |
| #include <dlfcn.h> |
| #include <string.h> |
| #if defined(__Fuchsia__) |
| #include <stdint.h> |
| |
| // 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 <execinfo.h> |
| #endif |
| |
| static void handler(int sig) { |
| #if defined(__Fuchsia__) |
| backtrace_request(); |
| #else |
| void* stack[64]; |
| const int count = backtrace(stack, std::size(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 = std::size(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 <signal.h> |
| |
| 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 <DbgHelp.h> |
| #include "include/private/base/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 %lu", code); |
| for (size_t i = 0; i < std::size(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 |
| |
| #if !defined(SK_WINUWP) |
| 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 +%llx\n", symbol->Name, offset); |
| } |
| #endif //SK_WINUWP |
| |
| // 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? |