blob: e8a8392ccda03885494e212dad568631a77b4e1b [file] [log] [blame]
Colin Cross7add50d2016-01-14 15:35:40 -08001/*
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
Colin Cross4c5bccd2016-03-04 16:34:42 -080089 allocator::map<pid_t, unsigned int> captured_threads_;
Colin Cross7add50d2016-01-14 15:35:40 -080090 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 int fd = open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY);
112 if (fd < 0) {
113 ALOGE("failed to open %s: %s", path, strerror(errno));
114 return false;
115 }
116 android::base::unique_fd fd_guard{fd};
117
118 struct linux_dirent64 {
119 uint64_t d_ino;
120 int64_t d_off;
121 uint16_t d_reclen;
122 char d_type;
123 char d_name[];
124 } __attribute((packed));
125 char dirent_buf[4096];
126 ssize_t nread;
127 do {
128 nread = syscall(SYS_getdents64, fd, dirent_buf, sizeof(dirent_buf));
129 if (nread < 0) {
130 ALOGE("failed to get directory entries from %s: %s", path, strerror(errno));
131 return false;
132 } else if (nread > 0) {
133 ssize_t off = 0;
134 while (off < nread) {
135 linux_dirent64* dirent = reinterpret_cast<linux_dirent64*>(dirent_buf + off);
136 off += dirent->d_reclen;
137 pid_t tid = atoi(dirent->d_name);
138 if (tid <= 0) {
139 continue;
140 }
141 tids.push_back(tid);
142 }
143 }
144
145 } while (nread != 0);
146
147 return true;
148}
149
150bool ThreadCaptureImpl::CaptureThreads() {
151 TidList tids{allocator_};
152
153 bool found_new_thread;
154 do {
155 if (!ListThreads(tids)) {
156 ReleaseThreads();
157 return false;
158 }
159
160 found_new_thread = false;
161
162 for (auto it = tids.begin(); it != tids.end(); it++) {
163 auto captured = captured_threads_.find(*it);
164 if (captured == captured_threads_.end()) {
165 if (CaptureThread(*it) < 0) {
166 ReleaseThreads();
167 return false;
168 }
169 found_new_thread = true;
170 }
171 }
172 } while (found_new_thread);
173
174 return true;
175}
176
177// Detatches from a thread, delivering signal if nonzero, logs on error
178void ThreadCaptureImpl::PtraceDetach(pid_t tid, unsigned int signal) {
179 void* sig_ptr = reinterpret_cast<void*>(static_cast<uintptr_t>(signal));
180 if (ptrace(PTRACE_DETACH, tid, NULL, sig_ptr) < 0 && errno != ESRCH) {
181 ALOGE("failed to detach from thread %d of process %d: %s", tid, pid_,
182 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
188int ThreadCaptureImpl::PtraceAttach(pid_t tid) {
189 int ret = ptrace(PTRACE_SEIZE, tid, NULL, NULL);
190 if (ret < 0) {
191 ALOGE("failed to attach to thread %d of process %d: %s", tid, pid_,
192 strerror(errno));
193 return -1;
194 }
195
196 if (inject_test_func_) {
197 inject_test_func_(tid);
198 }
199
200 if (ptrace(PTRACE_INTERRUPT, tid, 0, 0) < 0) {
201 if (errno == ESRCH) {
202 return 0;
203 } else {
204 ALOGE("failed to interrupt thread %d of process %d: %s", tid, pid_,
205 strerror(errno));
206 PtraceDetach(tid, 0);
207 return -1;
208 }
209 }
210 return 1;
211}
212
213bool ThreadCaptureImpl::PtraceThreadInfo(pid_t tid, ThreadInfo& thread_info) {
214 thread_info.tid = tid;
215
216 const unsigned int max_num_regs = 128; // larger than number of registers on any device
217 uintptr_t regs[max_num_regs];
218 struct iovec iovec;
219 iovec.iov_base = &regs;
220 iovec.iov_len = sizeof(regs);
221
222 if (ptrace(PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRSTATUS), &iovec)) {
223 ALOGE("ptrace getregset for thread %d of process %d failed: %s",
224 tid, pid_, strerror(errno));
225 return false;
226 }
227
228 unsigned int num_regs = iovec.iov_len / sizeof(uintptr_t);
229 thread_info.regs.assign(&regs[0], &regs[num_regs]);
230
231 const int sp =
232#if defined(__x86_64__)
233 offsetof(struct pt_regs, rsp) / sizeof(uintptr_t)
234#elif defined(__i386__)
235 offsetof(struct pt_regs, esp) / sizeof(uintptr_t)
236#elif defined(__arm__)
237 offsetof(struct pt_regs, ARM_sp) / sizeof(uintptr_t)
238#elif defined(__aarch64__)
239 offsetof(struct user_pt_regs, sp) / sizeof(uintptr_t)
240#elif defined(__mips__) || defined(__mips64__)
241 offsetof(struct pt_regs, regs[29]) / sizeof(uintptr_t)
242#else
243#error Unrecognized architecture
244#endif
245 ;
246
247 // TODO(ccross): use /proc/tid/status or /proc/pid/maps to get start_stack
248
249 thread_info.stack = std::pair<uintptr_t, uintptr_t>(regs[sp], 0);
250
251 return true;
252}
253
254int ThreadCaptureImpl::CaptureThread(pid_t tid) {
255 int ret = PtraceAttach(tid);
256 if (ret <= 0) {
257 return ret;
258 }
259
260 int status = 0;
261 if (TEMP_FAILURE_RETRY(waitpid(tid, &status, __WALL)) < 0) {
262 ALOGE("failed to wait for pause of thread %d of process %d: %s", tid, pid_,
263 strerror(errno));
264 PtraceDetach(tid, 0);
265 return -1;
266 }
267
268 if (!WIFSTOPPED(status)) {
269 ALOGE("thread %d of process %d was not paused after waitpid, killed?",
270 tid, pid_);
271 return 0;
272 }
273
274 unsigned int resume_signal = 0;
275
276 unsigned int signal = WSTOPSIG(status);
277 if ((status >> 16) == PTRACE_EVENT_STOP) {
278 switch (signal) {
279 case SIGSTOP:
280 case SIGTSTP:
281 case SIGTTIN:
282 case SIGTTOU:
283 // group-stop signals
284 break;
285 case SIGTRAP:
286 // normal ptrace interrupt stop
287 break;
288 default:
289 ALOGE("unexpected signal %d with PTRACE_EVENT_STOP for thread %d of process %d",
290 signal, tid, pid_);
291 return -1;
292 }
293 } else {
294 // signal-delivery-stop
295 resume_signal = signal;
296 }
297
298 captured_threads_[tid] = resume_signal;
299 return 1;
300}
301
302bool ThreadCaptureImpl::ReleaseThread(pid_t tid) {
303 auto it = captured_threads_.find(tid);
304 if (it == captured_threads_.end()) {
305 return false;
306 }
307 return ReleaseThread(it->first, it->second);
308}
309
310bool ThreadCaptureImpl::ReleaseThread(pid_t tid, unsigned int signal) {
311 PtraceDetach(tid, signal);
312 return true;
313}
314
315bool ThreadCaptureImpl::ReleaseThreads() {
316 bool ret = true;
317 for (auto it = captured_threads_.begin(); it != captured_threads_.end(); ) {
318 if (ReleaseThread(it->first, it->second)) {
319 it = captured_threads_.erase(it);
320 } else {
321 it++;
322 ret = false;
323 }
324 }
325 return ret;
326}
327
328bool ThreadCaptureImpl::CapturedThreadInfo(ThreadInfoList& threads) {
329 threads.clear();
330
331 for (auto it = captured_threads_.begin(); it != captured_threads_.end(); it++) {
332 ThreadInfo t{0, allocator::vector<uintptr_t>(allocator_), std::pair<uintptr_t, uintptr_t>(0, 0)};
333 if (!PtraceThreadInfo(it->first, t)) {
334 return false;
335 }
336 threads.push_back(t);
337 }
338 return true;
339}
340
341ThreadCapture::ThreadCapture(pid_t pid, Allocator<ThreadCapture> allocator) {
342 Allocator<ThreadCaptureImpl> impl_allocator = allocator;
343 impl_ = impl_allocator.make_unique(pid, impl_allocator);
344}
345
346ThreadCapture::~ThreadCapture() {}
347
348bool ThreadCapture::ListThreads(TidList& tids) {
349 return impl_->ListThreads(tids);
350}
351
352bool ThreadCapture::CaptureThreads() {
353 return impl_->CaptureThreads();
354}
355
356bool ThreadCapture::ReleaseThreads() {
357 return impl_->ReleaseThreads();
358}
359
360bool ThreadCapture::ReleaseThread(pid_t tid) {
361 return impl_->ReleaseThread(tid);
362}
363
364bool ThreadCapture::CapturedThreadInfo(ThreadInfoList& threads) {
365 return impl_->CapturedThreadInfo(threads);
366}
367
368void ThreadCapture::InjectTestFunc(std::function<void(pid_t)>&& f) {
369 impl_->InjectTestFunc(std::forward<std::function<void(pid_t)>>(f));
370}