blob: 7355f20bc05c35cbd650f1ba490d577dfab6b91e [file] [log] [blame]
mtklein30e6e2a2014-06-18 11:44:15 -07001#include "CrashHandler.h"
2
3#include "SkTypes.h"
4
mtklein30e6e2a2014-06-18 11:44:15 -07005#include <stdlib.h>
mtklein30e6e2a2014-06-18 11:44:15 -07006
mtklein0e3fac22014-07-02 14:30:47 -07007// Disable SetupCrashHandler() unless SK_CRASH_HANDLER is defined.
8#ifndef SK_CRASH_HANDLER
9 void SetupCrashHandler() { }
mtklein30e6e2a2014-06-18 11:44:15 -070010
11#else
12
mtklein0e3fac22014-07-02 14:30:47 -070013 #if defined(SK_BUILD_FOR_MAC)
mtklein30e6e2a2014-06-18 11:44:15 -070014
mtklein0e3fac22014-07-02 14:30:47 -070015 // We only use local unwinding, so we can define this to select a faster implementation.
16 #define UNW_LOCAL_ONLY
17 #include <libunwind.h>
18 #include <cxxabi.h>
19
20 static void handler(int sig) {
21 unw_context_t context;
22 unw_getcontext(&context);
23
24 unw_cursor_t cursor;
25 unw_init_local(&cursor, &context);
26
27 SkDebugf("\nSignal %d:\n", sig);
28 while (unw_step(&cursor) > 0) {
29 static const size_t kMax = 256;
30 char mangled[kMax], demangled[kMax];
31 unw_word_t offset;
32 unw_get_proc_name(&cursor, mangled, kMax, &offset);
33
34 int ok;
35 size_t len = kMax;
36 abi::__cxa_demangle(mangled, demangled, &len, &ok);
37
38 SkDebugf("%s (+0x%zx)\n", ok == 0 ? demangled : mangled, (size_t)offset);
39 }
40 SkDebugf("\n");
41
42 // Exit NOW. Don't notify other threads, don't call anything registered with atexit().
43 _Exit(sig);
44 }
45
46 #elif defined(SK_BUILD_FOR_UNIX) && !defined(SK_BUILD_FOR_NACL) // NACL doesn't have backtrace.
47
48 // We'd use libunwind here too, but it's a pain to get installed for
49 // both 32 and 64 bit on bots. Doesn't matter much: catchsegv is best anyway.
50 #include <execinfo.h>
51
52 static void handler(int sig) {
53 static const int kMax = 64;
54 void* stack[kMax];
55 const int count = backtrace(stack, kMax);
56
57 SkDebugf("\nSignal %d [%s]:\n", sig, strsignal(sig));
58 backtrace_symbols_fd(stack, count, 2/*stderr*/);
59
60 // Exit NOW. Don't notify other threads, don't call anything registered with atexit().
61 _Exit(sig);
62 }
63
64 #endif
65
66 #if (defined(SK_BUILD_FOR_MAC) || (defined(SK_BUILD_FOR_UNIX) && !defined(SK_BUILD_FOR_NACL)))
67 #include <signal.h>
68
69 void SetupCrashHandler() {
70 static const int kSignals[] = {
71 SIGABRT,
72 SIGBUS,
73 SIGFPE,
74 SIGILL,
75 SIGSEGV,
76 };
77
78 for (size_t i = 0; i < sizeof(kSignals) / sizeof(kSignals[0]); i++) {
79 // Register our signal handler unless something's already done so (e.g. catchsegv).
80 void (*prev)(int) = signal(kSignals[i], handler);
81 if (prev != SIG_DFL) {
82 signal(kSignals[i], prev);
83 }
84 }
85 }
86
87 #elif defined(SK_CRASH_HANDLER) && defined(SK_BUILD_FOR_WIN)
88
89 #include <DbgHelp.h>
90
91 static const struct {
92 const char* name;
93 int code;
94 } kExceptions[] = {
95 #define _(E) {#E, E}
96 _(EXCEPTION_ACCESS_VIOLATION),
97 _(EXCEPTION_BREAKPOINT),
98 _(EXCEPTION_INT_DIVIDE_BY_ZERO),
99 _(EXCEPTION_STACK_OVERFLOW),
100 // TODO: more?
101 #undef _
102 };
103
104 static LONG WINAPI handler(EXCEPTION_POINTERS* e) {
105 const DWORD code = e->ExceptionRecord->ExceptionCode;
106 SkDebugf("\nCaught exception %u", code);
107 for (size_t i = 0; i < SK_ARRAY_COUNT(kExceptions); i++) {
108 if (kExceptions[i].code == code) {
109 SkDebugf(" %s", kExceptions[i].name);
110 }
111 }
112 SkDebugf("\n");
113
114 // We need to run SymInitialize before doing any of the stack walking below.
115 HANDLE hProcess = GetCurrentProcess();
116 SymInitialize(hProcess, 0, true);
117
118 STACKFRAME64 frame;
119 sk_bzero(&frame, sizeof(frame));
120 // Start frame off from the frame that triggered the exception.
121 CONTEXT* c = e->ContextRecord;
122 frame.AddrPC.Mode = AddrModeFlat;
123 frame.AddrStack.Mode = AddrModeFlat;
124 frame.AddrFrame.Mode = AddrModeFlat;
125 #if defined(_X86_)
126 frame.AddrPC.Offset = c->Eip;
127 frame.AddrStack.Offset = c->Esp;
128 frame.AddrFrame.Offset = c->Ebp;
129 const DWORD machineType = IMAGE_FILE_MACHINE_I386;
130 #elif defined(_AMD64_)
131 frame.AddrPC.Offset = c->Rip;
132 frame.AddrStack.Offset = c->Rsp;
133 frame.AddrFrame.Offset = c->Rbp;
134 const DWORD machineType = IMAGE_FILE_MACHINE_AMD64;
135 #endif
136
137 while (StackWalk64(machineType,
138 GetCurrentProcess(),
139 GetCurrentThread(),
140 &frame,
141 c,
142 NULL,
143 SymFunctionTableAccess64,
144 SymGetModuleBase64,
145 NULL)) {
146 // Buffer to store symbol name in.
147 static const int kMaxNameLength = 1024;
148 uint8_t buffer[sizeof(IMAGEHLP_SYMBOL64) + kMaxNameLength];
149 sk_bzero(buffer, sizeof(buffer));
150
151 // We have to place IMAGEHLP_SYMBOL64 at the front, and fill in
152 // how much space it can use.
153 IMAGEHLP_SYMBOL64* symbol = reinterpret_cast<IMAGEHLP_SYMBOL64*>(&buffer);
154 symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
155 symbol->MaxNameLength = kMaxNameLength - 1;
156
157 // Translate the current PC into a symbol and byte offset from the symbol.
158 DWORD64 offset;
159 SymGetSymFromAddr64(hProcess, frame.AddrPC.Offset, &offset, symbol);
160
161 SkDebugf("%s +%x\n", symbol->Name, offset);
162 }
163
164 // Exit NOW. Don't notify other threads, don't call anything registered with atexit().
165 _exit(1);
166
167 // The compiler wants us to return something. This is what we'd do
168 // if we didn't _exit().
169 return EXCEPTION_EXECUTE_HANDLER;
170 }
171
172 void SetupCrashHandler() {
173 SetUnhandledExceptionFilter(handler);
174 }
175
176 #else // We asked for SK_CRASH_HANDLER, but it's not Mac, Linux, or Windows. Sorry!
177
178 void SetupCrashHandler() { }
179
180 #endif
181#endif // SK_CRASH_HANDLER