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