// 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 #include #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 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(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