aboutsummaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
authorqsr@chromium.org <qsr@chromium.org@4c0a9323-5329-0410-9bdc-e9ce6186880e>2012-03-09 19:11:58 +0000
committerqsr@chromium.org <qsr@chromium.org@4c0a9323-5329-0410-9bdc-e9ce6186880e>2012-03-09 19:11:58 +0000
commitb1f858f26b136044922eeae0d86488b17b54efff (patch)
treee805f24222008bd24199ca836915cc66587912f8 /src/client
parentAdd COPYING file per libcurl distribution requirements (diff)
downloadbreakpad-b1f858f26b136044922eeae0d86488b17b54efff.tar.xz
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
Diffstat (limited to 'src/client')
-rw-r--r--src/client/mac/handler/exception_handler.cc66
-rw-r--r--src/client/mac/handler/exception_handler.h10
-rw-r--r--src/client/mac/tests/exception_handler_test.cc24
3 files changed, 90 insertions, 10 deletions
diff --git a/src/client/mac/handler/exception_handler.cc b/src/client/mac/handler/exception_handler.cc
index 66b10b03..686b980d 100644
--- a/src/client/mac/handler/exception_handler.cc
+++ b/src/client/mac/handler/exception_handler.cc
@@ -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();
diff --git a/src/client/mac/handler/exception_handler.h b/src/client/mac/handler/exception_handler.h
index 4689e3af..ec091341 100644
--- a/src/client/mac/handler/exception_handler.h
+++ b/src/client/mac/handler/exception_handler.h
@@ -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_;
diff --git a/src/client/mac/tests/exception_handler_test.cc b/src/client/mac/tests/exception_handler_test.cc
index 02ff0f56..ee3ffa5b 100644
--- a/src/client/mac/tests/exception_handler_test.cc
+++ b/src/client/mac/tests/exception_handler_test.cc
@@ -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);