• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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