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