blob: cb26449d0f939583ae38cf69fb75c29578ec0dac [file] [log] [blame]
// 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 <signal.h>
#include <TargetConditionals.h>
#include "client/mac/handler/minidump_generator.h"
#include "client/ios/exception_handler_no_mach.h"
#ifndef USE_PROTECTED_ALLOCATIONS
#if TARGET_OS_TV
#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 "client/mac/handler/protected_memory_allocator.h"
extern ProtectedMemoryAllocator* gBreakpadAllocator;
#endif
namespace google_breakpad {
const int kExceptionSignals[] = {
// Core-generating signals.
SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGQUIT, SIGSEGV, SIGSYS, SIGTRAP, SIGEMT,
SIGXCPU, SIGXFSZ,
// Non-core-generating but terminating signals.
SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGPROF, SIGTERM, SIGUSR1, SIGUSR2,
SIGVTALRM, SIGXCPU, SIGXFSZ, SIGIO,
};
const int kNumHandledSignals =
sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]);
struct scoped_ptr<struct sigaction> old_handlers[kNumHandledSignals];
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;
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),
installed_exception_handler_(false),
is_in_teardown_(false) {
// This will update to the ID and C-string pointers
set_dump_path(dump_path);
MinidumpGenerator::GatherSystemInformation();
Setup();
}
// 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),
installed_exception_handler_(false),
is_in_teardown_(false) {
MinidumpGenerator::GatherSystemInformation();
Setup();
}
ExceptionHandler::~ExceptionHandler() {
Teardown();
}
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 !TARGET_OS_TV
exit_after_write = false;
#endif // !TARGET_OS_TV
if (directCallback_) {
if (directCallback_(callback_context_,
exception_type,
exception_code,
exception_subcode,
thread_name) ) {
if (exit_after_write)
_exit(exception_type);
}
} 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;
}
// 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::InstallHandlers() {
// If a handler is already installed, something is really wrong.
if (gProtectedData.handler != NULL)
return false;
for (int i = 0; i < kNumHandledSignals; ++i) {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, kExceptionSignals[i]);
sa.sa_sigaction = ExceptionHandler::SignalHandler;
sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
if (sigaction(kExceptionSignals[i], &sa, old_handlers[i].get()) == -1) {
return false;
}
}
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 // USE_PROTECTED_ALLOCATIONS
installed_exception_handler_ = true;
return true;
}
bool ExceptionHandler::UninstallHandlers() {
for (int i = 0; i < kNumHandledSignals; ++i) {
if (old_handlers[i].get()) {
sigaction(kExceptionSignals[i], old_handlers[i].get(), NULL);
old_handlers[i].reset();
}
}
#if USE_PROTECTED_ALLOCATIONS
mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ | PROT_WRITE);
#endif // USE_PROTECTED_ALLOCATIONS
gProtectedData.handler = NULL;
installed_exception_handler_ = false;
return true;
}
bool ExceptionHandler::Setup() {
if (!InstallHandlers())
return false;
return true;
}
bool ExceptionHandler::Teardown() {
is_in_teardown_ = true;
if (!UninstallHandlers())
return false;
return true;
}
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();
}
} // namespace google_breakpad