From 875fd55938fe993973948bfb59b9c3814c338ef4 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Wed, 9 Feb 2022 17:59:03 -0800 Subject: Add get initial regs to ThreadUnwinder. For debuggerd that wants to dump the initial registers of the thread being dumped, add a way to copy those registers. Add new unit tests to verify this register saving behavior. Bug: 208933016 Test: Ran unit tests on host and device. Change-Id: If84fd535654e3fb2a4612622aa595ca3f02c4a58 --- libunwindstack/ThreadUnwinder.cpp | 5 +- libunwindstack/include/unwindstack/Unwinder.h | 2 +- libunwindstack/tests/UnwindTest.cpp | 68 +++++++++++++++++++-------- 3 files changed, 53 insertions(+), 22 deletions(-) (limited to 'libunwindstack') diff --git a/libunwindstack/ThreadUnwinder.cpp b/libunwindstack/ThreadUnwinder.cpp index b649491..9a8a0a6 100644 --- a/libunwindstack/ThreadUnwinder.cpp +++ b/libunwindstack/ThreadUnwinder.cpp @@ -145,7 +145,7 @@ ThreadEntry* ThreadUnwinder::SendSignalToThread(int signal, pid_t tid) { return nullptr; } -void ThreadUnwinder::UnwindWithSignal(int signal, pid_t tid, +void ThreadUnwinder::UnwindWithSignal(int signal, pid_t tid, std::unique_ptr* initial_regs, const std::vector* initial_map_names_to_skip, const std::vector* map_suffixes_to_ignore) { ClearErrors(); @@ -164,6 +164,9 @@ void ThreadUnwinder::UnwindWithSignal(int signal, pid_t tid, } std::unique_ptr regs(Regs::CreateFromUcontext(Regs::CurrentArch(), entry->GetUcontext())); + if (initial_regs != nullptr) { + initial_regs->reset(regs->Clone()); + } SetRegs(regs.get()); UnwinderFromPid::Unwind(initial_map_names_to_skip, map_suffixes_to_ignore); diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h index d87e55c..e21e91d 100644 --- a/libunwindstack/include/unwindstack/Unwinder.h +++ b/libunwindstack/include/unwindstack/Unwinder.h @@ -185,7 +185,7 @@ class ThreadUnwinder : public UnwinderFromPid { void Unwind(const std::vector*, const std::vector*) override {} - void UnwindWithSignal(int signal, pid_t tid, + void UnwindWithSignal(int signal, pid_t tid, std::unique_ptr* initial_regs = nullptr, const std::vector* initial_map_names_to_skip = nullptr, const std::vector* map_suffixes_to_ignore = nullptr); diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp index 1996992..80f931b 100644 --- a/libunwindstack/tests/UnwindTest.cpp +++ b/libunwindstack/tests/UnwindTest.cpp @@ -467,8 +467,8 @@ TEST_F(UnwindTest, multiple_threads_unwind_same_map) { size_t frames[kNumConcurrentThreads]; for (size_t i = 0; i < kNumConcurrentThreads; i++) { std::thread* thread = new std::thread([i, &frames, &maps, &process_memory, &wait]() { - while (wait) - ; + while (wait) { + } std::unique_ptr regs(Regs::CreateFromLocal()); RegsGetLocal(regs.get()); @@ -495,8 +495,8 @@ TEST_F(UnwindTest, thread_unwind) { OuterFunction(TEST_TYPE_LOCAL_WAIT_FOR_FINISH); }); - while (tid.load() == 0) - ; + while (tid.load() == 0) { + } ThreadUnwinder unwinder(512); ASSERT_TRUE(unwinder.Init()); @@ -507,6 +507,34 @@ TEST_F(UnwindTest, thread_unwind) { thread.join(); } +TEST_F(UnwindTest, thread_unwind_copy_regs) { + ResetGlobals(); + + std::atomic_int tid(0); + std::thread thread([&tid]() { + tid = android::base::GetThreadId(); + OuterFunction(TEST_TYPE_LOCAL_WAIT_FOR_FINISH); + }); + + while (tid.load() == 0) { + } + + ThreadUnwinder unwinder(512); + ASSERT_TRUE(unwinder.Init()); + std::unique_ptr initial_regs; + unwinder.UnwindWithSignal(SIGRTMIN, tid, &initial_regs); + ASSERT_TRUE(initial_regs != nullptr); + // Verify the initial registers match the first frame pc/sp. + ASSERT_TRUE(unwinder.NumFrames() != 0); + auto initial_frame = unwinder.frames()[0]; + ASSERT_EQ(initial_regs->pc(), initial_frame.pc); + ASSERT_EQ(initial_regs->sp(), initial_frame.sp); + VerifyUnwindFrames(&unwinder, kFunctionOrder); + + g_finish = true; + thread.join(); +} + TEST_F(UnwindTest, thread_unwind_with_external_maps) { ResetGlobals(); @@ -516,8 +544,8 @@ TEST_F(UnwindTest, thread_unwind_with_external_maps) { OuterFunction(TEST_TYPE_LOCAL_WAIT_FOR_FINISH); }); - while (tid.load() == 0) - ; + while (tid.load() == 0) { + } LocalMaps maps; ASSERT_TRUE(maps.Parse()); @@ -546,8 +574,8 @@ static std::thread* CreateUnwindThread(std::atomic_int& tid, ThreadUnwinder& unw std::atomic_bool& start_unwinding, std::atomic_int& unwinders) { return new std::thread([&tid, &unwinder, &start_unwinding, &unwinders]() { - while (!start_unwinding.load()) - ; + while (!start_unwinding.load()) { + } ThreadUnwinder thread_unwinder(512, &unwinder); // Allow the unwind to timeout since this will be doing multiple @@ -573,8 +601,8 @@ TEST_F(UnwindTest, thread_unwind_same_thread_from_threads) { OuterFunction(TEST_TYPE_LOCAL_WAIT_FOR_FINISH); }); - while (g_waiters.load() != 1) - ; + while (g_waiters.load() != 1) { + } ThreadUnwinder unwinder(512); ASSERT_TRUE(unwinder.Init()); @@ -587,8 +615,8 @@ TEST_F(UnwindTest, thread_unwind_same_thread_from_threads) { } start_unwinding = true; - while (unwinders.load() != kNumThreads) - ; + while (unwinders.load() != kNumThreads) { + } for (auto* thread : threads) { thread->join(); @@ -613,8 +641,8 @@ TEST_F(UnwindTest, thread_unwind_multiple_thread_from_threads) { threads.push_back(thread); } - while (g_waiters.load() != kNumThreads) - ; + while (g_waiters.load() != kNumThreads) { + } ThreadUnwinder unwinder(512); ASSERT_TRUE(unwinder.Init()); @@ -627,8 +655,8 @@ TEST_F(UnwindTest, thread_unwind_multiple_thread_from_threads) { } start_unwinding = true; - while (unwinders.load() != kNumThreads) - ; + while (unwinders.load() != kNumThreads) { + } for (auto* thread : unwinder_threads) { thread->join(); @@ -663,8 +691,8 @@ TEST_F(UnwindTest, thread_unwind_multiple_thread_from_threads_updatable_maps) { threads.push_back(thread); } - while (g_waiters.load() != kNumThreads) - ; + while (g_waiters.load() != kNumThreads) { + } ThreadUnwinder unwinder(512, &maps); ASSERT_TRUE(unwinder.Init()); @@ -677,8 +705,8 @@ TEST_F(UnwindTest, thread_unwind_multiple_thread_from_threads_updatable_maps) { } start_unwinding = true; - while (unwinders.load() != kNumThreads) - ; + while (unwinders.load() != kNumThreads) { + } for (auto* thread : unwinder_threads) { thread->join(); -- cgit v1.2.3