summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '3rdParty/Breakpad/src/client/mac/handler/exception_handler.cc')
-rw-r--r--3rdParty/Breakpad/src/client/mac/handler/exception_handler.cc830
1 files changed, 830 insertions, 0 deletions
diff --git a/3rdParty/Breakpad/src/client/mac/handler/exception_handler.cc b/3rdParty/Breakpad/src/client/mac/handler/exception_handler.cc
new file mode 100644
index 0000000..4043019
--- /dev/null
+++ b/3rdParty/Breakpad/src/client/mac/handler/exception_handler.cc
@@ -0,0 +1,830 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <map>
+#include <mach/exc.h>
+#include <mach/mig.h>
+#include <pthread.h>
+#include <signal.h>
+#include <TargetConditionals.h>
+
+#include "client/mac/handler/exception_handler.h"
+#include "client/mac/handler/minidump_generator.h"
+#include "common/mac/macho_utilities.h"
+#include "common/mac/scoped_task_suspend-inl.h"
+#include "google_breakpad/common/minidump_exception_mac.h"
+
+#ifndef USE_PROTECTED_ALLOCATIONS
+#if TARGET_OS_IPHONE
+#define USE_PROTECTED_ALLOCATIONS 1
+#else
+#define USE_PROTECTED_ALLOCATIONS 0
+#endif
+#endif
+
+// If USE_PROTECTED_ALLOCATIONS is activated then the
+// gBreakpadAllocator needs to be setup in other code
+// ahead of time. Please see ProtectedMemoryAllocator.h
+// for more details.
+#if USE_PROTECTED_ALLOCATIONS
+ #include "protected_memory_allocator.h"
+ extern ProtectedMemoryAllocator *gBreakpadAllocator;
+#endif
+
+namespace google_breakpad {
+
+static union {
+#if USE_PROTECTED_ALLOCATIONS
+ char protected_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
+#endif
+ google_breakpad::ExceptionHandler *handler;
+} gProtectedData;
+
+using std::map;
+
+// These structures and techniques are illustrated in
+// Mac OS X Internals, Amit Singh, ch 9.7
+struct ExceptionMessage {
+ mach_msg_header_t header;
+ mach_msg_body_t body;
+ mach_msg_port_descriptor_t thread;
+ mach_msg_port_descriptor_t task;
+ NDR_record_t ndr;
+ exception_type_t exception;
+ mach_msg_type_number_t code_count;
+ integer_t code[EXCEPTION_CODE_MAX];
+ char padding[512];
+};
+
+struct ExceptionParameters {
+ ExceptionParameters() : count(0) {}
+ mach_msg_type_number_t count;
+ exception_mask_t masks[EXC_TYPES_COUNT];
+ mach_port_t ports[EXC_TYPES_COUNT];
+ exception_behavior_t behaviors[EXC_TYPES_COUNT];
+ thread_state_flavor_t flavors[EXC_TYPES_COUNT];
+};
+
+struct ExceptionReplyMessage {
+ mach_msg_header_t header;
+ NDR_record_t ndr;
+ kern_return_t return_code;
+};
+
+// Only catch these three exceptions. The other ones are nebulously defined
+// and may result in treating a non-fatal exception as fatal.
+exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS |
+EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT;
+
+#if !TARGET_OS_IPHONE
+extern "C"
+{
+ // Forward declarations for functions that need "C" style compilation
+ boolean_t exc_server(mach_msg_header_t *request,
+ mach_msg_header_t *reply);
+
+ // This symbol must be visible to dlsym() - see
+ // http://code.google.com/p/google-breakpad/issues/detail?id=345 for details.
+ kern_return_t catch_exception_raise(mach_port_t target_port,
+ mach_port_t failed_thread,
+ mach_port_t task,
+ exception_type_t exception,
+ exception_data_t code,
+ mach_msg_type_number_t code_count)
+ __attribute__((visibility("default")));
+}
+#endif
+
+kern_return_t ForwardException(mach_port_t task,
+ mach_port_t failed_thread,
+ exception_type_t exception,
+ exception_data_t code,
+ mach_msg_type_number_t code_count);
+
+#if TARGET_OS_IPHONE
+// Implementation is based on the implementation generated by mig.
+boolean_t breakpad_exc_server(mach_msg_header_t *InHeadP,
+ mach_msg_header_t *OutHeadP) {
+ OutHeadP->msgh_bits =
+ MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(InHeadP->msgh_bits), 0);
+ OutHeadP->msgh_remote_port = InHeadP->msgh_remote_port;
+ /* Minimal size: routine() will update it if different */
+ OutHeadP->msgh_size = (mach_msg_size_t)sizeof(mig_reply_error_t);
+ OutHeadP->msgh_local_port = MACH_PORT_NULL;
+ OutHeadP->msgh_id = InHeadP->msgh_id + 100;
+
+ if (InHeadP->msgh_id != 2401) {
+ ((mig_reply_error_t *)OutHeadP)->NDR = NDR_record;
+ ((mig_reply_error_t *)OutHeadP)->RetCode = MIG_BAD_ID;
+ return FALSE;
+ }
+
+#ifdef __MigPackStructs
+#pragma pack(4)
+#endif
+ typedef struct {
+ mach_msg_header_t Head;
+ /* start of the kernel processed data */
+ mach_msg_body_t msgh_body;
+ mach_msg_port_descriptor_t thread;
+ mach_msg_port_descriptor_t task;
+ /* end of the kernel processed data */
+ NDR_record_t NDR;
+ exception_type_t exception;
+ mach_msg_type_number_t codeCnt;
+ integer_t code[2];
+ mach_msg_trailer_t trailer;
+ } Request;
+
+ typedef struct {
+ mach_msg_header_t Head;
+ NDR_record_t NDR;
+ kern_return_t RetCode;
+ } Reply;
+#ifdef __MigPackStructs
+#pragma pack()
+#endif
+
+ Request *In0P = (Request *)InHeadP;
+ Reply *OutP = (Reply *)OutHeadP;
+
+ if (In0P->task.name != mach_task_self()) {
+ return FALSE;
+ }
+ OutP->RetCode = ForwardException(In0P->task.name,
+ In0P->thread.name,
+ In0P->exception,
+ In0P->code,
+ In0P->codeCnt);
+ OutP->NDR = NDR_record;
+ return TRUE;
+}
+#else
+boolean_t breakpad_exc_server(mach_msg_header_t *request,
+ mach_msg_header_t *reply) {
+ return exc_server(request, reply);
+}
+
+// Callback from exc_server()
+kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread,
+ mach_port_t task,
+ exception_type_t exception,
+ exception_data_t code,
+ mach_msg_type_number_t code_count) {
+ if (task != mach_task_self()) {
+ return KERN_FAILURE;
+ }
+ return ForwardException(task, failed_thread, exception, code, code_count);
+}
+#endif
+
+ExceptionHandler::ExceptionHandler(const string &dump_path,
+ FilterCallback filter,
+ MinidumpCallback callback,
+ void *callback_context,
+ bool install_handler,
+ const char *port_name)
+ : dump_path_(),
+ filter_(filter),
+ callback_(callback),
+ callback_context_(callback_context),
+ directCallback_(NULL),
+ handler_thread_(NULL),
+ handler_port_(MACH_PORT_NULL),
+ previous_(NULL),
+ installed_exception_handler_(false),
+ is_in_teardown_(false),
+ last_minidump_write_result_(false),
+ use_minidump_write_mutex_(false) {
+ // This will update to the ID and C-string pointers
+ set_dump_path(dump_path);
+ MinidumpGenerator::GatherSystemInformation();
+#if !TARGET_OS_IPHONE
+ if (port_name)
+ crash_generation_client_.reset(new CrashGenerationClient(port_name));
+#endif
+ Setup(install_handler);
+}
+
+// special constructor if we want to bypass minidump writing and
+// simply get a callback with the exception information
+ExceptionHandler::ExceptionHandler(DirectCallback callback,
+ void *callback_context,
+ bool install_handler)
+ : dump_path_(),
+ filter_(NULL),
+ callback_(NULL),
+ callback_context_(callback_context),
+ directCallback_(callback),
+ handler_thread_(NULL),
+ handler_port_(MACH_PORT_NULL),
+ previous_(NULL),
+ installed_exception_handler_(false),
+ is_in_teardown_(false),
+ last_minidump_write_result_(false),
+ use_minidump_write_mutex_(false) {
+ MinidumpGenerator::GatherSystemInformation();
+ Setup(install_handler);
+}
+
+ExceptionHandler::~ExceptionHandler() {
+ Teardown();
+}
+
+bool ExceptionHandler::WriteMinidump(bool write_exception_stream) {
+ // If we're currently writing, just return
+ if (use_minidump_write_mutex_)
+ return false;
+
+ use_minidump_write_mutex_ = true;
+ last_minidump_write_result_ = false;
+
+ // Lock the mutex. Since we just created it, this will return immediately.
+ if (pthread_mutex_lock(&minidump_write_mutex_) == 0) {
+ // Send an empty message to the handle port so that a minidump will
+ // be written
+ SendMessageToHandlerThread(write_exception_stream ?
+ kWriteDumpWithExceptionMessage :
+ kWriteDumpMessage);
+
+ // Wait for the minidump writer to complete its writing. It will unlock
+ // the mutex when completed
+ pthread_mutex_lock(&minidump_write_mutex_);
+ }
+
+ use_minidump_write_mutex_ = false;
+ UpdateNextID();
+ return last_minidump_write_result_;
+}
+
+// static
+bool ExceptionHandler::WriteMinidump(const string &dump_path,
+ bool write_exception_stream,
+ MinidumpCallback callback,
+ void *callback_context) {
+ ExceptionHandler handler(dump_path, NULL, callback, callback_context, false,
+ NULL);
+ return handler.WriteMinidump(write_exception_stream);
+}
+
+// static
+bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child,
+ mach_port_t child_blamed_thread,
+ const string &dump_path,
+ MinidumpCallback callback,
+ void *callback_context) {
+ ScopedTaskSuspend suspend(child);
+
+ MinidumpGenerator generator(child, MACH_PORT_NULL);
+ string dump_id;
+ string dump_filename = generator.UniqueNameInDirectory(dump_path, &dump_id);
+
+ generator.SetExceptionInformation(EXC_BREAKPOINT,
+#if defined (__i386__) || defined(__x86_64__)
+ EXC_I386_BPT,
+#elif defined (__ppc__) || defined (__ppc64__)
+ EXC_PPC_BREAKPOINT,
+#elif defined (__arm__)
+ EXC_ARM_BREAKPOINT,
+#else
+#error architecture not supported
+#endif
+ 0,
+ child_blamed_thread);
+ bool result = generator.Write(dump_filename.c_str());
+
+ if (callback) {
+ return callback(dump_path.c_str(), dump_id.c_str(),
+ callback_context, result);
+ }
+ return result;
+}
+
+bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
+ int exception_code,
+ int exception_subcode,
+ mach_port_t thread_name,
+ bool exit_after_write,
+ bool report_current_thread) {
+ bool result = false;
+
+ if (directCallback_) {
+ if (directCallback_(callback_context_,
+ exception_type,
+ exception_code,
+ exception_subcode,
+ thread_name) ) {
+ if (exit_after_write)
+ _exit(exception_type);
+ }
+#if !TARGET_OS_IPHONE
+ } else if (IsOutOfProcess()) {
+ if (exception_type && exception_code) {
+ // If this is a real exception, give the filter (if any) a chance to
+ // decide if this should be sent.
+ if (filter_ && !filter_(callback_context_))
+ return false;
+ return crash_generation_client_->RequestDumpForException(
+ exception_type,
+ exception_code,
+ exception_subcode,
+ thread_name);
+ }
+#endif
+ } else {
+ string minidump_id;
+
+ // Putting the MinidumpGenerator in its own context will ensure that the
+ // destructor is executed, closing the newly created minidump file.
+ if (!dump_path_.empty()) {
+ MinidumpGenerator md(mach_task_self(),
+ report_current_thread ? MACH_PORT_NULL :
+ mach_thread_self());
+ if (exception_type && exception_code) {
+ // If this is a real exception, give the filter (if any) a chance to
+ // decide if this should be sent.
+ if (filter_ && !filter_(callback_context_))
+ return false;
+
+ md.SetExceptionInformation(exception_type, exception_code,
+ exception_subcode, thread_name);
+ }
+
+ result = md.Write(next_minidump_path_c_);
+ }
+
+ // Call user specified callback (if any)
+ if (callback_) {
+ // If the user callback returned true and we're handling an exception
+ // (rather than just writing out the file), then we should exit without
+ // forwarding the exception to the next handler.
+ if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_,
+ result)) {
+ if (exit_after_write)
+ _exit(exception_type);
+ }
+ }
+ }
+
+ return result;
+}
+
+kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
+ exception_type_t exception,
+ exception_data_t code,
+ mach_msg_type_number_t code_count) {
+ // At this time, we should have called Uninstall() on the exception handler
+ // so that the current exception ports are the ones that we should be
+ // forwarding to.
+ ExceptionParameters current;
+
+ current.count = EXC_TYPES_COUNT;
+ mach_port_t current_task = mach_task_self();
+ task_get_exception_ports(current_task,
+ s_exception_mask,
+ current.masks,
+ &current.count,
+ current.ports,
+ current.behaviors,
+ current.flavors);
+
+ // Find the first exception handler that matches the exception
+ unsigned int found;
+ for (found = 0; found < current.count; ++found) {
+ if (current.masks[found] & (1 << exception)) {
+ break;
+ }
+ }
+
+ // Nothing to forward
+ if (found == current.count) {
+ fprintf(stderr, "** No previous ports for forwarding!! \n");
+ exit(KERN_FAILURE);
+ }
+
+ mach_port_t target_port = current.ports[found];
+ exception_behavior_t target_behavior = current.behaviors[found];
+
+ kern_return_t result;
+ switch (target_behavior) {
+ case EXCEPTION_DEFAULT:
+ result = exception_raise(target_port, failed_thread, task, exception,
+ code, code_count);
+ break;
+
+ default:
+ fprintf(stderr, "** Unknown exception behavior: %d\n", target_behavior);
+ result = KERN_FAILURE;
+ break;
+ }
+
+ return result;
+}
+
+// static
+void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
+ ExceptionHandler *self =
+ reinterpret_cast<ExceptionHandler *>(exception_handler_class);
+ ExceptionMessage receive;
+
+ // Wait for the exception info
+ while (1) {
+ receive.header.msgh_local_port = self->handler_port_;
+ receive.header.msgh_size = static_cast<mach_msg_size_t>(sizeof(receive));
+ kern_return_t result = mach_msg(&(receive.header),
+ MACH_RCV_MSG | MACH_RCV_LARGE, 0,
+ receive.header.msgh_size,
+ self->handler_port_,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+
+
+ if (result == KERN_SUCCESS) {
+ // Uninstall our handler so that we don't get in a loop if the process of
+ // writing out a minidump causes an exception. However, if the exception
+ // was caused by a fork'd process, don't uninstall things
+
+ // If the actual exception code is zero, then we're calling this handler
+ // in a way that indicates that we want to either exit this thread or
+ // generate a minidump
+ //
+ // While reporting, all threads (except this one) must be suspended
+ // to avoid misleading stacks. If appropriate they will be resumed
+ // afterwards.
+ if (!receive.exception) {
+ // Don't touch self, since this message could have been sent
+ // from its destructor.
+ if (receive.header.msgh_id == kShutdownMessage)
+ return NULL;
+
+ self->SuspendThreads();
+
+#if USE_PROTECTED_ALLOCATIONS
+ if (gBreakpadAllocator)
+ gBreakpadAllocator->Unprotect();
+#endif
+
+ mach_port_t thread = MACH_PORT_NULL;
+ int exception_type = 0;
+ int exception_code = 0;
+ if (receive.header.msgh_id == kWriteDumpWithExceptionMessage) {
+ thread = receive.thread.name;
+ exception_type = EXC_BREAKPOINT;
+#if defined (__i386__) || defined(__x86_64__)
+ exception_code = EXC_I386_BPT;
+#elif defined (__ppc__) || defined (__ppc64__)
+ exception_code = EXC_PPC_BREAKPOINT;
+#elif defined (__arm__)
+ exception_code = EXC_ARM_BREAKPOINT;
+#else
+#error architecture not supported
+#endif
+ }
+
+ // Write out the dump and save the result for later retrieval
+ self->last_minidump_write_result_ =
+ self->WriteMinidumpWithException(exception_type, exception_code,
+ 0, thread,
+ false, false);
+
+#if USE_PROTECTED_ALLOCATIONS
+ if (gBreakpadAllocator)
+ gBreakpadAllocator->Protect();
+#endif
+
+ self->ResumeThreads();
+
+ if (self->use_minidump_write_mutex_)
+ pthread_mutex_unlock(&self->minidump_write_mutex_);
+ } else {
+ // When forking a child process with the exception handler installed,
+ // if the child crashes, it will send the exception back to the parent
+ // process. The check for task == self_task() ensures that only
+ // exceptions that occur in the parent process are caught and
+ // processed. If the exception was not caused by this task, we
+ // still need to call into the exception server and have it return
+ // KERN_FAILURE (see catch_exception_raise) in order for the kernel
+ // to move onto the host exception handler for the child task
+ if (receive.task.name == mach_task_self()) {
+ self->SuspendThreads();
+
+#if USE_PROTECTED_ALLOCATIONS
+ if (gBreakpadAllocator)
+ gBreakpadAllocator->Unprotect();
+#endif
+
+ int subcode = 0;
+ if (receive.exception == EXC_BAD_ACCESS && receive.code_count > 1)
+ subcode = receive.code[1];
+
+ // Generate the minidump with the exception data.
+ self->WriteMinidumpWithException(receive.exception, receive.code[0],
+ subcode, receive.thread.name, true,
+ false);
+
+#if USE_PROTECTED_ALLOCATIONS
+ // This may have become protected again within
+ // WriteMinidumpWithException, but it needs to be unprotected for
+ // UninstallHandler.
+ if (gBreakpadAllocator)
+ gBreakpadAllocator->Unprotect();
+#endif
+
+ self->UninstallHandler(true);
+
+#if USE_PROTECTED_ALLOCATIONS
+ if (gBreakpadAllocator)
+ gBreakpadAllocator->Protect();
+#endif
+ }
+ // Pass along the exception to the server, which will setup the
+ // message and call catch_exception_raise() and put the return
+ // code into the reply.
+ ExceptionReplyMessage reply;
+ if (!breakpad_exc_server(&receive.header, &reply.header))
+ exit(1);
+
+ // Send a reply and exit
+ mach_msg(&(reply.header), MACH_SEND_MSG,
+ reply.header.msgh_size, 0, MACH_PORT_NULL,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+//static
+void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
+#if USE_PROTECTED_ALLOCATIONS
+ if (gBreakpadAllocator)
+ gBreakpadAllocator->Unprotect();
+#endif
+ gProtectedData.handler->WriteMinidumpWithException(
+ EXC_SOFTWARE,
+ MD_EXCEPTION_CODE_MAC_ABORT,
+ 0,
+ mach_thread_self(),
+ true,
+ true);
+#if USE_PROTECTED_ALLOCATIONS
+ if (gBreakpadAllocator)
+ gBreakpadAllocator->Protect();
+#endif
+}
+
+bool ExceptionHandler::InstallHandler() {
+ // If a handler is already installed, something is really wrong.
+ if (gProtectedData.handler != NULL) {
+ return false;
+ }
+#if TARGET_OS_IPHONE
+ if (!IsOutOfProcess()) {
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGABRT);
+ sa.sa_sigaction = ExceptionHandler::SignalHandler;
+ sa.sa_flags = SA_SIGINFO;
+
+ scoped_ptr<struct sigaction> old(new struct sigaction);
+ if (sigaction(SIGABRT, &sa, old.get()) == -1) {
+ return false;
+ }
+ old_handler_.swap(old);
+ gProtectedData.handler = this;
+#if USE_PROTECTED_ALLOCATIONS
+ assert(((size_t)(gProtectedData.protected_buffer) & PAGE_MASK) == 0);
+ mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ);
+#endif
+ }
+#endif
+
+ try {
+#if USE_PROTECTED_ALLOCATIONS
+ previous_ = new (gBreakpadAllocator->Allocate(sizeof(ExceptionParameters)) )
+ ExceptionParameters();
+#else
+ previous_ = new ExceptionParameters();
+#endif
+
+ }
+ catch (std::bad_alloc) {
+ return false;
+ }
+
+ // Save the current exception ports so that we can forward to them
+ previous_->count = EXC_TYPES_COUNT;
+ mach_port_t current_task = mach_task_self();
+ kern_return_t result = task_get_exception_ports(current_task,
+ s_exception_mask,
+ previous_->masks,
+ &previous_->count,
+ previous_->ports,
+ previous_->behaviors,
+ previous_->flavors);
+
+ // Setup the exception ports on this task
+ if (result == KERN_SUCCESS)
+ result = task_set_exception_ports(current_task, s_exception_mask,
+ handler_port_, EXCEPTION_DEFAULT,
+ THREAD_STATE_NONE);
+
+ installed_exception_handler_ = (result == KERN_SUCCESS);
+
+ return installed_exception_handler_;
+}
+
+bool ExceptionHandler::UninstallHandler(bool in_exception) {
+ kern_return_t result = KERN_SUCCESS;
+
+ if (old_handler_.get()) {
+ sigaction(SIGABRT, old_handler_.get(), NULL);
+#if USE_PROTECTED_ALLOCATIONS
+ mprotect(gProtectedData.protected_buffer, PAGE_SIZE,
+ PROT_READ | PROT_WRITE);
+#endif
+ old_handler_.reset();
+ gProtectedData.handler = NULL;
+ }
+
+ if (installed_exception_handler_) {
+ mach_port_t current_task = mach_task_self();
+
+ // Restore the previous ports
+ for (unsigned int i = 0; i < previous_->count; ++i) {
+ result = task_set_exception_ports(current_task, previous_->masks[i],
+ previous_->ports[i],
+ previous_->behaviors[i],
+ previous_->flavors[i]);
+ if (result != KERN_SUCCESS)
+ return false;
+ }
+
+ // this delete should NOT happen if an exception just occurred!
+ if (!in_exception) {
+#if USE_PROTECTED_ALLOCATIONS
+ previous_->~ExceptionParameters();
+#else
+ delete previous_;
+#endif
+ }
+
+ previous_ = NULL;
+ installed_exception_handler_ = false;
+ }
+
+ return result == KERN_SUCCESS;
+}
+
+bool ExceptionHandler::Setup(bool install_handler) {
+ if (pthread_mutex_init(&minidump_write_mutex_, NULL))
+ return false;
+
+ // Create a receive right
+ mach_port_t current_task = mach_task_self();
+ kern_return_t result = mach_port_allocate(current_task,
+ MACH_PORT_RIGHT_RECEIVE,
+ &handler_port_);
+ // Add send right
+ if (result == KERN_SUCCESS)
+ result = mach_port_insert_right(current_task, handler_port_, handler_port_,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ if (install_handler && result == KERN_SUCCESS)
+ if (!InstallHandler())
+ return false;
+
+ if (result == KERN_SUCCESS) {
+ // Install the handler in its own thread, detached as we won't be joining.
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ int thread_create_result = pthread_create(&handler_thread_, &attr,
+ &WaitForMessage, this);
+ pthread_attr_destroy(&attr);
+ result = thread_create_result ? KERN_FAILURE : KERN_SUCCESS;
+ }
+
+ return result == KERN_SUCCESS ? true : false;
+}
+
+bool ExceptionHandler::Teardown() {
+ kern_return_t result = KERN_SUCCESS;
+ is_in_teardown_ = true;
+
+ if (!UninstallHandler(false))
+ return false;
+
+ // Send an empty message so that the handler_thread exits
+ if (SendMessageToHandlerThread(kShutdownMessage)) {
+ mach_port_t current_task = mach_task_self();
+ result = mach_port_deallocate(current_task, handler_port_);
+ if (result != KERN_SUCCESS)
+ return false;
+ } else {
+ return false;
+ }
+
+ handler_thread_ = NULL;
+ handler_port_ = MACH_PORT_NULL;
+ pthread_mutex_destroy(&minidump_write_mutex_);
+
+ return result == KERN_SUCCESS;
+}
+
+bool ExceptionHandler::SendMessageToHandlerThread(
+ HandlerThreadMessage message_id) {
+ ExceptionMessage msg;
+ memset(&msg, 0, sizeof(msg));
+ msg.header.msgh_id = message_id;
+ if (message_id == kWriteDumpMessage ||
+ message_id == kWriteDumpWithExceptionMessage) {
+ // Include this thread's port.
+ msg.thread.name = mach_thread_self();
+ msg.thread.disposition = MACH_MSG_TYPE_PORT_SEND;
+ msg.thread.type = MACH_MSG_PORT_DESCRIPTOR;
+ }
+ msg.header.msgh_size = sizeof(msg) - sizeof(msg.padding);
+ msg.header.msgh_remote_port = handler_port_;
+ msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE);
+ kern_return_t result = mach_msg(&(msg.header),
+ MACH_SEND_MSG | MACH_SEND_TIMEOUT,
+ msg.header.msgh_size, 0, 0,
+ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+
+ return result == KERN_SUCCESS;
+}
+
+void ExceptionHandler::UpdateNextID() {
+ next_minidump_path_ =
+ (MinidumpGenerator::UniqueNameInDirectory(dump_path_, &next_minidump_id_));
+
+ next_minidump_path_c_ = next_minidump_path_.c_str();
+ next_minidump_id_c_ = next_minidump_id_.c_str();
+}
+
+bool ExceptionHandler::SuspendThreads() {
+ thread_act_port_array_t threads_for_task;
+ mach_msg_type_number_t thread_count;
+
+ if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
+ return false;
+
+ // suspend all of the threads except for this one
+ for (unsigned int i = 0; i < thread_count; ++i) {
+ if (threads_for_task[i] != mach_thread_self()) {
+ if (thread_suspend(threads_for_task[i]))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ExceptionHandler::ResumeThreads() {
+ thread_act_port_array_t threads_for_task;
+ mach_msg_type_number_t thread_count;
+
+ if (task_threads(mach_task_self(), &threads_for_task, &thread_count))
+ return false;
+
+ // resume all of the threads except for this one
+ for (unsigned int i = 0; i < thread_count; ++i) {
+ if (threads_for_task[i] != mach_thread_self()) {
+ if (thread_resume(threads_for_task[i]))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace google_breakpad