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.
53static 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
72class ThreadCaptureImpl {
73 public:
74  ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator);
75  ~ThreadCaptureImpl() {}
76  bool ListThreads(TidList& tids);
77  bool CaptureThreads();
78  bool ReleaseThreads();
79  bool ReleaseThread(pid_t tid);
80  bool CapturedThreadInfo(ThreadInfoList& threads);
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
95ThreadCaptureImpl::ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator) :
96    captured_threads_(allocator), allocator_(allocator), pid_(pid) {
97}
98
99bool 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
149bool 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
177void 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
187int 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
212bool 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
253int 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
301bool 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
309bool ThreadCaptureImpl::ReleaseThread(pid_t tid, unsigned int signal) {
310  PtraceDetach(tid, signal);
311  return true;
312}
313
314bool 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
327bool 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
340ThreadCapture::ThreadCapture(pid_t pid, Allocator<ThreadCapture> allocator) {
341  Allocator<ThreadCaptureImpl> impl_allocator = allocator;
342  impl_ = impl_allocator.make_unique(pid, impl_allocator);
343}
344
345ThreadCapture::~ThreadCapture() {}
346
347bool ThreadCapture::ListThreads(TidList& tids) {
348  return impl_->ListThreads(tids);
349}
350
351bool ThreadCapture::CaptureThreads() {
352  return impl_->CaptureThreads();
353}
354
355bool ThreadCapture::ReleaseThreads() {
356  return impl_->ReleaseThreads();
357}
358
359bool ThreadCapture::ReleaseThread(pid_t tid) {
360  return impl_->ReleaseThread(tid);
361}
362
363bool ThreadCapture::CapturedThreadInfo(ThreadInfoList& threads) {
364  return impl_->CapturedThreadInfo(threads);
365}
366
367void ThreadCapture::InjectTestFunc(std::function<void(pid_t)>&& f) {
368  impl_->InjectTestFunc(std::forward<std::function<void(pid_t)>>(f));
369}
370