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 <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> 30 #include <unistd.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 namespace android { 43 44 // 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. 55 static char* pid_to_str(char* buf, size_t len, pid_t pid) { 56 if (pid <= 0) { 57 return nullptr; 58 } 59 60 char* ptr = buf + len - 1; 61 *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 74 class 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; } 84 85 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 92 allocator::map<pid_t, unsigned int> captured_threads_; 93 Allocator<ThreadCaptureImpl> allocator_; 94 pid_t pid_; 95 std::function<void(pid_t)> inject_test_func_; 96 }; 97 98 ThreadCaptureImpl::ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator) 99 : captured_threads_(allocator), allocator_(allocator), pid_(pid) {} 100 101 bool 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 113 android::base::unique_fd fd(open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY)); 114 if (fd == -1) { 115 MEM_ALOGE("failed to open %s: %s", path, strerror(errno)); 116 return false; 117 } 118 119 struct linux_dirent64 { 120 uint64_t d_ino; 121 int64_t d_off; 122 uint16_t d_reclen; 123 char d_type; 124 char d_name[]; 125 } __attribute((packed)); 126 char dirent_buf[4096]; 127 ssize_t nread; 128 do { 129 nread = syscall(SYS_getdents64, fd.get(), dirent_buf, sizeof(dirent_buf)); 130 if (nread < 0) { 131 MEM_ALOGE("failed to get directory entries from %s: %s", path, strerror(errno)); 132 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 151 bool 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 179 void 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) { 182 MEM_ALOGE("failed to detach from thread %d of process %d: %s", tid, pid_, 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 188 int ThreadCaptureImpl::PtraceAttach(pid_t tid) { 189 int ret = ptrace(PTRACE_SEIZE, tid, NULL, NULL); 190 if (ret < 0) { 191 MEM_ALOGE("failed to attach to thread %d of process %d: %s", tid, pid_, 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 MEM_ALOGE("failed to interrupt thread %d of process %d: %s", tid, pid_, strerror(errno)); 204 PtraceDetach(tid, 0); 205 return -1; 206 } 207 } 208 return 1; 209 } 210 211 bool ThreadCaptureImpl::PtraceThreadInfo(pid_t tid, ThreadInfo& thread_info) { 212 thread_info.tid = tid; 213 214 const unsigned int max_num_regs = 128; // larger than number of registers on any device 215 uintptr_t regs[max_num_regs]; 216 struct iovec iovec; 217 iovec.iov_base = ®s; 218 iovec.iov_len = sizeof(regs); 219 220 if (ptrace(PTRACE_GETREGSET, tid, reinterpret_cast<void*>(NT_PRSTATUS), &iovec)) { 221 MEM_ALOGE("ptrace getregset for thread %d of process %d failed: %s", tid, pid_, strerror(errno)); 222 return false; 223 } 224 225 unsigned int num_regs = iovec.iov_len / sizeof(uintptr_t); 226 thread_info.regs.assign(®s[0], ®s[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 248 return true; 249 } 250 251 int 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) { 259 MEM_ALOGE("failed to wait for pause of thread %d of process %d: %s", tid, pid_, strerror(errno)); 260 PtraceDetach(tid, 0); 261 return -1; 262 } 263 264 if (!WIFSTOPPED(status)) { 265 MEM_ALOGE("thread %d of process %d was not paused after waitpid, killed?", tid, pid_); 266 return 0; 267 } 268 269 unsigned int resume_signal = 0; 270 271 unsigned int signal = WSTOPSIG(status); 272 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: 284 MEM_ALOGE("unexpected signal %d with PTRACE_EVENT_STOP for thread %d of process %d", signal, 285 tid, pid_); 286 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 297 bool 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 305 bool ThreadCaptureImpl::ReleaseThread(pid_t tid, unsigned int signal) { 306 PtraceDetach(tid, signal); 307 return true; 308 } 309 310 bool ThreadCaptureImpl::ReleaseThreads() { 311 bool ret = true; 312 for (auto it = captured_threads_.begin(); it != captured_threads_.end();) { 313 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 323 bool 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 336 ThreadCapture::ThreadCapture(pid_t pid, Allocator<ThreadCapture> allocator) { 337 Allocator<ThreadCaptureImpl> impl_allocator = allocator; 338 impl_ = impl_allocator.make_unique(pid, impl_allocator); 339 } 340 341 ThreadCapture::~ThreadCapture() {} 342 343 bool ThreadCapture::ListThreads(TidList& tids) { 344 return impl_->ListThreads(tids); 345 } 346 347 bool ThreadCapture::CaptureThreads() { 348 return impl_->CaptureThreads(); 349 } 350 351 bool ThreadCapture::ReleaseThreads() { 352 return impl_->ReleaseThreads(); 353 } 354 355 bool ThreadCapture::ReleaseThread(pid_t tid) { 356 return impl_->ReleaseThread(tid); 357 } 358 359 bool ThreadCapture::CapturedThreadInfo(ThreadInfoList& threads) { 360 return impl_->CapturedThreadInfo(threads); 361 } 362 363 void ThreadCapture::InjectTestFunc(std::function<void(pid_t)>&& f) { 364 impl_->InjectTestFunc(std::forward<std::function<void(pid_t)>>(f)); 365 } 366 367 } // namespace android 368