diff options
author | Christopher Ferris <cferris@google.com> | 2017-07-19 15:47:21 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2017-07-19 15:47:21 +0000 |
commit | 2f80aa506fd17d84f00371f42b9bb274162cab6d (patch) | |
tree | dbdf7638e139f3d77a4e2f97afeaed3abba6623f /libunwindstack/tests/UnwindTest.cpp | |
parent | 106c35568815f58d35ce6db7fbe30675f8d31c5b (diff) | |
parent | b945cc6de0312399099aee4d0578d0eb2291a06e (diff) |
Merge "Add signal handling to the register object." am: 33913ebfb5
am: b945cc6de0
Change-Id: I31c954a03229b264e1bb2a8a75ef5972e60b2bf2
Diffstat (limited to 'libunwindstack/tests/UnwindTest.cpp')
-rw-r--r-- | libunwindstack/tests/UnwindTest.cpp | 135 |
1 files changed, 107 insertions, 28 deletions
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp index 72065c95e2..3c69e2a49c 100644 --- a/libunwindstack/tests/UnwindTest.cpp +++ b/libunwindstack/tests/UnwindTest.cpp @@ -30,6 +30,7 @@ #include <sstream> #include <string> #include <thread> +#include <vector> #include <unwindstack/Elf.h> #include <unwindstack/MapInfo.h> @@ -42,16 +43,41 @@ namespace unwindstack { static std::atomic_bool g_ready(false); static volatile bool g_ready_for_remote = false; +static volatile bool g_signal_ready_for_remote = false; static std::atomic_bool g_finish(false); static std::atomic_uintptr_t g_ucontext; -static void Signal(int, siginfo_t*, void* sigcontext) { +static std::vector<const char*> kFunctionOrder{"InnerFunction", "MiddleFunction", "OuterFunction"}; + +static std::vector<const char*> kFunctionSignalOrder{"SignalInnerFunction", "SignalMiddleFunction", + "SignalOuterFunction", "InnerFunction", + "MiddleFunction", "OuterFunction"}; + +static void SignalHandler(int, siginfo_t*, void* sigcontext) { g_ucontext = reinterpret_cast<uintptr_t>(sigcontext); while (!g_finish.load()) { } } -static std::string ErrorMsg(const char** function_names, size_t index, +extern "C" void SignalInnerFunction() { + g_signal_ready_for_remote = true; + while (!g_finish.load()) { + } +} + +extern "C" void SignalMiddleFunction() { + SignalInnerFunction(); +} + +extern "C" void SignalOuterFunction() { + SignalMiddleFunction(); +} + +static void SignalCallerHandler(int, siginfo_t*, void*) { + SignalOuterFunction(); +} + +static std::string ErrorMsg(const std::vector<const char*>& function_names, size_t index, std::stringstream& unwind_stream) { return std::string( "Unwind completed without finding all frames\n" @@ -59,10 +85,8 @@ static std::string ErrorMsg(const char** function_names, size_t index, function_names[index] + "\n" + "Unwind data:\n" + unwind_stream.str(); } -static void VerifyUnwind(pid_t pid, Memory* memory, Maps* maps, Regs* regs) { - const char* function_names[] = { - "InnerFunction", "MiddleFunction", "OuterFunction", - }; +static void VerifyUnwind(pid_t pid, Memory* memory, Maps* maps, Regs* regs, + std::vector<const char*>& function_names) { size_t function_name_index = 0; std::stringstream unwind_stream; @@ -91,8 +115,7 @@ static void VerifyUnwind(pid_t pid, Memory* memory, Maps* maps, Regs* regs) { uint64_t func_offset; if (elf->GetFunctionName(adjusted_rel_pc, &name, &func_offset)) { if (name == function_names[function_name_index]) { - function_name_index++; - if (function_name_index == sizeof(function_names) / sizeof(const char*)) { + if (++function_name_index == function_names.size()) { return; } } @@ -116,7 +139,7 @@ extern "C" void InnerFunction(bool local) { RegsGetLocal(regs.get()); MemoryLocal memory; - VerifyUnwind(getpid(), &memory, &maps, regs.get()); + VerifyUnwind(getpid(), &memory, &maps, regs.get(), kFunctionOrder); } else { g_ready_for_remote = true; g_ready = true; @@ -137,39 +160,48 @@ TEST(UnwindTest, local) { OuterFunction(true); } -TEST(UnwindTest, remote) { - pid_t pid; - if ((pid = fork()) == 0) { - OuterFunction(false); - exit(0); - } - ASSERT_NE(-1, pid); - - bool ready = false; - uint64_t addr = reinterpret_cast<uint64_t>(&g_ready_for_remote); +void WaitForRemote(pid_t pid, uint64_t addr, bool leave_attached, bool* completed) { + *completed = false; + // Need to sleep before attempting first ptrace. Without this, on the + // host it becomes impossible to attach and ptrace set errno to EPERM. + usleep(1000); for (size_t i = 0; i < 100; i++) { ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, 0, 0)); for (size_t j = 0; j < 100; j++) { siginfo_t si; if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) { - // Check to see if process is ready to be unwound. MemoryRemote memory(pid); // Read the remote value to see if we are ready. bool value; if (memory.Read(addr, &value, sizeof(value)) && value) { - ready = true; + *completed = true; break; } } usleep(1000); } - if (ready) { + if (leave_attached && *completed) { break; } ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0)); + if (*completed) { + break; + } usleep(1000); } - ASSERT_TRUE(read) << "Timed out waiting for remote process to be ready."; +} + +TEST(UnwindTest, remote) { + pid_t pid; + if ((pid = fork()) == 0) { + OuterFunction(false); + exit(0); + } + ASSERT_NE(-1, pid); + + bool completed; + WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed); + ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready."; RemoteMaps maps(pid); ASSERT_TRUE(maps.Parse()); @@ -178,7 +210,7 @@ TEST(UnwindTest, remote) { std::unique_ptr<Regs> regs(Regs::RemoteGet(pid, &machine_type)); ASSERT_TRUE(regs.get() != nullptr); - VerifyUnwind(pid, &memory, &maps, regs.get()); + VerifyUnwind(pid, &memory, &maps, regs.get(), kFunctionOrder); ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0)); @@ -195,7 +227,7 @@ TEST(UnwindTest, from_context) { struct sigaction act, oldact; memset(&act, 0, sizeof(act)); - act.sa_sigaction = Signal; + act.sa_sigaction = SignalHandler; act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK; ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact)); // Wait for the tid to get set. @@ -207,8 +239,7 @@ TEST(UnwindTest, from_context) { } ASSERT_NE(0, tid.load()); // Portable tgkill method. - ASSERT_EQ(0, syscall(__NR_tgkill, getpid(), tid.load(), SIGUSR1)) << "Failed because " - << strerror(errno); + ASSERT_EQ(0, syscall(__NR_tgkill, getpid(), tid.load(), SIGUSR1)) << "Error: " << strerror(errno); // Wait for context data. void* ucontext; @@ -226,7 +257,7 @@ TEST(UnwindTest, from_context) { std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::GetMachineType(), ucontext)); MemoryLocal memory; - VerifyUnwind(tid.load(), &memory, &maps, regs.get()); + VerifyUnwind(tid.load(), &memory, &maps, regs.get(), kFunctionOrder); ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr)); @@ -234,4 +265,52 @@ TEST(UnwindTest, from_context) { thread.join(); } +static void RemoteThroughSignal(unsigned int sa_flags) { + g_ready = false; + g_signal_ready_for_remote = false; + g_finish = false; + + pid_t pid; + if ((pid = fork()) == 0) { + struct sigaction act, oldact; + memset(&act, 0, sizeof(act)); + act.sa_sigaction = SignalCallerHandler; + act.sa_flags = SA_RESTART | SA_ONSTACK | sa_flags; + ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact)); + + OuterFunction(false); + exit(0); + } + ASSERT_NE(-1, pid); + + bool completed; + WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), false, &completed); + ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready."; + ASSERT_EQ(0, kill(pid, SIGUSR1)); + WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_signal_ready_for_remote), true, &completed); + ASSERT_TRUE(completed) << "Timed out waiting for remote process to be in signal handler."; + + RemoteMaps maps(pid); + ASSERT_TRUE(maps.Parse()); + MemoryRemote memory(pid); + uint32_t machine_type; + std::unique_ptr<Regs> regs(Regs::RemoteGet(pid, &machine_type)); + ASSERT_TRUE(regs.get() != nullptr); + + VerifyUnwind(pid, &memory, &maps, regs.get(), kFunctionSignalOrder); + + ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0)); + + kill(pid, SIGKILL); + ASSERT_EQ(pid, wait(nullptr)); +} + +TEST(UnwindTest, remote_through_signal) { + RemoteThroughSignal(0); +} + +TEST(UnwindTest, remote_through_signal_sa_siginfo) { + RemoteThroughSignal(SA_SIGINFO); +} + } // namespace unwindstack |