| Colin Cross | bcb4ed3 | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 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. | 
|  | 53 | static 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 |  | 
|  | 72 | class 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 Cross | e4cbe0e | 2016-03-04 16:34:42 -0800 | [diff] [blame] | 89 | allocator::map<pid_t, unsigned int> captured_threads_; | 
| Colin Cross | bcb4ed3 | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 90 | Allocator<ThreadCaptureImpl> allocator_; | 
|  | 91 | pid_t pid_; | 
|  | 92 | std::function<void(pid_t)> inject_test_func_; | 
|  | 93 | }; | 
|  | 94 |  | 
|  | 95 | ThreadCaptureImpl::ThreadCaptureImpl(pid_t pid, Allocator<ThreadCaptureImpl>& allocator) : | 
|  | 96 | captured_threads_(allocator), allocator_(allocator), pid_(pid) { | 
|  | 97 | } | 
|  | 98 |  | 
|  | 99 | bool 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 |  | 
| Elliott Hughes | 2c5d1d7 | 2016-03-28 12:15:36 -0700 | [diff] [blame] | 111 | android::base::unique_fd fd(open(path, O_CLOEXEC | O_DIRECTORY | O_RDONLY)); | 
|  | 112 | if (fd == -1) { | 
| Colin Cross | bcb4ed3 | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 113 | ALOGE("failed to open %s: %s", path, strerror(errno)); | 
|  | 114 | return false; | 
|  | 115 | } | 
| Colin Cross | bcb4ed3 | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 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 { | 
| Elliott Hughes | 2c5d1d7 | 2016-03-28 12:15:36 -0700 | [diff] [blame] | 127 | nread = syscall(SYS_getdents64, fd.get(), dirent_buf, sizeof(dirent_buf)); | 
| Colin Cross | bcb4ed3 | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 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 |  | 
|  | 149 | bool 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 | 
|  | 177 | void 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 | 
|  | 187 | int 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 |  | 
|  | 212 | bool 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 |  | 
|  | 253 | int 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 |  | 
|  | 301 | bool 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 |  | 
|  | 309 | bool ThreadCaptureImpl::ReleaseThread(pid_t tid, unsigned int signal) { | 
|  | 310 | PtraceDetach(tid, signal); | 
|  | 311 | return true; | 
|  | 312 | } | 
|  | 313 |  | 
|  | 314 | bool 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 |  | 
|  | 327 | bool 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 |  | 
|  | 340 | ThreadCapture::ThreadCapture(pid_t pid, Allocator<ThreadCapture> allocator) { | 
|  | 341 | Allocator<ThreadCaptureImpl> impl_allocator = allocator; | 
|  | 342 | impl_ = impl_allocator.make_unique(pid, impl_allocator); | 
|  | 343 | } | 
|  | 344 |  | 
|  | 345 | ThreadCapture::~ThreadCapture() {} | 
|  | 346 |  | 
|  | 347 | bool ThreadCapture::ListThreads(TidList& tids) { | 
|  | 348 | return impl_->ListThreads(tids); | 
|  | 349 | } | 
|  | 350 |  | 
|  | 351 | bool ThreadCapture::CaptureThreads() { | 
|  | 352 | return impl_->CaptureThreads(); | 
|  | 353 | } | 
|  | 354 |  | 
|  | 355 | bool ThreadCapture::ReleaseThreads() { | 
|  | 356 | return impl_->ReleaseThreads(); | 
|  | 357 | } | 
|  | 358 |  | 
|  | 359 | bool ThreadCapture::ReleaseThread(pid_t tid) { | 
|  | 360 | return impl_->ReleaseThread(tid); | 
|  | 361 | } | 
|  | 362 |  | 
|  | 363 | bool ThreadCapture::CapturedThreadInfo(ThreadInfoList& threads) { | 
|  | 364 | return impl_->CapturedThreadInfo(threads); | 
|  | 365 | } | 
|  | 366 |  | 
|  | 367 | void ThreadCapture::InjectTestFunc(std::function<void(pid_t)>&& f) { | 
|  | 368 | impl_->InjectTestFunc(std::forward<std::function<void(pid_t)>>(f)); | 
|  | 369 | } |