blob: 45eb55de85f2bc63358d4962a46996aa2e3dbd95 [file] [log] [blame]
Colin Crossab7c9f12016-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>
Colin Crossab7c9f12016-01-14 15:35:40 -080024#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>
Colin Cross401319a2017-06-22 10:50:05 -070030#include <unistd.h>
Colin Crossab7c9f12016-01-14 15:35:40 -080031
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
Colin Cross1fa81f52017-06-21 13:13:00 -070042namespace android {
43
Colin Crossab7c9f12016-01-14 15:35:40 -080044// 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.
Colin Cross401319a2017-06-22 10:50:05 -070055static char* pid_to_str(char* buf, size_t len, pid_t pid) {
Colin Crossab7c9f12016-01-14 15:35:40 -080056 if (pid <= 0) {
57 return nullptr;
58 }
59
Colin Cross401319a2017-06-22 10:50:05 -070060 char* ptr = buf + len - 1;
Colin Crossab7c9f12016-01-14 15:35:40 -080061 *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
74class ThreadCaptureImpl {
75 public:
76 ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator);
77 ~ThreadCaptureImpl() {}
78 bool ListThreads(TidList& tids);
79 bool CaptureThreads();
80 bool ReleaseThreads();
81 bool ReleaseThread(pid_t tid);
82 bool CapturedThreadInfo(ThreadInfoList& threads);
83 void InjectTestFunc(std::function<void(pid_t)>&& f) { inject_test_func_ = f; }
Colin Cross401319a2017-06-22 10:50:05 -070084
Colin Crossab7c9f12016-01-14 15:35:40 -080085 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
Colin Cross20774fc2016-03-04 16:34:42 -080092 allocator::map<pid_t, unsigned int> captured_threads_;
Colin Crossab7c9f12016-01-14 15:35:40 -080093 Allocator<ThreadCaptureImpl> allocator_;
94 pid_t pid_;
95 std::function<void(pid_t)> inject_test_func_;
96};
97
Colin Cross401319a2017-06-22 10:50:05 -070098ThreadCaptureImpl::ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator)
99 : captured_threads_(allocator), allocator_(allocator), pid_(pid) {}
Colin Crossab7c9f12016-01-14 15:35:40 -0800100
101bool 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
Elliott Hughes18417e62016-03-28 12:15:36 -0700113 android::base::unique_fd fd(open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY));
114 if (fd == -1) {
Christopher Ferris56b8d862017-05-03 17:34:29 -0700115 MEM_ALOGE("failed to open %s: %s", path, strerror(errno));
Colin Crossab7c9f12016-01-14 15:35:40 -0800116 return false;
117 }
Colin Crossab7c9f12016-01-14 15:35:40 -0800118
119 struct linux_dirent64 {
Colin Cross401319a2017-06-22 10:50:05 -0700120 uint64_t d_ino;
121 int64_t d_off;
122 uint16_t d_reclen;
123 char d_type;
124 char d_name[];
Colin Crossab7c9f12016-01-14 15:35:40 -0800125 } __attribute((packed));
126 char dirent_buf[4096];
127 ssize_t nread;
128 do {
Elliott Hughes18417e62016-03-28 12:15:36 -0700129 nread = syscall(SYS_getdents64, fd.get(), dirent_buf, sizeof(dirent_buf));
Colin Crossab7c9f12016-01-14 15:35:40 -0800130 if (nread < 0) {
Christopher Ferris56b8d862017-05-03 17:34:29 -0700131 MEM_ALOGE("failed to get directory entries from %s: %s", path, strerror(errno));
Colin Crossab7c9f12016-01-14 15:35:40 -0800132 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
151bool 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
179void 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) {
Christopher Ferris56b8d862017-05-03 17:34:29 -0700182 MEM_ALOGE("failed to detach from thread %d of process %d: %s", tid, pid_, strerror(errno));
Colin Crossab7c9f12016-01-14 15:35:40 -0800183 }
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) {
Christopher Ferris56b8d862017-05-03 17:34:29 -0700191 MEM_ALOGE("failed to attach to thread %d of process %d: %s", tid, pid_, strerror(errno));
Colin Crossab7c9f12016-01-14 15:35:40 -0800192 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 {
Christopher Ferris56b8d862017-05-03 17:34:29 -0700203 MEM_ALOGE("failed to interrupt thread %d of process %d: %s", tid, pid_, strerror(errno));
Colin Crossab7c9f12016-01-14 15:35:40 -0800204 PtraceDetach(tid, 0);
205 return -1;
206 }
207 }
208 return 1;
209}
210
211bool ThreadCaptureImpl::PtraceThreadInfo(pid_t tid, ThreadInfo& thread_info) {
212 thread_info.tid = tid;
213
Colin Cross401319a2017-06-22 10:50:05 -0700214 const unsigned int max_num_regs = 128; // larger than number of registers on any device
Colin Crossab7c9f12016-01-14 15:35:40 -0800215 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)) {
Christopher Ferris56b8d862017-05-03 17:34:29 -0700221 MEM_ALOGE("ptrace getregset for thread %d of process %d failed: %s", tid, pid_, strerror(errno));
Colin Crossab7c9f12016-01-14 15:35:40 -0800222 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
Colin Cross401319a2017-06-22 10:50:05 -0700248 return true;
Colin Crossab7c9f12016-01-14 15:35:40 -0800249}
250
251int 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) {
Christopher Ferris56b8d862017-05-03 17:34:29 -0700259 MEM_ALOGE("failed to wait for pause of thread %d of process %d: %s", tid, pid_, strerror(errno));
Colin Crossab7c9f12016-01-14 15:35:40 -0800260 PtraceDetach(tid, 0);
261 return -1;
262 }
263
264 if (!WIFSTOPPED(status)) {
Christopher Ferris56b8d862017-05-03 17:34:29 -0700265 MEM_ALOGE("thread %d of process %d was not paused after waitpid, killed?", tid, pid_);
Colin Crossab7c9f12016-01-14 15:35:40 -0800266 return 0;
267 }
268
269 unsigned int resume_signal = 0;
270
Colin Cross401319a2017-06-22 10:50:05 -0700271 unsigned int signal = WSTOPSIG(status);
Colin Crossab7c9f12016-01-14 15:35:40 -0800272 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:
Christopher Ferris56b8d862017-05-03 17:34:29 -0700284 MEM_ALOGE("unexpected signal %d with PTRACE_EVENT_STOP for thread %d of process %d", signal,
285 tid, pid_);
Colin Crossab7c9f12016-01-14 15:35:40 -0800286 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
297bool 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
305bool ThreadCaptureImpl::ReleaseThread(pid_t tid, unsigned int signal) {
306 PtraceDetach(tid, signal);
307 return true;
308}
309
310bool ThreadCaptureImpl::ReleaseThreads() {
311 bool ret = true;
Colin Cross401319a2017-06-22 10:50:05 -0700312 for (auto it = captured_threads_.begin(); it != captured_threads_.end();) {
Colin Crossab7c9f12016-01-14 15:35:40 -0800313 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
323bool 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
336ThreadCapture::ThreadCapture(pid_t pid, Allocator<ThreadCapture> allocator) {
337 Allocator<ThreadCaptureImpl> impl_allocator = allocator;
338 impl_ = impl_allocator.make_unique(pid, impl_allocator);
339}
340
341ThreadCapture::~ThreadCapture() {}
342
343bool ThreadCapture::ListThreads(TidList& tids) {
344 return impl_->ListThreads(tids);
345}
346
347bool ThreadCapture::CaptureThreads() {
348 return impl_->CaptureThreads();
349}
350
351bool ThreadCapture::ReleaseThreads() {
352 return impl_->ReleaseThreads();
353}
354
355bool ThreadCapture::ReleaseThread(pid_t tid) {
356 return impl_->ReleaseThread(tid);
357}
358
359bool ThreadCapture::CapturedThreadInfo(ThreadInfoList& threads) {
360 return impl_->CapturedThreadInfo(threads);
361}
362
363void ThreadCapture::InjectTestFunc(std::function<void(pid_t)>&& f) {
364 impl_->InjectTestFunc(std::forward<std::function<void(pid_t)>>(f));
365}
Colin Cross1fa81f52017-06-21 13:13:00 -0700366
367} // namespace android