diff options
author | Josh Gao <jmgao@google.com> | 2017-08-21 14:31:17 -0700 |
---|---|---|
committer | Josh Gao <jmgao@google.com> | 2017-12-15 14:11:12 -0800 |
commit | 2b2ae0c88ef83c4c53297ff54fa601b18c014fa4 (patch) | |
tree | 832d3ab764da9a63d4e22001f18045fa13078dd5 /debuggerd/handler/debuggerd_fallback.cpp | |
parent | 385ea22741ed5bad794fb6b1dff2b46481f241c4 (diff) |
crash_dump: fork a copy of the target's address space.
Reduce the amount of time that a process remains paused by pausing its
threads, fetching their registers, and then performing unwinding on a
copy of its address space. This also works around a kernel change
that's in 4.9 that prevents ptrace from reading memory of processes
that we don't have immediate permissions to ptrace (even if we
previously ptraced them).
Bug: http://b/62112103
Bug: http://b/63989615
Test: treehugger
Change-Id: I7b9cc5dd8f54a354bc61f1bda0d2b7a8a55733c4
Diffstat (limited to 'debuggerd/handler/debuggerd_fallback.cpp')
-rw-r--r-- | debuggerd/handler/debuggerd_fallback.cpp | 96 |
1 files changed, 58 insertions, 38 deletions
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp index 06d4a9b72..5fddddcf2 100644 --- a/debuggerd/handler/debuggerd_fallback.cpp +++ b/debuggerd/handler/debuggerd_fallback.cpp @@ -36,10 +36,14 @@ #include <unistd.h> #include <atomic> +#include <memory> #include <android-base/file.h> #include <android-base/unique_fd.h> #include <async_safe/log.h> +#include <backtrace/BacktraceMap.h> +#include <unwindstack/Memory.h> +#include <unwindstack/Regs.h> #include "debuggerd/handler.h" #include "tombstoned/tombstoned.h" @@ -49,6 +53,7 @@ #include "libdebuggerd/tombstone.h" using android::base::unique_fd; +using unwindstack::Regs; extern "C" void __linker_enable_fallback_allocator(); extern "C" void __linker_disable_fallback_allocator(); @@ -61,7 +66,19 @@ extern "C" void __linker_disable_fallback_allocator(); // exhaustion. static void debuggerd_fallback_trace(int output_fd, ucontext_t* ucontext) { __linker_enable_fallback_allocator(); - dump_backtrace_ucontext(output_fd, ucontext); + { + std::unique_ptr<Regs> regs; + + ThreadInfo thread; + thread.pid = getpid(); + thread.tid = gettid(); + thread.thread_name = get_thread_name(gettid()); + thread.registers.reset(Regs::CreateFromUcontext(Regs::CurrentArch(), ucontext)); + + // TODO: Create this once and store it in a global? + std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid())); + dump_backtrace_thread(output_fd, map.get(), thread); + } __linker_disable_fallback_allocator(); } @@ -162,41 +179,41 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) { // Send a signal to all of our siblings, asking them to dump their stack. iterate_siblings( - [](pid_t tid, int output_fd) { - // Use a pipe, to be able to detect situations where the thread gracefully exits before - // receiving our signal. - unique_fd pipe_read, pipe_write; - if (!Pipe(&pipe_read, &pipe_write)) { - async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s", - strerror(errno)); - return false; - } - - trace_output_fd.store(pipe_write.get()); - - siginfo_t siginfo = {}; - siginfo.si_code = SI_QUEUE; - siginfo.si_value.sival_int = ~0; - siginfo.si_pid = getpid(); - siginfo.si_uid = getuid(); - - if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, DEBUGGER_SIGNAL, &siginfo) != 0) { - async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s", - tid, strerror(errno)); - return false; - } - - bool success = forward_output(pipe_read.get(), output_fd); - if (success) { - // The signaled thread has closed trace_output_fd already. - (void)pipe_write.release(); - } else { - trace_output_fd.store(-1); - } - - return true; - }, - output_fd.get()); + [](pid_t tid, int output_fd) { + // Use a pipe, to be able to detect situations where the thread gracefully exits before + // receiving our signal. + unique_fd pipe_read, pipe_write; + if (!Pipe(&pipe_read, &pipe_write)) { + async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s", + strerror(errno)); + return false; + } + + trace_output_fd.store(pipe_write.get()); + + siginfo_t siginfo = {}; + siginfo.si_code = SI_QUEUE; + siginfo.si_value.sival_int = ~0; + siginfo.si_pid = getpid(); + siginfo.si_uid = getuid(); + + if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, DEBUGGER_SIGNAL, &siginfo) != 0) { + async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s", + tid, strerror(errno)); + return false; + } + + bool success = forward_output(pipe_read.get(), output_fd); + if (success) { + // The signaled thread has closed trace_output_fd already. + (void)pipe_write.release(); + } else { + trace_output_fd.store(-1); + } + + return true; + }, + output_fd.get()); dump_backtrace_footer(output_fd.get()); tombstoned_notify_completion(tombstone_socket.get()); @@ -206,7 +223,8 @@ exit: } static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_message) { - // Only allow one thread to handle a crash. + // Only allow one thread to handle a crash at a time (this can happen multiple times without + // exit, since tombstones can be requested without a real crash happening.) static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER; int ret = pthread_mutex_lock(&crash_mutex); if (ret != 0) { @@ -221,11 +239,13 @@ static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_mes if (tombstoned_connected) { tombstoned_notify_completion(tombstone_socket.get()); } + + pthread_mutex_unlock(&crash_mutex); } extern "C" void debuggerd_fallback_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_message) { - if (info->si_signo == DEBUGGER_SIGNAL) { + if (info->si_signo == DEBUGGER_SIGNAL && info->si_value.sival_int != 0) { return trace_handler(info, ucontext); } else { return crash_handler(info, ucontext, abort_message); |