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 "SkTypes.h"
11
12#include <stdlib.h>
13
14// Disable SetupCrashHandler() unless SK_CRASH_HANDLER is defined.
15#ifndef SK_CRASH_HANDLER
16    void SetupCrashHandler() { }
17
18#elif defined(GOOGLE3)
19    #include "base/process_state.h"
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
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
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
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            int 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
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                               NULL,
154                               SymFunctionTableAccess64,
155                               SymGetModuleBase64,
156                               NULL)) {
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
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
189        void SetupCrashHandler() { }
190
191    #endif
192#endif // SK_CRASH_HANDLER
193