1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,849 @@ 1.4 +// Copyright (c) 2006, Google Inc. 1.5 +// All rights reserved. 1.6 +// 1.7 +// Redistribution and use in source and binary forms, with or without 1.8 +// modification, are permitted provided that the following conditions are 1.9 +// met: 1.10 +// 1.11 +// * Redistributions of source code must retain the above copyright 1.12 +// notice, this list of conditions and the following disclaimer. 1.13 +// * Redistributions in binary form must reproduce the above 1.14 +// copyright notice, this list of conditions and the following disclaimer 1.15 +// in the documentation and/or other materials provided with the 1.16 +// distribution. 1.17 +// * Neither the name of Google Inc. nor the names of its 1.18 +// contributors may be used to endorse or promote products derived from 1.19 +// this software without specific prior written permission. 1.20 +// 1.21 +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.22 +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.23 +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.24 +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.25 +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.26 +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.27 +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.28 +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.29 +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.30 +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.31 +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.32 + 1.33 +#include <mach/exc.h> 1.34 +#include <mach/mig.h> 1.35 +#include <pthread.h> 1.36 +#include <signal.h> 1.37 +#include <TargetConditionals.h> 1.38 + 1.39 +#include <map> 1.40 + 1.41 +#include "client/mac/handler/exception_handler.h" 1.42 +#include "client/mac/handler/minidump_generator.h" 1.43 +#include "common/mac/macho_utilities.h" 1.44 +#include "common/mac/scoped_task_suspend-inl.h" 1.45 +#include "google_breakpad/common/minidump_exception_mac.h" 1.46 + 1.47 +#ifndef __EXCEPTIONS 1.48 +// This file uses C++ try/catch (but shouldn't). Duplicate the macros from 1.49 +// <c++/4.2.1/exception_defines.h> allowing this file to work properly with 1.50 +// exceptions disabled even when other C++ libraries are used. #undef the try 1.51 +// and catch macros first in case libstdc++ is in use and has already provided 1.52 +// its own definitions. 1.53 +#undef try 1.54 +#define try if (true) 1.55 +#undef catch 1.56 +#define catch(X) if (false) 1.57 +#endif // __EXCEPTIONS 1.58 + 1.59 +#ifndef USE_PROTECTED_ALLOCATIONS 1.60 +#if TARGET_OS_IPHONE 1.61 +#define USE_PROTECTED_ALLOCATIONS 1 1.62 +#else 1.63 +#define USE_PROTECTED_ALLOCATIONS 0 1.64 +#endif 1.65 +#endif 1.66 + 1.67 +// If USE_PROTECTED_ALLOCATIONS is activated then the 1.68 +// gBreakpadAllocator needs to be setup in other code 1.69 +// ahead of time. Please see ProtectedMemoryAllocator.h 1.70 +// for more details. 1.71 +#if USE_PROTECTED_ALLOCATIONS 1.72 + #include "protected_memory_allocator.h" 1.73 + extern ProtectedMemoryAllocator *gBreakpadAllocator; 1.74 +#endif 1.75 + 1.76 +namespace google_breakpad { 1.77 + 1.78 +static union { 1.79 +#if USE_PROTECTED_ALLOCATIONS 1.80 + char protected_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); 1.81 +#endif 1.82 + google_breakpad::ExceptionHandler *handler; 1.83 +} gProtectedData; 1.84 + 1.85 +using std::map; 1.86 + 1.87 +// These structures and techniques are illustrated in 1.88 +// Mac OS X Internals, Amit Singh, ch 9.7 1.89 +struct ExceptionMessage { 1.90 + mach_msg_header_t header; 1.91 + mach_msg_body_t body; 1.92 + mach_msg_port_descriptor_t thread; 1.93 + mach_msg_port_descriptor_t task; 1.94 + NDR_record_t ndr; 1.95 + exception_type_t exception; 1.96 + mach_msg_type_number_t code_count; 1.97 + integer_t code[EXCEPTION_CODE_MAX]; 1.98 + char padding[512]; 1.99 +}; 1.100 + 1.101 +struct ExceptionParameters { 1.102 + ExceptionParameters() : count(0) {} 1.103 + mach_msg_type_number_t count; 1.104 + exception_mask_t masks[EXC_TYPES_COUNT]; 1.105 + mach_port_t ports[EXC_TYPES_COUNT]; 1.106 + exception_behavior_t behaviors[EXC_TYPES_COUNT]; 1.107 + thread_state_flavor_t flavors[EXC_TYPES_COUNT]; 1.108 +}; 1.109 + 1.110 +struct ExceptionReplyMessage { 1.111 + mach_msg_header_t header; 1.112 + NDR_record_t ndr; 1.113 + kern_return_t return_code; 1.114 +}; 1.115 + 1.116 +// Only catch these three exceptions. The other ones are nebulously defined 1.117 +// and may result in treating a non-fatal exception as fatal. 1.118 +exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS | 1.119 +EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT; 1.120 + 1.121 +#if !TARGET_OS_IPHONE 1.122 +extern "C" { 1.123 + // Forward declarations for functions that need "C" style compilation 1.124 + boolean_t exc_server(mach_msg_header_t* request, 1.125 + mach_msg_header_t* reply); 1.126 + 1.127 + // This symbol must be visible to dlsym() - see 1.128 + // http://code.google.com/p/google-breakpad/issues/detail?id=345 for details. 1.129 + kern_return_t catch_exception_raise(mach_port_t target_port, 1.130 + mach_port_t failed_thread, 1.131 + mach_port_t task, 1.132 + exception_type_t exception, 1.133 + exception_data_t code, 1.134 + mach_msg_type_number_t code_count) 1.135 + __attribute__((visibility("default"))); 1.136 +} 1.137 +#endif 1.138 + 1.139 +kern_return_t ForwardException(mach_port_t task, 1.140 + mach_port_t failed_thread, 1.141 + exception_type_t exception, 1.142 + exception_data_t code, 1.143 + mach_msg_type_number_t code_count); 1.144 + 1.145 +#if TARGET_OS_IPHONE 1.146 +// Implementation is based on the implementation generated by mig. 1.147 +boolean_t breakpad_exc_server(mach_msg_header_t* InHeadP, 1.148 + mach_msg_header_t* OutHeadP) { 1.149 + OutHeadP->msgh_bits = 1.150 + MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(InHeadP->msgh_bits), 0); 1.151 + OutHeadP->msgh_remote_port = InHeadP->msgh_remote_port; 1.152 + /* Minimal size: routine() will update it if different */ 1.153 + OutHeadP->msgh_size = (mach_msg_size_t)sizeof(mig_reply_error_t); 1.154 + OutHeadP->msgh_local_port = MACH_PORT_NULL; 1.155 + OutHeadP->msgh_id = InHeadP->msgh_id + 100; 1.156 + 1.157 + if (InHeadP->msgh_id != 2401) { 1.158 + ((mig_reply_error_t*)OutHeadP)->NDR = NDR_record; 1.159 + ((mig_reply_error_t*)OutHeadP)->RetCode = MIG_BAD_ID; 1.160 + return FALSE; 1.161 + } 1.162 + 1.163 +#ifdef __MigPackStructs 1.164 +#pragma pack(4) 1.165 +#endif 1.166 + typedef struct { 1.167 + mach_msg_header_t Head; 1.168 + /* start of the kernel processed data */ 1.169 + mach_msg_body_t msgh_body; 1.170 + mach_msg_port_descriptor_t thread; 1.171 + mach_msg_port_descriptor_t task; 1.172 + /* end of the kernel processed data */ 1.173 + NDR_record_t NDR; 1.174 + exception_type_t exception; 1.175 + mach_msg_type_number_t codeCnt; 1.176 + integer_t code[2]; 1.177 + mach_msg_trailer_t trailer; 1.178 + } Request; 1.179 + 1.180 + typedef struct { 1.181 + mach_msg_header_t Head; 1.182 + NDR_record_t NDR; 1.183 + kern_return_t RetCode; 1.184 + } Reply; 1.185 +#ifdef __MigPackStructs 1.186 +#pragma pack() 1.187 +#endif 1.188 + 1.189 + Request* In0P = (Request*)InHeadP; 1.190 + Reply* OutP = (Reply*)OutHeadP; 1.191 + 1.192 + if (In0P->task.name != mach_task_self()) { 1.193 + return FALSE; 1.194 + } 1.195 + OutP->RetCode = ForwardException(In0P->task.name, 1.196 + In0P->thread.name, 1.197 + In0P->exception, 1.198 + In0P->code, 1.199 + In0P->codeCnt); 1.200 + OutP->NDR = NDR_record; 1.201 + return TRUE; 1.202 +} 1.203 +#else 1.204 +boolean_t breakpad_exc_server(mach_msg_header_t* request, 1.205 + mach_msg_header_t* reply) { 1.206 + return exc_server(request, reply); 1.207 +} 1.208 + 1.209 +// Callback from exc_server() 1.210 +kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread, 1.211 + mach_port_t task, 1.212 + exception_type_t exception, 1.213 + exception_data_t code, 1.214 + mach_msg_type_number_t code_count) { 1.215 + if (task != mach_task_self()) { 1.216 + return KERN_FAILURE; 1.217 + } 1.218 + return ForwardException(task, failed_thread, exception, code, code_count); 1.219 +} 1.220 +#endif 1.221 + 1.222 +ExceptionHandler::ExceptionHandler(const string &dump_path, 1.223 + FilterCallback filter, 1.224 + MinidumpCallback callback, 1.225 + void* callback_context, 1.226 + bool install_handler, 1.227 + const char* port_name) 1.228 + : dump_path_(), 1.229 + filter_(filter), 1.230 + callback_(callback), 1.231 + callback_context_(callback_context), 1.232 + directCallback_(NULL), 1.233 + handler_thread_(NULL), 1.234 + handler_port_(MACH_PORT_NULL), 1.235 + previous_(NULL), 1.236 + installed_exception_handler_(false), 1.237 + is_in_teardown_(false), 1.238 + last_minidump_write_result_(false), 1.239 + use_minidump_write_mutex_(false) { 1.240 + // This will update to the ID and C-string pointers 1.241 + set_dump_path(dump_path); 1.242 + MinidumpGenerator::GatherSystemInformation(); 1.243 +#if !TARGET_OS_IPHONE 1.244 + if (port_name) 1.245 + crash_generation_client_.reset(new CrashGenerationClient(port_name)); 1.246 +#endif 1.247 + Setup(install_handler); 1.248 +} 1.249 + 1.250 +// special constructor if we want to bypass minidump writing and 1.251 +// simply get a callback with the exception information 1.252 +ExceptionHandler::ExceptionHandler(DirectCallback callback, 1.253 + void* callback_context, 1.254 + bool install_handler) 1.255 + : dump_path_(), 1.256 + filter_(NULL), 1.257 + callback_(NULL), 1.258 + callback_context_(callback_context), 1.259 + directCallback_(callback), 1.260 + handler_thread_(NULL), 1.261 + handler_port_(MACH_PORT_NULL), 1.262 + previous_(NULL), 1.263 + installed_exception_handler_(false), 1.264 + is_in_teardown_(false), 1.265 + last_minidump_write_result_(false), 1.266 + use_minidump_write_mutex_(false) { 1.267 + MinidumpGenerator::GatherSystemInformation(); 1.268 + Setup(install_handler); 1.269 +} 1.270 + 1.271 +ExceptionHandler::~ExceptionHandler() { 1.272 + Teardown(); 1.273 +} 1.274 + 1.275 +bool ExceptionHandler::WriteMinidump(bool write_exception_stream) { 1.276 + // If we're currently writing, just return 1.277 + if (use_minidump_write_mutex_) 1.278 + return false; 1.279 + 1.280 + use_minidump_write_mutex_ = true; 1.281 + last_minidump_write_result_ = false; 1.282 + 1.283 + // Lock the mutex. Since we just created it, this will return immediately. 1.284 + if (pthread_mutex_lock(&minidump_write_mutex_) == 0) { 1.285 + // Send an empty message to the handle port so that a minidump will 1.286 + // be written 1.287 + bool result = SendMessageToHandlerThread(write_exception_stream ? 1.288 + kWriteDumpWithExceptionMessage : 1.289 + kWriteDumpMessage); 1.290 + if (!result) { 1.291 + pthread_mutex_unlock(&minidump_write_mutex_); 1.292 + return false; 1.293 + } 1.294 + 1.295 + // Wait for the minidump writer to complete its writing. It will unlock 1.296 + // the mutex when completed 1.297 + pthread_mutex_lock(&minidump_write_mutex_); 1.298 + } 1.299 + 1.300 + use_minidump_write_mutex_ = false; 1.301 + UpdateNextID(); 1.302 + return last_minidump_write_result_; 1.303 +} 1.304 + 1.305 +// static 1.306 +bool ExceptionHandler::WriteMinidump(const string &dump_path, 1.307 + bool write_exception_stream, 1.308 + MinidumpCallback callback, 1.309 + void* callback_context) { 1.310 + ExceptionHandler handler(dump_path, NULL, callback, callback_context, false, 1.311 + NULL); 1.312 + return handler.WriteMinidump(write_exception_stream); 1.313 +} 1.314 + 1.315 +// static 1.316 +bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child, 1.317 + mach_port_t child_blamed_thread, 1.318 + const string &dump_path, 1.319 + MinidumpCallback callback, 1.320 + void* callback_context) { 1.321 + ScopedTaskSuspend suspend(child); 1.322 + 1.323 + MinidumpGenerator generator(child, MACH_PORT_NULL); 1.324 + string dump_id; 1.325 + string dump_filename = generator.UniqueNameInDirectory(dump_path, &dump_id); 1.326 + 1.327 + generator.SetExceptionInformation(EXC_BREAKPOINT, 1.328 +#if defined(__i386__) || defined(__x86_64__) 1.329 + EXC_I386_BPT, 1.330 +#elif defined(__ppc__) || defined(__ppc64__) 1.331 + EXC_PPC_BREAKPOINT, 1.332 +#elif defined(__arm__) 1.333 + EXC_ARM_BREAKPOINT, 1.334 +#else 1.335 +#error architecture not supported 1.336 +#endif 1.337 + 0, 1.338 + child_blamed_thread); 1.339 + bool result = generator.Write(dump_filename.c_str()); 1.340 + 1.341 + if (callback) { 1.342 + return callback(dump_path.c_str(), dump_id.c_str(), 1.343 + callback_context, result); 1.344 + } 1.345 + return result; 1.346 +} 1.347 + 1.348 +bool ExceptionHandler::WriteMinidumpWithException(int exception_type, 1.349 + int exception_code, 1.350 + int exception_subcode, 1.351 + ucontext_t* task_context, 1.352 + mach_port_t thread_name, 1.353 + bool exit_after_write, 1.354 + bool report_current_thread) { 1.355 + bool result = false; 1.356 + 1.357 + if (directCallback_) { 1.358 + if (directCallback_(callback_context_, 1.359 + exception_type, 1.360 + exception_code, 1.361 + exception_subcode, 1.362 + thread_name) ) { 1.363 + if (exit_after_write) 1.364 + _exit(exception_type); 1.365 + } 1.366 +#if !TARGET_OS_IPHONE 1.367 + } else if (IsOutOfProcess()) { 1.368 + if (exception_type && exception_code) { 1.369 + // If this is a real exception, give the filter (if any) a chance to 1.370 + // decide if this should be sent. 1.371 + if (filter_ && !filter_(callback_context_)) 1.372 + return false; 1.373 + result = crash_generation_client_->RequestDumpForException( 1.374 + exception_type, 1.375 + exception_code, 1.376 + exception_subcode, 1.377 + thread_name); 1.378 + if (result && exit_after_write) { 1.379 + _exit(exception_type); 1.380 + } 1.381 + } 1.382 +#endif 1.383 + } else { 1.384 + string minidump_id; 1.385 + 1.386 + // Putting the MinidumpGenerator in its own context will ensure that the 1.387 + // destructor is executed, closing the newly created minidump file. 1.388 + if (!dump_path_.empty()) { 1.389 + MinidumpGenerator md(mach_task_self(), 1.390 + report_current_thread ? MACH_PORT_NULL : 1.391 + mach_thread_self()); 1.392 + md.SetTaskContext(task_context); 1.393 + if (exception_type && exception_code) { 1.394 + // If this is a real exception, give the filter (if any) a chance to 1.395 + // decide if this should be sent. 1.396 + if (filter_ && !filter_(callback_context_)) 1.397 + return false; 1.398 + 1.399 + md.SetExceptionInformation(exception_type, exception_code, 1.400 + exception_subcode, thread_name); 1.401 + } 1.402 + 1.403 + result = md.Write(next_minidump_path_c_); 1.404 + } 1.405 + 1.406 + // Call user specified callback (if any) 1.407 + if (callback_) { 1.408 + // If the user callback returned true and we're handling an exception 1.409 + // (rather than just writing out the file), then we should exit without 1.410 + // forwarding the exception to the next handler. 1.411 + if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_, 1.412 + result)) { 1.413 + if (exit_after_write) 1.414 + _exit(exception_type); 1.415 + } 1.416 + } 1.417 + } 1.418 + 1.419 + return result; 1.420 +} 1.421 + 1.422 +kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread, 1.423 + exception_type_t exception, 1.424 + exception_data_t code, 1.425 + mach_msg_type_number_t code_count) { 1.426 + // At this time, we should have called Uninstall() on the exception handler 1.427 + // so that the current exception ports are the ones that we should be 1.428 + // forwarding to. 1.429 + ExceptionParameters current; 1.430 + 1.431 + current.count = EXC_TYPES_COUNT; 1.432 + mach_port_t current_task = mach_task_self(); 1.433 + task_get_exception_ports(current_task, 1.434 + s_exception_mask, 1.435 + current.masks, 1.436 + ¤t.count, 1.437 + current.ports, 1.438 + current.behaviors, 1.439 + current.flavors); 1.440 + 1.441 + // Find the first exception handler that matches the exception 1.442 + unsigned int found; 1.443 + for (found = 0; found < current.count; ++found) { 1.444 + if (current.masks[found] & (1 << exception)) { 1.445 + break; 1.446 + } 1.447 + } 1.448 + 1.449 + // Nothing to forward 1.450 + if (found == current.count) { 1.451 + fprintf(stderr, "** No previous ports for forwarding!! \n"); 1.452 + exit(KERN_FAILURE); 1.453 + } 1.454 + 1.455 + mach_port_t target_port = current.ports[found]; 1.456 + exception_behavior_t target_behavior = current.behaviors[found]; 1.457 + 1.458 + kern_return_t result; 1.459 + switch (target_behavior) { 1.460 + case EXCEPTION_DEFAULT: 1.461 + result = exception_raise(target_port, failed_thread, task, exception, 1.462 + code, code_count); 1.463 + break; 1.464 + 1.465 + default: 1.466 + fprintf(stderr, "** Unknown exception behavior: %d\n", target_behavior); 1.467 + result = KERN_FAILURE; 1.468 + break; 1.469 + } 1.470 + 1.471 + return result; 1.472 +} 1.473 + 1.474 +// static 1.475 +void* ExceptionHandler::WaitForMessage(void* exception_handler_class) { 1.476 + ExceptionHandler* self = 1.477 + reinterpret_cast<ExceptionHandler*>(exception_handler_class); 1.478 + ExceptionMessage receive; 1.479 + 1.480 + // Wait for the exception info 1.481 + while (1) { 1.482 + receive.header.msgh_local_port = self->handler_port_; 1.483 + receive.header.msgh_size = static_cast<mach_msg_size_t>(sizeof(receive)); 1.484 + kern_return_t result = mach_msg(&(receive.header), 1.485 + MACH_RCV_MSG | MACH_RCV_LARGE, 0, 1.486 + receive.header.msgh_size, 1.487 + self->handler_port_, 1.488 + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 1.489 + 1.490 + 1.491 + if (result == KERN_SUCCESS) { 1.492 + // Uninstall our handler so that we don't get in a loop if the process of 1.493 + // writing out a minidump causes an exception. However, if the exception 1.494 + // was caused by a fork'd process, don't uninstall things 1.495 + 1.496 + // If the actual exception code is zero, then we're calling this handler 1.497 + // in a way that indicates that we want to either exit this thread or 1.498 + // generate a minidump 1.499 + // 1.500 + // While reporting, all threads (except this one) must be suspended 1.501 + // to avoid misleading stacks. If appropriate they will be resumed 1.502 + // afterwards. 1.503 + if (!receive.exception) { 1.504 + // Don't touch self, since this message could have been sent 1.505 + // from its destructor. 1.506 + if (receive.header.msgh_id == kShutdownMessage) 1.507 + return NULL; 1.508 + 1.509 + self->SuspendThreads(); 1.510 + 1.511 +#if USE_PROTECTED_ALLOCATIONS 1.512 + if (gBreakpadAllocator) 1.513 + gBreakpadAllocator->Unprotect(); 1.514 +#endif 1.515 + 1.516 + mach_port_t thread = MACH_PORT_NULL; 1.517 + int exception_type = 0; 1.518 + int exception_code = 0; 1.519 + if (receive.header.msgh_id == kWriteDumpWithExceptionMessage) { 1.520 + thread = receive.thread.name; 1.521 + exception_type = EXC_BREAKPOINT; 1.522 +#if defined(__i386__) || defined(__x86_64__) 1.523 + exception_code = EXC_I386_BPT; 1.524 +#elif defined(__ppc__) || defined(__ppc64__) 1.525 + exception_code = EXC_PPC_BREAKPOINT; 1.526 +#elif defined(__arm__) 1.527 + exception_code = EXC_ARM_BREAKPOINT; 1.528 +#else 1.529 +#error architecture not supported 1.530 +#endif 1.531 + } 1.532 + 1.533 + // Write out the dump and save the result for later retrieval 1.534 + self->last_minidump_write_result_ = 1.535 + self->WriteMinidumpWithException(exception_type, exception_code, 1.536 + 0, NULL, thread, 1.537 + false, false); 1.538 + 1.539 +#if USE_PROTECTED_ALLOCATIONS 1.540 + if (gBreakpadAllocator) 1.541 + gBreakpadAllocator->Protect(); 1.542 +#endif 1.543 + 1.544 + self->ResumeThreads(); 1.545 + 1.546 + if (self->use_minidump_write_mutex_) 1.547 + pthread_mutex_unlock(&self->minidump_write_mutex_); 1.548 + } else { 1.549 + // When forking a child process with the exception handler installed, 1.550 + // if the child crashes, it will send the exception back to the parent 1.551 + // process. The check for task == self_task() ensures that only 1.552 + // exceptions that occur in the parent process are caught and 1.553 + // processed. If the exception was not caused by this task, we 1.554 + // still need to call into the exception server and have it return 1.555 + // KERN_FAILURE (see catch_exception_raise) in order for the kernel 1.556 + // to move onto the host exception handler for the child task 1.557 + if (receive.task.name == mach_task_self()) { 1.558 + self->SuspendThreads(); 1.559 + 1.560 +#if USE_PROTECTED_ALLOCATIONS 1.561 + if (gBreakpadAllocator) 1.562 + gBreakpadAllocator->Unprotect(); 1.563 +#endif 1.564 + 1.565 + int subcode = 0; 1.566 + if (receive.exception == EXC_BAD_ACCESS && receive.code_count > 1) 1.567 + subcode = receive.code[1]; 1.568 + 1.569 + // Generate the minidump with the exception data. 1.570 + self->WriteMinidumpWithException(receive.exception, receive.code[0], 1.571 + subcode, NULL, receive.thread.name, 1.572 + true, false); 1.573 + 1.574 +#if USE_PROTECTED_ALLOCATIONS 1.575 + // This may have become protected again within 1.576 + // WriteMinidumpWithException, but it needs to be unprotected for 1.577 + // UninstallHandler. 1.578 + if (gBreakpadAllocator) 1.579 + gBreakpadAllocator->Unprotect(); 1.580 +#endif 1.581 + 1.582 + self->UninstallHandler(true); 1.583 + 1.584 +#if USE_PROTECTED_ALLOCATIONS 1.585 + if (gBreakpadAllocator) 1.586 + gBreakpadAllocator->Protect(); 1.587 +#endif 1.588 + } 1.589 + // Pass along the exception to the server, which will setup the 1.590 + // message and call catch_exception_raise() and put the return 1.591 + // code into the reply. 1.592 + ExceptionReplyMessage reply; 1.593 + if (!breakpad_exc_server(&receive.header, &reply.header)) 1.594 + exit(1); 1.595 + 1.596 + // Send a reply and exit 1.597 + mach_msg(&(reply.header), MACH_SEND_MSG, 1.598 + reply.header.msgh_size, 0, MACH_PORT_NULL, 1.599 + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 1.600 + } 1.601 + } 1.602 + } 1.603 + 1.604 + return NULL; 1.605 +} 1.606 + 1.607 +// static 1.608 +void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) { 1.609 +#if USE_PROTECTED_ALLOCATIONS 1.610 + if (gBreakpadAllocator) 1.611 + gBreakpadAllocator->Unprotect(); 1.612 +#endif 1.613 + gProtectedData.handler->WriteMinidumpWithException( 1.614 + EXC_SOFTWARE, 1.615 + MD_EXCEPTION_CODE_MAC_ABORT, 1.616 + 0, 1.617 + static_cast<ucontext_t*>(uc), 1.618 + mach_thread_self(), 1.619 + true, 1.620 + true); 1.621 +#if USE_PROTECTED_ALLOCATIONS 1.622 + if (gBreakpadAllocator) 1.623 + gBreakpadAllocator->Protect(); 1.624 +#endif 1.625 +} 1.626 + 1.627 +bool ExceptionHandler::InstallHandler() { 1.628 + // If a handler is already installed, something is really wrong. 1.629 + if (gProtectedData.handler != NULL) { 1.630 + return false; 1.631 + } 1.632 + if (!IsOutOfProcess()) { 1.633 + struct sigaction sa; 1.634 + memset(&sa, 0, sizeof(sa)); 1.635 + sigemptyset(&sa.sa_mask); 1.636 + sigaddset(&sa.sa_mask, SIGABRT); 1.637 + sa.sa_sigaction = ExceptionHandler::SignalHandler; 1.638 + sa.sa_flags = SA_SIGINFO; 1.639 + 1.640 + scoped_ptr<struct sigaction> old(new struct sigaction); 1.641 + if (sigaction(SIGABRT, &sa, old.get()) == -1) { 1.642 + return false; 1.643 + } 1.644 + old_handler_.swap(old); 1.645 + gProtectedData.handler = this; 1.646 +#if USE_PROTECTED_ALLOCATIONS 1.647 + assert(((size_t)(gProtectedData.protected_buffer) & PAGE_MASK) == 0); 1.648 + mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ); 1.649 +#endif 1.650 + } 1.651 + 1.652 + try { 1.653 +#if USE_PROTECTED_ALLOCATIONS 1.654 + previous_ = new (gBreakpadAllocator->Allocate(sizeof(ExceptionParameters)) ) 1.655 + ExceptionParameters(); 1.656 +#else 1.657 + previous_ = new ExceptionParameters(); 1.658 +#endif 1.659 + } 1.660 + catch (std::bad_alloc) { 1.661 + return false; 1.662 + } 1.663 + 1.664 + // Save the current exception ports so that we can forward to them 1.665 + previous_->count = EXC_TYPES_COUNT; 1.666 + mach_port_t current_task = mach_task_self(); 1.667 + kern_return_t result = task_get_exception_ports(current_task, 1.668 + s_exception_mask, 1.669 + previous_->masks, 1.670 + &previous_->count, 1.671 + previous_->ports, 1.672 + previous_->behaviors, 1.673 + previous_->flavors); 1.674 + 1.675 + // Setup the exception ports on this task 1.676 + if (result == KERN_SUCCESS) 1.677 + result = task_set_exception_ports(current_task, s_exception_mask, 1.678 + handler_port_, EXCEPTION_DEFAULT, 1.679 + THREAD_STATE_NONE); 1.680 + 1.681 + installed_exception_handler_ = (result == KERN_SUCCESS); 1.682 + 1.683 + return installed_exception_handler_; 1.684 +} 1.685 + 1.686 +bool ExceptionHandler::UninstallHandler(bool in_exception) { 1.687 + kern_return_t result = KERN_SUCCESS; 1.688 + 1.689 + if (old_handler_.get()) { 1.690 + sigaction(SIGABRT, old_handler_.get(), NULL); 1.691 +#if USE_PROTECTED_ALLOCATIONS 1.692 + mprotect(gProtectedData.protected_buffer, PAGE_SIZE, 1.693 + PROT_READ | PROT_WRITE); 1.694 +#endif 1.695 + old_handler_.reset(); 1.696 + gProtectedData.handler = NULL; 1.697 + } 1.698 + 1.699 + if (installed_exception_handler_) { 1.700 + mach_port_t current_task = mach_task_self(); 1.701 + 1.702 + // Restore the previous ports 1.703 + for (unsigned int i = 0; i < previous_->count; ++i) { 1.704 + result = task_set_exception_ports(current_task, previous_->masks[i], 1.705 + previous_->ports[i], 1.706 + previous_->behaviors[i], 1.707 + previous_->flavors[i]); 1.708 + if (result != KERN_SUCCESS) 1.709 + return false; 1.710 + } 1.711 + 1.712 + // this delete should NOT happen if an exception just occurred! 1.713 + if (!in_exception) { 1.714 +#if USE_PROTECTED_ALLOCATIONS 1.715 + previous_->~ExceptionParameters(); 1.716 +#else 1.717 + delete previous_; 1.718 +#endif 1.719 + } 1.720 + 1.721 + previous_ = NULL; 1.722 + installed_exception_handler_ = false; 1.723 + } 1.724 + 1.725 + return result == KERN_SUCCESS; 1.726 +} 1.727 + 1.728 +bool ExceptionHandler::Setup(bool install_handler) { 1.729 + if (pthread_mutex_init(&minidump_write_mutex_, NULL)) 1.730 + return false; 1.731 + 1.732 + // Create a receive right 1.733 + mach_port_t current_task = mach_task_self(); 1.734 + kern_return_t result = mach_port_allocate(current_task, 1.735 + MACH_PORT_RIGHT_RECEIVE, 1.736 + &handler_port_); 1.737 + // Add send right 1.738 + if (result == KERN_SUCCESS) 1.739 + result = mach_port_insert_right(current_task, handler_port_, handler_port_, 1.740 + MACH_MSG_TYPE_MAKE_SEND); 1.741 + 1.742 + if (install_handler && result == KERN_SUCCESS) 1.743 + if (!InstallHandler()) 1.744 + return false; 1.745 + 1.746 + if (result == KERN_SUCCESS) { 1.747 + // Install the handler in its own thread, detached as we won't be joining. 1.748 + pthread_attr_t attr; 1.749 + pthread_attr_init(&attr); 1.750 + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 1.751 + int thread_create_result = pthread_create(&handler_thread_, &attr, 1.752 + &WaitForMessage, this); 1.753 + pthread_attr_destroy(&attr); 1.754 + result = thread_create_result ? KERN_FAILURE : KERN_SUCCESS; 1.755 + } 1.756 + 1.757 + return result == KERN_SUCCESS; 1.758 +} 1.759 + 1.760 +bool ExceptionHandler::Teardown() { 1.761 + kern_return_t result = KERN_SUCCESS; 1.762 + is_in_teardown_ = true; 1.763 + 1.764 + if (!UninstallHandler(false)) 1.765 + return false; 1.766 + 1.767 + // Send an empty message so that the handler_thread exits 1.768 + if (SendMessageToHandlerThread(kShutdownMessage)) { 1.769 + mach_port_t current_task = mach_task_self(); 1.770 + result = mach_port_deallocate(current_task, handler_port_); 1.771 + if (result != KERN_SUCCESS) 1.772 + return false; 1.773 + } else { 1.774 + return false; 1.775 + } 1.776 + 1.777 + handler_thread_ = NULL; 1.778 + handler_port_ = MACH_PORT_NULL; 1.779 + pthread_mutex_destroy(&minidump_write_mutex_); 1.780 + 1.781 + return result == KERN_SUCCESS; 1.782 +} 1.783 + 1.784 +bool ExceptionHandler::SendMessageToHandlerThread( 1.785 + HandlerThreadMessage message_id) { 1.786 + ExceptionMessage msg; 1.787 + memset(&msg, 0, sizeof(msg)); 1.788 + msg.header.msgh_id = message_id; 1.789 + if (message_id == kWriteDumpMessage || 1.790 + message_id == kWriteDumpWithExceptionMessage) { 1.791 + // Include this thread's port. 1.792 + msg.thread.name = mach_thread_self(); 1.793 + msg.thread.disposition = MACH_MSG_TYPE_PORT_SEND; 1.794 + msg.thread.type = MACH_MSG_PORT_DESCRIPTOR; 1.795 + } 1.796 + msg.header.msgh_size = sizeof(msg) - sizeof(msg.padding); 1.797 + msg.header.msgh_remote_port = handler_port_; 1.798 + msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 1.799 + MACH_MSG_TYPE_MAKE_SEND_ONCE); 1.800 + kern_return_t result = mach_msg(&(msg.header), 1.801 + MACH_SEND_MSG | MACH_SEND_TIMEOUT, 1.802 + msg.header.msgh_size, 0, 0, 1.803 + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 1.804 + 1.805 + return result == KERN_SUCCESS; 1.806 +} 1.807 + 1.808 +void ExceptionHandler::UpdateNextID() { 1.809 + next_minidump_path_ = 1.810 + (MinidumpGenerator::UniqueNameInDirectory(dump_path_, &next_minidump_id_)); 1.811 + 1.812 + next_minidump_path_c_ = next_minidump_path_.c_str(); 1.813 + next_minidump_id_c_ = next_minidump_id_.c_str(); 1.814 +} 1.815 + 1.816 +bool ExceptionHandler::SuspendThreads() { 1.817 + thread_act_port_array_t threads_for_task; 1.818 + mach_msg_type_number_t thread_count; 1.819 + 1.820 + if (task_threads(mach_task_self(), &threads_for_task, &thread_count)) 1.821 + return false; 1.822 + 1.823 + // suspend all of the threads except for this one 1.824 + for (unsigned int i = 0; i < thread_count; ++i) { 1.825 + if (threads_for_task[i] != mach_thread_self()) { 1.826 + if (thread_suspend(threads_for_task[i])) 1.827 + return false; 1.828 + } 1.829 + } 1.830 + 1.831 + return true; 1.832 +} 1.833 + 1.834 +bool ExceptionHandler::ResumeThreads() { 1.835 + thread_act_port_array_t threads_for_task; 1.836 + mach_msg_type_number_t thread_count; 1.837 + 1.838 + if (task_threads(mach_task_self(), &threads_for_task, &thread_count)) 1.839 + return false; 1.840 + 1.841 + // resume all of the threads except for this one 1.842 + for (unsigned int i = 0; i < thread_count; ++i) { 1.843 + if (threads_for_task[i] != mach_thread_self()) { 1.844 + if (thread_resume(threads_for_task[i])) 1.845 + return false; 1.846 + } 1.847 + } 1.848 + 1.849 + return true; 1.850 +} 1.851 + 1.852 +} // namespace google_breakpad