exception_handler_no_mach.cc
1 // Copyright 2006 Google LLC 2 // 3 // Redistribution and use in source and binary forms, with or without 4 // modification, are permitted provided that the following conditions are 5 // met: 6 // 7 // * Redistributions of source code must retain the above copyright 8 // notice, this list of conditions and the following disclaimer. 9 // * Redistributions in binary form must reproduce the above 10 // copyright notice, this list of conditions and the following disclaimer 11 // in the documentation and/or other materials provided with the 12 // distribution. 13 // * Neither the name of Google LLC nor the names of its 14 // contributors may be used to endorse or promote products derived from 15 // this software without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 #ifdef HAVE_CONFIG_H 30 #include <config.h> // Must come first 31 #endif 32 33 #include <signal.h> 34 #include <TargetConditionals.h> 35 36 #include "client/mac/handler/minidump_generator.h" 37 #include "client/ios/exception_handler_no_mach.h" 38 39 #ifndef USE_PROTECTED_ALLOCATIONS 40 #if TARGET_OS_TV 41 #define USE_PROTECTED_ALLOCATIONS 1 42 #else 43 #define USE_PROTECTED_ALLOCATIONS 0 44 #endif 45 #endif 46 47 // If USE_PROTECTED_ALLOCATIONS is activated then the 48 // gBreakpadAllocator needs to be setup in other code 49 // ahead of time. Please see ProtectedMemoryAllocator.h 50 // for more details. 51 #if USE_PROTECTED_ALLOCATIONS 52 #include "client/mac/handler/protected_memory_allocator.h" 53 extern ProtectedMemoryAllocator* gBreakpadAllocator; 54 #endif 55 56 namespace google_breakpad { 57 58 const int kExceptionSignals[] = { 59 // Core-generating signals. 60 SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGQUIT, SIGSEGV, SIGSYS, SIGTRAP, SIGEMT, 61 SIGXCPU, SIGXFSZ, 62 // Non-core-generating but terminating signals. 63 SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGPROF, SIGTERM, SIGUSR1, SIGUSR2, 64 SIGVTALRM, SIGXCPU, SIGXFSZ, SIGIO, 65 }; 66 const int kNumHandledSignals = 67 sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]); 68 struct scoped_ptr<struct sigaction> old_handlers[kNumHandledSignals]; 69 70 static union { 71 #if USE_PROTECTED_ALLOCATIONS 72 #if defined PAGE_MAX_SIZE 73 char protected_buffer[PAGE_MAX_SIZE] __attribute__((aligned(PAGE_MAX_SIZE))); 74 #else 75 char protected_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); 76 #endif // defined PAGE_MAX_SIZE 77 #endif // USE_PROTECTED_ALLOCATIONS 78 google_breakpad::ExceptionHandler* handler; 79 } gProtectedData; 80 81 ExceptionHandler::ExceptionHandler(const string& dump_path, 82 FilterCallback filter, 83 MinidumpCallback callback, 84 void* callback_context, 85 bool install_handler, 86 const char* port_name) 87 : dump_path_(), 88 filter_(filter), 89 callback_(callback), 90 callback_context_(callback_context), 91 directCallback_(NULL), 92 installed_exception_handler_(false), 93 is_in_teardown_(false) { 94 // This will update to the ID and C-string pointers 95 set_dump_path(dump_path); 96 MinidumpGenerator::GatherSystemInformation(); 97 Setup(); 98 } 99 100 // special constructor if we want to bypass minidump writing and 101 // simply get a callback with the exception information 102 ExceptionHandler::ExceptionHandler(DirectCallback callback, 103 void* callback_context, 104 bool install_handler) 105 : dump_path_(), 106 filter_(NULL), 107 callback_(NULL), 108 callback_context_(callback_context), 109 directCallback_(callback), 110 installed_exception_handler_(false), 111 is_in_teardown_(false) { 112 MinidumpGenerator::GatherSystemInformation(); 113 Setup(); 114 } 115 116 ExceptionHandler::~ExceptionHandler() { 117 Teardown(); 118 } 119 120 bool ExceptionHandler::WriteMinidumpWithException( 121 int exception_type, 122 int exception_code, 123 int exception_subcode, 124 breakpad_ucontext_t* task_context, 125 mach_port_t thread_name, 126 bool exit_after_write, 127 bool report_current_thread) { 128 bool result = false; 129 130 #if !TARGET_OS_TV 131 exit_after_write = false; 132 #endif // !TARGET_OS_TV 133 134 if (directCallback_) { 135 if (directCallback_(callback_context_, 136 exception_type, 137 exception_code, 138 exception_subcode, 139 thread_name) ) { 140 if (exit_after_write) 141 _exit(exception_type); 142 } 143 } else { 144 string minidump_id; 145 146 // Putting the MinidumpGenerator in its own context will ensure that the 147 // destructor is executed, closing the newly created minidump file. 148 if (!dump_path_.empty()) { 149 MinidumpGenerator md(mach_task_self(), 150 report_current_thread ? MACH_PORT_NULL : 151 mach_thread_self()); 152 md.SetTaskContext(task_context); 153 if (exception_type && exception_code) { 154 // If this is a real exception, give the filter (if any) a chance to 155 // decide if this should be sent. 156 if (filter_ && !filter_(callback_context_)) 157 return false; 158 159 md.SetExceptionInformation(exception_type, exception_code, 160 exception_subcode, thread_name); 161 } 162 163 result = md.Write(next_minidump_path_c_); 164 } 165 166 // Call user specified callback (if any) 167 if (callback_) { 168 // If the user callback returned true and we're handling an exception 169 // (rather than just writing out the file), then we should exit without 170 // forwarding the exception to the next handler. 171 if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_, 172 result)) { 173 if (exit_after_write) 174 _exit(exception_type); 175 } 176 } 177 } 178 179 return result; 180 } 181 182 // static 183 void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) { 184 #if USE_PROTECTED_ALLOCATIONS 185 if (gBreakpadAllocator) 186 gBreakpadAllocator->Unprotect(); 187 #endif 188 gProtectedData.handler->WriteMinidumpWithException( 189 EXC_SOFTWARE, 190 MD_EXCEPTION_CODE_MAC_ABORT, 191 0, 192 static_cast<breakpad_ucontext_t*>(uc), 193 mach_thread_self(), 194 true, 195 true); 196 #if USE_PROTECTED_ALLOCATIONS 197 if (gBreakpadAllocator) 198 gBreakpadAllocator->Protect(); 199 #endif 200 } 201 202 bool ExceptionHandler::InstallHandlers() { 203 // If a handler is already installed, something is really wrong. 204 if (gProtectedData.handler != NULL) 205 return false; 206 for (int i = 0; i < kNumHandledSignals; ++i) { 207 struct sigaction sa; 208 memset(&sa, 0, sizeof(sa)); 209 sigemptyset(&sa.sa_mask); 210 sigaddset(&sa.sa_mask, kExceptionSignals[i]); 211 sa.sa_sigaction = ExceptionHandler::SignalHandler; 212 sa.sa_flags = SA_ONSTACK | SA_SIGINFO; 213 214 if (sigaction(kExceptionSignals[i], &sa, old_handlers[i].get()) == -1) { 215 return false; 216 } 217 } 218 gProtectedData.handler = this; 219 #if USE_PROTECTED_ALLOCATIONS 220 assert(((size_t)(gProtectedData.protected_buffer) & PAGE_MASK) == 0); 221 mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ); 222 #endif // USE_PROTECTED_ALLOCATIONS 223 installed_exception_handler_ = true; 224 return true; 225 } 226 227 bool ExceptionHandler::UninstallHandlers() { 228 for (int i = 0; i < kNumHandledSignals; ++i) { 229 if (old_handlers[i].get()) { 230 sigaction(kExceptionSignals[i], old_handlers[i].get(), NULL); 231 old_handlers[i].reset(); 232 } 233 } 234 #if USE_PROTECTED_ALLOCATIONS 235 mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ | PROT_WRITE); 236 #endif // USE_PROTECTED_ALLOCATIONS 237 gProtectedData.handler = NULL; 238 installed_exception_handler_ = false; 239 return true; 240 } 241 242 bool ExceptionHandler::Setup() { 243 if (!InstallHandlers()) 244 return false; 245 return true; 246 } 247 248 bool ExceptionHandler::Teardown() { 249 is_in_teardown_ = true; 250 251 if (!UninstallHandlers()) 252 return false; 253 254 return true; 255 } 256 257 void ExceptionHandler::UpdateNextID() { 258 next_minidump_path_ = 259 (MinidumpGenerator::UniqueNameInDirectory(dump_path_, &next_minidump_id_)); 260 261 next_minidump_path_c_ = next_minidump_path_.c_str(); 262 next_minidump_id_c_ = next_minidump_id_.c_str(); 263 } 264 265 } // namespace google_breakpad