Fix fallback paths for dumping threads.
In the fallback path, if the non-main thread is the target to be dumped, then no other threads are dumped when creating a tombstone. Fix this and add unit tests to verify that this all threads, including the main thread are dumped. Bug: 234058038 Test: All unit tests pass. Test: debuggerd -b media.swcodec process Test: debuggerd media.swcodec process Change-Id: Ibb75264f7b3847acdbab939a66902d986c0d0e5c
This commit is contained in:
parent
ab2d6cdc8f
commit
7c2e7e31f6
4 changed files with 98 additions and 30 deletions
|
@ -1486,6 +1486,37 @@ TEST_F(CrasherTest, seccomp_tombstone_thread_abort) {
|
|||
ASSERT_BACKTRACE_FRAME(result, "abort");
|
||||
}
|
||||
|
||||
TEST_F(CrasherTest, seccomp_tombstone_multiple_threads_abort) {
|
||||
int intercept_result;
|
||||
unique_fd output_fd;
|
||||
|
||||
static const auto dump_type = kDebuggerdTombstone;
|
||||
StartProcess(
|
||||
[]() {
|
||||
std::thread a(foo);
|
||||
std::thread b(bar);
|
||||
|
||||
std::this_thread::sleep_for(100ms);
|
||||
|
||||
std::thread abort_thread([] { abort(); });
|
||||
abort_thread.join();
|
||||
},
|
||||
&seccomp_fork);
|
||||
|
||||
StartIntercept(&output_fd, dump_type);
|
||||
FinishCrasher();
|
||||
AssertDeath(SIGABRT);
|
||||
FinishIntercept(&intercept_result);
|
||||
ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
|
||||
|
||||
std::string result;
|
||||
ConsumeFd(std::move(output_fd), &result);
|
||||
ASSERT_BACKTRACE_FRAME(result, "abort");
|
||||
ASSERT_BACKTRACE_FRAME(result, "foo");
|
||||
ASSERT_BACKTRACE_FRAME(result, "bar");
|
||||
ASSERT_BACKTRACE_FRAME(result, "main");
|
||||
}
|
||||
|
||||
TEST_F(CrasherTest, seccomp_backtrace) {
|
||||
int intercept_result;
|
||||
unique_fd output_fd;
|
||||
|
@ -1516,6 +1547,40 @@ TEST_F(CrasherTest, seccomp_backtrace) {
|
|||
ASSERT_BACKTRACE_FRAME(result, "bar");
|
||||
}
|
||||
|
||||
TEST_F(CrasherTest, seccomp_backtrace_from_thread) {
|
||||
int intercept_result;
|
||||
unique_fd output_fd;
|
||||
|
||||
static const auto dump_type = kDebuggerdNativeBacktrace;
|
||||
StartProcess(
|
||||
[]() {
|
||||
std::thread a(foo);
|
||||
std::thread b(bar);
|
||||
|
||||
std::this_thread::sleep_for(100ms);
|
||||
|
||||
std::thread raise_thread([] {
|
||||
raise_debugger_signal(dump_type);
|
||||
_exit(0);
|
||||
});
|
||||
raise_thread.join();
|
||||
},
|
||||
&seccomp_fork);
|
||||
|
||||
StartIntercept(&output_fd, dump_type);
|
||||
FinishCrasher();
|
||||
AssertDeath(0);
|
||||
FinishIntercept(&intercept_result);
|
||||
ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
|
||||
|
||||
std::string result;
|
||||
ConsumeFd(std::move(output_fd), &result);
|
||||
ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
|
||||
ASSERT_BACKTRACE_FRAME(result, "foo");
|
||||
ASSERT_BACKTRACE_FRAME(result, "bar");
|
||||
ASSERT_BACKTRACE_FRAME(result, "main");
|
||||
}
|
||||
|
||||
TEST_F(CrasherTest, seccomp_crash_logcat) {
|
||||
StartProcess([]() { abort(); }, &seccomp_fork);
|
||||
FinishCrasher();
|
||||
|
|
|
@ -210,7 +210,10 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
|
|||
|
||||
// Send a signal to all of our siblings, asking them to dump their stack.
|
||||
pid_t current_tid = gettid();
|
||||
if (!iterate_tids(current_tid, [&output_fd](pid_t tid) {
|
||||
if (!iterate_tids(current_tid, [&output_fd, ¤t_tid](pid_t tid) {
|
||||
if (current_tid == tid) {
|
||||
return;
|
||||
}
|
||||
// Use a pipe, to be able to detect situations where the thread gracefully exits before
|
||||
// receiving our signal.
|
||||
unique_fd pipe_read, pipe_write;
|
||||
|
|
|
@ -55,15 +55,15 @@ void engrave_tombstone_ucontext(int tombstone_fd, int proto_fd, uint64_t abort_m
|
|||
siginfo_t* siginfo, ucontext_t* ucontext) {
|
||||
pid_t uid = getuid();
|
||||
pid_t pid = getpid();
|
||||
pid_t tid = gettid();
|
||||
pid_t target_tid = gettid();
|
||||
|
||||
log_t log;
|
||||
log.current_tid = tid;
|
||||
log.crashed_tid = tid;
|
||||
log.current_tid = target_tid;
|
||||
log.crashed_tid = target_tid;
|
||||
log.tfd = tombstone_fd;
|
||||
log.amfd_data = nullptr;
|
||||
|
||||
std::string thread_name = get_thread_name(tid);
|
||||
std::string thread_name = get_thread_name(target_tid);
|
||||
std::vector<std::string> command_line = get_command_line(pid);
|
||||
|
||||
std::unique_ptr<unwindstack::Regs> regs(
|
||||
|
@ -73,32 +73,34 @@ void engrave_tombstone_ucontext(int tombstone_fd, int proto_fd, uint64_t abort_m
|
|||
android::base::ReadFileToString("/proc/self/attr/current", &selinux_label);
|
||||
|
||||
std::map<pid_t, ThreadInfo> threads;
|
||||
threads[tid] = ThreadInfo{
|
||||
.registers = std::move(regs), .uid = uid, .tid = tid, .thread_name = std::move(thread_name),
|
||||
.pid = pid, .command_line = std::move(command_line), .selinux_label = std::move(selinux_label),
|
||||
.siginfo = siginfo,
|
||||
threads[target_tid] = ThreadInfo {
|
||||
.registers = std::move(regs), .uid = uid, .tid = target_tid,
|
||||
.thread_name = std::move(thread_name), .pid = pid, .command_line = std::move(command_line),
|
||||
.selinux_label = std::move(selinux_label), .siginfo = siginfo,
|
||||
#if defined(__aarch64__)
|
||||
// Only supported on aarch64 for now.
|
||||
.tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0),
|
||||
.pac_enabled_keys = prctl(PR_PAC_GET_ENABLED_KEYS, 0, 0, 0, 0),
|
||||
#endif
|
||||
};
|
||||
if (pid == tid) {
|
||||
const ThreadInfo& thread = threads[pid];
|
||||
if (!iterate_tids(pid, [&threads, &thread](pid_t tid) {
|
||||
threads[tid] = ThreadInfo{
|
||||
.uid = thread.uid,
|
||||
.tid = tid,
|
||||
.pid = thread.pid,
|
||||
.command_line = thread.command_line,
|
||||
.thread_name = get_thread_name(tid),
|
||||
.tagged_addr_ctrl = thread.tagged_addr_ctrl,
|
||||
.pac_enabled_keys = thread.pac_enabled_keys,
|
||||
};
|
||||
})) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to open /proc/%d/task: %s", pid,
|
||||
strerror(errno));
|
||||
}
|
||||
const ThreadInfo& thread = threads[pid];
|
||||
if (!iterate_tids(pid, [&threads, &thread, &target_tid](pid_t tid) {
|
||||
if (target_tid == tid) {
|
||||
return;
|
||||
}
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "Adding thread %d", tid);
|
||||
threads[tid] = ThreadInfo{
|
||||
.uid = thread.uid,
|
||||
.tid = tid,
|
||||
.pid = thread.pid,
|
||||
.command_line = thread.command_line,
|
||||
.thread_name = get_thread_name(tid),
|
||||
.tagged_addr_ctrl = thread.tagged_addr_ctrl,
|
||||
.pac_enabled_keys = thread.pac_enabled_keys,
|
||||
};
|
||||
})) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to open /proc/%d/task: %s", pid,
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
// Do not use the thread cache here because it will call pthread_key_create
|
||||
|
@ -116,8 +118,8 @@ void engrave_tombstone_ucontext(int tombstone_fd, int proto_fd, uint64_t abort_m
|
|||
|
||||
ProcessInfo process_info;
|
||||
process_info.abort_msg_address = abort_msg_address;
|
||||
engrave_tombstone(unique_fd(dup(tombstone_fd)), unique_fd(dup(proto_fd)), &unwinder, threads, tid,
|
||||
process_info, nullptr, nullptr);
|
||||
engrave_tombstone(unique_fd(dup(tombstone_fd)), unique_fd(dup(proto_fd)), &unwinder, threads,
|
||||
target_tid, process_info, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void engrave_tombstone(unique_fd output_fd, unique_fd proto_fd,
|
||||
|
|
|
@ -90,9 +90,7 @@ bool iterate_tids(pid_t pid, std::function<void(pid_t)> callback) {
|
|||
if (tid == 0) {
|
||||
continue;
|
||||
}
|
||||
if (pid != tid) {
|
||||
callback(tid);
|
||||
}
|
||||
callback(tid);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue