mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2024-11-28 00:34:16 +01:00
Add SIGABRT handler for mac and iOS.
SIGABRT were not handled while in process. This change add a signal handler to handle this. Review URL: https://breakpad.appspot.com/360001 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@933 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
61b5dcb168
commit
b1f858f26b
@ -29,6 +29,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
#include "client/mac/handler/exception_handler.h"
|
||||
@ -53,9 +54,15 @@
|
||||
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
|
||||
@ -274,7 +281,8 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
|
||||
int exception_code,
|
||||
int exception_subcode,
|
||||
mach_port_t thread_name,
|
||||
bool exit_after_write) {
|
||||
bool exit_after_write,
|
||||
bool report_current_thread) {
|
||||
bool result = false;
|
||||
|
||||
if (directCallback_) {
|
||||
@ -306,7 +314,8 @@ bool ExceptionHandler::WriteMinidumpWithException(int exception_type,
|
||||
// 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;
|
||||
MinidumpGenerator md(mach_task_self(),
|
||||
report_current_thread ? 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.
|
||||
@ -496,7 +505,7 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
|
||||
self->last_minidump_write_result_ =
|
||||
self->WriteMinidumpWithException(exception_type, exception_code,
|
||||
0, thread,
|
||||
false);
|
||||
false, false);
|
||||
|
||||
#if USE_PROTECTED_ALLOCATIONS
|
||||
if(gBreakpadAllocator)
|
||||
@ -530,7 +539,8 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
|
||||
|
||||
// Generate the minidump with the exception data.
|
||||
self->WriteMinidumpWithException(receive.exception, receive.code[0],
|
||||
subcode, receive.thread.name, true);
|
||||
subcode, receive.thread.name, true,
|
||||
false);
|
||||
|
||||
#if USE_PROTECTED_ALLOCATIONS
|
||||
// This may have become protected again within
|
||||
@ -565,7 +575,43 @@ void *ExceptionHandler::WaitForMessage(void *exception_handler_class) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//static
|
||||
void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
|
||||
gProtectedData.handler->WriteMinidumpWithException(EXC_CRASH,
|
||||
0xDEADBEEF,
|
||||
0,
|
||||
mach_thread_self(),
|
||||
true,
|
||||
true);
|
||||
}
|
||||
|
||||
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)) )
|
||||
@ -604,6 +650,16 @@ bool ExceptionHandler::InstallHandler() {
|
||||
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();
|
||||
|
||||
|
@ -187,12 +187,16 @@ class ExceptionHandler {
|
||||
int exception_code,
|
||||
int exception_subcode,
|
||||
mach_port_t thread_name,
|
||||
bool exit_after_write);
|
||||
bool exit_after_write,
|
||||
bool report_current_thread);
|
||||
|
||||
// When installed, this static function will be call from a newly created
|
||||
// pthread with |this| as the argument
|
||||
static void *WaitForMessage(void *exception_handler_class);
|
||||
|
||||
// Signal handler for SIGABRT.
|
||||
static void SignalHandler(int sig, siginfo_t* info, void* uc);
|
||||
|
||||
// disallow copy ctor and operator=
|
||||
explicit ExceptionHandler(const ExceptionHandler &);
|
||||
void operator=(const ExceptionHandler &);
|
||||
@ -258,6 +262,10 @@ class ExceptionHandler {
|
||||
// True, if we're using the mutext to indicate when mindump writing occurs
|
||||
bool use_minidump_write_mutex_;
|
||||
|
||||
// Old signal handler for SIGABRT. Used to be able to restore it when
|
||||
// uninstalling.
|
||||
scoped_ptr<struct sigaction> old_handler_;
|
||||
|
||||
#if !TARGET_OS_IPHONE
|
||||
// Client for out-of-process dump generation.
|
||||
scoped_ptr<CrashGenerationClient> crash_generation_client_;
|
||||
|
@ -64,6 +64,7 @@ using testing::Test;
|
||||
|
||||
class ExceptionHandlerTest : public Test {
|
||||
public:
|
||||
void InProcessCrash(bool aborting);
|
||||
AutoTempDir tempDir;
|
||||
string lastDumpName;
|
||||
};
|
||||
@ -75,8 +76,13 @@ static void Crasher() {
|
||||
fprintf(stdout, "A = %d", *a);
|
||||
}
|
||||
|
||||
static void SoonToCrash() {
|
||||
Crasher();
|
||||
static void AbortCrasher() {
|
||||
fprintf(stdout, "Going to crash...\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
static void SoonToCrash(void(*crasher)()) {
|
||||
crasher();
|
||||
}
|
||||
|
||||
static bool MDCallback(const char *dump_dir, const char *file_name,
|
||||
@ -94,7 +100,7 @@ static bool MDCallback(const char *dump_dir, const char *file_name,
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerTest, InProcess) {
|
||||
void ExceptionHandlerTest::InProcessCrash(bool aborting) {
|
||||
// Give the child process a pipe to report back on.
|
||||
int fds[2];
|
||||
ASSERT_EQ(0, pipe(fds));
|
||||
@ -105,7 +111,7 @@ TEST_F(ExceptionHandlerTest, InProcess) {
|
||||
close(fds[0]);
|
||||
ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
|
||||
// crash
|
||||
SoonToCrash();
|
||||
SoonToCrash(aborting ? &AbortCrasher : &Crasher);
|
||||
// not reached
|
||||
exit(1);
|
||||
}
|
||||
@ -128,6 +134,16 @@ TEST_F(ExceptionHandlerTest, InProcess) {
|
||||
EXPECT_EQ(0, WEXITSTATUS(ret));
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerTest, InProcess) {
|
||||
InProcessCrash(false);
|
||||
}
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
TEST_F(ExceptionHandlerTest, InProcessAbort) {
|
||||
InProcessCrash(true);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool DumpNameMDCallback(const char *dump_dir, const char *file_name,
|
||||
void *context, bool success) {
|
||||
ExceptionHandlerTest *self = reinterpret_cast<ExceptionHandlerTest*>(context);
|
||||
|
Loading…
Reference in New Issue
Block a user