1 /*
2 * Copyright (C) 2021 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 <errno.h>
18 #include <fcntl.h>
19 #include <sys/ptrace.h>
20 #include <sys/types.h>
21 #include <sys/wait.h>
22 #include <unistd.h>
23
24 #include <algorithm>
25 #include <array>
26 #include <atomic>
27 #include <csignal>
28 #include <cstddef>
29 #include <cstdio>
30 #include <cstring>
31 #include <memory>
32 #include <regex>
33 #include <string>
34 #include <unordered_set>
35 #include <vector>
36
37 #include <unwindstack/MapInfo.h>
38 #include <unwindstack/Maps.h>
39 #include <unwindstack/Regs.h>
40 #include <unwindstack/Unwinder.h>
41
42 #include <android-base/file.h>
43 #include <android-base/stringprintf.h>
44 #include <procinfo/process.h>
45
46 #include "ProcessTracer.h"
47
48 namespace unwindstack {
49
ProcessTracer(pid_t pid,bool is_tracing_threads)50 ProcessTracer::ProcessTracer(pid_t pid, bool is_tracing_threads)
51 : pid_(pid), is_tracing_threads_(is_tracing_threads) {
52 if (is_tracing_threads_) is_tracing_threads_ = InitProcessTids();
53 }
54
InitProcessTids()55 bool ProcessTracer::InitProcessTids() {
56 std::string error_msg;
57 if (!android::procinfo::GetProcessTids(pid_, &tids_, &error_msg)) {
58 fprintf(stderr,
59 "Failed to get process tids: %s. Reverting to tracing the "
60 "main thread only.\n",
61 error_msg.c_str());
62 return false;
63 }
64 if (tids_.erase(pid_) != 1) {
65 fprintf(stderr,
66 "Failed to erase the main thread from the thread id set. "
67 "Reverting to tracing the main thread only.\n");
68 return false;
69 }
70 return true;
71 }
72
~ProcessTracer()73 ProcessTracer::~ProcessTracer() {
74 if (cur_attached_tid_ != kNoThreadAttached) Detach(cur_attached_tid_);
75 if (!is_running_) Resume();
76 }
77
Stop()78 bool ProcessTracer::Stop() {
79 if (kill(pid_, SIGSTOP) == kKillFailed) {
80 fprintf(stderr, "Failed to send stop signal to pid %d: %s\n", pid_, strerror(errno));
81 return false;
82 }
83 usleep(1000); // 1 ms. Without this sleep, any attempt to resume right away may fail.
84
85 is_running_ = false;
86 return true;
87 }
88
Resume()89 bool ProcessTracer::Resume() {
90 if (kill(pid_, SIGCONT) == kKillFailed) {
91 fprintf(stderr, "Failed to send continue signal to pid %d: %s\n", pid_, strerror(errno));
92 return false;
93 }
94 usleep(1000); // 1 ms. Without this sleep, any attempt to stop right away may fail.
95
96 is_running_ = true;
97 return true;
98 }
99
Detach(pid_t tid)100 bool ProcessTracer::Detach(pid_t tid) {
101 if (tid != pid_ && tids_.find(tid) == tids_.end()) {
102 fprintf(stderr, "Tid %d does not belong to proc %d.\n", tid, pid_);
103 return false;
104 }
105
106 if (cur_attached_tid_ == kNoThreadAttached) {
107 fprintf(stderr, "Cannot detach because no thread is currently attached.\n");
108 return false;
109 }
110 if (is_running_ && !Stop()) return false;
111
112 if (ptrace(PTRACE_DETACH, tid, nullptr, nullptr) == kPtraceFailed) {
113 fprintf(stderr, "Failed to detach from tid %d: %s\n", tid, strerror(errno));
114 return false;
115 }
116
117 cur_attached_tid_ = kNoThreadAttached;
118 return true;
119 }
120
Attach(pid_t tid)121 bool ProcessTracer::Attach(pid_t tid) {
122 if (tid != pid_ && tids_.find(tid) == tids_.end()) {
123 fprintf(stderr, "Tid %d does not belong to proc %d.\n", tid, pid_);
124 return false;
125 }
126
127 if (is_running_) Stop();
128 if (cur_attached_tid_ != kNoThreadAttached) {
129 fprintf(stderr, "Cannot attatch to tid %d. Already attached to tid %d.\n", tid,
130 cur_attached_tid_);
131 return false;
132 }
133
134 if (ptrace(PTRACE_ATTACH, tid, nullptr, nullptr) == kPtraceFailed) {
135 fprintf(stderr, "Failed to attached to tid %d: %s\n", tid, strerror(errno));
136 return false;
137 }
138 int status;
139 if (waitpid(tid, &status, 0) == kWaitpidFailed) {
140 fprintf(stderr, "Failed to stop tid %d: %s\n", tid, strerror(errno));
141 return false;
142 }
143
144 cur_attached_tid_ = tid;
145 return true;
146 }
147
StopInDesiredElf(const std::string & elf_name)148 bool ProcessTracer::StopInDesiredElf(const std::string& elf_name) {
149 signal(SIGINT, [](int) { keepWaitingForPcInElf = false; });
150 bool pc_in_desired_elf = true;
151 do {
152 if (!Attach(pid_)) return false;
153 pc_in_desired_elf = ProcIsInDesiredElf(pid_, elf_name);
154 if (!Detach(pid_)) return false;
155
156 if (!pc_in_desired_elf) {
157 for (pid_t tid : tids_) {
158 if (!Attach(tid)) return false;
159 pc_in_desired_elf = ProcIsInDesiredElf(tid, elf_name);
160 if (!Detach(tid)) return false;
161 if (pc_in_desired_elf) break;
162 }
163 }
164
165 // If the process is not in the desired ELF, resume it for a short time, then check again.
166 if (!pc_in_desired_elf) {
167 Resume();
168 usleep(1000); // 1 ms
169 Stop();
170 }
171 } while (!pc_in_desired_elf && keepWaitingForPcInElf);
172
173 if (!pc_in_desired_elf) {
174 fprintf(stderr, "\nExited while waiting for pid %d to enter %s.\n", pid_, elf_name.c_str());
175 return false;
176 }
177 return true;
178 }
179
UsesSharedLibrary(pid_t pid,const std::string & desired_elf_name)180 bool ProcessTracer::UsesSharedLibrary(pid_t pid, const std::string& desired_elf_name) {
181 std::unique_ptr<Maps> maps = std::make_unique<RemoteMaps>(pid);
182 if (!maps->Parse()) {
183 fprintf(stderr, "Could not parse maps for pid %d.\n", pid);
184 return false;
185 }
186 for (const auto& map : *maps) {
187 if (android::base::Basename(map->name()).c_str() == desired_elf_name) return true;
188 }
189 return false;
190 }
191
ProcIsInDesiredElf(pid_t pid,const std::string & desired_elf_name)192 bool ProcessTracer::ProcIsInDesiredElf(pid_t pid, const std::string& desired_elf_name) {
193 std::unique_ptr<Regs> regs(Regs::RemoteGet(pid));
194 if (regs == nullptr) {
195 fprintf(stderr, "Unable to get remote reg data.\n");
196 return false;
197 }
198 UnwinderFromPid unwinder(1024, pid);
199 unwinder.SetRegs(regs.get());
200 if (!unwinder.Init()) {
201 fprintf(stderr, "Unable to intitialize unwinder.\n");
202 return false;
203 }
204 Maps* maps = unwinder.GetMaps();
205 auto map_info = maps->Find(regs->pc());
206 if (map_info == nullptr) {
207 regs->fallback_pc();
208 map_info = maps->Find(regs->pc());
209 if (map_info == nullptr) {
210 return false;
211 }
212 }
213
214 const std::string& current_elf_name = android::base::Basename(map_info->name()).c_str();
215 bool in_desired_elf = current_elf_name == desired_elf_name;
216 if (in_desired_elf) printf("pid %d is in %s! Unwinding...\n\n", pid, desired_elf_name.c_str());
217 return in_desired_elf;
218 }
219 } // namespace unwindstack
220