summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc')
-rw-r--r--toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc854
1 files changed, 0 insertions, 854 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc
deleted file mode 100644
index dd0e1678c..000000000
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc
+++ /dev/null
@@ -1,854 +0,0 @@
-// 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 <mach/exc.h>
-#include <mach/mig.h>
-#include <pthread.h>
-#include <signal.h>
-#include <TargetConditionals.h>
-
-#include <map>
-
-#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 __EXCEPTIONS
-// This file uses C++ try/catch (but shouldn't). Duplicate the macros from
-// <c++/4.2.1/exception_defines.h> allowing this file to work properly with
-// exceptions disabled even when other C++ libraries are used. #undef the try
-// and catch macros first in case libstdc++ is in use and has already provided
-// its own definitions.
-#undef try
-#define try if (true)
-#undef catch
-#define catch(X) if (false)
-#endif // __EXCEPTIONS
-
-#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
-#if defined PAGE_MAX_SIZE
- char protected_buffer[PAGE_MAX_SIZE] __attribute__((aligned(PAGE_MAX_SIZE)));
-#else
- char protected_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
-#endif // defined PAGE_MAX_SIZE
-#endif // USE_PROTECTED_ALLOCATIONS
- 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
- bool result = SendMessageToHandlerThread(write_exception_stream ?
- kWriteDumpWithExceptionMessage :
- kWriteDumpMessage);
- if (!result) {
- pthread_mutex_unlock(&minidump_write_mutex_);
- return false;
- }
-
- // 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__) || defined(__aarch64__)
- 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,
- breakpad_ucontext_t* task_context,
- 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;
- result = crash_generation_client_->RequestDumpForException(
- exception_type,
- exception_code,
- exception_subcode,
- thread_name);
- if (result && exit_after_write) {
- _exit(exception_type);
- }
- }
-#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());
- md.SetTaskContext(task_context);
- 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;
- // TODO: Handle the case where |target_behavior| has MACH_EXCEPTION_CODES
- // set. https://code.google.com/p/google-breakpad/issues/detail?id=551
- 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__) || defined(__aarch64__)
- 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, NULL, 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, NULL, 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,
- static_cast<breakpad_ucontext_t*>(uc),
- 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;
- }
-
- 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
-
- 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;
-}
-
-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