| borenet | 4808757 | 2015-04-02 12:16:36 -0700 | [diff] [blame] | 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 |  | 
| mtklein | 30e6e2a | 2014-06-18 11:44:15 -0700 | [diff] [blame] | 8 | #include "CrashHandler.h" | 
 | 9 |  | 
| halcanary | 4dbbd04 | 2016-06-07 17:21:10 -0700 | [diff] [blame] | 10 | #include "../private/SkLeanWindows.h" | 
| mtklein | 30e6e2a | 2014-06-18 11:44:15 -0700 | [diff] [blame] | 11 |  | 
| mtklein | 30e6e2a | 2014-06-18 11:44:15 -0700 | [diff] [blame] | 12 | #include <stdlib.h> | 
| mtklein | 30e6e2a | 2014-06-18 11:44:15 -0700 | [diff] [blame] | 13 |  | 
| mtklein | 0e3fac2 | 2014-07-02 14:30:47 -0700 | [diff] [blame] | 14 | // Disable SetupCrashHandler() unless SK_CRASH_HANDLER is defined. | 
 | 15 | #ifndef SK_CRASH_HANDLER | 
 | 16 |     void SetupCrashHandler() { } | 
| mtklein | 30e6e2a | 2014-06-18 11:44:15 -0700 | [diff] [blame] | 17 |  | 
| Mike Klein | 6613cc5 | 2017-12-19 09:09:33 -0500 | [diff] [blame] | 18 | #elif defined(SK_BUILD_FOR_GOOGLE3) | 
| mtklein | b47bba7 | 2015-01-14 06:38:28 -0800 | [diff] [blame] | 19 |     #include "base/process_state.h" | 
 | 20 |     void SetupCrashHandler() { InstallSignalHandlers(); } | 
 | 21 |  | 
| mtklein | 30e6e2a | 2014-06-18 11:44:15 -0700 | [diff] [blame] | 22 | #else | 
 | 23 |  | 
| mtklein | 0e3fac2 | 2014-07-02 14:30:47 -0700 | [diff] [blame] | 24 |     #if defined(SK_BUILD_FOR_MAC) | 
| mtklein | 30e6e2a | 2014-06-18 11:44:15 -0700 | [diff] [blame] | 25 |  | 
| mtklein | 0e3fac2 | 2014-07-02 14:30:47 -0700 | [diff] [blame] | 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 |  | 
| borenet | 4808757 | 2015-04-02 12:16:36 -0700 | [diff] [blame] | 57 |     #elif defined(SK_BUILD_FOR_UNIX) | 
| mtklein | 0e3fac2 | 2014-07-02 14:30:47 -0700 | [diff] [blame] | 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 |  | 
| borenet | 4808757 | 2015-04-02 12:16:36 -0700 | [diff] [blame] | 77 |     #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) | 
| mtklein | 0e3fac2 | 2014-07-02 14:30:47 -0700 | [diff] [blame] | 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; | 
| bungeman | 4ab57e0 | 2016-03-19 15:51:05 -0700 | [diff] [blame] | 104 |             const DWORD code; | 
| mtklein | 0e3fac2 | 2014-07-02 14:30:47 -0700 | [diff] [blame] | 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, | 
| halcanary | 96fcdcc | 2015-08-27 07:41:13 -0700 | [diff] [blame] | 153 |                                nullptr, | 
| mtklein | 0e3fac2 | 2014-07-02 14:30:47 -0700 | [diff] [blame] | 154 |                                SymFunctionTableAccess64, | 
 | 155 |                                SymGetModuleBase64, | 
| halcanary | 96fcdcc | 2015-08-27 07:41:13 -0700 | [diff] [blame] | 156 |                                nullptr)) { | 
| mtklein | 0e3fac2 | 2014-07-02 14:30:47 -0700 | [diff] [blame] | 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 |