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