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