xref: /aosp_15_r20/system/unwinding/libunwindstack/utils/tests/ProcessTracerTest.cpp (revision eb293b8f56ee8303637c5595cfcdeef8039e85c6)
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <dlfcn.h>
18 #include <gtest/gtest.h>
19 #include <sys/ptrace.h>
20 #include <sys/types.h>
21 #include <unistd.h>
22 
23 #include <algorithm>
24 #include <array>
25 #include <atomic>
26 #include <cerrno>
27 #include <csignal>
28 #include <cstddef>
29 #include <cstdio>
30 #include <cstdlib>
31 #include <cstring>
32 #include <filesystem>
33 #include <memory>
34 #include <string>
35 #include <system_error>
36 #include <thread>
37 
38 #include <android-base/file.h>
39 #include <procinfo/process.h>
40 
41 #include "OfflineUnwindUtils.h"
42 #include "ProcessTracer.h"
43 #include "tests/TestUtils.h"
44 
45 namespace unwindstack {
46 namespace {
47 
48 class ProcessTracerTest : public ::testing::TestWithParam<bool> {
49  protected:
50   enum class BoolOrTimeout {
51     kSuccess = 0,
52     kFail,
53     kTimeout,
54   };
55 
56   // Setup a child process that has a few threads that simply busy wait.
SetUp()57   void SetUp() override {
58     // Setup signal handlers for child to let parent know that it is ready and for parent
59     // to kill the child.
60     child_is_ready_ = false;
61     ASSERT_NE(SIG_ERR, signal(kChildIsReadySignal, [](int) { child_is_ready_ = true; }))
62         << "Failed to set up signal handler for kChildIsReadySignal: " << strerror(errno);
63     child_keep_running_ = true;
64     ASSERT_NE(SIG_ERR, signal(kStopChildSignal, [](int) { child_keep_running_ = false; }))
65         << "Failed to set up signal handler for kStopChildSignal: " << strerror(errno);
66 
67     pid_t parent_pid = getpid();
68     child_pid_ = fork();
69     if (child_pid_ == static_cast<pid_t>(-1)) FAIL() << "SetUp: fork() failed: " << strerror(errno);
70     if (child_pid_ == 0) {
71       ASSERT_NO_FATAL_FAILURE(ChildProcSpin(parent_pid));
72     }
73 
74     // Make sure the child process has set up its threads before running the test.
75     sigset_t signal_mask, old_mask;
76     sigemptyset(&signal_mask);
77     sigaddset(&signal_mask, kChildIsReadySignal);
78     sigprocmask(SIG_BLOCK, &signal_mask, &old_mask);
79     while (!child_is_ready_) sigsuspend(&old_mask);
80     sigprocmask(SIG_UNBLOCK, &signal_mask, NULL);
81   }
82 
TearDown()83   void TearDown() override {
84     // Send signal to join threads and exit.
85     if (-1 == kill(child_pid_, kStopChildSignal)) {
86       std::cerr << "TearDown: kill sending kStopChildSignal failed: " << strerror(errno) << ".\n";
87       kill(child_pid_, SIGKILL);
88     }
89   }
90 
ChildProcSpin(pid_t parent_pid)91   void ChildProcSpin(pid_t parent_pid) {
92     // Busy wait in a dlopened local library so we can reliably test (across different
93     // architecture) if a process is within a desired ELF.
94     std::unique_ptr<void, decltype(&dlclose)> test_lib_handle(GetTestLibHandle(), &dlclose);
95     ASSERT_TRUE(test_lib_handle);
96     int (*busy_wait_func)() = reinterpret_cast<int (*)()>(dlsym(test_lib_handle.get(), "BusyWait"));
97     ASSERT_NE(nullptr, busy_wait_func);
98 
99     std::array<std::thread, kNumThreads> threads;
100     std::array<std::atomic_bool, kNumThreads> threads_are_ready{false, false, false, false, false};
101     for (size_t i = 0; i < kNumThreads; ++i) {
102       threads.at(i) = std::thread([&threads_are_ready, i, &busy_wait_func]() {
103         while (child_keep_running_) {
104           DoNotOptimize(busy_wait_func());
105           threads_are_ready.at(i) = true;
106         }
107       });
108     }
109     // Wait until all threads have entered the loop before informing parent child is
110     // ready to avoid a race.
111     while (!std::all_of(threads_are_ready.begin(), threads_are_ready.end(),
112                         [&](const std::atomic_bool& el) { return el == true; })) {
113       usleep(100);
114     }
115     ASSERT_NE(-1, kill(parent_pid, kChildIsReadySignal) == -1)
116         << "TearDown: kill sending kChildIsReady failed: " << strerror(errno) << ".\n";
117     for (size_t i = 0; i < kNumThreads; ++i) {
118       threads.at(i).join();
119     }
120     exit(EXIT_SUCCESS);
121   }
122 
StopInDesiredElfTimeout(ProcessTracer & proc,const std::string & elf_name,size_t timeout_sec=2)123   BoolOrTimeout StopInDesiredElfTimeout(ProcessTracer& proc, const std::string& elf_name,
124                                         size_t timeout_sec = 2) {
125     static BoolOrTimeout result = BoolOrTimeout::kSuccess;
126     if (SIG_ERR == signal(SIGALRM, [](int) {
127           result = BoolOrTimeout::kTimeout;
128           // StopInDesiredElf contains signal handler for SIGINT mainly so that we could stop the
129           // search easily when running unwind_for_offline and we can use it here too.
130           kill(getpid(), SIGINT);
131         })) {
132       std::cerr << "Failed to set up signal handler for SIGALRM: " << strerror(errno) << ".\n";
133       exit(EXIT_FAILURE);
134     }
135     alarm(timeout_sec);
136     if (proc.StopInDesiredElf(elf_name)) {
137       result = BoolOrTimeout::kSuccess;
138     } else if (result != BoolOrTimeout::kTimeout) {
139       result = BoolOrTimeout::kFail;
140     }
141     alarm(0);
142     return result;
143   }
144 
145   static constexpr size_t kNumThreads = 5;
146   static constexpr int kChildIsReadySignal = SIGUSR1;
147   static constexpr int kStopChildSignal = SIGUSR2;
148   static inline std::atomic_bool child_is_ready_ = false;
149   static inline std::atomic_bool child_keep_running_ = true;
150   pid_t child_pid_;
151 };
152 
VerifyState(pid_t tid,bool running)153 static void VerifyState(pid_t tid, bool running) {
154   while (true) {
155     android::procinfo::ProcessInfo proc_info;
156     ASSERT_TRUE(GetProcessInfo(tid, &proc_info));
157     if (running) {
158       if (proc_info.state == android::procinfo::kProcessStateRunning ||
159           proc_info.state == android::procinfo::kProcessStateSleeping) {
160         break;
161       }
162     } else if (proc_info.state == android::procinfo::kProcessStateStopped) {
163       break;
164     }
165     usleep(1000);
166   }
167 }
168 
VerifyState(ProcessTracer & proc,bool running)169 static void VerifyState(ProcessTracer& proc, bool running) {
170   // Verify that the main thread and all threads are in the expected state.
171   VerifyState(proc.pid(), running);
172   if (::testing::Test::HasFatalFailure()) return;
173   for (const pid_t& tid : proc.tids()) {
174     VerifyState(tid, running);
175     if (::testing::Test::HasFatalFailure()) return;
176   }
177 }
178 
TEST_P(ProcessTracerTest,stop_and_resume)179 TEST_P(ProcessTracerTest, stop_and_resume) {
180   ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
181 
182   ASSERT_TRUE(proc.Stop());
183   VerifyState(proc, /*running*/ false);
184   if (::testing::Test::HasFatalFailure()) return;
185 
186   ASSERT_TRUE(proc.Resume());
187   VerifyState(proc, /*running*/ true);
188   if (::testing::Test::HasFatalFailure()) return;
189 }
190 
TEST_P(ProcessTracerTest,attach_and_detach)191 TEST_P(ProcessTracerTest, attach_and_detach) {
192   ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
193 
194   ASSERT_TRUE(proc.Attach(child_pid_));
195   // Attaching to the same pid should result in failure and errno indicating that we cannot trace
196   // the priocess because it is already being traced after the call to Attach().
197   ASSERT_EQ(-1, ptrace(PTRACE_ATTACH, child_pid_, nullptr, nullptr));
198   ASSERT_EQ(EPERM, errno);
199   ASSERT_TRUE(proc.Detach(child_pid_));
200   for (const pid_t& tid : proc.tids()) {
201     ASSERT_TRUE(proc.Attach(tid));
202     ASSERT_EQ(-1, ptrace(PTRACE_ATTACH, tid, nullptr, nullptr));
203     ASSERT_EQ(EPERM, errno);
204     ASSERT_TRUE(proc.Detach(tid));
205   }
206 }
207 
TEST_P(ProcessTracerTest,consecutive_attach_fail)208 TEST_P(ProcessTracerTest, consecutive_attach_fail) {
209   if (!GetParam()) GTEST_SKIP();
210   ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
211 
212   bool is_first_thread = true;
213   for (const pid_t& tid : proc.tids()) {
214     if (is_first_thread) {
215       ASSERT_TRUE(proc.Attach(tid));
216       is_first_thread = false;
217     } else {
218       ASSERT_FALSE(proc.Attach(tid));
219     }
220   }
221 }
222 
TEST_P(ProcessTracerTest,trace_invalid_tid)223 TEST_P(ProcessTracerTest, trace_invalid_tid) {
224   if (GetParam()) GTEST_SKIP();
225   ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
226   ASSERT_FALSE(proc.Attach(getpid()));
227   ASSERT_FALSE(proc.Detach(getpid()));
228 }
229 
TEST_P(ProcessTracerTest,detach_with_no_attached)230 TEST_P(ProcessTracerTest, detach_with_no_attached) {
231   if (GetParam()) GTEST_SKIP();
232   ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
233   ASSERT_FALSE(proc.Detach(child_pid_));
234 }
235 
TEST_P(ProcessTracerTest,uses_shared_library)236 TEST_P(ProcessTracerTest, uses_shared_library) {
237   ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
238 
239   std::string elf_name = "libunwindstack_local.so";
240   ASSERT_TRUE(proc.UsesSharedLibrary(child_pid_, elf_name));
241   for (const pid_t& tid : proc.tids()) {
242     ASSERT_TRUE(proc.UsesSharedLibrary(tid, elf_name));
243   }
244 }
245 
TEST_P(ProcessTracerTest,does_not_use_shared_library)246 TEST_P(ProcessTracerTest, does_not_use_shared_library) {
247   ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
248 
249   std::string elf_name = "libfake.so";
250   ASSERT_FALSE(proc.UsesSharedLibrary(child_pid_, elf_name));
251   for (const pid_t& tid : proc.tids()) {
252     ASSERT_FALSE(proc.UsesSharedLibrary(tid, elf_name));
253   }
254 }
255 
TEST_P(ProcessTracerTest,stop_in_elf_we_use)256 TEST_P(ProcessTracerTest, stop_in_elf_we_use) {
257   // Skip the run with is_tracing_threads=false because main thread only uses
258   // the threading library.
259   if (!GetParam()) GTEST_SKIP();
260   ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
261   std::string elf_name = "libunwindstack_local.so";
262 
263   EXPECT_EQ(BoolOrTimeout::kSuccess, StopInDesiredElfTimeout(proc, elf_name));
264 }
265 
TEST_P(ProcessTracerTest,timeout_when_try_to_stop_in_elf_we_do_not_use)266 TEST_P(ProcessTracerTest, timeout_when_try_to_stop_in_elf_we_do_not_use) {
267   ProcessTracer proc(child_pid_, /*is_tracing_threads*/ GetParam());
268   std::string elf_name = "libfake.so";
269 
270   EXPECT_EQ(BoolOrTimeout::kTimeout, StopInDesiredElfTimeout(proc, elf_name));
271 }
272 
273 INSTANTIATE_TEST_CASE_P(IsTracingThreads, ProcessTracerTest, testing::Values(false, true));
274 
275 }  // namespace
276 }  // namespace unwindstack
277