• 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 <elf.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <limits.h>
23 #include <stdlib.h>
24 #include <sys/ptrace.h>
25 #include <sys/stat.h>
26 #include <sys/syscall.h>
27 #include <sys/types.h>
28 #include <sys/uio.h>
29 #include <sys/wait.h>
30 #include <unistd.h>
31 
32 #include <map>
33 #include <memory>
34 #include <set>
35 #include <vector>
36 
37 #include <android-base/unique_fd.h>
38 
39 #include "Allocator.h"
40 #include "log.h"
41 
42 namespace android {
43 
44 // bionic interfaces used:
45 // atoi
46 // strlcat
47 // writev
48 
49 // bionic interfaces reimplemented to avoid allocation:
50 // getdents64
51 
52 // Convert a pid > 0 to a string.  sprintf might allocate, so we can't use it.
53 // Returns a pointer somewhere in buf to a null terminated string, or NULL
54 // on error.
pid_to_str(char * buf,size_t len,pid_t pid)55 static char* pid_to_str(char* buf, size_t len, pid_t pid) {
56   if (pid <= 0) {
57     return nullptr;
58   }
59 
60   char* ptr = buf + len - 1;
61   *ptr = 0;
62   while (pid > 0) {
63     ptr--;
64     if (ptr < buf) {
65       return nullptr;
66     }
67     *ptr = '0' + (pid % 10);
68     pid /= 10;
69   }
70 
71   return ptr;
72 }
73 
74 class ThreadCaptureImpl {
75  public:
76   ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator);
~ThreadCaptureImpl()77   ~ThreadCaptureImpl() {}
78   bool ListThreads(TidList& tids);
79   bool CaptureThreads();
80   bool ReleaseThreads();
81   bool ReleaseThread(pid_t tid);
82   bool CapturedThreadInfo(ThreadInfoList& threads);
InjectTestFunc(std::function<void (pid_t)> && f)83   void InjectTestFunc(std::function<void(pid_t)>&& f) { inject_test_func_ = f; }
84 
85  private:
86   int CaptureThread(pid_t tid);
87   bool ReleaseThread(pid_t tid, unsigned int signal);
88   int PtraceAttach(pid_t tid);
89   void PtraceDetach(pid_t tid, unsigned int signal);
90   bool PtraceThreadInfo(pid_t tid, ThreadInfo& thread_info);
91 
92   allocator::map<pid_t, unsigned int> captured_threads_;
93   Allocator<ThreadCaptureImpl> allocator_;
94   pid_t pid_;
95   std::function<void(pid_t)> inject_test_func_;
96 };
97 
ThreadCaptureImpl(pid_t pid,Allocator<ThreadCaptureImpl> & allocator)98 ThreadCaptureImpl::ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator)
99     : captured_threads_(allocator), allocator_(allocator), pid_(pid) {}
100 
ListThreads(TidList & tids)101 bool ThreadCaptureImpl::ListThreads(TidList& tids) {
102   tids.clear();
103 
104   char pid_buf[11];
105   char path[256] = "/proc/";
106   char* pid_str = pid_to_str(pid_buf, sizeof(pid_buf), pid_);
107   if (!pid_str) {
108     return false;
109   }
110   strlcat(path, pid_str, sizeof(path));
111   strlcat(path, "/task", sizeof(path));
112 
113   android::base::unique_fd fd(open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY));
114   if (fd == -1) {
115     MEM_ALOGE("failed to open %s: %s", path, strerror(errno));
116     return false;
117   }
118 
119   struct linux_dirent64 {
120     uint64_t d_ino;
121     int64_t d_off;
122     uint16_t d_reclen;
123     char d_type;
124     char d_name[];
125   } __attribute((packed));
126   char dirent_buf[4096];
127   ssize_t nread;
128   do {
129     nread = syscall(SYS_getdents64, fd.get(), dirent_buf, sizeof(dirent_buf));
130     if (nread < 0) {
131       MEM_ALOGE("failed to get directory entries from %s: %s", path, strerror(errno));
132       return false;
133     } else if (nread > 0) {
134       ssize_t off = 0;
135       while (off < nread) {
136         linux_dirent64* dirent = reinterpret_cast<linux_dirent64*>(dirent_buf + off);
137         off += dirent->d_reclen;
138         pid_t tid = atoi(dirent->d_name);
139         if (tid <= 0) {
140           continue;
141         }
142         tids.push_back(tid);
143       }
144     }
145 
146   } while (nread != 0);
147 
148   return true;
149 }
150 
CaptureThreads()151 bool ThreadCaptureImpl::CaptureThreads() {
152   TidList tids{allocator_};
153 
154   bool found_new_thread;
155   do {
156     if (!ListThreads(tids)) {
157       ReleaseThreads();
158       return false;
159     }
160 
161     found_new_thread = false;
162 
163     for (auto it = tids.begin(); it != tids.end(); it++) {
164       auto captured = captured_threads_.find(*it);
165       if (captured == captured_threads_.end()) {
166         if (CaptureThread(*it) < 0) {
167           ReleaseThreads();
168           return false;
169         }
170         found_new_thread = true;
171       }
172     }
173   } while (found_new_thread);
174 
175   return true;
176 }
177 
178 // Detatches from a thread, delivering signal if nonzero, logs on error
PtraceDetach(pid_t tid,unsigned int signal)179 void ThreadCaptureImpl::PtraceDetach(pid_t tid, unsigned int signal) {
180   void* sig_ptr = reinterpret_cast<void*>(static_cast<uintptr_t>(signal));
181   if (ptrace(PTRACE_DETACH, tid, NULL, sig_ptr) < 0 && errno != ESRCH) {
182     MEM_ALOGE("failed to detach from thread %d of process %d: %s", tid, pid_, strerror(errno));
183   }
184 }
185 
186 // Attaches to and pauses thread.
187 // Returns 1 on attach, 0 on tid not found, -1 and logs on error
PtraceAttach(pid_t tid)188 int ThreadCaptureImpl::PtraceAttach(pid_t tid) {
189   int ret = ptrace(PTRACE_SEIZE, tid, NULL, NULL);
190   if (ret < 0) {
191     MEM_ALOGE("failed to attach to thread %d of process %d: %s", tid, pid_, strerror(errno));
192     return -1;
193   }
194 
195   if (inject_test_func_) {
196     inject_test_func_(tid);
197   }
198 
199   if (ptrace(PTRACE_INTERRUPT, tid, 0, 0) < 0) {
200     if (errno == ESRCH) {
201       return 0;
202     } else {
203       MEM_ALOGE("failed to interrupt thread %d of process %d: %s", tid, pid_, strerror(errno));
204       PtraceDetach(tid, 0);
205       return -1;
206     }
207   }
208   return 1;
209 }
210 
PtraceThreadInfo(pid_t tid,ThreadInfo & thread_info)211 bool ThreadCaptureImpl::PtraceThreadInfo(pid_t tid, ThreadInfo& thread_info) {
212   thread_info.tid = tid;
213 
214   const unsigned int max_num_regs = 128;  // larger than number of registers on any device
215   uintptr_t regs[max_num_regs];
216   struct iovec iovec;
217   iovec.iov_base = &regs;
218   iovec.iov_len = sizeof(regs);
219 
220   if (ptrace(PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRSTATUS), &iovec)) {
221     MEM_ALOGE("ptrace getregset for thread %d of process %d failed: %s", tid, pid_, strerror(errno));
222     return false;
223   }
224 
225   unsigned int num_regs = iovec.iov_len / sizeof(uintptr_t);
226   thread_info.regs.assign(&regs[0], &regs[num_regs]);
227 
228   const int sp =
229 #if defined(__x86_64__)
230       offsetof(struct pt_regs, rsp) / sizeof(uintptr_t)
231 #elif defined(__i386__)
232       offsetof(struct pt_regs, esp) / sizeof(uintptr_t)
233 #elif defined(__arm__)
234       offsetof(struct pt_regs, ARM_sp) / sizeof(uintptr_t)
235 #elif defined(__aarch64__)
236       offsetof(struct user_pt_regs, sp) / sizeof(uintptr_t)
237 #elif defined(__mips__) || defined(__mips64__)
238       offsetof(struct pt_regs, regs[29]) / sizeof(uintptr_t)
239 #else
240 #error Unrecognized architecture
241 #endif
242       ;
243 
244   // TODO(ccross): use /proc/tid/status or /proc/pid/maps to get start_stack
245 
246   thread_info.stack = std::pair<uintptr_t, uintptr_t>(regs[sp], 0);
247 
248   return true;
249 }
250 
CaptureThread(pid_t tid)251 int ThreadCaptureImpl::CaptureThread(pid_t tid) {
252   int ret = PtraceAttach(tid);
253   if (ret <= 0) {
254     return ret;
255   }
256 
257   int status = 0;
258   if (TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL)) < 0) {
259     MEM_ALOGE("failed to wait for pause of thread %d of process %d: %s", tid, pid_, strerror(errno));
260     PtraceDetach(tid, 0);
261     return -1;
262   }
263 
264   if (!WIFSTOPPED(status)) {
265     MEM_ALOGE("thread %d of process %d was not paused after waitpid, killed?", tid, pid_);
266     return 0;
267   }
268 
269   unsigned int resume_signal = 0;
270 
271   unsigned int signal = WSTOPSIG(status);
272   if ((status >> 16) == PTRACE_EVENT_STOP) {
273     switch (signal) {
274       case SIGSTOP:
275       case SIGTSTP:
276       case SIGTTIN:
277       case SIGTTOU:
278         // group-stop signals
279         break;
280       case SIGTRAP:
281         // normal ptrace interrupt stop
282         break;
283       default:
284         MEM_ALOGE("unexpected signal %d with PTRACE_EVENT_STOP for thread %d of process %d", signal,
285                   tid, pid_);
286         return -1;
287     }
288   } else {
289     // signal-delivery-stop
290     resume_signal = signal;
291   }
292 
293   captured_threads_[tid] = resume_signal;
294   return 1;
295 }
296 
ReleaseThread(pid_t tid)297 bool ThreadCaptureImpl::ReleaseThread(pid_t tid) {
298   auto it = captured_threads_.find(tid);
299   if (it == captured_threads_.end()) {
300     return false;
301   }
302   return ReleaseThread(it->first, it->second);
303 }
304 
ReleaseThread(pid_t tid,unsigned int signal)305 bool ThreadCaptureImpl::ReleaseThread(pid_t tid, unsigned int signal) {
306   PtraceDetach(tid, signal);
307   return true;
308 }
309 
ReleaseThreads()310 bool ThreadCaptureImpl::ReleaseThreads() {
311   bool ret = true;
312   for (auto it = captured_threads_.begin(); it != captured_threads_.end();) {
313     if (ReleaseThread(it->first, it->second)) {
314       it = captured_threads_.erase(it);
315     } else {
316       it++;
317       ret = false;
318     }
319   }
320   return ret;
321 }
322 
CapturedThreadInfo(ThreadInfoList & threads)323 bool ThreadCaptureImpl::CapturedThreadInfo(ThreadInfoList& threads) {
324   threads.clear();
325 
326   for (auto it = captured_threads_.begin(); it != captured_threads_.end(); it++) {
327     ThreadInfo t{0, allocator::vector<uintptr_t>(allocator_), std::pair<uintptr_t, uintptr_t>(0, 0)};
328     if (!PtraceThreadInfo(it->first, t)) {
329       return false;
330     }
331     threads.push_back(t);
332   }
333   return true;
334 }
335 
ThreadCapture(pid_t pid,Allocator<ThreadCapture> allocator)336 ThreadCapture::ThreadCapture(pid_t pid, Allocator<ThreadCapture> allocator) {
337   Allocator<ThreadCaptureImpl> impl_allocator = allocator;
338   impl_ = impl_allocator.make_unique(pid, impl_allocator);
339 }
340 
~ThreadCapture()341 ThreadCapture::~ThreadCapture() {}
342 
ListThreads(TidList & tids)343 bool ThreadCapture::ListThreads(TidList& tids) {
344   return impl_->ListThreads(tids);
345 }
346 
CaptureThreads()347 bool ThreadCapture::CaptureThreads() {
348   return impl_->CaptureThreads();
349 }
350 
ReleaseThreads()351 bool ThreadCapture::ReleaseThreads() {
352   return impl_->ReleaseThreads();
353 }
354 
ReleaseThread(pid_t tid)355 bool ThreadCapture::ReleaseThread(pid_t tid) {
356   return impl_->ReleaseThread(tid);
357 }
358 
CapturedThreadInfo(ThreadInfoList & threads)359 bool ThreadCapture::CapturedThreadInfo(ThreadInfoList& threads) {
360   return impl_->CapturedThreadInfo(threads);
361 }
362 
InjectTestFunc(std::function<void (pid_t)> && f)363 void ThreadCapture::InjectTestFunc(std::function<void(pid_t)>&& f) {
364   impl_->InjectTestFunc(std::forward<std::function<void(pid_t)>>(f));
365 }
366 
367 }  // namespace android
368