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 "tools/CrashHandler.h"
9 
10 #include "src/core/SkLeanWindows.h"
11 
12 #include <stdlib.h>
13 
14 #if defined(SK_BUILD_FOR_GOOGLE3)
15     #include "base/config.h"   // May define GOOGLE_ENABLE_SIGNAL_HANDLERS.
16 #endif
17 
18 #if defined(GOOGLE_ENABLE_SIGNAL_HANDLERS)
19     #include "base/process_state.h"
SetupCrashHandler()20     void SetupCrashHandler() { InstallSignalHandlers(); }
21 
22 #else
23 
24     #if defined(SK_BUILD_FOR_MAC)
25         // We only use local unwinding, so we can define this to select a faster implementation.
26         #define UNW_LOCAL_ONLY
27         #include <libunwind.h>
28         #include <cxxabi.h>
29 
handler(int sig)30         static void handler(int sig) {
31             unw_context_t context;
32             unw_getcontext(&context);
33 
34             unw_cursor_t cursor;
35             unw_init_local(&cursor, &context);
36 
37             SkDebugf("\nSignal %d:\n", sig);
38             while (unw_step(&cursor) > 0) {
39                 static const size_t kMax = 256;
40                 char mangled[kMax], demangled[kMax];
41                 unw_word_t offset;
42                 unw_get_proc_name(&cursor, mangled, kMax, &offset);
43 
44                 int ok;
45                 size_t len = kMax;
46                 abi::__cxa_demangle(mangled, demangled, &len, &ok);
47 
48                 SkDebugf("%s (+0x%zx)\n", ok == 0 ? demangled : mangled, (size_t)offset);
49             }
50             SkDebugf("\n");
51 
52             // Exit NOW.  Don't notify other threads, don't call anything registered with atexit().
53             _Exit(sig);
54         }
55 
56     #elif defined(SK_BUILD_FOR_UNIX)
57         // We'd use libunwind here too, but it's a pain to get installed for
58         // both 32 and 64 bit on bots.  Doesn't matter much: catchsegv is best anyway.
59         #include <cxxabi.h>
60         #include <dlfcn.h>
61         #include <string.h>
62 #if defined(__Fuchsia__)
63         #include <stdint.h>
64 
65         // syslog crash reporting from Fuchsia's backtrace_request.h
66         //
67         // Special value we put in the first register to let the exception handler know
68         // that we are just requesting a backtrace and we should resume the thread.
69         #define BACKTRACE_REQUEST_MAGIC ((uint64_t)0xee726573756d65ee)
70 
71         // Prints a backtrace, resuming the thread without killing the process.
backtrace_request(void)72         __attribute__((always_inline)) static inline void backtrace_request(void) {
73           // Two instructions: one that sets a software breakpoint ("int3" on x64,
74           // "brk" on arm64) and one that writes the "magic" value in the first
75           // register ("a" on x64, "x0" on arm64).
76           //
77           // We set a software breakpoint to trigger the exception handling in
78           // crashsvc, which will print the debug info, including the backtrace.
79           //
80           // We write the "magic" value in the first register so that the exception
81           // handler can check for it and resume the thread if present.
82           #ifdef __x86_64__
83             __asm__("int3" : : "a"(BACKTRACE_REQUEST_MAGIC));
84           #endif
85           #ifdef __aarch64__
86             // This is what gdb uses.
87             __asm__(
88                 "mov x0, %0\n"
89                 "\tbrk 0"
90                 :
91                 : "r"(BACKTRACE_REQUEST_MAGIC)
92                 : "x0");
93           #endif
94         }
95 #else
96         #include <execinfo.h>
97 #endif
98 
handler(int sig)99         static void handler(int sig) {
100 #if defined(__Fuchsia__)
101             backtrace_request();
102 #else
103             void* stack[64];
104             const int count = backtrace(stack, SK_ARRAY_COUNT(stack));
105             char** symbols = backtrace_symbols(stack, count);
106 
107             SkDebugf("\nSignal %d [%s]:\n", sig, strsignal(sig));
108             for (int i = 0; i < count; i++) {
109                 Dl_info info;
110                 if (dladdr(stack[i], &info) && info.dli_sname) {
111                     char demangled[256];
112                     size_t len = SK_ARRAY_COUNT(demangled);
113                     int ok;
114 
115                     abi::__cxa_demangle(info.dli_sname, demangled, &len, &ok);
116                     if (ok == 0) {
117                         SkDebugf("    %s\n", demangled);
118                         continue;
119                     }
120                 }
121                 SkDebugf("    %s\n", symbols[i]);
122             }
123 #endif
124             // Exit NOW.  Don't notify other threads, don't call anything registered with
125             // atexit().
126             _Exit(sig);
127         }
128     #endif
129 
130     #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX)
131         #include <signal.h>
132 
SetupCrashHandler()133         void SetupCrashHandler() {
134             static const int kSignals[] = {
135                 SIGABRT,
136                 SIGBUS,
137                 SIGFPE,
138                 SIGILL,
139                 SIGSEGV,
140                 SIGTRAP,
141             };
142 
143             for (size_t i = 0; i < sizeof(kSignals) / sizeof(kSignals[0]); i++) {
144                 // Register our signal handler unless something's already done so (e.g. catchsegv).
145                 void (*prev)(int) = signal(kSignals[i], handler);
146                 if (prev != SIG_DFL) {
147                     signal(kSignals[i], prev);
148                 }
149             }
150         }
151 
152     #elif defined(SK_BUILD_FOR_WIN)
153 
154         #include <DbgHelp.h>
155         #include "include/private/SkMalloc.h"
156 
157         static const struct {
158             const char* name;
159             const DWORD code;
160         } kExceptions[] = {
161         #define _(E) {#E, E}
162             _(EXCEPTION_ACCESS_VIOLATION),
163             _(EXCEPTION_BREAKPOINT),
164             _(EXCEPTION_INT_DIVIDE_BY_ZERO),
165             _(EXCEPTION_STACK_OVERFLOW),
166             // TODO: more?
167         #undef _
168         };
169 
handler(EXCEPTION_POINTERS * e)170         static LONG WINAPI handler(EXCEPTION_POINTERS* e) {
171             const DWORD code = e->ExceptionRecord->ExceptionCode;
172             SkDebugf("\nCaught exception %u", code);
173             for (size_t i = 0; i < SK_ARRAY_COUNT(kExceptions); i++) {
174                 if (kExceptions[i].code == code) {
175                     SkDebugf(" %s", kExceptions[i].name);
176                 }
177             }
178             SkDebugf("\n");
179 
180             // We need to run SymInitialize before doing any of the stack walking below.
181             HANDLE hProcess = GetCurrentProcess();
182             SymInitialize(hProcess, 0, true);
183 
184             STACKFRAME64 frame;
185             sk_bzero(&frame, sizeof(frame));
186             // Start frame off from the frame that triggered the exception.
187             CONTEXT* c = e->ContextRecord;
188             frame.AddrPC.Mode      = AddrModeFlat;
189             frame.AddrStack.Mode   = AddrModeFlat;
190             frame.AddrFrame.Mode   = AddrModeFlat;
191         #if defined(_X86_)
192             frame.AddrPC.Offset    = c->Eip;
193             frame.AddrStack.Offset = c->Esp;
194             frame.AddrFrame.Offset = c->Ebp;
195             const DWORD machineType = IMAGE_FILE_MACHINE_I386;
196         #elif defined(_AMD64_)
197             frame.AddrPC.Offset    = c->Rip;
198             frame.AddrStack.Offset = c->Rsp;
199             frame.AddrFrame.Offset = c->Rbp;
200             const DWORD machineType = IMAGE_FILE_MACHINE_AMD64;
201         #elif defined(_M_ARM64)
202             frame.AddrPC.Offset    = c->Pc;
203             frame.AddrStack.Offset = c->Sp;
204             frame.AddrFrame.Offset = c->Fp;
205             const DWORD machineType = IMAGE_FILE_MACHINE_ARM64;
206         #endif
207 
208         #if !defined(SK_WINUWP)
209             while (StackWalk64(machineType,
210                                GetCurrentProcess(),
211                                GetCurrentThread(),
212                                &frame,
213                                c,
214                                nullptr,
215                                SymFunctionTableAccess64,
216                                SymGetModuleBase64,
217                                nullptr)) {
218                 // Buffer to store symbol name in.
219                 static const int kMaxNameLength = 1024;
220                 uint8_t buffer[sizeof(IMAGEHLP_SYMBOL64) + kMaxNameLength];
221                 sk_bzero(buffer, sizeof(buffer));
222 
223                 // We have to place IMAGEHLP_SYMBOL64 at the front, and fill in
224                 // how much space it can use.
225                 IMAGEHLP_SYMBOL64* symbol = reinterpret_cast<IMAGEHLP_SYMBOL64*>(&buffer);
226                 symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
227                 symbol->MaxNameLength = kMaxNameLength - 1;
228 
229                 // Translate the current PC into a symbol and byte offset from the symbol.
230                 DWORD64 offset;
231                 SymGetSymFromAddr64(hProcess, frame.AddrPC.Offset, &offset, symbol);
232 
233                 SkDebugf("%s +%x\n", symbol->Name, offset);
234             }
235         #endif //SK_WINUWP
236 
237             // Exit NOW.  Don't notify other threads, don't call anything registered with atexit().
238             _exit(1);
239 
240             // The compiler wants us to return something.  This is what we'd do
241             // if we didn't _exit().
242             return EXCEPTION_EXECUTE_HANDLER;
243         }
244 
SetupCrashHandler()245         void SetupCrashHandler() {
246             SetUnhandledExceptionFilter(handler);
247         }
248 
249     #else
250 
SetupCrashHandler()251         void SetupCrashHandler() { }
252 
253     #endif
254 #endif // SK_BUILD_FOR_GOOGLE3?
255