1 /*
2 * Copyright (C) 2016 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 "ThreadCapture.h"
18
19 #include <fcntl.h>
20 #include <pthread.h>
21 #include <sys/syscall.h>
22 #include <sys/types.h>
23
24 #include <algorithm>
25 #include <functional>
26 #include <memory>
27 #include <thread>
28
29 #include <gtest/gtest.h>
30
31 #include "Allocator.h"
32 #include "ScopedDisableMalloc.h"
33 #include "ScopedPipe.h"
34
35 using namespace std::chrono_literals;
36
37 namespace android {
38
39 class ThreadListTest : public ::testing::TestWithParam<int> {
40 public:
ThreadListTest()41 ThreadListTest() : stop_(false) {}
42
~ThreadListTest()43 ~ThreadListTest() {
44 // pthread_join may return before the entry in /proc/pid/task/ is gone,
45 // loop until ListThreads only finds the main thread so the next test
46 // doesn't fail.
47 WaitForThreads();
48 }
49
TearDown()50 virtual void TearDown() { ASSERT_TRUE(heap.empty()); }
51
52 protected:
53 template <class Function>
StartThreads(unsigned int threads,Function && func)54 void StartThreads(unsigned int threads, Function&& func) {
55 threads_.reserve(threads);
56 tids_.reserve(threads);
57 for (unsigned int i = 0; i < threads; i++) {
58 threads_.emplace_back([&, threads, this]() {
59 {
60 std::lock_guard<std::mutex> lk(m_);
61 tids_.push_back(gettid());
62 if (tids_.size() == threads) {
63 cv_start_.notify_one();
64 }
65 }
66
67 func();
68
69 {
70 std::unique_lock<std::mutex> lk(m_);
71 cv_stop_.wait(lk, [&] { return stop_; });
72 }
73 });
74 }
75
76 {
77 std::unique_lock<std::mutex> lk(m_);
78 cv_start_.wait(lk, [&] { return tids_.size() == threads; });
79 }
80 }
81
StopThreads()82 void StopThreads() {
83 {
84 std::lock_guard<std::mutex> lk(m_);
85 stop_ = true;
86 }
87 cv_stop_.notify_all();
88
89 for (auto i = threads_.begin(); i != threads_.end(); i++) {
90 i->join();
91 }
92 threads_.clear();
93 tids_.clear();
94 }
95
tids()96 std::vector<pid_t>& tids() { return tids_; }
97
98 Heap heap;
99
100 private:
WaitForThreads()101 void WaitForThreads() {
102 auto tids = TidList{heap};
103 ThreadCapture thread_capture{getpid(), heap};
104
105 for (unsigned int i = 0; i < 100; i++) {
106 EXPECT_TRUE(thread_capture.ListThreads(tids));
107 if (tids.size() == 1) {
108 break;
109 }
110 std::this_thread::sleep_for(10ms);
111 }
112 EXPECT_EQ(1U, tids.size());
113 }
114
115 std::mutex m_;
116 std::condition_variable cv_start_;
117 std::condition_variable cv_stop_;
118 bool stop_;
119 std::vector<pid_t> tids_;
120
121 std::vector<std::thread> threads_;
122 };
123
TEST_F(ThreadListTest,list_one)124 TEST_F(ThreadListTest, list_one) {
125 ScopedDisableMallocTimeout disable_malloc;
126
127 ThreadCapture thread_capture(getpid(), heap);
128
129 auto expected_tids = allocator::vector<pid_t>(1, getpid(), heap);
130 auto list_tids = allocator::vector<pid_t>(heap);
131
132 ASSERT_TRUE(thread_capture.ListThreads(list_tids));
133
134 ASSERT_EQ(expected_tids, list_tids);
135
136 if (!HasFailure()) {
137 ASSERT_FALSE(disable_malloc.timed_out());
138 }
139 }
140
TEST_P(ThreadListTest,list_some)141 TEST_P(ThreadListTest, list_some) {
142 const unsigned int threads = GetParam() - 1;
143
144 StartThreads(threads, []() {});
145 std::vector<pid_t> expected_tids = tids();
146 expected_tids.push_back(getpid());
147
148 auto list_tids = allocator::vector<pid_t>(heap);
149
150 {
151 ScopedDisableMallocTimeout disable_malloc;
152
153 ThreadCapture thread_capture(getpid(), heap);
154
155 ASSERT_TRUE(thread_capture.ListThreads(list_tids));
156
157 if (!HasFailure()) {
158 ASSERT_FALSE(disable_malloc.timed_out());
159 }
160 }
161
162 StopThreads();
163
164 std::sort(list_tids.begin(), list_tids.end());
165 std::sort(expected_tids.begin(), expected_tids.end());
166
167 ASSERT_EQ(expected_tids.size(), list_tids.size());
168 EXPECT_TRUE(std::equal(expected_tids.begin(), expected_tids.end(), list_tids.begin()));
169 }
170
171 INSTANTIATE_TEST_CASE_P(ThreadListTest, ThreadListTest, ::testing::Values(1, 2, 10, 1024));
172
173 class ThreadCaptureTest : public ThreadListTest {
174 public:
ThreadCaptureTest()175 ThreadCaptureTest() {}
~ThreadCaptureTest()176 ~ThreadCaptureTest() {}
Fork(std::function<void ()> && child_init,std::function<void ()> && child_cleanup,std::function<void (pid_t)> && parent)177 void Fork(std::function<void()>&& child_init, std::function<void()>&& child_cleanup,
178 std::function<void(pid_t)>&& parent) {
179 ScopedPipe start_pipe;
180 ScopedPipe stop_pipe;
181
182 int pid = fork();
183
184 if (pid == 0) {
185 // child
186 child_init();
187 EXPECT_EQ(1, TEMP_FAILURE_RETRY(write(start_pipe.Sender(), "+", 1))) << strerror(errno);
188 char buf;
189 EXPECT_EQ(1, TEMP_FAILURE_RETRY(read(stop_pipe.Receiver(), &buf, 1))) << strerror(errno);
190 child_cleanup();
191 _exit(0);
192 } else {
193 // parent
194 ASSERT_GT(pid, 0);
195 char buf;
196 ASSERT_EQ(1, TEMP_FAILURE_RETRY(read(start_pipe.Receiver(), &buf, 1))) << strerror(errno);
197
198 parent(pid);
199
200 ASSERT_EQ(1, TEMP_FAILURE_RETRY(write(stop_pipe.Sender(), "+", 1))) << strerror(errno);
201 siginfo_t info{};
202 ASSERT_EQ(0, TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED))) << strerror(errno);
203 }
204 }
205 };
206
TEST_P(ThreadCaptureTest,capture_some)207 TEST_P(ThreadCaptureTest, capture_some) {
208 const unsigned int threads = GetParam();
209
210 Fork(
211 [&]() {
212 // child init
213 StartThreads(threads - 1, []() {});
214 },
215 [&]() {
216 // child cleanup
217 StopThreads();
218 },
219 [&](pid_t child) {
220 // parent
221 ASSERT_GT(child, 0);
222
223 {
224 ScopedDisableMallocTimeout disable_malloc;
225
226 ThreadCapture thread_capture(child, heap);
227 auto list_tids = allocator::vector<pid_t>(heap);
228
229 ASSERT_TRUE(thread_capture.ListThreads(list_tids));
230 ASSERT_EQ(threads, list_tids.size());
231
232 ASSERT_TRUE(thread_capture.CaptureThreads());
233
234 auto thread_info = allocator::vector<ThreadInfo>(heap);
235 ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
236 ASSERT_EQ(threads, thread_info.size());
237 ASSERT_TRUE(thread_capture.ReleaseThreads());
238
239 if (!HasFailure()) {
240 ASSERT_FALSE(disable_malloc.timed_out());
241 }
242 }
243 });
244 }
245
246 INSTANTIATE_TEST_CASE_P(ThreadCaptureTest, ThreadCaptureTest, ::testing::Values(1, 2, 10, 1024));
247
TEST_F(ThreadCaptureTest,capture_kill)248 TEST_F(ThreadCaptureTest, capture_kill) {
249 int ret = fork();
250
251 if (ret == 0) {
252 // child
253 sleep(10);
254 } else {
255 // parent
256 ASSERT_GT(ret, 0);
257
258 {
259 ScopedDisableMallocTimeout disable_malloc;
260
261 ThreadCapture thread_capture(ret, heap);
262 thread_capture.InjectTestFunc([&](pid_t tid) {
263 syscall(SYS_tgkill, ret, tid, SIGKILL);
264 usleep(10000);
265 });
266 auto list_tids = allocator::vector<pid_t>(heap);
267
268 ASSERT_TRUE(thread_capture.ListThreads(list_tids));
269 ASSERT_EQ(1U, list_tids.size());
270
271 ASSERT_FALSE(thread_capture.CaptureThreads());
272
273 if (!HasFailure()) {
274 ASSERT_FALSE(disable_malloc.timed_out());
275 }
276 }
277 }
278 }
279
TEST_F(ThreadCaptureTest,capture_signal)280 TEST_F(ThreadCaptureTest, capture_signal) {
281 const int sig = SIGUSR1;
282
283 ScopedPipe pipe;
284
285 // For signal handler
286 static ScopedPipe* g_pipe;
287
288 Fork(
289 [&]() {
290 // child init
291 pipe.CloseReceiver();
292
293 g_pipe = &pipe;
294
295 struct sigaction act {};
296 act.sa_handler = [](int) {
297 char buf = '+';
298 write(g_pipe->Sender(), &buf, 1);
299 g_pipe->CloseSender();
300 };
301 sigaction(sig, &act, NULL);
302 sigset_t set;
303 sigemptyset(&set);
304 sigaddset(&set, sig);
305 pthread_sigmask(SIG_UNBLOCK, &set, NULL);
306 },
307 [&]() {
308 // child cleanup
309 g_pipe = nullptr;
310 pipe.Close();
311 },
312 [&](pid_t child) {
313 // parent
314 ASSERT_GT(child, 0);
315 pipe.CloseSender();
316
317 {
318 ScopedDisableMallocTimeout disable_malloc;
319
320 ThreadCapture thread_capture(child, heap);
321 thread_capture.InjectTestFunc([&](pid_t tid) {
322 syscall(SYS_tgkill, child, tid, sig);
323 usleep(10000);
324 });
325 auto list_tids = allocator::vector<pid_t>(heap);
326
327 ASSERT_TRUE(thread_capture.ListThreads(list_tids));
328 ASSERT_EQ(1U, list_tids.size());
329
330 ASSERT_TRUE(thread_capture.CaptureThreads());
331
332 auto thread_info = allocator::vector<ThreadInfo>(heap);
333 ASSERT_TRUE(thread_capture.CapturedThreadInfo(thread_info));
334 ASSERT_EQ(1U, thread_info.size());
335 ASSERT_TRUE(thread_capture.ReleaseThreads());
336
337 usleep(100000);
338 char buf;
339 ASSERT_EQ(1, TEMP_FAILURE_RETRY(read(pipe.Receiver(), &buf, 1)));
340 ASSERT_EQ(buf, '+');
341
342 if (!HasFailure()) {
343 ASSERT_FALSE(disable_malloc.timed_out());
344 }
345 }
346 });
347 }
348
349 } // namespace android
350