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