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