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