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 = ®s; 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(®s[0], ®s[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