From 2b2ae0c88ef83c4c53297ff54fa601b18c014fa4 Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Mon, 21 Aug 2017 14:31:17 -0700 Subject: [PATCH] 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 --- debuggerd/Android.bp | 28 +- debuggerd/crash_dump.cpp | 529 ++++++++++------- debuggerd/debuggerd_test.cpp | 34 +- debuggerd/handler/debuggerd_fallback.cpp | 86 +-- debuggerd/handler/debuggerd_handler.cpp | 282 ++++----- debuggerd/libdebuggerd/arm/machine.cpp | 91 --- debuggerd/libdebuggerd/arm64/machine.cpp | 105 ---- debuggerd/libdebuggerd/backtrace.cpp | 93 ++- debuggerd/libdebuggerd/elf_utils.cpp | 28 +- .../include/libdebuggerd/backtrace.h | 13 +- .../include/libdebuggerd/elf_utils.h | 6 +- .../include/libdebuggerd/machine.h | 30 - .../include/libdebuggerd/open_files_list.h | 2 +- .../include/libdebuggerd/tombstone.h | 16 +- .../libdebuggerd/types.h} | 22 +- .../include/libdebuggerd/utility.h | 46 +- debuggerd/libdebuggerd/mips/machine.cpp | 99 ---- debuggerd/libdebuggerd/mips64/machine.cpp | 99 ---- debuggerd/libdebuggerd/open_files_list.cpp | 2 +- debuggerd/libdebuggerd/test/BacktraceMock.h | 74 --- .../libdebuggerd/test/dump_memory_test.cpp | 150 +++-- debuggerd/libdebuggerd/test/elf_fake.cpp | 6 +- debuggerd/libdebuggerd/test/ptrace_fake.cpp | 53 -- .../libdebuggerd/test/tombstone_test.cpp | 173 +----- debuggerd/libdebuggerd/tombstone.cpp | 540 ++++++++---------- debuggerd/libdebuggerd/utility.cpp | 202 ++++++- debuggerd/libdebuggerd/x86/machine.cpp | 63 -- debuggerd/libdebuggerd/x86_64/machine.cpp | 68 --- debuggerd/protocol.h | 11 + debuggerd/util.cpp | 16 + debuggerd/util.h | 5 + 31 files changed, 1187 insertions(+), 1785 deletions(-) delete mode 100644 debuggerd/libdebuggerd/arm/machine.cpp delete mode 100644 debuggerd/libdebuggerd/arm64/machine.cpp delete mode 100644 debuggerd/libdebuggerd/include/libdebuggerd/machine.h rename debuggerd/libdebuggerd/{test/ptrace_fake.h => include/libdebuggerd/types.h} (62%) delete mode 100644 debuggerd/libdebuggerd/mips/machine.cpp delete mode 100644 debuggerd/libdebuggerd/mips64/machine.cpp delete mode 100644 debuggerd/libdebuggerd/test/ptrace_fake.cpp delete mode 100644 debuggerd/libdebuggerd/x86/machine.cpp delete mode 100644 debuggerd/libdebuggerd/x86_64/machine.cpp diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp index 17a9f3a7c..c473e3395 100644 --- a/debuggerd/Android.bp +++ b/debuggerd/Android.bp @@ -4,9 +4,12 @@ cc_defaults { "-Wall", "-Wextra", "-Werror", + "-Wno-unused-argument", + "-Wno-unused-function", "-Wno-nullability-completeness", "-Os", ], + cpp_std: "gnu++17", local_include_dirs: ["include"], } @@ -73,6 +76,7 @@ cc_library_static { whole_static_libs: [ "libasync_safe", + "libcutils", "libdebuggerd", ], @@ -148,27 +152,6 @@ cc_library_static { "libdebuggerd/utility.cpp", ], - target: { - android_arm: { - srcs: ["libdebuggerd/arm/machine.cpp"], - }, - android_arm64: { - srcs: ["libdebuggerd/arm64/machine.cpp"], - }, - android_mips: { - srcs: ["libdebuggerd/mips/machine.cpp"], - }, - android_mips64: { - srcs: ["libdebuggerd/mips64/machine.cpp"], - }, - android_x86: { - srcs: ["libdebuggerd/x86/machine.cpp"], - }, - android_x86_64: { - srcs: ["libdebuggerd/x86_64/machine.cpp"], - }, - }, - local_include_dirs: ["libdebuggerd/include"], export_include_dirs: ["libdebuggerd/include"], @@ -193,7 +176,6 @@ cc_test { "libdebuggerd/test/elf_fake.cpp", "libdebuggerd/test/log_fake.cpp", "libdebuggerd/test/open_files_list_test.cpp", - "libdebuggerd/test/ptrace_fake.cpp", "libdebuggerd/test/tombstone_test.cpp", ], @@ -218,6 +200,7 @@ cc_test { static_libs: [ "libdebuggerd", + "libunwindstack", ], local_include_dirs: [ @@ -264,6 +247,7 @@ cc_binary { "libbase", "liblog", "libprocinfo", + "libunwindstack", ], } diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp index 827420e1b..a1f021184 100644 --- a/debuggerd/crash_dump.cpp +++ b/debuggerd/crash_dump.cpp @@ -18,11 +18,11 @@ #include #include #include -#include #include #include #include #include +#include #include #include @@ -47,6 +47,8 @@ #define ATRACE_TAG ATRACE_TAG_BIONIC #include +#include + #include "libdebuggerd/backtrace.h" #include "libdebuggerd/tombstone.h" #include "libdebuggerd/utility.h" @@ -58,21 +60,9 @@ #include "util.h" using android::base::unique_fd; -using android::base::ReadFileToString; using android::base::StringPrintf; -using android::base::Trim; -static std::string get_process_name(pid_t pid) { - std::string result = ""; - ReadFileToString(StringPrintf("/proc/%d/cmdline", pid), &result); - return result; -} - -static std::string get_thread_name(pid_t tid) { - std::string result = ""; - ReadFileToString(StringPrintf("/proc/%d/comm", tid), &result); - return Trim(result); -} +using unwindstack::Regs; static bool pid_contains_tid(int pid_proc_fd, pid_t tid) { struct stat st; @@ -90,8 +80,8 @@ static pid_t get_tracer(pid_t tracee) { } // Attach to a thread, and verify that it's still a member of the given process -static bool ptrace_seize_thread(int pid_proc_fd, pid_t tid, std::string* error) { - if (ptrace(PTRACE_SEIZE, tid, 0, 0) != 0) { +static bool ptrace_seize_thread(int pid_proc_fd, pid_t tid, std::string* error, int flags = 0) { + if (ptrace(PTRACE_SEIZE, tid, 0, flags) != 0) { if (errno == EPERM) { pid_t tracer = get_tracer(tid); if (tracer != -1) { @@ -108,18 +98,43 @@ static bool ptrace_seize_thread(int pid_proc_fd, pid_t tid, std::string* error) // Make sure that the task we attached to is actually part of the pid we're dumping. if (!pid_contains_tid(pid_proc_fd, tid)) { if (ptrace(PTRACE_DETACH, tid, 0, 0) != 0) { - PLOG(FATAL) << "failed to detach from thread " << tid; + PLOG(WARNING) << "failed to detach from thread " << tid; } *error = StringPrintf("thread %d is not in process", tid); return false; } - // Put the task into ptrace-stop state. - if (ptrace(PTRACE_INTERRUPT, tid, 0, 0) != 0) { - PLOG(FATAL) << "failed to interrupt thread " << tid; + return true; +} + +static bool wait_for_stop(pid_t tid, int* received_signal) { + while (true) { + int status; + pid_t result = waitpid(tid, &status, __WALL); + if (result != tid) { + PLOG(ERROR) << "waitpid failed on " << tid << " while detaching"; + return false; + } + + if (WIFSTOPPED(status)) { + if (status >> 16 == PTRACE_EVENT_STOP) { + *received_signal = 0; + } else { + *received_signal = WSTOPSIG(status); + } + return true; + } + } +} + +// Interrupt a process and wait for it to be interrupted. +static bool ptrace_interrupt(pid_t tid, int* received_signal) { + if (ptrace(PTRACE_INTERRUPT, tid, 0, 0) == 0) { + return wait_for_stop(tid, received_signal); } - return true; + PLOG(ERROR) << "failed to interrupt " << tid << " to detach"; + return false; } static bool activity_manager_notify(pid_t pid, int signal, const std::string& amfd_data) { @@ -169,70 +184,39 @@ static bool activity_manager_notify(pid_t pid, int signal, const std::string& am return true; } -static void signal_handler(int) { - // We can't log easily, because the heap might be corrupt. - // Just die and let the surrounding log context explain things. - _exit(1); -} - -static void abort_handler(pid_t target, const bool tombstoned_connected, - unique_fd& tombstoned_socket, unique_fd& output_fd, - const char* abort_msg) { - // If we abort before we get an output fd, contact tombstoned to let any - // potential listeners know that we failed. - if (!tombstoned_connected) { - if (!tombstoned_connect(target, &tombstoned_socket, &output_fd, kDebuggerdAnyIntercept)) { - // We failed to connect, not much we can do. - LOG(ERROR) << "failed to connected to tombstoned to report failure"; - _exit(1); - } - } - - dprintf(output_fd.get(), "crash_dump failed to dump process"); - if (target != 1) { - dprintf(output_fd.get(), " %d: %s\n", target, abort_msg); - } else { - dprintf(output_fd.get(), ": %s\n", abort_msg); - } - - _exit(1); -} - -static void drop_capabilities() { - ATRACE_CALL(); - __user_cap_header_struct capheader; - memset(&capheader, 0, sizeof(capheader)); - capheader.version = _LINUX_CAPABILITY_VERSION_3; - capheader.pid = 0; - - __user_cap_data_struct capdata[2]; - memset(&capdata, 0, sizeof(capdata)); - - if (capset(&capheader, &capdata[0]) == -1) { - PLOG(FATAL) << "failed to drop capabilities"; - } - - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) { - PLOG(FATAL) << "failed to set PR_SET_NO_NEW_PRIVS"; - } -} - -int main(int argc, char** argv) { - atrace_begin(ATRACE_TAG, "before reparent"); - - pid_t target = getppid(); - bool tombstoned_connected = false; - unique_fd tombstoned_socket; - unique_fd output_fd; +// Globals used by the abort handler. +static pid_t g_target_thread = -1; +static bool g_tombstoned_connected = false; +static unique_fd g_tombstoned_socket; +static unique_fd g_output_fd; +static void Initialize(char** argv) { android::base::InitLogging(argv); - android::base::SetAborter([&](const char* abort_msg) { - abort_handler(target, tombstoned_connected, tombstoned_socket, output_fd, abort_msg); + android::base::SetAborter([](const char* abort_msg) { + // If we abort before we get an output fd, contact tombstoned to let any + // potential listeners know that we failed. + if (!g_tombstoned_connected) { + if (!tombstoned_connect(g_target_thread, &g_tombstoned_socket, &g_output_fd, + kDebuggerdAnyIntercept)) { + // We failed to connect, not much we can do. + LOG(ERROR) << "failed to connected to tombstoned to report failure"; + _exit(1); + } + } + + dprintf(g_output_fd.get(), "crash_dump failed to dump process"); + if (g_target_thread != 1) { + dprintf(g_output_fd.get(), " %d: %s\n", g_target_thread, abort_msg); + } else { + dprintf(g_output_fd.get(), ": %s\n", abort_msg); + } + + _exit(1); }); // Don't try to dump ourselves. struct sigaction action = {}; - action.sa_handler = signal_handler; + action.sa_handler = SIG_DFL; debuggerd_register_handlers(&action); sigset_t mask; @@ -240,216 +224,328 @@ int main(int argc, char** argv) { if (sigprocmask(SIG_SETMASK, &mask, nullptr) != 0) { PLOG(FATAL) << "failed to set signal mask"; } +} +static void ParseArgs(int argc, char** argv, pid_t* pseudothread_tid, DebuggerdDumpType* dump_type) { if (argc != 4) { - LOG(FATAL) << "Wrong number of args: " << argc << " (expected 4)"; + LOG(FATAL) << "wrong number of args: " << argc << " (expected 4)"; } - pid_t main_tid; - pid_t pseudothread_tid; - int dump_type; - - if (!android::base::ParseInt(argv[1], &main_tid, 1, std::numeric_limits::max())) { - LOG(FATAL) << "invalid main tid: " << argv[1]; + if (!android::base::ParseInt(argv[1], &g_target_thread, 1, std::numeric_limits::max())) { + LOG(FATAL) << "invalid target tid: " << argv[1]; } - if (!android::base::ParseInt(argv[2], &pseudothread_tid, 1, std::numeric_limits::max())) { + if (!android::base::ParseInt(argv[2], pseudothread_tid, 1, std::numeric_limits::max())) { LOG(FATAL) << "invalid pseudothread tid: " << argv[2]; } - if (!android::base::ParseInt(argv[3], &dump_type, 0, 1)) { + int dump_type_int; + if (!android::base::ParseInt(argv[3], &dump_type_int, 0, 1)) { LOG(FATAL) << "invalid requested dump type: " << argv[3]; } + *dump_type = static_cast(dump_type_int); +} - if (target == 1) { - LOG(FATAL) << "target died before we could attach (received main tid = " << main_tid << ")"; +static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo, + std::unique_ptr* regs, uintptr_t* abort_address) { + std::aligned_storage::type buf; + ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &buf, sizeof(buf))); + if (rc == -1) { + PLOG(FATAL) << "failed to read target ucontext"; + } else if (rc != sizeof(CrashInfo)) { + LOG(FATAL) << "read " << rc << " bytes when reading target crash information, expected " + << sizeof(CrashInfo); } - android::procinfo::ProcessInfo target_info; - if (!android::procinfo::GetProcessInfo(main_tid, &target_info)) { - LOG(FATAL) << "failed to fetch process info for target " << main_tid; + CrashInfo* crash_info = reinterpret_cast(&buf); + if (crash_info->version != 1) { + LOG(FATAL) << "version mismatch, expected 1, received " << crash_info->version; } - if (main_tid != target_info.tid || target != target_info.pid) { - LOG(FATAL) << "target info mismatch, expected pid " << target << ", tid " << main_tid - << ", received pid " << target_info.pid << ", tid " << target_info.tid; + *siginfo = crash_info->siginfo; + regs->reset(Regs::CreateFromUcontext(Regs::CurrentArch(), &crash_info->ucontext)); + *abort_address = crash_info->abort_msg_address; +} + +// Wait for a process to clone and return the child's pid. +// Note: this leaves the parent in PTRACE_EVENT_STOP. +static pid_t wait_for_clone(pid_t pid, bool resume_child) { + int status; + pid_t result = TEMP_FAILURE_RETRY(waitpid(pid, &status, __WALL)); + if (result == -1) { + PLOG(FATAL) << "failed to waitpid"; } - // Open /proc/`getppid()` in the original process, and pass it down to the forked child. - std::string target_proc_path = "/proc/" + std::to_string(target); + if (WIFEXITED(status)) { + LOG(FATAL) << "traced process exited with status " << WEXITSTATUS(status); + } else if (WIFSIGNALED(status)) { + LOG(FATAL) << "traced process exited with signal " << WTERMSIG(status); + } else if (!WIFSTOPPED(status)) { + LOG(FATAL) << "process didn't stop? (status = " << status << ")"; + } + + if (status >> 8 != (SIGTRAP | (PTRACE_EVENT_CLONE << 8))) { + LOG(FATAL) << "process didn't stop due to PTRACE_O_TRACECLONE (status = " << status << ")"; + } + + pid_t child; + if (ptrace(PTRACE_GETEVENTMSG, pid, 0, &child) != 0) { + PLOG(FATAL) << "failed to get child pid via PTRACE_GETEVENTMSG"; + } + + int stop_signal; + if (!wait_for_stop(child, &stop_signal)) { + PLOG(FATAL) << "failed to waitpid on child"; + } + + CHECK_EQ(0, stop_signal); + + if (resume_child) { + if (ptrace(PTRACE_CONT, child, 0, 0) != 0) { + PLOG(FATAL) << "failed to resume child (pid = " << child << ")"; + } + } + + return child; +} + +static pid_t wait_for_vm_process(pid_t pseudothread_tid) { + // The pseudothread will double-fork, we want its grandchild. + pid_t intermediate = wait_for_clone(pseudothread_tid, true); + pid_t vm_pid = wait_for_clone(intermediate, false); + if (ptrace(PTRACE_DETACH, intermediate, 0, 0) != 0) { + PLOG(FATAL) << "failed to detach from intermediate vm process"; + } + + return vm_pid; +} + +int main(int argc, char** argv) { + atrace_begin(ATRACE_TAG, "before reparent"); + pid_t target_process = getppid(); + + // Open /proc/`getppid()` before we daemonize. + std::string target_proc_path = "/proc/" + std::to_string(target_process); int target_proc_fd = open(target_proc_path.c_str(), O_DIRECTORY | O_RDONLY); if (target_proc_fd == -1) { PLOG(FATAL) << "failed to open " << target_proc_path; } - // Make sure our parent didn't die. - if (getppid() != target) { - PLOG(FATAL) << "parent died"; + // Make sure getppid() hasn't changed. + if (getppid() != target_process) { + LOG(FATAL) << "parent died"; } - atrace_end(ATRACE_TAG); // Reparent ourselves to init, so that the signal handler can waitpid on the // original process to avoid leaving a zombie for non-fatal dumps. + // Move the input/output pipes off of stdout/stderr, out of paranoia. + unique_fd output_pipe(dup(STDOUT_FILENO)); + unique_fd input_pipe(dup(STDIN_FILENO)); + + unique_fd fork_exit_read, fork_exit_write; + if (!Pipe(&fork_exit_read, &fork_exit_write)) { + PLOG(FATAL) << "failed to create pipe"; + } + pid_t forkpid = fork(); if (forkpid == -1) { PLOG(FATAL) << "fork failed"; - } else if (forkpid != 0) { - exit(0); + } else if (forkpid == 0) { + fork_exit_read.reset(); + } else { + // We need the pseudothread to live until we get around to verifying the vm pid against it. + // The last thing it does is block on a waitpid on us, so wait until our child tells us to die. + fork_exit_write.reset(); + char buf; + TEMP_FAILURE_RETRY(read(fork_exit_read.get(), &buf, sizeof(buf))); + _exit(0); } ATRACE_NAME("after reparent"); + pid_t pseudothread_tid; + DebuggerdDumpType dump_type; + uintptr_t abort_address = 0; + + Initialize(argv); + ParseArgs(argc, argv, &pseudothread_tid, &dump_type); // Die if we take too long. // // Note: processes with many threads and minidebug-info can take a bit to // unwind, do not make this too small. b/62828735 - alarm(5); + alarm(30); - std::string attach_error; - - std::map threads; - - { - ATRACE_NAME("ptrace"); - // Seize the main thread. - if (!ptrace_seize_thread(target_proc_fd, main_tid, &attach_error)) { - LOG(FATAL) << attach_error; - } - - // Seize the siblings. - { - std::set siblings; - if (!android::procinfo::GetProcessTids(target, &siblings)) { - PLOG(FATAL) << "failed to get process siblings"; - } - - // but not the already attached main thread. - siblings.erase(main_tid); - // or the handler pseudothread. - siblings.erase(pseudothread_tid); - - for (pid_t sibling_tid : siblings) { - if (!ptrace_seize_thread(target_proc_fd, sibling_tid, &attach_error)) { - LOG(WARNING) << attach_error; - } else { - threads.emplace(sibling_tid, get_thread_name(sibling_tid)); - } - } - } - } - - // Collect the backtrace map, open files, and process/thread names, while we still have caps. - std::unique_ptr backtrace_map; - { - ATRACE_NAME("backtrace map"); - backtrace_map.reset(BacktraceMap::Create(main_tid)); - if (!backtrace_map) { - LOG(FATAL) << "failed to create backtrace map"; - } - } + // Get the process name (aka cmdline). + std::string process_name = get_process_name(g_target_thread); // Collect the list of open files. OpenFilesList open_files; { ATRACE_NAME("open files"); - populate_open_files_list(target, &open_files); + populate_open_files_list(g_target_thread, &open_files); } - std::string process_name = get_process_name(main_tid); - threads.emplace(main_tid, get_thread_name(main_tid)); + // In order to reduce the duration that we pause the process for, we ptrace + // the threads, fetch their registers and associated information, and then + // fork a separate process as a snapshot of the process's address space. + std::set threads; + if (!android::procinfo::GetProcessTids(g_target_thread, &threads)) { + PLOG(FATAL) << "failed to get process threads"; + } - // Drop our capabilities now that we've attached to the threads we care about. + std::map thread_info; + siginfo_t siginfo; + std::string error; + + { + ATRACE_NAME("ptrace"); + for (pid_t thread : threads) { + // Trace the pseudothread separately, so we can use different options. + if (thread == pseudothread_tid) { + continue; + } + + if (!ptrace_seize_thread(target_proc_fd, thread, &error)) { + bool fatal = thread == g_target_thread; + LOG(fatal ? FATAL : WARNING) << error; + } + + ThreadInfo info; + info.pid = target_process; + info.tid = thread; + info.process_name = process_name; + info.thread_name = get_thread_name(thread); + + if (!ptrace_interrupt(thread, &info.signo)) { + PLOG(WARNING) << "failed to ptrace interrupt thread " << thread; + ptrace(PTRACE_DETACH, thread, 0, 0); + continue; + } + + if (thread == g_target_thread) { + // Read the thread's registers along with the rest of the crash info out of the pipe. + ReadCrashInfo(input_pipe, &siginfo, &info.registers, &abort_address); + info.siginfo = &siginfo; + info.signo = info.siginfo->si_signo; + } else { + info.registers.reset(Regs::RemoteGet(thread)); + if (!info.registers) { + PLOG(WARNING) << "failed to fetch registers for thread " << thread; + ptrace(PTRACE_DETACH, thread, 0, 0); + continue; + } + } + + thread_info[thread] = std::move(info); + } + } + + // Trace the pseudothread with PTRACE_O_TRACECLONE and tell it to fork. + if (!ptrace_seize_thread(target_proc_fd, pseudothread_tid, &error, PTRACE_O_TRACECLONE)) { + LOG(FATAL) << "failed to seize pseudothread: " << error; + } + + if (TEMP_FAILURE_RETRY(write(output_pipe.get(), "\1", 1)) != 1) { + PLOG(FATAL) << "failed to write to pseudothread"; + } + + pid_t vm_pid = wait_for_vm_process(pseudothread_tid); + if (ptrace(PTRACE_DETACH, pseudothread_tid, 0, 0) != 0) { + PLOG(FATAL) << "failed to detach from pseudothread"; + } + + // The pseudothread can die now. + fork_exit_write.reset(); + + // Defer the message until later, for readability. + bool wait_for_gdb = android::base::GetBoolProperty("debug.debuggerd.wait_for_gdb", false); + if (siginfo.si_signo == DEBUGGER_SIGNAL) { + wait_for_gdb = false; + } + + // Detach from all of our attached threads before resuming. + for (const auto& [tid, thread] : thread_info) { + int resume_signal = thread.signo == DEBUGGER_SIGNAL ? 0 : thread.signo; + if (wait_for_gdb) { + resume_signal = 0; + if (tgkill(target_process, tid, SIGSTOP) != 0) { + PLOG(WARNING) << "failed to send SIGSTOP to " << tid; + } + } + + LOG(DEBUG) << "detaching from thread " << tid; + if (ptrace(PTRACE_DETACH, tid, 0, resume_signal) != 0) { + PLOG(ERROR) << "failed to detach from thread " << tid; + } + } + + // Drop our capabilities now that we've fetched all of the information we need. drop_capabilities(); { ATRACE_NAME("tombstoned_connect"); - const DebuggerdDumpType dump_type_enum = static_cast(dump_type); - LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type_enum; - tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd, dump_type_enum); + LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type; + g_tombstoned_connected = + tombstoned_connect(g_target_thread, &g_tombstoned_socket, &g_output_fd, dump_type); } - // Write a '\1' to stdout to tell the crashing process to resume. - // It also restores the value of PR_SET_DUMPABLE at this point. - if (TEMP_FAILURE_RETRY(write(STDOUT_FILENO, "\1", 1)) == -1) { - PLOG(ERROR) << "failed to communicate to target process"; - } - - if (tombstoned_connected) { - if (TEMP_FAILURE_RETRY(dup2(output_fd.get(), STDOUT_FILENO)) == -1) { - PLOG(ERROR) << "failed to dup2 output fd (" << output_fd.get() << ") to STDOUT_FILENO"; + if (g_tombstoned_connected) { + if (TEMP_FAILURE_RETRY(dup2(g_output_fd.get(), STDOUT_FILENO)) == -1) { + PLOG(ERROR) << "failed to dup2 output fd (" << g_output_fd.get() << ") to STDOUT_FILENO"; } } else { unique_fd devnull(TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR))); TEMP_FAILURE_RETRY(dup2(devnull.get(), STDOUT_FILENO)); - output_fd = std::move(devnull); + g_output_fd = std::move(devnull); } - LOG(INFO) << "performing dump of process " << target << " (target tid = " << main_tid << ")"; - - // At this point, the thread that made the request has been attached and is - // in ptrace-stopped state. After resumption, the triggering signal that has - // been queued will be delivered. - if (ptrace(PTRACE_CONT, main_tid, 0, 0) != 0) { - PLOG(ERROR) << "PTRACE_CONT(" << main_tid << ") failed"; - exit(1); - } - - siginfo_t siginfo = {}; - { - ATRACE_NAME("wait_for_signal"); - if (!wait_for_signal(main_tid, &siginfo)) { - printf("failed to wait for signal in tid %d: %s\n", main_tid, strerror(errno)); - exit(1); - } - } + LOG(INFO) << "performing dump of process " << target_process << " (target tid = " << g_target_thread + << ")"; int signo = siginfo.si_signo; bool fatal_signal = signo != DEBUGGER_SIGNAL; bool backtrace = false; - uintptr_t abort_address = 0; - // si_value can represent three things: + // si_value is special when used with DEBUGGER_SIGNAL. // 0: dump tombstone // 1: dump backtrace - // everything else: abort message address (implies dump tombstone) - if (siginfo.si_value.sival_int == 1) { - backtrace = true; - } else if (siginfo.si_value.sival_ptr != nullptr) { - abort_address = reinterpret_cast(siginfo.si_value.sival_ptr); + if (!fatal_signal) { + int si_val = siginfo.si_value.sival_int; + if (si_val == 0) { + backtrace = false; + } else if (si_val == 1) { + backtrace = true; + } else { + LOG(WARNING) << "unknown si_value value " << si_val; + } } // TODO: Use seccomp to lock ourselves down. + std::unique_ptr map(BacktraceMap::Create(vm_pid, false)); + if (!map) { + LOG(FATAL) << "failed to create backtrace map"; + } + + std::shared_ptr process_memory = map->GetProcessMemory(); + if (!process_memory) { + LOG(FATAL) << "failed to get unwindstack::Memory handle"; + } std::string amfd_data; if (backtrace) { ATRACE_NAME("dump_backtrace"); - dump_backtrace(output_fd.get(), backtrace_map.get(), target, main_tid, process_name, threads, 0); + dump_backtrace(std::move(g_output_fd), map.get(), thread_info, g_target_thread); } else { ATRACE_NAME("engrave_tombstone"); - engrave_tombstone(output_fd.get(), backtrace_map.get(), &open_files, target, main_tid, - process_name, threads, abort_address, fatal_signal ? &amfd_data : nullptr); + engrave_tombstone(std::move(g_output_fd), map.get(), process_memory.get(), thread_info, + g_target_thread, abort_address, &open_files, &amfd_data); } - // We don't actually need to PTRACE_DETACH, as long as our tracees aren't in - // group-stop state, which is true as long as no stopping signals are sent. - - bool wait_for_gdb = android::base::GetBoolProperty("debug.debuggerd.wait_for_gdb", false); - if (!fatal_signal || siginfo.si_code == SI_USER) { - // Don't wait_for_gdb when the process didn't actually crash. - wait_for_gdb = false; - } - - // If the process crashed or we need to send it SIGSTOP for wait_for_gdb, - // get it in a state where it can receive signals, and then send the relevant - // signal. - if (wait_for_gdb || fatal_signal) { - if (ptrace(PTRACE_INTERRUPT, main_tid, 0, 0) != 0) { - PLOG(ERROR) << "failed to use PTRACE_INTERRUPT on " << main_tid; - } - - if (tgkill(target, main_tid, wait_for_gdb ? SIGSTOP : signo) != 0) { - PLOG(ERROR) << "failed to resend signal " << signo << " to " << main_tid; + if (fatal_signal) { + // Don't try to notify ActivityManager if it just crashed, or we might hang until timeout. + if (thread_info[target_process].thread_name != "system_server") { + activity_manager_notify(target_process, signo, amfd_data); } } @@ -463,19 +559,12 @@ int main(int argc, char** argv) { "* gdbclient.py -p %d\n" "*\n" "***********************************************************", - target, target); - } - - if (fatal_signal) { - // Don't try to notify ActivityManager if it just crashed, or we might hang until timeout. - if (target_info.name != "system_server" || target_info.uid != AID_SYSTEM) { - activity_manager_notify(target, signo, amfd_data); - } + target_process, target_process); } // Close stdout before we notify tombstoned of completion. close(STDOUT_FILENO); - if (tombstoned_connected && !tombstoned_notify_completion(tombstoned_socket.get())) { + if (g_tombstoned_connected && !tombstoned_notify_completion(g_tombstoned_socket.get())) { LOG(ERROR) << "failed to notify tombstoned of completion"; } diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp index 8d0c98bb0..0d17a3b5c 100644 --- a/debuggerd/debuggerd_test.cpp +++ b/debuggerd/debuggerd_test.cpp @@ -245,6 +245,8 @@ void CrasherTest::AssertDeath(int signo) { int status; pid_t pid = TIMEOUT(5, waitpid(crasher_pid, &status, 0)); if (pid != crasher_pid) { + printf("failed to wait for crasher (pid %d)\n", crasher_pid); + sleep(100); FAIL() << "failed to wait for crasher: " << strerror(errno); } @@ -341,13 +343,12 @@ TEST_F(CrasherTest, signal) { int intercept_result; unique_fd output_fd; StartProcess([]() { - abort(); + while (true) { + sleep(1); + } }); StartIntercept(&output_fd); - - // Wait for a bit, or we might end up killing the process before the signal - // handler even gets a chance to be registered. - std::this_thread::sleep_for(100ms); + FinishCrasher(); ASSERT_EQ(0, kill(crasher_pid, SIGSEGV)); AssertDeath(SIGSEGV); @@ -439,19 +440,6 @@ TEST_F(CrasherTest, wait_for_gdb) { AssertDeath(SIGABRT); } -// wait_for_gdb shouldn't trigger on manually sent signals. -TEST_F(CrasherTest, wait_for_gdb_signal) { - if (!android::base::SetProperty(kWaitForGdbKey, "1")) { - FAIL() << "failed to enable wait_for_gdb"; - } - - StartProcess([]() { - abort(); - }); - ASSERT_EQ(0, kill(crasher_pid, SIGSEGV)) << strerror(errno); - AssertDeath(SIGSEGV); -} - TEST_F(CrasherTest, backtrace) { std::string result; int intercept_result; @@ -596,15 +584,13 @@ TEST_F(CrasherTest, competing_tracer) { int intercept_result; unique_fd output_fd; StartProcess([]() { - while (true) { - } + raise(SIGABRT); }); StartIntercept(&output_fd); - FinishCrasher(); ASSERT_EQ(0, ptrace(PTRACE_SEIZE, crasher_pid, 0, 0)); - ASSERT_EQ(0, kill(crasher_pid, SIGABRT)); + FinishCrasher(); int status; ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, 0)); @@ -622,6 +608,10 @@ TEST_F(CrasherTest, competing_tracer) { regex += R"( \(.+debuggerd_test)"; ASSERT_MATCH(result, regex.c_str()); + ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, 0)); + ASSERT_TRUE(WIFSTOPPED(status)); + ASSERT_EQ(SIGABRT, WSTOPSIG(status)); + ASSERT_EQ(0, ptrace(PTRACE_DETACH, crasher_pid, 0, SIGABRT)); AssertDeath(SIGABRT); } 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 #include +#include #include #include #include +#include +#include +#include #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; + + 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 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; - } + [](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()); + 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(); + 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; - } + 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); - } + 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()); + 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); diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp index bd202ff14..02bc4b84f 100644 --- a/debuggerd/handler/debuggerd_handler.cpp +++ b/debuggerd/handler/debuggerd_handler.cpp @@ -44,15 +44,21 @@ #include #include #include +#include #include #include #include #include #include +#include + +#include #include "dump_type.h" +#include "protocol.h" +using android::base::Pipe; using android::base::unique_fd; // see man(2) prctl, specifically the section about PR_GET_NAME @@ -114,7 +120,7 @@ static void __noreturn __printflike(1, 2) fatal_errno(const char* fmt, ...) { va_list args; va_start(args, fmt); - char buf[4096]; + char buf[256]; async_safe_format_buffer_va_list(buf, sizeof(buf), fmt, args); fatal("%s: %s", buf, strerror(err)); } @@ -147,7 +153,7 @@ static bool get_main_thread_name(char* buf, size_t len) { * mutex is being held, so we don't want to use any libc functions that * could allocate memory or hold a lock. */ -static void log_signal_summary(int signum, const siginfo_t* info) { +static void log_signal_summary(const siginfo_t* info) { char thread_name[MAX_TASK_NAME_LEN + 1]; // one more for termination if (prctl(PR_GET_NAME, reinterpret_cast(thread_name), 0, 0, 0) != 0) { strcpy(thread_name, ""); @@ -157,57 +163,19 @@ static void log_signal_summary(int signum, const siginfo_t* info) { thread_name[MAX_TASK_NAME_LEN] = 0; } - if (signum == DEBUGGER_SIGNAL) { + if (info->si_signo == DEBUGGER_SIGNAL) { async_safe_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for tid %d (%s)", __gettid(), thread_name); return; } - const char* signal_name = "???"; - bool has_address = false; - switch (signum) { - case SIGABRT: - signal_name = "SIGABRT"; - break; - case SIGBUS: - signal_name = "SIGBUS"; - has_address = true; - break; - case SIGFPE: - signal_name = "SIGFPE"; - has_address = true; - break; - case SIGILL: - signal_name = "SIGILL"; - has_address = true; - break; - case SIGSEGV: - signal_name = "SIGSEGV"; - has_address = true; - break; -#if defined(SIGSTKFLT) - case SIGSTKFLT: - signal_name = "SIGSTKFLT"; - break; -#endif - case SIGSYS: - signal_name = "SIGSYS"; - break; - case SIGTRAP: - signal_name = "SIGTRAP"; - break; - } + const char* signal_name = get_signame(info->si_signo); + bool has_address = signal_has_si_addr(info->si_signo, info->si_code); - // "info" will be null if the siginfo_t information was not available. - // Many signals don't have an address or a code. - char code_desc[32]; // ", code -6" - char addr_desc[32]; // ", fault addr 0x1234" - addr_desc[0] = code_desc[0] = 0; - if (info != nullptr) { - async_safe_format_buffer(code_desc, sizeof(code_desc), ", code %d", info->si_code); - if (has_address) { - async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr); - } + // Many signals don't have an address. + char addr_desc[32] = ""; // ", fault addr 0x1234" + if (has_address) { + async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr); } char main_thread_name[MAX_TASK_NAME_LEN + 1]; @@ -216,8 +184,9 @@ static void log_signal_summary(int signum, const siginfo_t* info) { } async_safe_format_log( - ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s)%s%s in tid %d (%s), pid %d (%s)", signum, - signal_name, code_desc, addr_desc, __gettid(), thread_name, __getpid(), main_thread_name); + ANDROID_LOG_FATAL, "libc", "Fatal signal %d (%s), code %d (%s)%s in tid %d (%s), pid %d (%s)", + info->si_signo, signal_name, info->si_code, get_sigcode(info->si_signo, info->si_code), + addr_desc, __gettid(), thread_name, __getpid(), main_thread_name); } /* @@ -268,12 +237,44 @@ static void raise_caps() { } } +static pid_t __fork() { + return clone(nullptr, nullptr, 0, nullptr); +} + +// Double-clone, with CLONE_FILES to share the file descriptor table for kcmp validation. +// Returns 0 in the orphaned child, the pid of the orphan in the original process, or -1 on failure. +static void create_vm_process() { + pid_t first = clone(nullptr, nullptr, CLONE_FILES, nullptr); + if (first == -1) { + fatal_errno("failed to clone vm process"); + } else if (first == 0) { + drop_capabilities(); + + if (clone(nullptr, nullptr, CLONE_FILES, nullptr) == -1) { + _exit(errno); + } + + // Exit immediately on both sides of the fork. + // crash_dump is ptracing us, so it'll get to do whatever it wants in between. + _exit(0); + } + + int status; + if (TEMP_FAILURE_RETRY(waitpid(first, &status, __WCLONE)) != first) { + fatal_errno("failed to waitpid in double fork"); + } else if (!WIFEXITED(status)) { + fatal("intermediate process didn't exit cleanly in double fork (status = %d)", status); + } else if (WEXITSTATUS(status)) { + fatal("second clone failed: %s", strerror(WEXITSTATUS(status))); + } +} + struct debugger_thread_info { - bool crash_dump_started; pid_t crashing_tid; pid_t pseudothread_tid; - int signal_number; - siginfo_t* info; + siginfo_t* siginfo; + void* ucontext; + uintptr_t abort_msg; }; // Logging and contacting debuggerd requires free file descriptors, which we might not have. @@ -284,7 +285,8 @@ struct debugger_thread_info { static void* pseudothread_stack; static DebuggerdDumpType get_dump_type(const debugger_thread_info* thread_info) { - if (thread_info->signal_number == DEBUGGER_SIGNAL && thread_info->info->si_value.sival_int) { + if (thread_info->siginfo->si_signo == DEBUGGER_SIGNAL && + thread_info->siginfo->si_value.sival_int) { return kDebuggerdNativeBacktrace; } @@ -299,25 +301,58 @@ static int debuggerd_dispatch_pseudothread(void* arg) { } int devnull = TEMP_FAILURE_RETRY(open("/dev/null", O_RDWR)); + if (devnull == -1) { + fatal_errno("failed to open /dev/null"); + } else if (devnull != 0) { + fatal_errno("expected /dev/null fd to be 0, actually %d", devnull); + } // devnull will be 0. - TEMP_FAILURE_RETRY(dup2(devnull, STDOUT_FILENO)); - TEMP_FAILURE_RETRY(dup2(devnull, STDERR_FILENO)); + TEMP_FAILURE_RETRY(dup2(devnull, 1)); + TEMP_FAILURE_RETRY(dup2(devnull, 2)); - unique_fd pipe_read, pipe_write; - if (!android::base::Pipe(&pipe_read, &pipe_write)) { + unique_fd input_read, input_write; + unique_fd output_read, output_write; + if (!Pipe(&input_read, &input_write) != 0 || !Pipe(&output_read, &output_write)) { fatal_errno("failed to create pipe"); } + // ucontext_t is absurdly large on AArch64, so piece it together manually with writev. + uint32_t version = 1; + constexpr size_t expected = + sizeof(version) + sizeof(siginfo_t) + sizeof(ucontext_t) + sizeof(uintptr_t); + + errno = 0; + if (fcntl(output_write.get(), F_SETPIPE_SZ, expected) < static_cast(expected)) { + fatal_errno("failed to set pipe bufer size"); + } + + struct iovec iovs[4] = { + {.iov_base = &version, .iov_len = sizeof(version)}, + {.iov_base = thread_info->siginfo, .iov_len = sizeof(siginfo_t)}, + {.iov_base = thread_info->ucontext, .iov_len = sizeof(ucontext_t)}, + {.iov_base = &thread_info->abort_msg, .iov_len = sizeof(uintptr_t)}, + }; + + ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, 4)); + if (rc == -1) { + fatal_errno("failed to write crash info"); + } else if (rc != expected) { + fatal("failed to write crash info, wrote %zd bytes, expected %zd", rc, expected); + } + // Don't use fork(2) to avoid calling pthread_atfork handlers. - int forkpid = clone(nullptr, nullptr, 0, nullptr); - if (forkpid == -1) { + pid_t crash_dump_pid = __fork(); + if (crash_dump_pid == -1) { async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to fork in debuggerd signal handler: %s", strerror(errno)); - } else if (forkpid == 0) { - TEMP_FAILURE_RETRY(dup2(pipe_write.get(), STDOUT_FILENO)); - pipe_write.reset(); - pipe_read.reset(); + } else if (crash_dump_pid == 0) { + TEMP_FAILURE_RETRY(dup2(input_write.get(), STDOUT_FILENO)); + TEMP_FAILURE_RETRY(dup2(output_read.get(), STDIN_FILENO)); + input_read.reset(); + input_write.reset(); + output_read.reset(); + output_write.reset(); raise_caps(); @@ -332,45 +367,49 @@ static int debuggerd_dispatch_pseudothread(void* arg) { execle(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, debuggerd_dump_type, nullptr, nullptr); - fatal_errno("exec failed"); - } else { - pipe_write.reset(); - char buf[4]; - ssize_t rc = TEMP_FAILURE_RETRY(read(pipe_read.get(), &buf, sizeof(buf))); - if (rc == -1) { - async_safe_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s", - strerror(errno)); - } else if (rc == 0) { - async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper failed to exec"); - } else if (rc != 1) { - async_safe_format_log(ANDROID_LOG_FATAL, "libc", - "read of IPC pipe returned unexpected value: %zd", rc); - } else { - if (buf[0] != '\1') { - async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure"); - } else { - thread_info->crash_dump_started = true; - } - } - pipe_read.reset(); - - // Don't leave a zombie child. - int status; - if (TEMP_FAILURE_RETRY(waitpid(forkpid, &status, 0)) == -1) { - async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s", - strerror(errno)); - } else if (WIFSTOPPED(status) || WIFSIGNALED(status)) { - async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped"); - thread_info->crash_dump_started = false; - } } - syscall(__NR_exit, 0); + input_write.reset(); + output_read.reset(); + + // crash_dump will ptrace and pause all of our threads, and then write to the pipe to tell + // us to fork off a process to read memory from. + char buf[4]; + rc = TEMP_FAILURE_RETRY(read(input_read.get(), &buf, sizeof(buf))); + if (rc == -1) { + async_safe_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s", strerror(errno)); + return 1; + } else if (rc == 0) { + async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper failed to exec"); + return 1; + } else if (rc != 1) { + async_safe_format_log(ANDROID_LOG_FATAL, "libc", + "read of IPC pipe returned unexpected value: %zd", rc); + return 1; + } else if (buf[0] != '\1') { + async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure"); + return 1; + } + + // crash_dump is ptracing us, fork off a copy of our address space for it to use. + create_vm_process(); + + input_read.reset(); + input_write.reset(); + + // Don't leave a zombie child. + int status; + if (TEMP_FAILURE_RETRY(waitpid(crash_dump_pid, &status, 0)) == -1) { + async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s", + strerror(errno)); + } else if (WIFSTOPPED(status) || WIFSIGNALED(status)) { + async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped"); + } return 0; } -static void resend_signal(siginfo_t* info, bool crash_dump_started) { +static void resend_signal(siginfo_t* info) { // Signals can either be fatal or nonfatal. // For fatal signals, crash_dump will send us the signal we crashed with // before resuming us, so that processes using waitpid on us will see that we @@ -379,16 +418,6 @@ static void resend_signal(siginfo_t* info, bool crash_dump_started) { // to deregister our signal handler for that signal before continuing. if (info->si_signo != DEBUGGER_SIGNAL) { signal(info->si_signo, SIG_DFL); - } - - // We need to return from our signal handler so that crash_dump can see the - // signal via ptrace and dump the thread that crashed. However, returning - // does not guarantee that the signal will be thrown again, even for SIGSEGV - // and friends, since the signal could have been sent manually. We blocked - // all signals when registering the handler, so resending the signal (using - // rt_tgsigqueueinfo(2) to preserve SA_SIGINFO) will cause it to be delivered - // when our signal handler returns. - if (crash_dump_started || info->si_signo != DEBUGGER_SIGNAL) { int rc = syscall(SYS_rt_tgsigqueueinfo, __getpid(), __gettid(), info->si_signo, info); if (rc != 0) { fatal_errno("failed to resend signal during crash"); @@ -425,7 +454,7 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c } void* abort_message = nullptr; - if (g_callbacks.get_abort_message) { + if (signal_number != DEBUGGER_SIGNAL && g_callbacks.get_abort_message) { abort_message = g_callbacks.get_abort_message(); } @@ -439,7 +468,7 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c // you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing // ANR trace. debuggerd_fallback_handler(info, static_cast(context), abort_message); - resend_signal(info, false); + resend_signal(info); return; } @@ -450,20 +479,14 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c return; } - log_signal_summary(signal_number, info); - - // If this was a fatal crash, populate si_value with the abort message address if possible. - // Note that applications can set an abort message without aborting. - if (abort_message && signal_number != DEBUGGER_SIGNAL) { - info->si_value.sival_ptr = abort_message; - } + log_signal_summary(info); debugger_thread_info thread_info = { - .crash_dump_started = false, - .pseudothread_tid = -1, - .crashing_tid = __gettid(), - .signal_number = signal_number, - .info = info + .pseudothread_tid = -1, + .crashing_tid = __gettid(), + .siginfo = info, + .ucontext = context, + .abort_msg = reinterpret_cast(abort_message), }; // Set PR_SET_DUMPABLE to 1, so that crash_dump can ptrace us. @@ -472,7 +495,8 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c fatal_errno("failed to set dumpable"); } - // Essentially pthread_create without CLONE_FILES (see debuggerd_dispatch_pseudothread). + // Essentially pthread_create without CLONE_FILES, so we still work during file descriptor + // exhaustion. pid_t child_pid = clone(debuggerd_dispatch_pseudothread, pseudothread_stack, CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID, @@ -484,7 +508,7 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c // Wait for the child to start... futex_wait(&thread_info.pseudothread_tid, -1); - // and then wait for it to finish. + // and then wait for it to terminate. futex_wait(&thread_info.pseudothread_tid, child_pid); // Restore PR_SET_DUMPABLE to its original value. @@ -492,21 +516,13 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c fatal_errno("failed to restore dumpable"); } - // Signals can either be fatal or nonfatal. - // For fatal signals, crash_dump will PTRACE_CONT us with the signal we - // crashed with, so that processes using waitpid on us will see that we - // exited with the correct exit status (e.g. so that sh will report - // "Segmentation fault" instead of "Killed"). For this to work, we need - // to deregister our signal handler for that signal before continuing. - if (signal_number != DEBUGGER_SIGNAL) { - signal(signal_number, SIG_DFL); - } - - resend_signal(info, thread_info.crash_dump_started); if (info->si_signo == DEBUGGER_SIGNAL) { // If the signal is fatal, don't unlock the mutex to prevent other crashing threads from // starting to dump right before our death. pthread_mutex_unlock(&crash_mutex); + } else { + // Resend the signal, so that either gdb or the parent's waitpid sees it. + resend_signal(info); } } diff --git a/debuggerd/libdebuggerd/arm/machine.cpp b/debuggerd/libdebuggerd/arm/machine.cpp deleted file mode 100644 index bfb5ea4e9..000000000 --- a/debuggerd/libdebuggerd/arm/machine.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * - * Copyright 2006, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "DEBUG" - -#include "libdebuggerd/machine.h" - -#include -#include -#include -#include - -#include -#include - -#include "libdebuggerd/utility.h" - -void dump_memory_and_code(log_t* log, Backtrace* backtrace) { - pt_regs regs; - if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, ®s)) { - ALOGE("cannot get registers: %s\n", strerror(errno)); - return; - } - - static const char reg_names[] = "r0r1r2r3r4r5r6r7r8r9slfpipsp"; - - for (int reg = 0; reg < 14; reg++) { - dump_memory(log, backtrace, regs.uregs[reg], "memory near %.2s:", ®_names[reg * 2]); - } - - dump_memory(log, backtrace, static_cast(regs.ARM_pc), "code around pc:"); - - if (regs.ARM_pc != regs.ARM_lr) { - dump_memory(log, backtrace, static_cast(regs.ARM_lr), "code around lr:"); - } -} - -#define DUMP_GP_REGISTERS(log, reg_prefix) \ - _LOG(log, logtype::REGISTERS, " r0 %08x r1 %08x r2 %08x r3 %08x\n", \ - static_cast(reg_prefix##r0), static_cast(reg_prefix##r1), \ - static_cast(reg_prefix##r2), static_cast(reg_prefix##r3)); \ - _LOG(log, logtype::REGISTERS, " r4 %08x r5 %08x r6 %08x r7 %08x\n", \ - static_cast(reg_prefix##r4), static_cast(reg_prefix##r5), \ - static_cast(reg_prefix##r6), static_cast(reg_prefix##r7)); \ - _LOG(log, logtype::REGISTERS, " r8 %08x r9 %08x sl %08x fp %08x\n", \ - static_cast(reg_prefix##r8), static_cast(reg_prefix##r9), \ - static_cast(reg_prefix##r10), static_cast(reg_prefix##fp)); \ - _LOG(log, logtype::REGISTERS, " ip %08x sp %08x lr %08x pc %08x cpsr %08x\n", \ - static_cast(reg_prefix##ip), static_cast(reg_prefix##sp), \ - static_cast(reg_prefix##lr), static_cast(reg_prefix##pc), \ - static_cast(reg_prefix##cpsr)) - -void dump_registers(log_t* log, pid_t tid) { - pt_regs r; - if (ptrace(PTRACE_GETREGS, tid, 0, &r)) { - ALOGE("cannot get registers: %s\n", strerror(errno)); - return; - } - - DUMP_GP_REGISTERS(log, r.ARM_); - - user_vfp vfp_regs; - if (ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) { - ALOGE("cannot get FP registers: %s\n", strerror(errno)); - return; - } - - for (size_t i = 0; i < 32; i += 2) { - _LOG(log, logtype::FP_REGISTERS, " d%-2d %016llx d%-2d %016llx\n", - i, vfp_regs.fpregs[i], i+1, vfp_regs.fpregs[i+1]); - } - _LOG(log, logtype::FP_REGISTERS, " scr %08lx\n", vfp_regs.fpscr); -} - -void dump_registers(log_t* log, const ucontext_t* uc) { - DUMP_GP_REGISTERS(log, uc->uc_mcontext.arm_); -} diff --git a/debuggerd/libdebuggerd/arm64/machine.cpp b/debuggerd/libdebuggerd/arm64/machine.cpp deleted file mode 100644 index ad1c95110..000000000 --- a/debuggerd/libdebuggerd/arm64/machine.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * - * Copyright 2014, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "DEBUG" - -#include "libdebuggerd/machine.h" - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "libdebuggerd/utility.h" - -void dump_memory_and_code(log_t* log, Backtrace* backtrace) { - struct user_pt_regs regs; - struct iovec io; - io.iov_base = ®s; - io.iov_len = sizeof(regs); - - if (ptrace(PTRACE_GETREGSET, backtrace->Tid(), reinterpret_cast(NT_PRSTATUS), &io) == -1) { - ALOGE("ptrace failed to get registers: %s", strerror(errno)); - return; - } - - for (int reg = 0; reg < 31; reg++) { - dump_memory(log, backtrace, regs.regs[reg], "memory near x%d:", reg); - } - - dump_memory(log, backtrace, static_cast(regs.pc), "code around pc:"); - - if (regs.pc != regs.sp) { - dump_memory(log, backtrace, static_cast(regs.sp), "code around sp:"); - } -} - -#define DUMP_GP_REGISTERS(log) \ - for (int i = 0; i < 28; i += 4) { \ - const char* fmt = " x%-2d %016llx x%-2d %016llx x%-2d %016llx x%-2d %016llx\n"; \ - _LOG(log, logtype::REGISTERS, fmt, i, r.regs[i], i + 1, r.regs[i + 1], i + 2, r.regs[i + 2], \ - i + 3, r.regs[i + 3]); \ - } \ - _LOG(log, logtype::REGISTERS, " x28 %016llx x29 %016llx x30 %016llx\n", r.regs[28], \ - r.regs[29], r.regs[30]); \ - _LOG(log, logtype::REGISTERS, " sp %016llx pc %016llx pstate %016llx\n", r.sp, r.pc, \ - r.pstate) - -void dump_registers(log_t* log, pid_t tid) { - struct user_pt_regs r; - struct iovec io; - io.iov_base = &r; - io.iov_len = sizeof(r); - - if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRSTATUS, (void*) &io) == -1) { - ALOGE("ptrace error: %s\n", strerror(errno)); - return; - } - - DUMP_GP_REGISTERS(log); - - struct user_fpsimd_state f; - io.iov_base = &f; - io.iov_len = sizeof(f); - - if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRFPREG, (void*) &io) == -1) { - ALOGE("ptrace error: %s\n", strerror(errno)); - return; - } - - for (int i = 0; i < 32; i += 2) { - _LOG(log, logtype::FP_REGISTERS, - " v%-2d %016" PRIx64 "%016" PRIx64 " v%-2d %016" PRIx64 "%016" PRIx64 "\n", - i, - static_cast(f.vregs[i] >> 64), - static_cast(f.vregs[i]), - i+1, - static_cast(f.vregs[i+1] >> 64), - static_cast(f.vregs[i+1])); - } - _LOG(log, logtype::FP_REGISTERS, " fpsr %08x fpcr %08x\n", f.fpsr, f.fpcr); -} - -void dump_registers(log_t* log, const ucontext_t* ucontext) { - const mcontext_t& r = ucontext->uc_mcontext; - DUMP_GP_REGISTERS(log); -} diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp index f616e1ba0..f0a01f41c 100644 --- a/debuggerd/libdebuggerd/backtrace.cpp +++ b/debuggerd/libdebuggerd/backtrace.cpp @@ -30,12 +30,15 @@ #include #include +#include #include #include +#include #include #include +#include "libdebuggerd/types.h" #include "libdebuggerd/utility.h" static void dump_process_header(log_t* log, pid_t pid, const char* process_name) { @@ -56,62 +59,46 @@ static void dump_process_footer(log_t* log, pid_t pid) { _LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid); } -static void log_thread_name(log_t* log, pid_t tid, const char* thread_name) { - _LOG(log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", thread_name, tid); -} - -static void dump_thread(log_t* log, BacktraceMap* map, pid_t pid, pid_t tid, - const std::string& thread_name) { - log_thread_name(log, tid, thread_name.c_str()); - - std::unique_ptr backtrace(Backtrace::Create(pid, tid, map)); - if (backtrace->Unwind(0)) { - dump_backtrace_to_log(backtrace.get(), log, " "); - } else { - ALOGE("Unwind failed: tid = %d: %s", tid, - backtrace->GetErrorString(backtrace->GetError()).c_str()); - } -} - -void dump_backtrace(int fd, BacktraceMap* map, pid_t pid, pid_t tid, const std::string& process_name, - const std::map& threads, std::string* amfd_data) { - log_t log; - log.tfd = fd; - log.amfd_data = amfd_data; - - dump_process_header(&log, pid, process_name.c_str()); - dump_thread(&log, map, pid, tid, threads.find(tid)->second.c_str()); - - for (const auto& it : threads) { - pid_t thread_tid = it.first; - const std::string& thread_name = it.second; - if (thread_tid != tid) { - dump_thread(&log, map, pid, thread_tid, thread_name.c_str()); - } - } - - dump_process_footer(&log, pid); -} - -void dump_backtrace_ucontext(int output_fd, ucontext_t* ucontext) { - pid_t pid = getpid(); - pid_t tid = gettid(); - +void dump_backtrace_thread(int output_fd, BacktraceMap* map, const ThreadInfo& thread) { log_t log; log.tfd = output_fd; log.amfd_data = nullptr; - char thread_name[16]; - read_with_default("/proc/self/comm", thread_name, sizeof(thread_name), ""); - log_thread_name(&log, tid, thread_name); + _LOG(&log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", thread.thread_name.c_str(), thread.tid); - std::unique_ptr backtrace(Backtrace::Create(pid, tid)); - if (backtrace->Unwind(0, ucontext)) { - dump_backtrace_to_log(backtrace.get(), &log, " "); - } else { - ALOGE("Unwind failed: tid = %d: %s", tid, - backtrace->GetErrorString(backtrace->GetError()).c_str()); + std::vector frames; + if (!Backtrace::Unwind(thread.registers.get(), map, &frames, 0, nullptr)) { + _LOG(&log, logtype::THREAD, "Unwind failed: tid = %d", thread.tid); + return; } + + for (auto& frame : frames) { + _LOG(&log, logtype::BACKTRACE, " %s\n", Backtrace::FormatFrameData(&frame).c_str()); + } +} + +void dump_backtrace(android::base::unique_fd output_fd, BacktraceMap* map, + const std::map& thread_info, pid_t target_thread) { + log_t log; + log.tfd = output_fd.get(); + log.amfd_data = nullptr; + + auto target = thread_info.find(target_thread); + if (target == thread_info.end()) { + ALOGE("failed to find target thread in thread info"); + return; + } + + dump_process_header(&log, target->second.pid, target->second.process_name.c_str()); + + dump_backtrace_thread(output_fd.get(), map, target->second); + for (const auto& [tid, info] : thread_info) { + if (tid != target_thread) { + dump_backtrace_thread(output_fd.get(), map, info); + } + } + + dump_process_footer(&log, target->second.pid); } void dump_backtrace_header(int output_fd) { @@ -131,9 +118,3 @@ void dump_backtrace_footer(int output_fd) { dump_process_footer(&log, getpid()); } - -void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix) { - for (size_t i = 0; i < backtrace->NumFrames(); i++) { - _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, backtrace->FormatFrameData(i).c_str()); - } -} diff --git a/debuggerd/libdebuggerd/elf_utils.cpp b/debuggerd/libdebuggerd/elf_utils.cpp index a35102f63..d7afc0b49 100644 --- a/debuggerd/libdebuggerd/elf_utils.cpp +++ b/debuggerd/libdebuggerd/elf_utils.cpp @@ -26,28 +26,28 @@ #include #include -#include #include +#include #define NOTE_ALIGN(size) (((size) + 3) & ~3) template -static bool get_build_id( - Backtrace* backtrace, uintptr_t base_addr, uint8_t* e_ident, std::string* build_id) { +static bool get_build_id(unwindstack::Memory* memory, uintptr_t base_addr, uint8_t* e_ident, + std::string* build_id) { HdrType hdr; memcpy(&hdr.e_ident[0], e_ident, EI_NIDENT); // First read the rest of the header. - if (backtrace->Read(base_addr + EI_NIDENT, reinterpret_cast(&hdr) + EI_NIDENT, - sizeof(HdrType) - EI_NIDENT) != sizeof(HdrType) - EI_NIDENT) { + if (memory->Read(base_addr + EI_NIDENT, reinterpret_cast(&hdr) + EI_NIDENT, + sizeof(HdrType) - EI_NIDENT) != sizeof(HdrType) - EI_NIDENT) { return false; } for (size_t i = 0; i < hdr.e_phnum; i++) { PhdrType phdr; - if (backtrace->Read(base_addr + hdr.e_phoff + i * hdr.e_phentsize, - reinterpret_cast(&phdr), sizeof(phdr)) != sizeof(phdr)) { + if (memory->Read(base_addr + hdr.e_phoff + i * hdr.e_phentsize, + reinterpret_cast(&phdr), sizeof(phdr)) != sizeof(phdr)) { return false; } // Looking for the .note.gnu.build-id note. @@ -56,7 +56,7 @@ static bool get_build_id( uintptr_t addr = base_addr + phdr.p_offset; while (hdr_size >= sizeof(NhdrType)) { NhdrType nhdr; - if (backtrace->Read(addr, reinterpret_cast(&nhdr), sizeof(nhdr)) != sizeof(nhdr)) { + if (memory->Read(addr, reinterpret_cast(&nhdr), sizeof(nhdr)) != sizeof(nhdr)) { return false; } addr += sizeof(nhdr); @@ -69,7 +69,7 @@ static bool get_build_id( nhdr.n_descsz); return false; } - if (backtrace->Read(addr, build_id_data, nhdr.n_descsz) != nhdr.n_descsz) { + if (memory->Read(addr, build_id_data, nhdr.n_descsz) != nhdr.n_descsz) { return false; } @@ -95,10 +95,10 @@ static bool get_build_id( return false; } -bool elf_get_build_id(Backtrace* backtrace, uintptr_t addr, std::string* build_id) { +bool elf_get_build_id(unwindstack::Memory* memory, uintptr_t addr, std::string* build_id) { // Read and verify the elf magic number first. uint8_t e_ident[EI_NIDENT]; - if (backtrace->Read(addr, e_ident, SELFMAG) != SELFMAG) { + if (memory->Read(addr, e_ident, SELFMAG) != SELFMAG) { return false; } @@ -107,14 +107,14 @@ bool elf_get_build_id(Backtrace* backtrace, uintptr_t addr, std::string* build_i } // Read the rest of EI_NIDENT. - if (backtrace->Read(addr + SELFMAG, e_ident + SELFMAG, EI_NIDENT - SELFMAG) != EI_NIDENT - SELFMAG) { + if (memory->Read(addr + SELFMAG, e_ident + SELFMAG, EI_NIDENT - SELFMAG) != EI_NIDENT - SELFMAG) { return false; } if (e_ident[EI_CLASS] == ELFCLASS32) { - return get_build_id(backtrace, addr, e_ident, build_id); + return get_build_id(memory, addr, e_ident, build_id); } else if (e_ident[EI_CLASS] == ELFCLASS64) { - return get_build_id(backtrace, addr, e_ident, build_id); + return get_build_id(memory, addr, e_ident, build_id); } return false; diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h index fe738f1c7..119e59b47 100644 --- a/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h +++ b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h @@ -23,21 +23,20 @@ #include #include +#include + +#include "types.h" #include "utility.h" -class Backtrace; class BacktraceMap; // Dumps a backtrace using a format similar to what Dalvik uses so that the result // can be intermixed in a bug report. -void dump_backtrace(int fd, BacktraceMap* map, pid_t pid, pid_t tid, const std::string& process_name, - const std::map& threads, std::string* amfd_data); +void dump_backtrace(android::base::unique_fd output_fd, BacktraceMap* map, + const std::map& thread_info, pid_t target_thread); -/* Dumps the backtrace in the backtrace data structure to the log. */ -void dump_backtrace_to_log(Backtrace* backtrace, log_t* log, const char* prefix); - -void dump_backtrace_ucontext(int output_fd, ucontext_t* ucontext); void dump_backtrace_header(int output_fd); +void dump_backtrace_thread(int output_fd, BacktraceMap* map, const ThreadInfo& thread); void dump_backtrace_footer(int output_fd); #endif // _DEBUGGERD_BACKTRACE_H diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h b/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h index 11d0a4348..5d0d92495 100644 --- a/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h +++ b/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h @@ -20,8 +20,10 @@ #include #include -class Backtrace; +namespace unwindstack { +class Memory; +} -bool elf_get_build_id(Backtrace*, uintptr_t, std::string*); +bool elf_get_build_id(unwindstack::Memory*, uintptr_t, std::string*); #endif // _DEBUGGERD_ELF_UTILS_H diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/machine.h b/debuggerd/libdebuggerd/include/libdebuggerd/machine.h deleted file mode 100644 index 5e5668253..000000000 --- a/debuggerd/libdebuggerd/include/libdebuggerd/machine.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _DEBUGGERD_MACHINE_H -#define _DEBUGGERD_MACHINE_H - -#include - -#include - -#include "utility.h" - -void dump_memory_and_code(log_t* log, Backtrace* backtrace); -void dump_registers(log_t* log, pid_t tid); -void dump_registers(log_t* log, const ucontext_t* uc); - -#endif // _DEBUGGERD_MACHINE_H diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h b/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h index b37228d03..4727ca4d7 100644 --- a/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h +++ b/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h @@ -31,6 +31,6 @@ typedef std::vector> OpenFilesList; void populate_open_files_list(pid_t pid, OpenFilesList* list); /* Dumps the open files list to the log. */ -void dump_open_files_list_to_log(const OpenFilesList& files, log_t* log, const char* prefix); +void dump_open_files_list(log_t* log, const OpenFilesList& files, const char* prefix); #endif // _DEBUGGERD_OPEN_FILES_LIST_H diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h index 79743b61c..198c48b26 100644 --- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h +++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h @@ -24,7 +24,10 @@ #include #include +#include + #include "open_files_list.h" +#include "types.h" class BacktraceMap; @@ -43,11 +46,10 @@ void engrave_tombstone(int tombstone_fd, BacktraceMap* map, const OpenFilesList* void engrave_tombstone_ucontext(int tombstone_fd, uintptr_t abort_msg_address, siginfo_t* siginfo, ucontext_t* ucontext); -// Compatibility shim. -__attribute__((__unused__)) -static void engrave_tombstone_ucontext(int tombstone_fd, pid_t, pid_t, uintptr_t abort_msg_address, - siginfo_t* siginfo, ucontext_t* ucontext) { - engrave_tombstone_ucontext(tombstone_fd, abort_msg_address, siginfo, ucontext); -} +void engrave_tombstone(android::base::unique_fd output_fd, BacktraceMap* map, + unwindstack::Memory* process_memory, + const std::map& thread_info, pid_t target_thread, + uintptr_t abort_msg_address, OpenFilesList* open_files, + std::string* amfd_data); -#endif // _DEBUGGERD_TOMBSTONE_H +#endif // _DEBUGGERD_TOMBSTONE_H diff --git a/debuggerd/libdebuggerd/test/ptrace_fake.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h similarity index 62% rename from debuggerd/libdebuggerd/test/ptrace_fake.h rename to debuggerd/libdebuggerd/include/libdebuggerd/types.h index fdbb66361..70583af30 100644 --- a/debuggerd/libdebuggerd/test/ptrace_fake.h +++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h @@ -1,5 +1,7 @@ +#pragma once + /* - * Copyright (C) 2015 The Android Open Source Project + * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +16,19 @@ * limitations under the License. */ -#ifndef _DEBUGGERD_TEST_PTRACE_FAKE_H -#define _DEBUGGERD_TEST_PTRACE_FAKE_H +#include +#include -#include +#include -void ptrace_set_fake_getsiginfo(const siginfo_t&); +struct ThreadInfo { + std::unique_ptr registers; + pid_t tid; + std::string thread_name; -#endif // _DEBUGGERD_TEST_PTRACE_FAKE_H + pid_t pid; + std::string process_name; + + int signo = 0; + siginfo_t* siginfo = nullptr; +}; diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h index f481b78b8..c5abfe24f 100644 --- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h +++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h @@ -27,21 +27,24 @@ #include #include -struct log_t{ - // Tombstone file descriptor. - int tfd; - // Data to be sent to the Activity Manager. - std::string* amfd_data; - // The tid of the thread that crashed. - pid_t crashed_tid; - // The tid of the thread we are currently working with. - pid_t current_tid; - // logd daemon crash, can block asking for logcat data, allow suppression. - bool should_retrieve_logcat; +struct log_t { + // Tombstone file descriptor. + int tfd; + // Data to be sent to the Activity Manager. + std::string* amfd_data; + // The tid of the thread that crashed. + pid_t crashed_tid; + // The tid of the thread we are currently working with. + pid_t current_tid; + // logd daemon crash, can block asking for logcat data, allow suppression. + bool should_retrieve_logcat; - log_t() - : tfd(-1), amfd_data(nullptr), crashed_tid(-1), current_tid(-1), - should_retrieve_logcat(true) {} + log_t() + : tfd(-1), + amfd_data(nullptr), + crashed_tid(-1), + current_tid(-1), + should_retrieve_logcat(true) {} }; // List of types of logs to simplify the logging decision in _LOG @@ -59,13 +62,20 @@ enum logtype { }; // Log information onto the tombstone. -void _LOG(log_t* log, logtype ltype, const char *fmt, ...) - __attribute__ ((format(printf, 3, 4))); +void _LOG(log_t* log, logtype ltype, const char* fmt, ...) __attribute__((format(printf, 3, 4))); -bool wait_for_signal(pid_t tid, siginfo_t* siginfo); +namespace unwindstack { +class Memory; +} -void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...); +void dump_memory(log_t* log, unwindstack::Memory* backtrace, uintptr_t addr, const char* fmt, ...); void read_with_default(const char* path, char* buf, size_t len, const char* default_value); +void drop_capabilities(); + +bool signal_has_si_addr(int si_signo, int si_code); +const char* get_signame(int sig); +const char* get_sigcode(int signo, int code); + #endif // _DEBUGGERD_UTILITY_H diff --git a/debuggerd/libdebuggerd/mips/machine.cpp b/debuggerd/libdebuggerd/mips/machine.cpp deleted file mode 100644 index 1fc690b4a..000000000 --- a/debuggerd/libdebuggerd/mips/machine.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2012, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "DEBUG" - -#include "libdebuggerd/machine.h" - -#include -#include -#include -#include -#include - -#include -#include - -#include "libdebuggerd/utility.h" - -#define R(x) (static_cast(x)) - -// If configured to do so, dump memory around *all* registers -// for the crashing thread. -void dump_memory_and_code(log_t* log, Backtrace* backtrace) { - pt_regs r; - if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) { - ALOGE("cannot get registers: %s\n", strerror(errno)); - return; - } - - static const char reg_names[] = "$0atv0v1a0a1a2a3t0t1t2t3t4t5t6t7s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra"; - - for (int reg = 0; reg < 32; reg++) { - // skip uninteresting registers - if (reg == 0 // $0 - || reg == 26 // $k0 - || reg == 27 // $k1 - || reg == 31 // $ra (done below) - ) - continue; - - dump_memory(log, backtrace, R(r.regs[reg]), "memory near %.2s:", ®_names[reg * 2]); - } - - uintptr_t pc = R(r.cp0_epc); - uintptr_t ra = R(r.regs[31]); - dump_memory(log, backtrace, pc, "code around pc:"); - if (pc != ra) { - dump_memory(log, backtrace, ra, "code around ra:"); - } -} - -void dump_registers(log_t* log, pid_t tid) { - pt_regs r; - if(ptrace(PTRACE_GETREGS, tid, 0, &r)) { - ALOGE("cannot get registers: %s\n", strerror(errno)); - return; - } - - _LOG(log, logtype::REGISTERS, " zr %08" PRIxPTR " at %08" PRIxPTR - " v0 %08" PRIxPTR " v1 %08" PRIxPTR "\n", - R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3])); - _LOG(log, logtype::REGISTERS, " a0 %08" PRIxPTR " a1 %08" PRIxPTR - " a2 %08" PRIxPTR " a3 %08" PRIxPTR "\n", - R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7])); - _LOG(log, logtype::REGISTERS, " t0 %08" PRIxPTR " t1 %08" PRIxPTR - " t2 %08" PRIxPTR " t3 %08" PRIxPTR "\n", - R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11])); - _LOG(log, logtype::REGISTERS, " t4 %08" PRIxPTR " t5 %08" PRIxPTR - " t6 %08" PRIxPTR " t7 %08" PRIxPTR "\n", - R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15])); - _LOG(log, logtype::REGISTERS, " s0 %08" PRIxPTR " s1 %08" PRIxPTR - " s2 %08" PRIxPTR " s3 %08" PRIxPTR "\n", - R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19])); - _LOG(log, logtype::REGISTERS, " s4 %08" PRIxPTR " s5 %08" PRIxPTR - " s6 %08" PRIxPTR " s7 %08" PRIxPTR "\n", - R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23])); - _LOG(log, logtype::REGISTERS, " t8 %08" PRIxPTR " t9 %08" PRIxPTR - " k0 %08" PRIxPTR " k1 %08" PRIxPTR "\n", - R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27])); - _LOG(log, logtype::REGISTERS, " gp %08" PRIxPTR " sp %08" PRIxPTR - " s8 %08" PRIxPTR " ra %08" PRIxPTR "\n", - R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31])); - _LOG(log, logtype::REGISTERS, " hi %08" PRIxPTR " lo %08" PRIxPTR - " bva %08" PRIxPTR " epc %08" PRIxPTR "\n", - R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc)); -} diff --git a/debuggerd/libdebuggerd/mips64/machine.cpp b/debuggerd/libdebuggerd/mips64/machine.cpp deleted file mode 100644 index 955e50728..000000000 --- a/debuggerd/libdebuggerd/mips64/machine.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2014, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "DEBUG" - -#include "libdebuggerd/machine.h" - -#include -#include -#include -#include -#include - -#include -#include - -#include "libdebuggerd/utility.h" - -#define R(x) (static_cast(x)) - -// If configured to do so, dump memory around *all* registers -// for the crashing thread. -void dump_memory_and_code(log_t* log, Backtrace* backtrace) { - pt_regs r; - if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) { - ALOGE("cannot get registers: %s\n", strerror(errno)); - return; - } - - static const char reg_names[] = "$0atv0v1a0a1a2a3a4a5a6a7t0t1t2t3s0s1s2s3s4s5s6s7t8t9k0k1gpsps8ra"; - - for (int reg = 0; reg < 32; reg++) { - // skip uninteresting registers - if (reg == 0 // $0 - || reg == 26 // $k0 - || reg == 27 // $k1 - || reg == 31 // $ra (done below) - ) - continue; - - dump_memory(log, backtrace, R(r.regs[reg]), "memory near %.2s:", ®_names[reg * 2]); - } - - uintptr_t pc = R(r.cp0_epc); - uintptr_t ra = R(r.regs[31]); - dump_memory(log, backtrace, pc, "code around pc:"); - if (pc != ra) { - dump_memory(log, backtrace, ra, "code around ra:"); - } -} - -void dump_registers(log_t* log, pid_t tid) { - pt_regs r; - if(ptrace(PTRACE_GETREGS, tid, 0, &r)) { - ALOGE("cannot get registers: %s\n", strerror(errno)); - return; - } - - _LOG(log, logtype::REGISTERS, " zr %016" PRIxPTR " at %016" PRIxPTR - " v0 %016" PRIxPTR " v1 %016" PRIxPTR "\n", - R(r.regs[0]), R(r.regs[1]), R(r.regs[2]), R(r.regs[3])); - _LOG(log, logtype::REGISTERS, " a0 %016" PRIxPTR " a1 %016" PRIxPTR - " a2 %016" PRIxPTR " a3 %016" PRIxPTR "\n", - R(r.regs[4]), R(r.regs[5]), R(r.regs[6]), R(r.regs[7])); - _LOG(log, logtype::REGISTERS, " a4 %016" PRIxPTR " a5 %016" PRIxPTR - " a6 %016" PRIxPTR " a7 %016" PRIxPTR "\n", - R(r.regs[8]), R(r.regs[9]), R(r.regs[10]), R(r.regs[11])); - _LOG(log, logtype::REGISTERS, " t0 %016" PRIxPTR " t1 %016" PRIxPTR - " t2 %016" PRIxPTR " t3 %016" PRIxPTR "\n", - R(r.regs[12]), R(r.regs[13]), R(r.regs[14]), R(r.regs[15])); - _LOG(log, logtype::REGISTERS, " s0 %016" PRIxPTR " s1 %016" PRIxPTR - " s2 %016" PRIxPTR " s3 %016" PRIxPTR "\n", - R(r.regs[16]), R(r.regs[17]), R(r.regs[18]), R(r.regs[19])); - _LOG(log, logtype::REGISTERS, " s4 %016" PRIxPTR " s5 %016" PRIxPTR - " s6 %016" PRIxPTR " s7 %016" PRIxPTR "\n", - R(r.regs[20]), R(r.regs[21]), R(r.regs[22]), R(r.regs[23])); - _LOG(log, logtype::REGISTERS, " t8 %016" PRIxPTR " t9 %016" PRIxPTR - " k0 %016" PRIxPTR " k1 %016" PRIxPTR "\n", - R(r.regs[24]), R(r.regs[25]), R(r.regs[26]), R(r.regs[27])); - _LOG(log, logtype::REGISTERS, " gp %016" PRIxPTR " sp %016" PRIxPTR - " s8 %016" PRIxPTR " ra %016" PRIxPTR "\n", - R(r.regs[28]), R(r.regs[29]), R(r.regs[30]), R(r.regs[31])); - _LOG(log, logtype::REGISTERS, " hi %016" PRIxPTR " lo %016" PRIxPTR - " bva %016" PRIxPTR " epc %016" PRIxPTR "\n", - R(r.hi), R(r.lo), R(r.cp0_badvaddr), R(r.cp0_epc)); -} diff --git a/debuggerd/libdebuggerd/open_files_list.cpp b/debuggerd/libdebuggerd/open_files_list.cpp index e199db8b4..b12703e14 100644 --- a/debuggerd/libdebuggerd/open_files_list.cpp +++ b/debuggerd/libdebuggerd/open_files_list.cpp @@ -61,7 +61,7 @@ void populate_open_files_list(pid_t pid, OpenFilesList* list) { } } -void dump_open_files_list_to_log(const OpenFilesList& files, log_t* log, const char* prefix) { +void dump_open_files_list(log_t* log, const OpenFilesList& files, const char* prefix) { for (auto& file : files) { _LOG(log, logtype::OPEN_FILES, "%sfd %i: %s\n", prefix, file.first, file.second.c_str()); } diff --git a/debuggerd/libdebuggerd/test/BacktraceMock.h b/debuggerd/libdebuggerd/test/BacktraceMock.h index 6104f7e0d..e7dbed764 100644 --- a/debuggerd/libdebuggerd/test/BacktraceMock.h +++ b/debuggerd/libdebuggerd/test/BacktraceMock.h @@ -17,15 +17,6 @@ #ifndef _DEBUGGERD_TEST_BACKTRACE_MOCK_H #define _DEBUGGERD_TEST_BACKTRACE_MOCK_H -#include -#include -#include -#include - -#include -#include - -#include #include class BacktraceMapMock : public BacktraceMap { @@ -38,69 +29,4 @@ class BacktraceMapMock : public BacktraceMap { } }; - -class BacktraceMock : public Backtrace { - public: - explicit BacktraceMock(BacktraceMapMock* map) : Backtrace(0, 0, map) { - if (map_ == nullptr) { - abort(); - } - } - virtual ~BacktraceMock() {} - - virtual bool Unwind(size_t, ucontext_t*) { return false; } - virtual bool ReadWord(uintptr_t, word_t*) { return false;} - - virtual std::string GetFunctionNameRaw(uintptr_t, uintptr_t*) { return ""; } - - virtual size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes) { - size_t offset = 0; - if (last_read_addr_ > 0) { - offset = addr - last_read_addr_; - } - size_t bytes_available = buffer_.size() - offset; - - if (do_partial_read_) { - // Do a partial read. - if (bytes > bytes_partial_read_) { - bytes = bytes_partial_read_; - } - bytes_partial_read_ -= bytes; - // Only support a single partial read. - do_partial_read_ = false; - } else if (bytes > bytes_available) { - bytes = bytes_available; - } - - if (bytes > 0) { - memcpy(buffer, buffer_.data() + offset, bytes); - } - - last_read_addr_ = addr; - return bytes; - } - - void SetReadData(uint8_t* buffer, size_t bytes) { - buffer_.resize(bytes); - memcpy(buffer_.data(), buffer, bytes); - bytes_partial_read_ = 0; - do_partial_read_ = false; - last_read_addr_ = 0; - } - - void SetPartialReadAmount(size_t bytes) { - if (bytes > buffer_.size()) { - abort(); - } - bytes_partial_read_ = bytes; - do_partial_read_ = true; - } - - private: - std::vector buffer_; - size_t bytes_partial_read_ = 0; - uintptr_t last_read_addr_ = 0; - bool do_partial_read_ = false; -}; - #endif // _DEBUGGERD_TEST_BACKTRACE_MOCK_H diff --git a/debuggerd/libdebuggerd/test/dump_memory_test.cpp b/debuggerd/libdebuggerd/test/dump_memory_test.cpp index 0fad2cf7c..7c8a0ea14 100644 --- a/debuggerd/libdebuggerd/test/dump_memory_test.cpp +++ b/debuggerd/libdebuggerd/test/dump_memory_test.cpp @@ -19,12 +19,12 @@ #include #include -#include #include +#include +#include #include "libdebuggerd/utility.h" -#include "BacktraceMock.h" #include "log_fake.h" const char g_expected_full_dump[] = @@ -103,11 +103,59 @@ const char g_expected_partial_dump[] = \ " 123456d0 -------- -------- -------- -------- ................\n"; #endif +class MemoryMock : public unwindstack::Memory { + public: + virtual ~MemoryMock() = default; + + virtual size_t Read(uint64_t addr, void* buffer, size_t bytes) override { + size_t offset = 0; + if (last_read_addr_ > 0) { + offset = addr - last_read_addr_; + } + size_t bytes_available = buffer_.size() - offset; + + if (partial_read_) { + bytes = std::min(bytes, bytes_partial_read_); + bytes_partial_read_ -= bytes; + partial_read_ = bytes_partial_read_; + } else if (bytes > bytes_available) { + bytes = bytes_available; + } + + if (bytes > 0) { + memcpy(buffer, buffer_.data() + offset, bytes); + } + + last_read_addr_ = addr; + return bytes; + } + + void SetReadData(uint8_t* buffer, size_t bytes) { + buffer_.resize(bytes); + memcpy(buffer_.data(), buffer, bytes); + bytes_partial_read_ = 0; + last_read_addr_ = 0; + } + + void SetPartialReadAmount(size_t bytes) { + if (bytes > buffer_.size()) { + abort(); + } + partial_read_ = true; + bytes_partial_read_ = bytes; + } + + private: + std::vector buffer_; + bool partial_read_ = false; + size_t bytes_partial_read_ = 0; + uintptr_t last_read_addr_ = 0; +}; + class DumpMemoryTest : public ::testing::Test { protected: virtual void SetUp() { - map_mock_.reset(new BacktraceMapMock()); - backtrace_mock_.reset(new BacktraceMock(map_mock_.get())); + memory_mock_ = std::make_unique(); char tmp_file[256]; const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX"; @@ -138,10 +186,10 @@ class DumpMemoryTest : public ::testing::Test { if (log_.tfd >= 0) { close(log_.tfd); } + memory_mock_.reset(); } - std::unique_ptr map_mock_; - std::unique_ptr backtrace_mock_; + std::unique_ptr memory_mock_; log_t log_; }; @@ -151,9 +199,9 @@ TEST_F(DumpMemoryTest, aligned_addr) { for (size_t i = 0; i < sizeof(buffer); i++) { buffer[i] = i; } - backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + memory_mock_->SetReadData(buffer, sizeof(buffer)); - dump_memory(&log_, backtrace_mock_.get(), 0x12345678, "memory near %.2s:", "r1"); + dump_memory(&log_, memory_mock_.get(), 0x12345678, "memory near %.2s:", "r1"); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); @@ -170,10 +218,10 @@ TEST_F(DumpMemoryTest, partial_read) { for (size_t i = 0; i < sizeof(buffer); i++) { buffer[i] = i; } - backtrace_mock_->SetReadData(buffer, sizeof(buffer)); - backtrace_mock_->SetPartialReadAmount(96); + memory_mock_->SetReadData(buffer, sizeof(buffer)); + memory_mock_->SetPartialReadAmount(96); - dump_memory(&log_, backtrace_mock_.get(), 0x12345679, "memory near %.2s:", "r1"); + dump_memory(&log_, memory_mock_.get(), 0x12345679, "memory near %.2s:", "r1"); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); @@ -190,9 +238,9 @@ TEST_F(DumpMemoryTest, unaligned_addr) { for (size_t i = 0; i < sizeof(buffer); i++) { buffer[i] = i; } - backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + memory_mock_->SetReadData(buffer, sizeof(buffer)); - dump_memory(&log_, backtrace_mock_.get(), 0x12345679, "memory near %.2s:", "r1"); + dump_memory(&log_, memory_mock_.get(), 0x12345679, "memory near %.2s:", "r1"); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); @@ -205,7 +253,7 @@ TEST_F(DumpMemoryTest, unaligned_addr) { } TEST_F(DumpMemoryTest, memory_unreadable) { - dump_memory(&log_, backtrace_mock_.get(), 0xa2345678, "memory near pc:"); + dump_memory(&log_, memory_mock_.get(), 0xa2345678, "memory near pc:"); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); @@ -259,9 +307,9 @@ TEST_F(DumpMemoryTest, memory_partially_unreadable) { for (size_t i = 0; i < sizeof(buffer); i++) { buffer[i] = i; } - backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + memory_mock_->SetReadData(buffer, sizeof(buffer)); - dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:"); + dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc:"); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); @@ -278,10 +326,10 @@ TEST_F(DumpMemoryTest, memory_partially_unreadable_unaligned_return) { for (size_t i = 0; i < sizeof(buffer); i++) { buffer[i] = i; } - backtrace_mock_->SetReadData(buffer, sizeof(buffer)); - backtrace_mock_->SetPartialReadAmount(102); + memory_mock_->SetReadData(buffer, sizeof(buffer)); + memory_mock_->SetPartialReadAmount(102); - dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:"); + dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc:"); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); @@ -303,10 +351,10 @@ TEST_F(DumpMemoryTest, memory_partially_unreadable_two_unaligned_reads) { for (size_t i = 0; i < sizeof(buffer); i++) { buffer[i] = i; } - backtrace_mock_->SetReadData(buffer, sizeof(buffer)); - backtrace_mock_->SetPartialReadAmount(45); + memory_mock_->SetReadData(buffer, sizeof(buffer)); + memory_mock_->SetPartialReadAmount(45); - dump_memory(&log_, backtrace_mock_.get(), 0x12345600, "memory near pc:"); + dump_memory(&log_, memory_mock_.get(), 0x12345600, "memory near pc:"); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); @@ -330,9 +378,9 @@ TEST_F(DumpMemoryTest, memory_partially_unreadable_two_unaligned_reads) { TEST_F(DumpMemoryTest, address_low_fence) { uint8_t buffer[256]; memset(buffer, 0, sizeof(buffer)); - backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + memory_mock_->SetReadData(buffer, sizeof(buffer)); - dump_memory(&log_, backtrace_mock_.get(), 0x1000, "memory near %.2s:", "r1"); + dump_memory(&log_, memory_mock_.get(), 0x1000, "memory near %.2s:", "r1"); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); @@ -384,9 +432,9 @@ TEST_F(DumpMemoryTest, address_low_fence) { TEST_F(DumpMemoryTest, memory_address_too_low) { uint8_t buffer[256]; memset(buffer, 0, sizeof(buffer)); - backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + memory_mock_->SetReadData(buffer, sizeof(buffer)); - dump_memory(&log_, backtrace_mock_.get(), 0, "memory near %.2s:", "r1"); + dump_memory(&log_, memory_mock_.get(), 0, "memory near %.2s:", "r1"); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); @@ -401,16 +449,16 @@ TEST_F(DumpMemoryTest, memory_address_too_low) { TEST_F(DumpMemoryTest, memory_address_too_high) { uint8_t buffer[256]; memset(buffer, 0, sizeof(buffer)); - backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + memory_mock_->SetReadData(buffer, sizeof(buffer)); #if defined(__LP64__) - dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL, "memory near %.2s:", "r1"); - dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 32, "memory near %.2s:", "r1"); - dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 216, "memory near %.2s:", "r1"); + dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL, "memory near %.2s:", "r1"); + dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 32, "memory near %.2s:", "r1"); + dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 216, "memory near %.2s:", "r1"); #else - dump_memory(&log_, backtrace_mock_.get(), 0xffff0000, "memory near %.2s:", "r1"); - dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 32, "memory near %.2s:", "r1"); - dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 220, "memory near %.2s:", "r1"); + dump_memory(&log_, memory_mock_.get(), 0xffff0000, "memory near %.2s:", "r1"); + dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 32, "memory near %.2s:", "r1"); + dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 220, "memory near %.2s:", "r1"); #endif std::string tombstone_contents; @@ -426,12 +474,12 @@ TEST_F(DumpMemoryTest, memory_address_too_high) { TEST_F(DumpMemoryTest, memory_address_would_overflow) { uint8_t buffer[256]; memset(buffer, 0, sizeof(buffer)); - backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + memory_mock_->SetReadData(buffer, sizeof(buffer)); #if defined(__LP64__) - dump_memory(&log_, backtrace_mock_.get(), 0xfffffffffffffff0, "memory near %.2s:", "r1"); + dump_memory(&log_, memory_mock_.get(), 0xfffffffffffffff0, "memory near %.2s:", "r1"); #else - dump_memory(&log_, backtrace_mock_.get(), 0xfffffff0, "memory near %.2s:", "r1"); + dump_memory(&log_, memory_mock_.get(), 0xfffffff0, "memory near %.2s:", "r1"); #endif std::string tombstone_contents; @@ -449,12 +497,12 @@ TEST_F(DumpMemoryTest, memory_address_nearly_too_high) { for (size_t i = 0; i < sizeof(buffer); i++) { buffer[i] = i; } - backtrace_mock_->SetReadData(buffer, sizeof(buffer)); + memory_mock_->SetReadData(buffer, sizeof(buffer)); #if defined(__LP64__) - dump_memory(&log_, backtrace_mock_.get(), 0x4000000000000000UL - 224, "memory near %.2s:", "r4"); + dump_memory(&log_, memory_mock_.get(), 0x4000000000000000UL - 224, "memory near %.2s:", "r4"); #else - dump_memory(&log_, backtrace_mock_.get(), 0xffff0000 - 224, "memory near %.2s:", "r4"); + dump_memory(&log_, memory_mock_.get(), 0xffff0000 - 224, "memory near %.2s:", "r4"); #endif std::string tombstone_contents; @@ -509,12 +557,12 @@ TEST_F(DumpMemoryTest, first_read_empty) { for (size_t i = 0; i < sizeof(buffer); i++) { buffer[i] = i; } - backtrace_mock_->SetReadData(buffer, sizeof(buffer)); - backtrace_mock_->SetPartialReadAmount(0); + memory_mock_->SetReadData(buffer, sizeof(buffer)); + memory_mock_->SetPartialReadAmount(0); size_t page_size = sysconf(_SC_PAGE_SIZE); uintptr_t addr = 0x10000020 + page_size - 120; - dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4"); + dump_memory(&log_, memory_mock_.get(), addr, "memory near %.2s:", "r4"); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); @@ -568,12 +616,12 @@ TEST_F(DumpMemoryTest, first_read_empty_second_read_stops) { for (size_t i = 0; i < sizeof(buffer); i++) { buffer[i] = i; } - backtrace_mock_->SetReadData(buffer, sizeof(buffer)); - backtrace_mock_->SetPartialReadAmount(0); + memory_mock_->SetReadData(buffer, sizeof(buffer)); + memory_mock_->SetPartialReadAmount(0); size_t page_size = sysconf(_SC_PAGE_SIZE); uintptr_t addr = 0x10000020 + page_size - 192; - dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4"); + dump_memory(&log_, memory_mock_.get(), addr, "memory near %.2s:", "r4"); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); @@ -627,11 +675,11 @@ TEST_F(DumpMemoryTest, first_read_empty_next_page_out_of_range) { for (size_t i = 0; i < sizeof(buffer); i++) { buffer[i] = i; } - backtrace_mock_->SetReadData(buffer, sizeof(buffer)); - backtrace_mock_->SetPartialReadAmount(0); + memory_mock_->SetReadData(buffer, sizeof(buffer)); + memory_mock_->SetPartialReadAmount(0); uintptr_t addr = 0x10000020; - dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4"); + dump_memory(&log_, memory_mock_.get(), addr, "memory near %.2s:", "r4"); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); @@ -685,13 +733,13 @@ TEST_F(DumpMemoryTest, first_read_empty_next_page_out_of_range_fence_post) { for (size_t i = 0; i < sizeof(buffer); i++) { buffer[i] = i; } - backtrace_mock_->SetReadData(buffer, sizeof(buffer)); - backtrace_mock_->SetPartialReadAmount(0); + memory_mock_->SetReadData(buffer, sizeof(buffer)); + memory_mock_->SetPartialReadAmount(0); size_t page_size = sysconf(_SC_PAGE_SIZE); uintptr_t addr = 0x10000020 + page_size - 256; - dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4"); + dump_memory(&log_, memory_mock_.get(), addr, "memory near %.2s:", "r4"); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); diff --git a/debuggerd/libdebuggerd/test/elf_fake.cpp b/debuggerd/libdebuggerd/test/elf_fake.cpp index f8cbca771..9b8281a70 100644 --- a/debuggerd/libdebuggerd/test/elf_fake.cpp +++ b/debuggerd/libdebuggerd/test/elf_fake.cpp @@ -20,7 +20,9 @@ #include -class Backtrace; +namespace unwindstack { +class Memory; +} std::string g_build_id; @@ -28,7 +30,7 @@ void elf_set_fake_build_id(const std::string& build_id) { g_build_id = build_id; } -bool elf_get_build_id(Backtrace*, uintptr_t, std::string* build_id) { +bool elf_get_build_id(unwindstack::Memory*, uintptr_t, std::string* build_id) { if (g_build_id != "") { *build_id = g_build_id; return true; diff --git a/debuggerd/libdebuggerd/test/ptrace_fake.cpp b/debuggerd/libdebuggerd/test/ptrace_fake.cpp deleted file mode 100644 index 0d4080ebf..000000000 --- a/debuggerd/libdebuggerd/test/ptrace_fake.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "ptrace_fake.h" - -#include -#include -#include -#include - -#include - -siginfo_t g_fake_si = {.si_signo = 0}; - -void ptrace_set_fake_getsiginfo(const siginfo_t& si) { - g_fake_si = si; -} - -#if !defined(__BIONIC__) -extern "C" long ptrace_fake(enum __ptrace_request request, ...) { -#else -extern "C" long ptrace_fake(int request, ...) { -#endif - if (request == PTRACE_GETSIGINFO) { - if (g_fake_si.si_signo == 0) { - errno = EFAULT; - return -1; - } - - va_list ap; - va_start(ap, request); - va_arg(ap, int); - va_arg(ap, int); - siginfo_t* si = va_arg(ap, siginfo*); - va_end(ap); - *si = g_fake_si; - return 0; - } - return -1; -} diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp index 59a43b73b..1e3a10f08 100644 --- a/debuggerd/libdebuggerd/test/tombstone_test.cpp +++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp @@ -29,11 +29,6 @@ #include "elf_fake.h" #include "host_signal_fixup.h" #include "log_fake.h" -#include "ptrace_fake.h" - -// In order to test this code, we need to include the tombstone.cpp code. -// Including it, also allows us to override the ptrace function. -#define ptrace ptrace_fake #include "tombstone.cpp" @@ -50,7 +45,6 @@ class TombstoneTest : public ::testing::Test { protected: virtual void SetUp() { map_mock_.reset(new BacktraceMapMock()); - backtrace_mock_.reset(new BacktraceMock(map_mock_.get())); char tmp_file[256]; const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX"; @@ -77,11 +71,6 @@ class TombstoneTest : public ::testing::Test { resetLogs(); elf_set_fake_build_id(""); - siginfo_t si; - memset(&si, 0, sizeof(si)); - si.si_signo = SIGABRT; - si.si_code = SI_KERNEL; - ptrace_set_fake_getsiginfo(si); } virtual void TearDown() { @@ -91,7 +80,6 @@ class TombstoneTest : public ::testing::Test { } std::unique_ptr map_mock_; - std::unique_ptr backtrace_mock_; log_t log_; std::string amfd_data_; @@ -108,7 +96,7 @@ TEST_F(TombstoneTest, single_map) { #endif map_mock_->AddMap(map); - dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100); + dump_all_maps(&log_, map_mock_.get(), nullptr, 0); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); @@ -143,7 +131,7 @@ TEST_F(TombstoneTest, single_map_elf_build_id) { map_mock_->AddMap(map); elf_set_fake_build_id("abcdef1234567890abcdef1234567890"); - dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100); + dump_all_maps(&log_, map_mock_.get(), nullptr, 0); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); @@ -182,7 +170,7 @@ TEST_F(TombstoneTest, single_map_no_build_id) { map_mock_->AddMap(map); elf_set_fake_build_id("abcdef1234567890abcdef1234567890"); - dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100); + dump_all_maps(&log_, map_mock_.get(), nullptr, 0); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); @@ -240,7 +228,7 @@ TEST_F(TombstoneTest, multiple_maps) { map.name = "/system/lib/fake.so"; map_mock_->AddMap(map); - dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100); + dump_all_maps(&log_, map_mock_.get(), nullptr, 0); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); @@ -294,13 +282,7 @@ TEST_F(TombstoneTest, multiple_maps_fault_address_before) { map.name = "/system/lib/fake.so"; map_mock_->AddMap(map); - siginfo_t si; - memset(&si, 0, sizeof(si)); - si.si_signo = SIGBUS; - si.si_code = SI_KERNEL; - si.si_addr = reinterpret_cast(0x1000); - ptrace_set_fake_getsiginfo(si); - dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100); + dump_all_maps(&log_, map_mock_.get(), nullptr, 0x1000); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); @@ -352,13 +334,7 @@ TEST_F(TombstoneTest, multiple_maps_fault_address_between) { map.name = "/system/lib/fake.so"; map_mock_->AddMap(map); - siginfo_t si; - memset(&si, 0, sizeof(si)); - si.si_signo = SIGBUS; - si.si_code = SI_KERNEL; - si.si_addr = reinterpret_cast(0xa533000); - ptrace_set_fake_getsiginfo(si); - dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100); + dump_all_maps(&log_, map_mock_.get(), nullptr, 0xa533000); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); @@ -410,13 +386,7 @@ TEST_F(TombstoneTest, multiple_maps_fault_address_in_map) { map.name = "/system/lib/fake.so"; map_mock_->AddMap(map); - siginfo_t si; - memset(&si, 0, sizeof(si)); - si.si_signo = SIGBUS; - si.si_code = SI_KERNEL; - si.si_addr = reinterpret_cast(0xa534040); - ptrace_set_fake_getsiginfo(si); - dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100); + dump_all_maps(&log_, map_mock_.get(), nullptr, 0xa534040); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); @@ -466,17 +436,12 @@ TEST_F(TombstoneTest, multiple_maps_fault_address_after) { map.name = "/system/lib/fake.so"; map_mock_->AddMap(map); - siginfo_t si; - memset(&si, 0, sizeof(si)); - si.si_signo = SIGBUS; - si.si_code = SI_KERNEL; #if defined(__LP64__) - si.si_addr = reinterpret_cast(0x12345a534040UL); + uintptr_t addr = 0x12345a534040UL; #else - si.si_addr = reinterpret_cast(0xf534040UL); + uintptr_t addr = 0xf534040UL; #endif - ptrace_set_fake_getsiginfo(si); - dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100); + dump_all_maps(&log_, map_mock_.get(), nullptr, addr); std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); @@ -503,124 +468,6 @@ TEST_F(TombstoneTest, multiple_maps_fault_address_after) { ASSERT_STREQ("", getFakeLogPrint().c_str()); } -TEST_F(TombstoneTest, multiple_maps_getsiginfo_fail) { - backtrace_map_t map; - - map.start = 0xa434000; - map.end = 0xa435000; - map.offset = 0x1000; - map.load_bias = 0xd000; - map.flags = PROT_WRITE; - map_mock_->AddMap(map); - - siginfo_t si; - memset(&si, 0, sizeof(si)); - ptrace_set_fake_getsiginfo(si); - dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100); - - std::string tombstone_contents; - ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); - ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); - const char* expected_dump = - "\nmemory map (1 entry):\n" -#if defined(__LP64__) - " 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n"; -#else - " 0a434000-0a434fff -w- 1000 1000 (load bias 0xd000)\n"; -#endif - ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); - - ASSERT_STREQ("", amfd_data_.c_str()); - - // Verify that the log buf is empty, and no error messages. - ASSERT_STREQ("", getFakeLogBuf().c_str()); - ASSERT_STREQ("6 DEBUG Cannot get siginfo for 100: Bad address\n\n", getFakeLogPrint().c_str()); -} - -TEST_F(TombstoneTest, multiple_maps_check_signal_has_si_addr) { - backtrace_map_t map; - - map.start = 0xa434000; - map.end = 0xa435000; - map.flags = PROT_WRITE; - map_mock_->AddMap(map); - - for (int i = 1; i < 255; i++) { - ASSERT_TRUE(ftruncate(log_.tfd, 0) == 0); - ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); - - siginfo_t si; - memset(&si, 0, sizeof(si)); - si.si_signo = i; - si.si_code = SI_KERNEL; - si.si_addr = reinterpret_cast(0x1000); - ptrace_set_fake_getsiginfo(si); - dump_all_maps(backtrace_mock_.get(), map_mock_.get(), &log_, 100); - - std::string tombstone_contents; - ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); - ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); - bool has_addr = false; - switch (si.si_signo) { - case SIGBUS: - case SIGFPE: - case SIGILL: - case SIGSEGV: - case SIGTRAP: - has_addr = true; - break; - } - - const char* expected_addr_dump = \ -"\nmemory map (1 entry):\n" -#if defined(__LP64__) -"--->Fault address falls at 00000000'00001000 before any mapped regions\n" -" 00000000'0a434000-00000000'0a434fff -w- 0 1000\n"; -#else -"--->Fault address falls at 00001000 before any mapped regions\n" -" 0a434000-0a434fff -w- 0 1000\n"; -#endif - const char* expected_dump = \ -"\nmemory map (1 entry):\n" -#if defined(__LP64__) -" 00000000'0a434000-00000000'0a434fff -w- 0 1000\n"; -#else -" 0a434000-0a434fff -w- 0 1000\n"; -#endif - if (has_addr) { - ASSERT_STREQ(expected_addr_dump, tombstone_contents.c_str()) - << "Signal " << si.si_signo << " expected to include an address."; - } else { - ASSERT_STREQ(expected_dump, tombstone_contents.c_str()) - << "Signal " << si.si_signo << " is not expected to include an address."; - } - - ASSERT_STREQ("", amfd_data_.c_str()); - - // Verify that the log buf is empty, and no error messages. - ASSERT_STREQ("", getFakeLogBuf().c_str()); - ASSERT_STREQ("", getFakeLogPrint().c_str()); - } -} - -TEST_F(TombstoneTest, dump_signal_info_error) { - siginfo_t si; - memset(&si, 0, sizeof(si)); - ptrace_set_fake_getsiginfo(si); - - dump_signal_info(&log_, 123); - - std::string tombstone_contents; - ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); - ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); - ASSERT_STREQ("", tombstone_contents.c_str()); - - ASSERT_STREQ("", getFakeLogBuf().c_str()); - ASSERT_STREQ("6 DEBUG cannot get siginfo: Bad address\n\n", getFakeLogPrint().c_str()); - - ASSERT_STREQ("", amfd_data_.c_str()); -} - TEST_F(TombstoneTest, dump_log_file_error) { log_.should_retrieve_logcat = true; dump_log_file(&log_, 123, "/fake/filename", 10); diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp index a0ba81b68..99da8011b 100644 --- a/debuggerd/libdebuggerd/tombstone.cpp +++ b/debuggerd/libdebuggerd/tombstone.cpp @@ -35,8 +35,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -44,169 +46,27 @@ #include #include #include +#include +#include // Needed to get DEBUGGER_SIGNAL. #include "debuggerd/handler.h" #include "libdebuggerd/backtrace.h" #include "libdebuggerd/elf_utils.h" -#include "libdebuggerd/machine.h" #include "libdebuggerd/open_files_list.h" +#include "libdebuggerd/utility.h" using android::base::GetBoolProperty; using android::base::GetProperty; using android::base::StringPrintf; +using android::base::unique_fd; + +using unwindstack::Memory; +using unwindstack::Regs; #define STACK_WORDS 16 -static bool signal_has_si_addr(int si_signo, int si_code) { - // Manually sent signals won't have si_addr. - if (si_code == SI_USER || si_code == SI_QUEUE || si_code == SI_TKILL) { - return false; - } - - switch (si_signo) { - case SIGBUS: - case SIGFPE: - case SIGILL: - case SIGSEGV: - case SIGTRAP: - return true; - default: - return false; - } -} - -static const char* get_signame(int sig) { - switch (sig) { - case SIGABRT: return "SIGABRT"; - case SIGBUS: return "SIGBUS"; - case SIGFPE: return "SIGFPE"; - case SIGILL: return "SIGILL"; - case SIGSEGV: return "SIGSEGV"; -#if defined(SIGSTKFLT) - case SIGSTKFLT: return "SIGSTKFLT"; -#endif - case SIGSTOP: return "SIGSTOP"; - case SIGSYS: return "SIGSYS"; - case SIGTRAP: return "SIGTRAP"; - case DEBUGGER_SIGNAL: return ""; - default: return "?"; - } -} - -static const char* get_sigcode(int signo, int code) { - // Try the signal-specific codes... - switch (signo) { - case SIGILL: - switch (code) { - case ILL_ILLOPC: return "ILL_ILLOPC"; - case ILL_ILLOPN: return "ILL_ILLOPN"; - case ILL_ILLADR: return "ILL_ILLADR"; - case ILL_ILLTRP: return "ILL_ILLTRP"; - case ILL_PRVOPC: return "ILL_PRVOPC"; - case ILL_PRVREG: return "ILL_PRVREG"; - case ILL_COPROC: return "ILL_COPROC"; - case ILL_BADSTK: return "ILL_BADSTK"; - } - static_assert(NSIGILL == ILL_BADSTK, "missing ILL_* si_code"); - break; - case SIGBUS: - switch (code) { - case BUS_ADRALN: return "BUS_ADRALN"; - case BUS_ADRERR: return "BUS_ADRERR"; - case BUS_OBJERR: return "BUS_OBJERR"; - case BUS_MCEERR_AR: return "BUS_MCEERR_AR"; - case BUS_MCEERR_AO: return "BUS_MCEERR_AO"; - } - static_assert(NSIGBUS == BUS_MCEERR_AO, "missing BUS_* si_code"); - break; - case SIGFPE: - switch (code) { - case FPE_INTDIV: return "FPE_INTDIV"; - case FPE_INTOVF: return "FPE_INTOVF"; - case FPE_FLTDIV: return "FPE_FLTDIV"; - case FPE_FLTOVF: return "FPE_FLTOVF"; - case FPE_FLTUND: return "FPE_FLTUND"; - case FPE_FLTRES: return "FPE_FLTRES"; - case FPE_FLTINV: return "FPE_FLTINV"; - case FPE_FLTSUB: return "FPE_FLTSUB"; - } - static_assert(NSIGFPE == FPE_FLTSUB, "missing FPE_* si_code"); - break; - case SIGSEGV: - switch (code) { - case SEGV_MAPERR: return "SEGV_MAPERR"; - case SEGV_ACCERR: return "SEGV_ACCERR"; -#if defined(SEGV_BNDERR) - case SEGV_BNDERR: return "SEGV_BNDERR"; -#endif -#if defined(SEGV_PKUERR) - case SEGV_PKUERR: return "SEGV_PKUERR"; -#endif - } -#if defined(SEGV_PKUERR) - static_assert(NSIGSEGV == SEGV_PKUERR, "missing SEGV_* si_code"); -#elif defined(SEGV_BNDERR) - static_assert(NSIGSEGV == SEGV_BNDERR, "missing SEGV_* si_code"); -#else - static_assert(NSIGSEGV == SEGV_ACCERR, "missing SEGV_* si_code"); -#endif - break; -#if defined(SYS_SECCOMP) // Our glibc is too old, and we build this for the host too. - case SIGSYS: - switch (code) { - case SYS_SECCOMP: return "SYS_SECCOMP"; - } - static_assert(NSIGSYS == SYS_SECCOMP, "missing SYS_* si_code"); - break; -#endif - case SIGTRAP: - switch (code) { - case TRAP_BRKPT: return "TRAP_BRKPT"; - case TRAP_TRACE: return "TRAP_TRACE"; - case TRAP_BRANCH: return "TRAP_BRANCH"; - case TRAP_HWBKPT: return "TRAP_HWBKPT"; - } - if ((code & 0xff) == SIGTRAP) { - switch ((code >> 8) & 0xff) { - case PTRACE_EVENT_FORK: - return "PTRACE_EVENT_FORK"; - case PTRACE_EVENT_VFORK: - return "PTRACE_EVENT_VFORK"; - case PTRACE_EVENT_CLONE: - return "PTRACE_EVENT_CLONE"; - case PTRACE_EVENT_EXEC: - return "PTRACE_EVENT_EXEC"; - case PTRACE_EVENT_VFORK_DONE: - return "PTRACE_EVENT_VFORK_DONE"; - case PTRACE_EVENT_EXIT: - return "PTRACE_EVENT_EXIT"; - case PTRACE_EVENT_SECCOMP: - return "PTRACE_EVENT_SECCOMP"; - case PTRACE_EVENT_STOP: - return "PTRACE_EVENT_STOP"; - } - } - static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code"); - break; - } - // Then the other codes... - switch (code) { - case SI_USER: return "SI_USER"; - case SI_KERNEL: return "SI_KERNEL"; - case SI_QUEUE: return "SI_QUEUE"; - case SI_TIMER: return "SI_TIMER"; - case SI_MESGQ: return "SI_MESGQ"; - case SI_ASYNCIO: return "SI_ASYNCIO"; - case SI_SIGIO: return "SI_SIGIO"; - case SI_TKILL: return "SI_TKILL"; - case SI_DETHREAD: return "SI_DETHREAD"; - } - // Then give up... - return "?"; -} - static void dump_header_info(log_t* log) { auto fingerprint = GetProperty("ro.build.fingerprint", "unknown"); auto revision = GetProperty("ro.revision", "unknown"); @@ -216,73 +76,64 @@ static void dump_header_info(log_t* log) { _LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING); } -static void dump_probable_cause(log_t* log, const siginfo_t& si) { +static void dump_probable_cause(log_t* log, const siginfo_t* si) { std::string cause; - if (si.si_signo == SIGSEGV && si.si_code == SEGV_MAPERR) { - if (si.si_addr < reinterpret_cast(4096)) { + if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) { + if (si->si_addr < reinterpret_cast(4096)) { cause = StringPrintf("null pointer dereference"); - } else if (si.si_addr == reinterpret_cast(0xffff0ffc)) { + } else if (si->si_addr == reinterpret_cast(0xffff0ffc)) { cause = "call to kuser_helper_version"; - } else if (si.si_addr == reinterpret_cast(0xffff0fe0)) { + } else if (si->si_addr == reinterpret_cast(0xffff0fe0)) { cause = "call to kuser_get_tls"; - } else if (si.si_addr == reinterpret_cast(0xffff0fc0)) { + } else if (si->si_addr == reinterpret_cast(0xffff0fc0)) { cause = "call to kuser_cmpxchg"; - } else if (si.si_addr == reinterpret_cast(0xffff0fa0)) { + } else if (si->si_addr == reinterpret_cast(0xffff0fa0)) { cause = "call to kuser_memory_barrier"; - } else if (si.si_addr == reinterpret_cast(0xffff0f60)) { + } else if (si->si_addr == reinterpret_cast(0xffff0f60)) { cause = "call to kuser_cmpxchg64"; } - } else if (si.si_signo == SIGSYS && si.si_code == SYS_SECCOMP) { - cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", - ABI_STRING, si.si_syscall); + } else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) { + cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING, + si->si_syscall); } if (!cause.empty()) _LOG(log, logtype::HEADER, "Cause: %s\n", cause.c_str()); } -static void dump_signal_info(log_t* log, const siginfo_t* siginfo) { - const siginfo_t& si = *siginfo; +static void dump_signal_info(log_t* log, const siginfo_t* si) { char addr_desc[32]; // ", fault addr 0x1234" - if (signal_has_si_addr(si.si_signo, si.si_code)) { - snprintf(addr_desc, sizeof(addr_desc), "%p", si.si_addr); + if (signal_has_si_addr(si->si_signo, si->si_code)) { + snprintf(addr_desc, sizeof(addr_desc), "%p", si->si_addr); } else { snprintf(addr_desc, sizeof(addr_desc), "--------"); } - _LOG(log, logtype::HEADER, "signal %d (%s), code %d (%s), fault addr %s\n", si.si_signo, - get_signame(si.si_signo), si.si_code, get_sigcode(si.si_signo, si.si_code), addr_desc); + _LOG(log, logtype::HEADER, "signal %d (%s), code %d (%s), fault addr %s\n", si->si_signo, + get_signame(si->si_signo), si->si_code, get_sigcode(si->si_signo, si->si_code), addr_desc); dump_probable_cause(log, si); } -static void dump_signal_info(log_t* log, pid_t tid) { - siginfo_t si; - memset(&si, 0, sizeof(si)); - if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) == -1) { - ALOGE("cannot get siginfo: %s\n", strerror(errno)); - return; - } - - dump_signal_info(log, &si); -} - -static void dump_thread_info(log_t* log, pid_t pid, pid_t tid, const char* process_name, - const char* thread_name) { +static void dump_thread_info(log_t* log, const ThreadInfo& thread_info) { // Blacklist logd, logd.reader, logd.writer, logd.auditd, logd.control ... // TODO: Why is this controlled by thread name? - if (strcmp(thread_name, "logd") == 0 || strncmp(thread_name, "logd.", 4) == 0) { + if (thread_info.thread_name == "logd" || + android::base::StartsWith(thread_info.thread_name, "logd.")) { log->should_retrieve_logcat = false; } - _LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s >>> %s <<<\n", pid, tid, thread_name, - process_name); + _LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s >>> %s <<<\n", thread_info.pid, + thread_info.tid, thread_info.thread_name.c_str(), thread_info.process_name.c_str()); } -static void dump_stack_segment( - Backtrace* backtrace, log_t* log, uintptr_t* sp, size_t words, int label) { +static void dump_stack_segment(log_t* log, BacktraceMap* backtrace_map, Memory* process_memory, + uintptr_t* sp, size_t words, int label) { // Read the data all at once. word_t stack_data[words]; - size_t bytes_read = backtrace->Read(*sp, reinterpret_cast(&stack_data[0]), sizeof(word_t) * words); + + // TODO: Do we need to word align this for crashes caused by a misaligned sp? + // The process_vm_readv implementation of Memory should handle this appropriately? + size_t bytes_read = process_memory->Read(*sp, stack_data, sizeof(word_t) * words); words = bytes_read / sizeof(word_t); std::string line; for (size_t i = 0; i < words; i++) { @@ -296,11 +147,11 @@ static void dump_stack_segment( line += StringPrintf("%" PRIPTR " %" PRIPTR, *sp, stack_data[i]); backtrace_map_t map; - backtrace->FillInMap(stack_data[i], &map); + backtrace_map->FillIn(stack_data[i], &map); if (BacktraceMap::IsValid(map) && !map.name.empty()) { line += " " + map.name; uintptr_t offset = 0; - std::string func_name(backtrace->GetFunctionName(stack_data[i], &offset, &map)); + std::string func_name = backtrace_map->GetFunctionName(stack_data[i], &offset); if (!func_name.empty()) { line += " (" + func_name; if (offset) { @@ -315,36 +166,38 @@ static void dump_stack_segment( } } -static void dump_stack(Backtrace* backtrace, log_t* log) { +static void dump_stack(log_t* log, BacktraceMap* backtrace_map, Memory* process_memory, + std::vector& frames) { size_t first = 0, last; - for (size_t i = 0; i < backtrace->NumFrames(); i++) { - const backtrace_frame_data_t* frame = backtrace->GetFrame(i); - if (frame->sp) { + for (size_t i = 0; i < frames.size(); i++) { + const backtrace_frame_data_t& frame = frames[i]; + if (frame.sp) { if (!first) { first = i+1; } last = i; } } + if (!first) { return; } first--; // Dump a few words before the first frame. - word_t sp = backtrace->GetFrame(first)->sp - STACK_WORDS * sizeof(word_t); - dump_stack_segment(backtrace, log, &sp, STACK_WORDS, -1); + word_t sp = frames[first].sp - STACK_WORDS * sizeof(word_t); + dump_stack_segment(log, backtrace_map, process_memory, &sp, STACK_WORDS, -1); // Dump a few words from all successive frames. // Only log the first 3 frames, put the rest in the tombstone. for (size_t i = first; i <= last; i++) { - const backtrace_frame_data_t* frame = backtrace->GetFrame(i); + const backtrace_frame_data_t* frame = &frames[i]; if (sp != frame->sp) { _LOG(log, logtype::STACK, " ........ ........\n"); sp = frame->sp; } if (i == last) { - dump_stack_segment(backtrace, log, &sp, STACK_WORDS, i); + dump_stack_segment(log, backtrace_map, process_memory, &sp, STACK_WORDS, i); if (sp < frame->sp + frame->stack_size) { _LOG(log, logtype::STACK, " ........ ........\n"); } @@ -355,7 +208,7 @@ static void dump_stack(Backtrace* backtrace, log_t* log) { } else if (words > STACK_WORDS) { words = STACK_WORDS; } - dump_stack_segment(backtrace, log, &sp, words, i); + dump_stack_segment(log, backtrace_map, process_memory, &sp, words, i); } } } @@ -372,44 +225,34 @@ static std::string get_addr_string(uintptr_t addr) { return addr_str; } -static void dump_abort_message(Backtrace* backtrace, log_t* log, uintptr_t address) { +static void dump_abort_message(log_t* log, Memory* process_memory, uintptr_t address) { if (address == 0) { return; } - address += sizeof(size_t); // Skip the buffer length. + size_t length; + if (!process_memory->ReadFully(address, &length, sizeof(length))) { + _LOG(log, logtype::HEADER, "Failed to read abort message header: %s\n", strerror(errno)); + return; + } char msg[512]; - memset(msg, 0, sizeof(msg)); - char* p = &msg[0]; - while (p < &msg[sizeof(msg)]) { - word_t data; - size_t len = sizeof(word_t); - if (!backtrace->ReadWord(address, &data)) { - break; - } - address += sizeof(word_t); - - while (len > 0 && (*p++ = (data >> (sizeof(word_t) - len) * 8) & 0xff) != 0) { - len--; - } + if (length >= sizeof(msg)) { + _LOG(log, logtype::HEADER, "Abort message too long: claimed length = %zd\n", length); + return; } - msg[sizeof(msg) - 1] = '\0'; + if (!process_memory->ReadFully(address + sizeof(length), msg, length)) { + _LOG(log, logtype::HEADER, "Failed to read abort message: %s\n", strerror(errno)); + return; + } + + msg[length] = '\0'; _LOG(log, logtype::HEADER, "Abort message: '%s'\n", msg); } -static void dump_all_maps(Backtrace* backtrace, BacktraceMap* map, log_t* log, pid_t tid) { - bool print_fault_address_marker = false; - uintptr_t addr = 0; - siginfo_t si; - memset(&si, 0, sizeof(si)); - if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) != -1) { - print_fault_address_marker = signal_has_si_addr(si.si_signo, si.si_code); - addr = reinterpret_cast(si.si_addr); - } else { - ALOGE("Cannot get siginfo for %d: %s\n", tid, strerror(errno)); - } +static void dump_all_maps(log_t* log, BacktraceMap* map, Memory* process_memory, uintptr_t addr) { + bool print_fault_address_marker = addr; ScopedBacktraceMapIteratorLock lock(map); _LOG(log, logtype::MAPS, @@ -464,7 +307,7 @@ static void dump_all_maps(Backtrace* backtrace, BacktraceMap* map, log_t* log, p space_needed = false; line += " " + entry->name; std::string build_id; - if ((entry->flags & PROT_READ) && elf_get_build_id(backtrace, entry->start, &build_id)) { + if ((entry->flags & PROT_READ) && elf_get_build_id(process_memory, entry->start, &build_id)) { line += " (BuildId: " + build_id + ")"; } } @@ -482,50 +325,117 @@ static void dump_all_maps(Backtrace* backtrace, BacktraceMap* map, log_t* log, p } } -static void dump_backtrace_and_stack(Backtrace* backtrace, log_t* log) { - if (backtrace->NumFrames()) { - _LOG(log, logtype::BACKTRACE, "\nbacktrace:\n"); - dump_backtrace_to_log(backtrace, log, " "); - - _LOG(log, logtype::STACK, "\nstack:\n"); - dump_stack(backtrace, log); +void dump_backtrace(log_t* log, std::vector& frames, const char* prefix) { + for (auto& frame : frames) { + _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, Backtrace::FormatFrameData(&frame).c_str()); } } -// Weak noop implementation, real implementations are in /machine.cpp. -__attribute__((weak)) void dump_registers(log_t* log, const ucontext_t*) { - _LOG(log, logtype::REGISTERS, " register dumping unimplemented on this architecture"); +static void print_register_row(log_t* log, + const std::vector>& registers) { + std::string output; + for (auto& [name, value] : registers) { + output += android::base::StringPrintf(" %-3s %0*" PRIxPTR, name.c_str(), + static_cast(2 * sizeof(void*)), + static_cast(value)); + } + + _LOG(log, logtype::REGISTERS, " %s\n", output.c_str()); } -static void dump_thread(log_t* log, pid_t pid, pid_t tid, const std::string& process_name, - const std::string& thread_name, BacktraceMap* map, - uintptr_t abort_msg_address, bool primary_thread) { - log->current_tid = tid; +void dump_registers(log_t* log, Regs* regs) { + // Split lr/sp/pc into their own special row. + static constexpr size_t column_count = 4; + std::vector> current_row; + std::vector> special_row; + +#if defined(__arm__) || defined(__aarch64__) + static constexpr const char* special_registers[] = {"ip", "lr", "sp", "pc"}; +#elif defined(__i386__) + static constexpr const char* special_registers[] = {"ebp", "esp", "eip"}; +#elif defined(__x86_64__) + static constexpr const char* special_registers[] = {"rbp", "rsp", "rip"}; +#else + static constexpr const char* special_registers[] = {}; +#endif + + regs->IterateRegisters([log, ¤t_row, &special_row](const char* name, uint64_t value) { + auto row = ¤t_row; + for (const char* special_name : special_registers) { + if (strcmp(special_name, name) == 0) { + row = &special_row; + break; + } + } + + row->emplace_back(name, value); + if (current_row.size() == column_count) { + print_register_row(log, current_row); + current_row.clear(); + } + }); + + if (!current_row.empty()) { + print_register_row(log, current_row); + } + + print_register_row(log, special_row); +} + +void dump_memory_and_code(log_t* log, Memory* memory, Regs* regs) { + regs->IterateRegisters([log, memory](const char* name, uint64_t value) { + dump_memory(log, memory, value, "memory near %s:", name); + }); +} + +static bool dump_thread(log_t* log, BacktraceMap* map, Memory* process_memory, + const ThreadInfo& thread_info, uintptr_t abort_msg_address, + bool primary_thread) { + UNUSED(process_memory); + log->current_tid = thread_info.tid; if (!primary_thread) { _LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n"); } - dump_thread_info(log, pid, tid, process_name.c_str(), thread_name.c_str()); - dump_signal_info(log, tid); + dump_thread_info(log, thread_info); - std::unique_ptr backtrace(Backtrace::Create(pid, tid, map)); - if (primary_thread) { - dump_abort_message(backtrace.get(), log, abort_msg_address); + if (thread_info.siginfo) { + dump_signal_info(log, thread_info.siginfo); } - dump_registers(log, tid); - if (backtrace->Unwind(0)) { - dump_backtrace_and_stack(backtrace.get(), log); - } else { - ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid); + + dump_registers(log, thread_info.registers.get()); + + std::vector frames; + if (!Backtrace::Unwind(thread_info.registers.get(), map, &frames, 0, nullptr)) { + _LOG(log, logtype::THREAD, "Failed to unwind"); + return false; + } + + if (!frames.empty()) { + _LOG(log, logtype::BACKTRACE, "\nbacktrace:\n"); + dump_backtrace(log, frames, " "); + + _LOG(log, logtype::STACK, "\nstack:\n"); + dump_stack(log, map, process_memory, frames); } if (primary_thread) { - dump_memory_and_code(log, backtrace.get()); + dump_abort_message(log, process_memory, abort_msg_address); + } + + if (primary_thread) { + dump_memory_and_code(log, process_memory, thread_info.registers.get()); if (map) { - dump_all_maps(backtrace.get(), map, log, tid); + uintptr_t addr = 0; + siginfo_t* si = thread_info.siginfo; + if (signal_has_si_addr(si->si_signo, si->si_code)) { + addr = reinterpret_cast(si->si_addr); + } + dump_all_maps(log, map, process_memory, addr); } } log->current_tid = log->crashed_tid; + return true; } // Reads the contents of the specified log device, filters out the entries @@ -534,8 +444,7 @@ static void dump_thread(log_t* log, pid_t pid, pid_t tid, const std::string& pro // If "tail" is non-zero, log the last "tail" number of lines. static EventTagMap* g_eventTagMap = NULL; -static void dump_log_file( - log_t* log, pid_t pid, const char* filename, unsigned int tail) { +static void dump_log_file(log_t* log, pid_t pid, const char* filename, unsigned int tail) { bool first = true; struct logger_list* logger_list; @@ -654,56 +563,15 @@ static void dump_log_file( // Dumps the logs generated by the specified pid to the tombstone, from both // "system" and "main" log devices. Ideally we'd interleave the output. static void dump_logs(log_t* log, pid_t pid, unsigned int tail) { + if (pid == getpid()) { + // Cowardly refuse to dump logs while we're running in-process. + return; + } + dump_log_file(log, pid, "system", tail); dump_log_file(log, pid, "main", tail); } -// Dumps all information about the specified pid to the tombstone. -static void dump_crash(log_t* log, BacktraceMap* map, const OpenFilesList* open_files, pid_t pid, - pid_t tid, const std::string& process_name, - const std::map& threads, uintptr_t abort_msg_address) { - // don't copy log messages to tombstone unless this is a dev device - bool want_logs = GetBoolProperty("ro.debuggable", false); - - _LOG(log, logtype::HEADER, - "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"); - dump_header_info(log); - dump_thread(log, pid, tid, process_name, threads.find(tid)->second, map, abort_msg_address, true); - if (want_logs) { - dump_logs(log, pid, 5); - } - - for (const auto& it : threads) { - pid_t thread_tid = it.first; - const std::string& thread_name = it.second; - - if (thread_tid != tid) { - dump_thread(log, pid, thread_tid, process_name, thread_name, map, 0, false); - } - } - - if (open_files) { - _LOG(log, logtype::OPEN_FILES, "\nopen files:\n"); - dump_open_files_list_to_log(*open_files, log, " "); - } - - if (want_logs) { - dump_logs(log, pid, 0); - } -} - -void engrave_tombstone(int tombstone_fd, BacktraceMap* map, const OpenFilesList* open_files, - pid_t pid, pid_t tid, const std::string& process_name, - const std::map& threads, uintptr_t abort_msg_address, - std::string* amfd_data) { - log_t log; - log.current_tid = tid; - log.crashed_tid = tid; - log.tfd = tombstone_fd; - log.amfd_data = amfd_data; - dump_crash(&log, map, open_files, pid, tid, process_name, threads, abort_msg_address); -} - void engrave_tombstone_ucontext(int tombstone_fd, uintptr_t abort_msg_address, siginfo_t* siginfo, ucontext_t* ucontext) { pid_t pid = getpid(); @@ -721,31 +589,69 @@ void engrave_tombstone_ucontext(int tombstone_fd, uintptr_t abort_msg_address, s read_with_default("/proc/self/comm", thread_name, sizeof(thread_name), ""); read_with_default("/proc/self/cmdline", process_name, sizeof(process_name), ""); + std::unique_ptr regs(Regs::CreateFromUcontext(Regs::CurrentArch(), ucontext)); + + std::map threads; + threads[gettid()] = ThreadInfo{ + .registers = std::move(regs), + .tid = tid, + .thread_name = thread_name, + .pid = pid, + .process_name = process_name, + .siginfo = siginfo, + }; + + std::unique_ptr backtrace_map(BacktraceMap::Create(getpid(), false)); + if (!backtrace_map) { + ALOGE("failed to create backtrace map"); + _exit(1); + } + + std::shared_ptr process_memory = backtrace_map->GetProcessMemory(); + engrave_tombstone(unique_fd(dup(tombstone_fd)), backtrace_map.get(), process_memory.get(), + threads, tid, abort_msg_address, nullptr, nullptr); +} + +void engrave_tombstone(unique_fd output_fd, BacktraceMap* map, Memory* process_memory, + const std::map& threads, pid_t target_thread, + uintptr_t abort_msg_address, OpenFilesList* open_files, + std::string* amfd_data) { + // don't copy log messages to tombstone unless this is a dev device + bool want_logs = android::base::GetBoolProperty("ro.debuggable", false); + + log_t log; + log.current_tid = target_thread; + log.crashed_tid = target_thread; + log.tfd = output_fd.get(); + log.amfd_data = amfd_data; + _LOG(&log, logtype::HEADER, "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"); dump_header_info(&log); - dump_thread_info(&log, pid, tid, thread_name, process_name); - dump_signal_info(&log, siginfo); - std::unique_ptr backtrace(Backtrace::Create(pid, tid)); - dump_abort_message(backtrace.get(), &log, abort_msg_address); - dump_registers(&log, ucontext); + auto it = threads.find(target_thread); + if (it == threads.end()) { + LOG(FATAL) << "failed to find target thread"; + } + dump_thread(&log, map, process_memory, it->second, abort_msg_address, true); - if (backtrace->Unwind(0, ucontext)) { - dump_backtrace_and_stack(backtrace.get(), &log); - } else { - ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid); + if (want_logs) { + dump_logs(&log, it->second.pid, 5); } - // TODO: Make this match the format of dump_all_maps above. - _LOG(&log, logtype::MAPS, "memory map:\n"); - android::base::unique_fd maps_fd(open("/proc/self/maps", O_RDONLY | O_CLOEXEC)); - if (maps_fd == -1) { - _LOG(&log, logtype::MAPS, " failed to open /proc/self/maps: %s", strerror(errno)); - } else { - char buf[256]; - ssize_t rc; - while ((rc = TEMP_FAILURE_RETRY(read(maps_fd.get(), buf, sizeof(buf)))) > 0) { - android::base::WriteFully(tombstone_fd, buf, rc); + for (auto& [tid, thread_info] : threads) { + if (tid == target_thread) { + continue; } + + dump_thread(&log, map, process_memory, thread_info, 0, false); + } + + if (open_files) { + _LOG(&log, logtype::OPEN_FILES, "\nopen files:\n"); + dump_open_files_list(&log, *open_files, " "); + } + + if (want_logs) { + dump_logs(&log, it->second.pid, 0); } } diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp index 1b746527e..247d806ba 100644 --- a/debuggerd/libdebuggerd/utility.cpp +++ b/debuggerd/libdebuggerd/utility.cpp @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include #include @@ -34,7 +36,9 @@ #include #include #include +#include #include +#include using android::base::unique_fd; @@ -117,34 +121,10 @@ void _LOG(log_t* log, enum logtype ltype, const char* fmt, ...) { } } -bool wait_for_signal(pid_t tid, siginfo_t* siginfo) { - while (true) { - int status; - pid_t n = TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL)); - if (n == -1) { - ALOGE("waitpid failed: tid %d, %s", tid, strerror(errno)); - return false; - } else if (n == tid) { - if (WIFSTOPPED(status)) { - if (ptrace(PTRACE_GETSIGINFO, tid, nullptr, siginfo) != 0) { - ALOGE("PTRACE_GETSIGINFO failed: %s", strerror(errno)); - return false; - } - return true; - } else { - ALOGE("unexpected waitpid response: n=%d, status=%08x\n", n, status); - // This is the only circumstance under which we can allow a detach - // to fail with ESRCH, which indicates the tid has exited. - return false; - } - } - } -} - #define MEMORY_BYTES_TO_DUMP 256 #define MEMORY_BYTES_PER_LINE 16 -void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* fmt, ...) { +void dump_memory(log_t* log, unwindstack::Memory* memory, uintptr_t addr, const char* fmt, ...) { std::string log_msg; va_list ap; va_start(ap, fmt); @@ -172,7 +152,7 @@ void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* f // Dump 256 bytes uintptr_t data[MEMORY_BYTES_TO_DUMP/sizeof(uintptr_t)]; memset(data, 0, MEMORY_BYTES_TO_DUMP); - size_t bytes = backtrace->Read(addr, reinterpret_cast(data), sizeof(data)); + size_t bytes = memory->Read(addr, reinterpret_cast(data), sizeof(data)); if (bytes % sizeof(uintptr_t) != 0) { // This should never happen, but just in case. ALOGE("Bytes read %zu, is not a multiple of %zu", bytes, sizeof(uintptr_t)); @@ -199,8 +179,8 @@ void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* f // into a readable map. Only requires one extra read because a map has // to contain at least one page, and the total number of bytes to dump // is smaller than a page. - size_t bytes2 = backtrace->Read(addr + start + bytes, reinterpret_cast(data) + bytes, - sizeof(data) - bytes - start); + size_t bytes2 = memory->Read(addr + start + bytes, reinterpret_cast(data) + bytes, + sizeof(data) - bytes - start); bytes += bytes2; if (bytes2 > 0 && bytes % sizeof(uintptr_t) != 0) { // This should never happen, but we'll try and continue any way. @@ -264,3 +244,169 @@ void read_with_default(const char* path, char* buf, size_t len, const char* defa } strcpy(buf, default_value); } + +void drop_capabilities() { + __user_cap_header_struct capheader; + memset(&capheader, 0, sizeof(capheader)); + capheader.version = _LINUX_CAPABILITY_VERSION_3; + capheader.pid = 0; + + __user_cap_data_struct capdata[2]; + memset(&capdata, 0, sizeof(capdata)); + + if (capset(&capheader, &capdata[0]) == -1) { + PLOG(FATAL) << "failed to drop capabilities"; + } + + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) { + PLOG(FATAL) << "failed to set PR_SET_NO_NEW_PRIVS"; + } +} + +bool signal_has_si_addr(int si_signo, int si_code) { + // Manually sent signals won't have si_addr. + if (si_code == SI_USER || si_code == SI_QUEUE || si_code == SI_TKILL) { + return false; + } + + switch (si_signo) { + case SIGBUS: + case SIGFPE: + case SIGILL: + case SIGSEGV: + case SIGTRAP: + return true; + default: + return false; + } +} + +const char* get_signame(int sig) { + switch (sig) { + case SIGABRT: return "SIGABRT"; + case SIGBUS: return "SIGBUS"; + case SIGFPE: return "SIGFPE"; + case SIGILL: return "SIGILL"; + case SIGSEGV: return "SIGSEGV"; +#if defined(SIGSTKFLT) + case SIGSTKFLT: return "SIGSTKFLT"; +#endif + case SIGSTOP: return "SIGSTOP"; + case SIGSYS: return "SIGSYS"; + case SIGTRAP: return "SIGTRAP"; + case DEBUGGER_SIGNAL: return ""; + default: return "?"; + } +} + +const char* get_sigcode(int signo, int code) { + // Try the signal-specific codes... + switch (signo) { + case SIGILL: + switch (code) { + case ILL_ILLOPC: return "ILL_ILLOPC"; + case ILL_ILLOPN: return "ILL_ILLOPN"; + case ILL_ILLADR: return "ILL_ILLADR"; + case ILL_ILLTRP: return "ILL_ILLTRP"; + case ILL_PRVOPC: return "ILL_PRVOPC"; + case ILL_PRVREG: return "ILL_PRVREG"; + case ILL_COPROC: return "ILL_COPROC"; + case ILL_BADSTK: return "ILL_BADSTK"; + } + static_assert(NSIGILL == ILL_BADSTK, "missing ILL_* si_code"); + break; + case SIGBUS: + switch (code) { + case BUS_ADRALN: return "BUS_ADRALN"; + case BUS_ADRERR: return "BUS_ADRERR"; + case BUS_OBJERR: return "BUS_OBJERR"; + case BUS_MCEERR_AR: return "BUS_MCEERR_AR"; + case BUS_MCEERR_AO: return "BUS_MCEERR_AO"; + } + static_assert(NSIGBUS == BUS_MCEERR_AO, "missing BUS_* si_code"); + break; + case SIGFPE: + switch (code) { + case FPE_INTDIV: return "FPE_INTDIV"; + case FPE_INTOVF: return "FPE_INTOVF"; + case FPE_FLTDIV: return "FPE_FLTDIV"; + case FPE_FLTOVF: return "FPE_FLTOVF"; + case FPE_FLTUND: return "FPE_FLTUND"; + case FPE_FLTRES: return "FPE_FLTRES"; + case FPE_FLTINV: return "FPE_FLTINV"; + case FPE_FLTSUB: return "FPE_FLTSUB"; + } + static_assert(NSIGFPE == FPE_FLTSUB, "missing FPE_* si_code"); + break; + case SIGSEGV: + switch (code) { + case SEGV_MAPERR: return "SEGV_MAPERR"; + case SEGV_ACCERR: return "SEGV_ACCERR"; +#if defined(SEGV_BNDERR) + case SEGV_BNDERR: return "SEGV_BNDERR"; +#endif +#if defined(SEGV_PKUERR) + case SEGV_PKUERR: return "SEGV_PKUERR"; +#endif + } +#if defined(SEGV_PKUERR) + static_assert(NSIGSEGV == SEGV_PKUERR, "missing SEGV_* si_code"); +#elif defined(SEGV_BNDERR) + static_assert(NSIGSEGV == SEGV_BNDERR, "missing SEGV_* si_code"); +#else + static_assert(NSIGSEGV == SEGV_ACCERR, "missing SEGV_* si_code"); +#endif + break; +#if defined(SYS_SECCOMP) // Our glibc is too old, and we build this for the host too. + case SIGSYS: + switch (code) { + case SYS_SECCOMP: return "SYS_SECCOMP"; + } + static_assert(NSIGSYS == SYS_SECCOMP, "missing SYS_* si_code"); + break; +#endif + case SIGTRAP: + switch (code) { + case TRAP_BRKPT: return "TRAP_BRKPT"; + case TRAP_TRACE: return "TRAP_TRACE"; + case TRAP_BRANCH: return "TRAP_BRANCH"; + case TRAP_HWBKPT: return "TRAP_HWBKPT"; + } + if ((code & 0xff) == SIGTRAP) { + switch ((code >> 8) & 0xff) { + case PTRACE_EVENT_FORK: + return "PTRACE_EVENT_FORK"; + case PTRACE_EVENT_VFORK: + return "PTRACE_EVENT_VFORK"; + case PTRACE_EVENT_CLONE: + return "PTRACE_EVENT_CLONE"; + case PTRACE_EVENT_EXEC: + return "PTRACE_EVENT_EXEC"; + case PTRACE_EVENT_VFORK_DONE: + return "PTRACE_EVENT_VFORK_DONE"; + case PTRACE_EVENT_EXIT: + return "PTRACE_EVENT_EXIT"; + case PTRACE_EVENT_SECCOMP: + return "PTRACE_EVENT_SECCOMP"; + case PTRACE_EVENT_STOP: + return "PTRACE_EVENT_STOP"; + } + } + static_assert(NSIGTRAP == TRAP_HWBKPT, "missing TRAP_* si_code"); + break; + } + // Then the other codes... + switch (code) { + case SI_USER: return "SI_USER"; + case SI_KERNEL: return "SI_KERNEL"; + case SI_QUEUE: return "SI_QUEUE"; + case SI_TIMER: return "SI_TIMER"; + case SI_MESGQ: return "SI_MESGQ"; + case SI_ASYNCIO: return "SI_ASYNCIO"; + case SI_SIGIO: return "SI_SIGIO"; + case SI_TKILL: return "SI_TKILL"; + case SI_DETHREAD: return "SI_DETHREAD"; + } + // Then give up... + return "?"; +} diff --git a/debuggerd/libdebuggerd/x86/machine.cpp b/debuggerd/libdebuggerd/x86/machine.cpp deleted file mode 100644 index 09a64cdb6..000000000 --- a/debuggerd/libdebuggerd/x86/machine.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2006, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "DEBUG" - -#include "libdebuggerd/machine.h" - -#include -#include -#include -#include - -#include -#include - -#include "libdebuggerd/utility.h" - -void dump_memory_and_code(log_t* log, Backtrace* backtrace) { - struct pt_regs r; - if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) { - ALOGE("cannot get registers: %s\n", strerror(errno)); - return; - } - - dump_memory(log, backtrace, static_cast(r.eax), "memory near eax:"); - dump_memory(log, backtrace, static_cast(r.ebx), "memory near ebx:"); - dump_memory(log, backtrace, static_cast(r.ecx), "memory near ecx:"); - dump_memory(log, backtrace, static_cast(r.edx), "memory near edx:"); - dump_memory(log, backtrace, static_cast(r.esi), "memory near esi:"); - dump_memory(log, backtrace, static_cast(r.edi), "memory near edi:"); - - dump_memory(log, backtrace, static_cast(r.eip), "code around eip:"); -} - -void dump_registers(log_t* log, pid_t tid) { - struct pt_regs r; - if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) { - ALOGE("cannot get registers: %s\n", strerror(errno)); - return; - } - - _LOG(log, logtype::REGISTERS, " eax %08lx ebx %08lx ecx %08lx edx %08lx\n", - r.eax, r.ebx, r.ecx, r.edx); - _LOG(log, logtype::REGISTERS, " esi %08lx edi %08lx\n", - r.esi, r.edi); - _LOG(log, logtype::REGISTERS, " xcs %08x xds %08x xes %08x xfs %08x xss %08x\n", - r.xcs, r.xds, r.xes, r.xfs, r.xss); - _LOG(log, logtype::REGISTERS, " eip %08lx ebp %08lx esp %08lx flags %08lx\n", - r.eip, r.ebp, r.esp, r.eflags); -} diff --git a/debuggerd/libdebuggerd/x86_64/machine.cpp b/debuggerd/libdebuggerd/x86_64/machine.cpp deleted file mode 100644 index de1c26836..000000000 --- a/debuggerd/libdebuggerd/x86_64/machine.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* -** Copyright 2013, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#define LOG_TAG "DEBUG" - -#include "libdebuggerd/machine.h" - -#include -#include -#include -#include -#include - -#include -#include - -#include "libdebuggerd/utility.h" - -void dump_memory_and_code(log_t* log, Backtrace* backtrace) { - struct user_regs_struct r; - if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) { - ALOGE("cannot get registers: %s\n", strerror(errno)); - return; - } - - dump_memory(log, backtrace, static_cast(r.rax), "memory near rax:"); - dump_memory(log, backtrace, static_cast(r.rbx), "memory near rbx:"); - dump_memory(log, backtrace, static_cast(r.rcx), "memory near rcx:"); - dump_memory(log, backtrace, static_cast(r.rdx), "memory near rdx:"); - dump_memory(log, backtrace, static_cast(r.rsi), "memory near rsi:"); - dump_memory(log, backtrace, static_cast(r.rdi), "memory near rdi:"); - - dump_memory(log, backtrace, static_cast(r.rip), "code around rip:"); -} - -void dump_registers(log_t* log, pid_t tid) { - struct user_regs_struct r; - if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) { - ALOGE("cannot get registers: %s\n", strerror(errno)); - return; - } - - _LOG(log, logtype::REGISTERS, " rax %016lx rbx %016lx rcx %016lx rdx %016lx\n", - r.rax, r.rbx, r.rcx, r.rdx); - _LOG(log, logtype::REGISTERS, " rsi %016lx rdi %016lx\n", - r.rsi, r.rdi); - _LOG(log, logtype::REGISTERS, " r8 %016lx r9 %016lx r10 %016lx r11 %016lx\n", - r.r8, r.r9, r.r10, r.r11); - _LOG(log, logtype::REGISTERS, " r12 %016lx r13 %016lx r14 %016lx r15 %016lx\n", - r.r12, r.r13, r.r14, r.r15); - _LOG(log, logtype::REGISTERS, " cs %016lx ss %016lx\n", - r.cs, r.ss); - _LOG(log, logtype::REGISTERS, " rip %016lx rbp %016lx rsp %016lx eflags %016lx\n", - r.rip, r.rbp, r.rsp, r.eflags); -} diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h index 7e1961e75..6903b0e55 100644 --- a/debuggerd/protocol.h +++ b/debuggerd/protocol.h @@ -16,7 +16,10 @@ #pragma once +#include #include +#include +#include #include "dump_type.h" @@ -76,3 +79,11 @@ struct InterceptResponse { InterceptStatus status; char error_message[127]; // always null-terminated }; + +// Sent from handler to crash_dump via pipe. +struct __attribute__((__packed__)) CrashInfo { + uint32_t version; // must be 1. + siginfo_t siginfo; + ucontext_t ucontext; + uintptr_t abort_msg_address; +}; diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp index 0bb07ac1b..50c5efc99 100644 --- a/debuggerd/util.cpp +++ b/debuggerd/util.cpp @@ -18,8 +18,12 @@ #include +#include #include +#include +#include +#include #include #include #include "protocol.h" @@ -86,3 +90,15 @@ ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len, unique_fd* _Nullabl return result; } + +std::string get_process_name(pid_t pid) { + std::string result = ""; + android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid), &result); + return result; +} + +std::string get_thread_name(pid_t tid) { + std::string result = ""; + android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/comm", tid), &result); + return android::base::Trim(result); +} diff --git a/debuggerd/util.h b/debuggerd/util.h index 171e07a49..8260b4496 100644 --- a/debuggerd/util.h +++ b/debuggerd/util.h @@ -16,6 +16,8 @@ #pragma once +#include + #include #include @@ -42,3 +44,6 @@ ssize_t send_fd(int sockfd, const void* _Nonnull data, size_t len, android::base // plus any errors returned by the underlying recvmsg. ssize_t recv_fd(int sockfd, void* _Nonnull data, size_t len, android::base::unique_fd* _Nullable out_fd); + +std::string get_process_name(pid_t pid); +std::string get_thread_name(pid_t tid);