• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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