michael@0: // Copyright (c) 2010 Google Inc. michael@0: // All rights reserved. michael@0: // michael@0: // Redistribution and use in source and binary forms, with or without michael@0: // modification, are permitted provided that the following conditions are michael@0: // met: michael@0: // michael@0: // * Redistributions of source code must retain the above copyright michael@0: // notice, this list of conditions and the following disclaimer. michael@0: // * Redistributions in binary form must reproduce the above michael@0: // copyright notice, this list of conditions and the following disclaimer michael@0: // in the documentation and/or other materials provided with the michael@0: // distribution. michael@0: // * Neither the name of Google Inc. nor the names of its michael@0: // contributors may be used to endorse or promote products derived from michael@0: // this software without specific prior written permission. michael@0: // michael@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: michael@0: // The ExceptionHandler object installs signal handlers for a number of michael@0: // signals. We rely on the signal handler running on the thread which crashed michael@0: // in order to identify it. This is true of the synchronous signals (SEGV etc), michael@0: // but not true of ABRT. Thus, if you send ABRT to yourself in a program which michael@0: // uses ExceptionHandler, you need to use tgkill to direct it to the current michael@0: // thread. michael@0: // michael@0: // The signal flow looks like this: michael@0: // michael@0: // SignalHandler (uses a global stack of ExceptionHandler objects to find michael@0: // | one to handle the signal. If the first rejects it, try michael@0: // | the second etc...) michael@0: // V michael@0: // HandleSignal ----------------------------| (clones a new process which michael@0: // | | shares an address space with michael@0: // (wait for cloned | the crashed process. This michael@0: // process) | allows us to ptrace the crashed michael@0: // | | process) michael@0: // V V michael@0: // (set signal handler to ThreadEntry (static function to bounce michael@0: // SIG_DFL and rethrow, | back into the object) michael@0: // killing the crashed | michael@0: // process) V michael@0: // DoDump (writes minidump) michael@0: // | michael@0: // V michael@0: // sys_exit michael@0: // michael@0: michael@0: // This code is a little fragmented. Different functions of the ExceptionHandler michael@0: // class run in a number of different contexts. Some of them run in a normal michael@0: // context and are easy to code, others run in a compromised context and the michael@0: // restrictions at the top of minidump_writer.cc apply: no libc and use the michael@0: // alternative malloc. Each function should have comment above it detailing the michael@0: // context which it runs in. michael@0: michael@0: #include "client/linux/handler/exception_handler.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "common/linux/linux_libc_support.h" michael@0: #include "common/memory.h" michael@0: #include "client/linux/log/log.h" michael@0: #include "client/linux/minidump_writer/linux_dumper.h" michael@0: #include "client/linux/minidump_writer/minidump_writer.h" michael@0: #include "common/linux/eintr_wrapper.h" michael@0: #include "third_party/lss/linux_syscall_support.h" michael@0: michael@0: #include "linux/sched.h" michael@0: michael@0: #ifndef PR_SET_PTRACER michael@0: #define PR_SET_PTRACER 0x59616d61 michael@0: #endif michael@0: michael@0: // A wrapper for the tgkill syscall: send a signal to a specific thread. michael@0: static int tgkill(pid_t tgid, pid_t tid, int sig) { michael@0: return syscall(__NR_tgkill, tgid, tid, sig); michael@0: return 0; michael@0: } michael@0: michael@0: namespace google_breakpad { michael@0: michael@0: namespace { michael@0: // The list of signals which we consider to be crashes. The default action for michael@0: // all these signals must be Core (see man 7 signal) because we rethrow the michael@0: // signal after handling it and expect that it'll be fatal. michael@0: const int kExceptionSignals[] = { michael@0: SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS michael@0: }; michael@0: const int kNumHandledSignals = michael@0: sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]); michael@0: struct sigaction old_handlers[kNumHandledSignals]; michael@0: bool handlers_installed = false; michael@0: michael@0: // InstallAlternateStackLocked will store the newly installed stack in new_stack michael@0: // and (if it exists) the previously installed stack in old_stack. michael@0: stack_t old_stack; michael@0: stack_t new_stack; michael@0: bool stack_installed = false; michael@0: michael@0: // Create an alternative stack to run the signal handlers on. This is done since michael@0: // the signal might have been caused by a stack overflow. michael@0: // Runs before crashing: normal context. michael@0: void InstallAlternateStackLocked() { michael@0: if (stack_installed) michael@0: return; michael@0: michael@0: memset(&old_stack, 0, sizeof(old_stack)); michael@0: memset(&new_stack, 0, sizeof(new_stack)); michael@0: michael@0: // SIGSTKSZ may be too small to prevent the signal handlers from overrunning michael@0: // the alternative stack. Ensure that the size of the alternative stack is michael@0: // large enough. michael@0: static const unsigned kSigStackSize = std::max(8192, SIGSTKSZ); michael@0: michael@0: // Only set an alternative stack if there isn't already one, or if the current michael@0: // one is too small. michael@0: if (sys_sigaltstack(NULL, &old_stack) == -1 || !old_stack.ss_sp || michael@0: old_stack.ss_size < kSigStackSize) { michael@0: new_stack.ss_sp = malloc(kSigStackSize); michael@0: new_stack.ss_size = kSigStackSize; michael@0: michael@0: if (sys_sigaltstack(&new_stack, NULL) == -1) { michael@0: free(new_stack.ss_sp); michael@0: return; michael@0: } michael@0: stack_installed = true; michael@0: } michael@0: } michael@0: michael@0: // Runs before crashing: normal context. michael@0: void RestoreAlternateStackLocked() { michael@0: if (!stack_installed) michael@0: return; michael@0: michael@0: stack_t current_stack; michael@0: if (sys_sigaltstack(NULL, ¤t_stack) == -1) michael@0: return; michael@0: michael@0: // Only restore the old_stack if the current alternative stack is the one michael@0: // installed by the call to InstallAlternateStackLocked. michael@0: if (current_stack.ss_sp == new_stack.ss_sp) { michael@0: if (old_stack.ss_sp) { michael@0: if (sys_sigaltstack(&old_stack, NULL) == -1) michael@0: return; michael@0: } else { michael@0: stack_t disable_stack; michael@0: disable_stack.ss_flags = SS_DISABLE; michael@0: if (sys_sigaltstack(&disable_stack, NULL) == -1) michael@0: return; michael@0: } michael@0: } michael@0: michael@0: free(new_stack.ss_sp); michael@0: stack_installed = false; michael@0: } michael@0: michael@0: } // namespace michael@0: michael@0: // We can stack multiple exception handlers. In that case, this is the global michael@0: // which holds the stack. michael@0: std::vector* ExceptionHandler::handler_stack_ = NULL; michael@0: pthread_mutex_t ExceptionHandler::handler_stack_mutex_ = michael@0: PTHREAD_MUTEX_INITIALIZER; michael@0: michael@0: // Runs before crashing: normal context. michael@0: ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor, michael@0: FilterCallback filter, michael@0: MinidumpCallback callback, michael@0: void* callback_context, michael@0: bool install_handler, michael@0: const int server_fd) michael@0: : filter_(filter), michael@0: callback_(callback), michael@0: callback_context_(callback_context), michael@0: minidump_descriptor_(descriptor), michael@0: crash_handler_(NULL) { michael@0: if (server_fd >= 0) michael@0: crash_generation_client_.reset(CrashGenerationClient::TryCreate(server_fd)); michael@0: michael@0: if (!IsOutOfProcess() && !minidump_descriptor_.IsFD()) michael@0: minidump_descriptor_.UpdatePath(); michael@0: michael@0: pthread_mutex_lock(&handler_stack_mutex_); michael@0: if (!handler_stack_) michael@0: handler_stack_ = new std::vector; michael@0: if (install_handler) { michael@0: InstallAlternateStackLocked(); michael@0: InstallHandlersLocked(); michael@0: } michael@0: handler_stack_->push_back(this); michael@0: pthread_mutex_unlock(&handler_stack_mutex_); michael@0: } michael@0: michael@0: // Runs before crashing: normal context. michael@0: ExceptionHandler::~ExceptionHandler() { michael@0: pthread_mutex_lock(&handler_stack_mutex_); michael@0: std::vector::iterator handler = michael@0: std::find(handler_stack_->begin(), handler_stack_->end(), this); michael@0: handler_stack_->erase(handler); michael@0: if (handler_stack_->empty()) { michael@0: RestoreAlternateStackLocked(); michael@0: RestoreHandlersLocked(); michael@0: } michael@0: pthread_mutex_unlock(&handler_stack_mutex_); michael@0: } michael@0: michael@0: // Runs before crashing: normal context. michael@0: // static michael@0: bool ExceptionHandler::InstallHandlersLocked() { michael@0: if (handlers_installed) michael@0: return false; michael@0: michael@0: // Fail if unable to store all the old handlers. michael@0: for (int i = 0; i < kNumHandledSignals; ++i) { michael@0: if (sigaction(kExceptionSignals[i], NULL, &old_handlers[i]) == -1) michael@0: return false; michael@0: } michael@0: michael@0: struct sigaction sa; michael@0: memset(&sa, 0, sizeof(sa)); michael@0: sigemptyset(&sa.sa_mask); michael@0: michael@0: // Mask all exception signals when we're handling one of them. michael@0: for (int i = 0; i < kNumHandledSignals; ++i) michael@0: sigaddset(&sa.sa_mask, kExceptionSignals[i]); michael@0: michael@0: sa.sa_sigaction = SignalHandler; michael@0: sa.sa_flags = SA_ONSTACK | SA_SIGINFO; michael@0: michael@0: for (int i = 0; i < kNumHandledSignals; ++i) { michael@0: if (sigaction(kExceptionSignals[i], &sa, NULL) == -1) { michael@0: // At this point it is impractical to back out changes, and so failure to michael@0: // install a signal is intentionally ignored. michael@0: } michael@0: } michael@0: handlers_installed = true; michael@0: return true; michael@0: } michael@0: michael@0: // This function runs in a compromised context: see the top of the file. michael@0: // Runs on the crashing thread. michael@0: // static michael@0: void ExceptionHandler::RestoreHandlersLocked() { michael@0: if (!handlers_installed) michael@0: return; michael@0: michael@0: for (int i = 0; i < kNumHandledSignals; ++i) { michael@0: if (sigaction(kExceptionSignals[i], &old_handlers[i], NULL) == -1) { michael@0: signal(kExceptionSignals[i], SIG_DFL); michael@0: } michael@0: } michael@0: handlers_installed = false; michael@0: } michael@0: michael@0: // void ExceptionHandler::set_crash_handler(HandlerCallback callback) { michael@0: // crash_handler_ = callback; michael@0: // } michael@0: michael@0: // This function runs in a compromised context: see the top of the file. michael@0: // Runs on the crashing thread. michael@0: // static michael@0: void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) { michael@0: // All the exception signals are blocked at this point. michael@0: pthread_mutex_lock(&handler_stack_mutex_); michael@0: michael@0: // Sometimes, Breakpad runs inside a process where some other buggy code michael@0: // saves and restores signal handlers temporarily with 'signal' michael@0: // instead of 'sigaction'. This loses the SA_SIGINFO flag associated michael@0: // with this function. As a consequence, the values of 'info' and 'uc' michael@0: // become totally bogus, generally inducing a crash. michael@0: // michael@0: // The following code tries to detect this case. When it does, it michael@0: // resets the signal handlers with sigaction + SA_SIGINFO and returns. michael@0: // This forces the signal to be thrown again, but this time the kernel michael@0: // will call the function with the right arguments. michael@0: struct sigaction cur_handler; michael@0: if (sigaction(sig, NULL, &cur_handler) == 0 && michael@0: (cur_handler.sa_flags & SA_SIGINFO) == 0) { michael@0: // Reset signal handler with the right flags. michael@0: sigemptyset(&cur_handler.sa_mask); michael@0: sigaddset(&cur_handler.sa_mask, sig); michael@0: michael@0: cur_handler.sa_sigaction = SignalHandler; michael@0: cur_handler.sa_flags = SA_ONSTACK | SA_SIGINFO; michael@0: michael@0: if (sigaction(sig, &cur_handler, NULL) == -1) { michael@0: // When resetting the handler fails, try to reset the michael@0: // default one to avoid an infinite loop here. michael@0: signal(sig, SIG_DFL); michael@0: } michael@0: pthread_mutex_unlock(&handler_stack_mutex_); michael@0: return; michael@0: } michael@0: michael@0: bool handled = false; michael@0: for (int i = handler_stack_->size() - 1; !handled && i >= 0; --i) { michael@0: handled = (*handler_stack_)[i]->HandleSignal(sig, info, uc); michael@0: } michael@0: michael@0: // Upon returning from this signal handler, sig will become unmasked and then michael@0: // it will be retriggered. If one of the ExceptionHandlers handled it michael@0: // successfully, restore the default handler. Otherwise, restore the michael@0: // previously installed handler. Then, when the signal is retriggered, it will michael@0: // be delivered to the appropriate handler. michael@0: if (handled) { michael@0: signal(sig, SIG_DFL); michael@0: } else { michael@0: RestoreHandlersLocked(); michael@0: } michael@0: michael@0: pthread_mutex_unlock(&handler_stack_mutex_); michael@0: michael@0: if (info->si_code <= 0) { michael@0: // This signal was sent by another process. (Positive values of michael@0: // si_code are reserved for kernel-originated signals.) In order michael@0: // to retrigger it, we have to queue a new signal. michael@0: if (tgkill(getpid(), syscall(__NR_gettid), sig) < 0) { michael@0: // If we failed to kill ourselves (e.g. because a sandbox disallows us michael@0: // to do so), we instead resort to terminating our process. This will michael@0: // result in an incorrect exit code. michael@0: _exit(1); michael@0: } michael@0: } else { michael@0: // This was a synchronous signal triggered by a hard fault (e.g. SIGSEGV). michael@0: // No need to reissue the signal. It will automatically trigger again, michael@0: // when we return from the signal handler. michael@0: } michael@0: } michael@0: michael@0: struct ThreadArgument { michael@0: pid_t pid; // the crashing process michael@0: const MinidumpDescriptor* minidump_descriptor; michael@0: ExceptionHandler* handler; michael@0: const void* context; // a CrashContext structure michael@0: size_t context_size; michael@0: }; michael@0: michael@0: // This is the entry function for the cloned process. We are in a compromised michael@0: // context here: see the top of the file. michael@0: // static michael@0: int ExceptionHandler::ThreadEntry(void *arg) { michael@0: const ThreadArgument *thread_arg = reinterpret_cast(arg); michael@0: michael@0: // Block here until the crashing process unblocks us when michael@0: // we're allowed to use ptrace michael@0: thread_arg->handler->WaitForContinueSignal(); michael@0: michael@0: return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context, michael@0: thread_arg->context_size) == false; michael@0: } michael@0: michael@0: // This function runs in a compromised context: see the top of the file. michael@0: // Runs on the crashing thread. michael@0: bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) { michael@0: if (filter_ && !filter_(callback_context_)) michael@0: return false; michael@0: michael@0: // Allow ourselves to be dumped if the signal is trusted. michael@0: bool signal_trusted = info->si_code > 0; michael@0: bool signal_pid_trusted = info->si_code == SI_USER || michael@0: info->si_code == SI_TKILL; michael@0: if (signal_trusted || (signal_pid_trusted && info->si_pid == getpid())) { michael@0: sys_prctl(PR_SET_DUMPABLE, 1); michael@0: } michael@0: CrashContext context; michael@0: memcpy(&context.siginfo, info, sizeof(siginfo_t)); michael@0: memcpy(&context.context, uc, sizeof(struct ucontext)); michael@0: #if !defined(__ARM_EABI__) michael@0: // FP state is not part of user ABI on ARM Linux. michael@0: struct ucontext *uc_ptr = (struct ucontext*)uc; michael@0: if (uc_ptr->uc_mcontext.fpregs) { michael@0: memcpy(&context.float_state, michael@0: uc_ptr->uc_mcontext.fpregs, michael@0: sizeof(context.float_state)); michael@0: } michael@0: #endif michael@0: context.tid = syscall(__NR_gettid); michael@0: if (crash_handler_ != NULL) { michael@0: if (crash_handler_(&context, sizeof(context), callback_context_)) { michael@0: return true; michael@0: } michael@0: } michael@0: return GenerateDump(&context); michael@0: } michael@0: michael@0: // This is a public interface to HandleSignal that allows the client to michael@0: // generate a crash dump. This function may run in a compromised context. michael@0: bool ExceptionHandler::SimulateSignalDelivery(int sig) { michael@0: siginfo_t siginfo = {}; michael@0: // Mimic a trusted signal to allow tracing the process (see michael@0: // ExceptionHandler::HandleSignal(). michael@0: siginfo.si_code = SI_USER; michael@0: siginfo.si_pid = getpid(); michael@0: struct ucontext context; michael@0: getcontext(&context); michael@0: return HandleSignal(sig, &siginfo, &context); michael@0: } michael@0: michael@0: // This function may run in a compromised context: see the top of the file. michael@0: bool ExceptionHandler::GenerateDump(CrashContext *context) { michael@0: if (IsOutOfProcess()) michael@0: return crash_generation_client_->RequestDump(context, sizeof(*context)); michael@0: michael@0: static const unsigned kChildStackSize = 8000; michael@0: PageAllocator allocator; michael@0: uint8_t* stack = (uint8_t*) allocator.Alloc(kChildStackSize); michael@0: if (!stack) michael@0: return false; michael@0: // clone() needs the top-most address. (scrub just to be safe) michael@0: stack += kChildStackSize; michael@0: my_memset(stack - 16, 0, 16); michael@0: michael@0: ThreadArgument thread_arg; michael@0: thread_arg.handler = this; michael@0: thread_arg.minidump_descriptor = &minidump_descriptor_; michael@0: thread_arg.pid = getpid(); michael@0: thread_arg.context = context; michael@0: thread_arg.context_size = sizeof(*context); michael@0: michael@0: // We need to explicitly enable ptrace of parent processes on some michael@0: // kernels, but we need to know the PID of the cloned process before we michael@0: // can do this. Create a pipe here which we can use to block the michael@0: // cloned process after creating it, until we have explicitly enabled ptrace michael@0: if(sys_pipe(fdes) == -1) { michael@0: // Creating the pipe failed. We'll log an error but carry on anyway, michael@0: // as we'll probably still get a useful crash report. All that will happen michael@0: // is the write() and read() calls will fail with EBADF michael@0: static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump \ michael@0: sys_pipe failed:"; michael@0: logger::write(no_pipe_msg, sizeof(no_pipe_msg) - 1); michael@0: logger::write(strerror(errno), strlen(strerror(errno))); michael@0: logger::write("\n", 1); michael@0: } michael@0: michael@0: const pid_t child = sys_clone( michael@0: ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED, michael@0: &thread_arg, NULL, NULL, NULL); michael@0: michael@0: int r, status; michael@0: // Allow the child to ptrace us michael@0: sys_prctl(PR_SET_PTRACER, child); michael@0: SendContinueSignalToChild(); michael@0: do { michael@0: r = sys_waitpid(child, &status, __WALL); michael@0: } while (r == -1 && errno == EINTR); michael@0: michael@0: sys_close(fdes[0]); michael@0: sys_close(fdes[1]); michael@0: michael@0: if (r == -1) { michael@0: static const char msg[] = "ExceptionHandler::GenerateDump waitpid failed:"; michael@0: logger::write(msg, sizeof(msg) - 1); michael@0: logger::write(strerror(errno), strlen(strerror(errno))); michael@0: logger::write("\n", 1); michael@0: } michael@0: michael@0: bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0; michael@0: if (callback_) michael@0: success = callback_(minidump_descriptor_, callback_context_, success); michael@0: return success; michael@0: } michael@0: michael@0: // This function runs in a compromised context: see the top of the file. michael@0: void ExceptionHandler::SendContinueSignalToChild() { michael@0: static const char okToContinueMessage = 'a'; michael@0: int r; michael@0: r = HANDLE_EINTR(sys_write(fdes[1], &okToContinueMessage, sizeof(char))); michael@0: if(r == -1) { michael@0: static const char msg[] = "ExceptionHandler::SendContinueSignalToChild \ michael@0: sys_write failed:"; michael@0: logger::write(msg, sizeof(msg) - 1); michael@0: logger::write(strerror(errno), strlen(strerror(errno))); michael@0: logger::write("\n", 1); michael@0: } michael@0: } michael@0: michael@0: // This function runs in a compromised context: see the top of the file. michael@0: // Runs on the cloned process. michael@0: void ExceptionHandler::WaitForContinueSignal() { michael@0: int r; michael@0: char receivedMessage; michael@0: r = HANDLE_EINTR(sys_read(fdes[0], &receivedMessage, sizeof(char))); michael@0: if(r == -1) { michael@0: static const char msg[] = "ExceptionHandler::WaitForContinueSignal \ michael@0: sys_read failed:"; michael@0: logger::write(msg, sizeof(msg) - 1); michael@0: logger::write(strerror(errno), strlen(strerror(errno))); michael@0: logger::write("\n", 1); michael@0: } michael@0: } michael@0: michael@0: // This function runs in a compromised context: see the top of the file. michael@0: // Runs on the cloned process. michael@0: bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context, michael@0: size_t context_size) { michael@0: if (minidump_descriptor_.IsFD()) { michael@0: return google_breakpad::WriteMinidump(minidump_descriptor_.fd(), michael@0: minidump_descriptor_.size_limit(), michael@0: crashing_process, michael@0: context, michael@0: context_size, michael@0: mapping_list_, michael@0: app_memory_list_); michael@0: } michael@0: return google_breakpad::WriteMinidump(minidump_descriptor_.path(), michael@0: minidump_descriptor_.size_limit(), michael@0: crashing_process, michael@0: context, michael@0: context_size, michael@0: mapping_list_, michael@0: app_memory_list_); michael@0: } michael@0: michael@0: // static michael@0: bool ExceptionHandler::WriteMinidump(const string& dump_path, michael@0: MinidumpCallback callback, michael@0: void* callback_context) { michael@0: MinidumpDescriptor descriptor(dump_path); michael@0: ExceptionHandler eh(descriptor, NULL, callback, callback_context, false, -1); michael@0: return eh.WriteMinidump(); michael@0: } michael@0: michael@0: bool ExceptionHandler::WriteMinidump() { michael@0: if (!IsOutOfProcess() && !minidump_descriptor_.IsFD()) { michael@0: // Update the path of the minidump so that this can be called multiple times michael@0: // and new files are created for each minidump. This is done before the michael@0: // generation happens, as clients may want to access the MinidumpDescriptor michael@0: // after this call to find the exact path to the minidump file. michael@0: minidump_descriptor_.UpdatePath(); michael@0: } else if (minidump_descriptor_.IsFD()) { michael@0: // Reposition the FD to its beginning and resize it to get rid of the michael@0: // previous minidump info. michael@0: lseek(minidump_descriptor_.fd(), 0, SEEK_SET); michael@0: static_cast(ftruncate(minidump_descriptor_.fd(), 0)); michael@0: } michael@0: michael@0: // Allow this process to be dumped. michael@0: sys_prctl(PR_SET_DUMPABLE, 1); michael@0: michael@0: CrashContext context; michael@0: int getcontext_result = getcontext(&context.context); michael@0: if (getcontext_result) michael@0: return false; michael@0: #if !defined(__ARM_EABI__) michael@0: // FPU state is not part of ARM EABI ucontext_t. michael@0: memcpy(&context.float_state, context.context.uc_mcontext.fpregs, michael@0: sizeof(context.float_state)); michael@0: #endif michael@0: context.tid = sys_gettid(); michael@0: michael@0: // Add an exception stream to the minidump for better reporting. michael@0: memset(&context.siginfo, 0, sizeof(context.siginfo)); michael@0: context.siginfo.si_signo = MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED; michael@0: #if defined(__i386__) michael@0: context.siginfo.si_addr = michael@0: reinterpret_cast(context.context.uc_mcontext.gregs[REG_EIP]); michael@0: #elif defined(__x86_64__) michael@0: context.siginfo.si_addr = michael@0: reinterpret_cast(context.context.uc_mcontext.gregs[REG_RIP]); michael@0: #elif defined(__arm__) michael@0: context.siginfo.si_addr = michael@0: reinterpret_cast(context.context.uc_mcontext.arm_pc); michael@0: #else michael@0: #error "This code has not been ported to your platform yet." michael@0: #endif michael@0: michael@0: return GenerateDump(&context); michael@0: } michael@0: michael@0: void ExceptionHandler::AddMappingInfo(const string& name, michael@0: const uint8_t identifier[sizeof(MDGUID)], michael@0: uintptr_t start_address, michael@0: size_t mapping_size, michael@0: size_t file_offset) { michael@0: MappingInfo info; michael@0: info.start_addr = start_address; michael@0: info.size = mapping_size; michael@0: info.offset = file_offset; michael@0: strncpy(info.name, name.c_str(), sizeof(info.name) - 1); michael@0: info.name[sizeof(info.name) - 1] = '\0'; michael@0: michael@0: MappingEntry mapping; michael@0: mapping.first = info; michael@0: memcpy(mapping.second, identifier, sizeof(MDGUID)); michael@0: mapping_list_.push_back(mapping); michael@0: } michael@0: michael@0: void ExceptionHandler::RegisterAppMemory(void* ptr, size_t length) { michael@0: AppMemoryList::iterator iter = michael@0: std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr); michael@0: if (iter != app_memory_list_.end()) { michael@0: // Don't allow registering the same pointer twice. michael@0: return; michael@0: } michael@0: michael@0: AppMemory app_memory; michael@0: app_memory.ptr = ptr; michael@0: app_memory.length = length; michael@0: app_memory_list_.push_back(app_memory); michael@0: } michael@0: michael@0: void ExceptionHandler::UnregisterAppMemory(void* ptr) { michael@0: AppMemoryList::iterator iter = michael@0: std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr); michael@0: if (iter != app_memory_list_.end()) { michael@0: app_memory_list_.erase(iter); michael@0: } michael@0: } michael@0: michael@0: // static michael@0: bool ExceptionHandler::WriteMinidumpForChild(pid_t child, michael@0: pid_t child_blamed_thread, michael@0: const string& dump_path, michael@0: MinidumpCallback callback, michael@0: void* callback_context) { michael@0: // This function is not run in a compromised context. michael@0: MinidumpDescriptor descriptor(dump_path); michael@0: descriptor.UpdatePath(); michael@0: if (!google_breakpad::WriteMinidump(descriptor.path(), michael@0: child, michael@0: child_blamed_thread)) michael@0: return false; michael@0: michael@0: return callback ? callback(descriptor, callback_context, true) : true; michael@0: } michael@0: michael@0: } // namespace google_breakpad