1#include "CrashHandler.h"
2
3#include "SkTypes.h"
4
5#include <stdlib.h>
6
7// Disable SetupCrashHandler() unless SK_CRASH_HANDLER is defined.
8#ifndef SK_CRASH_HANDLER
9    void SetupCrashHandler() { }
10
11#else
12
13    #if defined(SK_BUILD_FOR_MAC)
14
15        // 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
182