2015-04-02 19:16:36 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2014 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "tools/CrashHandler.h"
|
2014-06-18 18:44:15 +00:00
|
|
|
|
2019-05-20 15:02:49 +00:00
|
|
|
#include "src/core/SkLeanWindows.h"
|
2014-06-18 18:44:15 +00:00
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2019-04-15 16:32:16 +00:00
|
|
|
#if defined(SK_BUILD_FOR_GOOGLE3)
|
2015-01-14 14:38:28 +00:00
|
|
|
#include "base/process_state.h"
|
|
|
|
void SetupCrashHandler() { InstallSignalHandlers(); }
|
|
|
|
|
2014-07-02 21:30:47 +00:00
|
|
|
#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);
|
|
|
|
}
|
|
|
|
|
2015-04-02 19:16:36 +00:00
|
|
|
#elif defined(SK_BUILD_FOR_UNIX)
|
2014-07-02 21:30:47 +00:00
|
|
|
// 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.
|
2019-04-15 16:32:16 +00:00
|
|
|
#include <cxxabi.h>
|
|
|
|
#include <dlfcn.h>
|
|
|
|
#include <string.h>
|
2019-09-25 21:14:35 +00:00
|
|
|
#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
|
2014-07-02 21:30:47 +00:00
|
|
|
|
|
|
|
static void handler(int sig) {
|
2019-09-25 21:14:35 +00:00
|
|
|
#if defined(__Fuchsia__)
|
|
|
|
backtrace_request();
|
|
|
|
#else
|
2019-04-15 16:32:16 +00:00
|
|
|
void* stack[64];
|
|
|
|
const int count = backtrace(stack, SK_ARRAY_COUNT(stack));
|
|
|
|
char** symbols = backtrace_symbols(stack, count);
|
2014-07-02 21:30:47 +00:00
|
|
|
|
|
|
|
SkDebugf("\nSignal %d [%s]:\n", sig, strsignal(sig));
|
2019-04-15 16:32:16 +00:00
|
|
|
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]);
|
|
|
|
}
|
2019-09-25 21:14:35 +00:00
|
|
|
#endif
|
|
|
|
// Exit NOW. Don't notify other threads, don't call anything registered with
|
|
|
|
// atexit().
|
2014-07-02 21:30:47 +00:00
|
|
|
_Exit(sig);
|
2014-06-18 18:44:15 +00:00
|
|
|
}
|
2014-07-02 21:30:47 +00:00
|
|
|
#endif
|
|
|
|
|
2015-04-02 19:16:36 +00:00
|
|
|
#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX)
|
2014-07-02 21:30:47 +00:00
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
void SetupCrashHandler() {
|
|
|
|
static const int kSignals[] = {
|
|
|
|
SIGABRT,
|
|
|
|
SIGBUS,
|
|
|
|
SIGFPE,
|
|
|
|
SIGILL,
|
|
|
|
SIGSEGV,
|
2019-04-15 16:32:16 +00:00
|
|
|
SIGTRAP,
|
2014-07-02 21:30:47 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2014-06-19 14:41:58 +00:00
|
|
|
}
|
2014-06-18 18:44:15 +00:00
|
|
|
|
2019-04-15 16:32:16 +00:00
|
|
|
#elif defined(SK_BUILD_FOR_WIN)
|
2014-07-02 21:30:47 +00:00
|
|
|
|
|
|
|
#include <DbgHelp.h>
|
2019-04-23 17:05:21 +00:00
|
|
|
#include "include/private/SkMalloc.h"
|
2014-07-02 21:30:47 +00:00
|
|
|
|
|
|
|
static const struct {
|
|
|
|
const char* name;
|
2016-03-19 22:51:05 +00:00
|
|
|
const DWORD code;
|
2014-07-02 21:30:47 +00:00
|
|
|
} 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;
|
2019-04-15 21:03:29 +00:00
|
|
|
#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;
|
2014-07-02 21:30:47 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
while (StackWalk64(machineType,
|
|
|
|
GetCurrentProcess(),
|
|
|
|
GetCurrentThread(),
|
|
|
|
&frame,
|
|
|
|
c,
|
2015-08-27 14:41:13 +00:00
|
|
|
nullptr,
|
2014-07-02 21:30:47 +00:00
|
|
|
SymFunctionTableAccess64,
|
|
|
|
SymGetModuleBase64,
|
2015-08-27 14:41:13 +00:00
|
|
|
nullptr)) {
|
2014-07-02 21:30:47 +00:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2019-04-15 16:32:16 +00:00
|
|
|
#else
|
2014-06-18 18:44:15 +00:00
|
|
|
|
2014-07-02 21:30:47 +00:00
|
|
|
void SetupCrashHandler() { }
|
2014-06-18 18:44:15 +00:00
|
|
|
|
2014-07-02 21:30:47 +00:00
|
|
|
#endif
|
2019-04-15 16:32:16 +00:00
|
|
|
#endif // SK_BUILD_FOR_GOOGLE3?
|