You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
192 lines
6.5 KiB
192 lines
6.5 KiB
/* |
|
* 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(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
|
|
|