From b1f858f26b136044922eeae0d86488b17b54efff Mon Sep 17 00:00:00 2001 From: "qsr@chromium.org" Date: Fri, 9 Mar 2012 19:11:58 +0000 Subject: 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 --- src/client/mac/handler/exception_handler.cc | 66 ++++++++++++++++++++++++-- src/client/mac/handler/exception_handler.h | 10 +++- src/client/mac/tests/exception_handler_test.cc | 24 ++++++++-- 3 files changed, 90 insertions(+), 10 deletions(-) (limited to 'src/client/mac') 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 #include +#include #include #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 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 old_handler_; + #if !TARGET_OS_IPHONE // Client for out-of-process dump generation. scoped_ptr 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(context); -- cgit v1.2.1