xref: /openbsd-src/gnu/llvm/lldb/source/Host/linux/Host.cpp (revision 5a38ef86d0b61900239c7913d24a05e7b88a58f0)
1 //===-- source/Host/linux/Host.cpp ----------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include <cerrno>
10 #include <cstdio>
11 #include <cstring>
12 #include <dirent.h>
13 #include <fcntl.h>
14 #include <sys/stat.h>
15 #include <sys/types.h>
16 #include <sys/utsname.h>
17 #include <unistd.h>
18 
19 #include "llvm/ADT/StringSwitch.h"
20 #include "llvm/Object/ELF.h"
21 #include "llvm/Support/ScopedPrinter.h"
22 
23 #include "lldb/Utility/Log.h"
24 #include "lldb/Utility/ProcessInfo.h"
25 #include "lldb/Utility/Status.h"
26 
27 #include "lldb/Host/FileSystem.h"
28 #include "lldb/Host/Host.h"
29 #include "lldb/Host/HostInfo.h"
30 #include "lldb/Host/linux/Host.h"
31 #include "lldb/Host/linux/Support.h"
32 #include "lldb/Utility/DataExtractor.h"
33 
34 using namespace lldb;
35 using namespace lldb_private;
36 
37 namespace {
38 enum class ProcessState {
39   Unknown,
40   Dead,
41   DiskSleep,
42   Idle,
43   Paging,
44   Parked,
45   Running,
46   Sleeping,
47   TracedOrStopped,
48   Zombie,
49 };
50 }
51 
52 namespace lldb_private {
53 class ProcessLaunchInfo;
54 }
55 
56 static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo,
57                           ProcessState &State, ::pid_t &TracerPid,
58                           ::pid_t &Tgid) {
59   Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
60 
61   auto BufferOrError = getProcFile(Pid, "status");
62   if (!BufferOrError)
63     return false;
64 
65   llvm::StringRef Rest = BufferOrError.get()->getBuffer();
66   while (!Rest.empty()) {
67     llvm::StringRef Line;
68     std::tie(Line, Rest) = Rest.split('\n');
69 
70     if (Line.consume_front("Gid:")) {
71       // Real, effective, saved set, and file system GIDs. Read the first two.
72       Line = Line.ltrim();
73       uint32_t RGid, EGid;
74       Line.consumeInteger(10, RGid);
75       Line = Line.ltrim();
76       Line.consumeInteger(10, EGid);
77 
78       ProcessInfo.SetGroupID(RGid);
79       ProcessInfo.SetEffectiveGroupID(EGid);
80     } else if (Line.consume_front("Uid:")) {
81       // Real, effective, saved set, and file system UIDs. Read the first two.
82       Line = Line.ltrim();
83       uint32_t RUid, EUid;
84       Line.consumeInteger(10, RUid);
85       Line = Line.ltrim();
86       Line.consumeInteger(10, EUid);
87 
88       ProcessInfo.SetUserID(RUid);
89       ProcessInfo.SetEffectiveUserID(EUid);
90     } else if (Line.consume_front("PPid:")) {
91       ::pid_t PPid;
92       Line.ltrim().consumeInteger(10, PPid);
93       ProcessInfo.SetParentProcessID(PPid);
94     } else if (Line.consume_front("State:")) {
95       State = llvm::StringSwitch<ProcessState>(Line.ltrim().take_front(1))
96                   .Case("D", ProcessState::DiskSleep)
97                   .Case("I", ProcessState::Idle)
98                   .Case("R", ProcessState::Running)
99                   .Case("S", ProcessState::Sleeping)
100                   .CaseLower("T", ProcessState::TracedOrStopped)
101                   .Case("W", ProcessState::Paging)
102                   .Case("P", ProcessState::Parked)
103                   .Case("X", ProcessState::Dead)
104                   .Case("Z", ProcessState::Zombie)
105                   .Default(ProcessState::Unknown);
106       if (State == ProcessState::Unknown) {
107         LLDB_LOG(log, "Unknown process state {0}", Line);
108       }
109     } else if (Line.consume_front("TracerPid:")) {
110       Line = Line.ltrim();
111       Line.consumeInteger(10, TracerPid);
112     } else if (Line.consume_front("Tgid:")) {
113       Line = Line.ltrim();
114       Line.consumeInteger(10, Tgid);
115     }
116   }
117   return true;
118 }
119 
120 static bool IsDirNumeric(const char *dname) {
121   for (; *dname; dname++) {
122     if (!isdigit(*dname))
123       return false;
124   }
125   return true;
126 }
127 
128 static ArchSpec GetELFProcessCPUType(llvm::StringRef exe_path) {
129   Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
130 
131   auto buffer_sp = FileSystem::Instance().CreateDataBuffer(exe_path, 0x20, 0);
132   if (!buffer_sp)
133     return ArchSpec();
134 
135   uint8_t exe_class =
136       llvm::object::getElfArchType(
137           {buffer_sp->GetChars(), size_t(buffer_sp->GetByteSize())})
138           .first;
139 
140   switch (exe_class) {
141   case llvm::ELF::ELFCLASS32:
142     return HostInfo::GetArchitecture(HostInfo::eArchKind32);
143   case llvm::ELF::ELFCLASS64:
144     return HostInfo::GetArchitecture(HostInfo::eArchKind64);
145   default:
146     LLDB_LOG(log, "Unknown elf class ({0}) in file {1}", exe_class, exe_path);
147     return ArchSpec();
148   }
149 }
150 
151 static void GetProcessArgs(::pid_t pid, ProcessInstanceInfo &process_info) {
152   auto BufferOrError = getProcFile(pid, "cmdline");
153   if (!BufferOrError)
154     return;
155   std::unique_ptr<llvm::MemoryBuffer> Cmdline = std::move(*BufferOrError);
156 
157   llvm::StringRef Arg0, Rest;
158   std::tie(Arg0, Rest) = Cmdline->getBuffer().split('\0');
159   process_info.SetArg0(Arg0);
160   while (!Rest.empty()) {
161     llvm::StringRef Arg;
162     std::tie(Arg, Rest) = Rest.split('\0');
163     process_info.GetArguments().AppendArgument(Arg);
164   }
165 }
166 
167 static void GetExePathAndArch(::pid_t pid, ProcessInstanceInfo &process_info) {
168   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
169   std::string ExePath(PATH_MAX, '\0');
170 
171   // We can't use getProcFile here because proc/[pid]/exe is a symbolic link.
172   llvm::SmallString<64> ProcExe;
173   (llvm::Twine("/proc/") + llvm::Twine(pid) + "/exe").toVector(ProcExe);
174 
175   ssize_t len = readlink(ProcExe.c_str(), &ExePath[0], PATH_MAX);
176   if (len > 0) {
177     ExePath.resize(len);
178   } else {
179     LLDB_LOG(log, "failed to read link exe link for {0}: {1}", pid,
180              Status(errno, eErrorTypePOSIX));
181     ExePath.resize(0);
182   }
183   // If the binary has been deleted, the link name has " (deleted)" appended.
184   // Remove if there.
185   llvm::StringRef PathRef = ExePath;
186   PathRef.consume_back(" (deleted)");
187 
188   if (!PathRef.empty()) {
189     process_info.GetExecutableFile().SetFile(PathRef, FileSpec::Style::native);
190     process_info.SetArchitecture(GetELFProcessCPUType(PathRef));
191   }
192 }
193 
194 static void GetProcessEnviron(::pid_t pid, ProcessInstanceInfo &process_info) {
195   // Get the process environment.
196   auto BufferOrError = getProcFile(pid, "environ");
197   if (!BufferOrError)
198     return;
199 
200   std::unique_ptr<llvm::MemoryBuffer> Environ = std::move(*BufferOrError);
201   llvm::StringRef Rest = Environ->getBuffer();
202   while (!Rest.empty()) {
203     llvm::StringRef Var;
204     std::tie(Var, Rest) = Rest.split('\0');
205     process_info.GetEnvironment().insert(Var);
206   }
207 }
208 
209 static bool GetProcessAndStatInfo(::pid_t pid,
210                                   ProcessInstanceInfo &process_info,
211                                   ProcessState &State, ::pid_t &tracerpid) {
212   ::pid_t tgid;
213   tracerpid = 0;
214   process_info.Clear();
215 
216   process_info.SetProcessID(pid);
217 
218   GetExePathAndArch(pid, process_info);
219   GetProcessArgs(pid, process_info);
220   GetProcessEnviron(pid, process_info);
221 
222   // Get User and Group IDs and get tracer pid.
223   if (!GetStatusInfo(pid, process_info, State, tracerpid, tgid))
224     return false;
225 
226   return true;
227 }
228 
229 uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info,
230                                  ProcessInstanceInfoList &process_infos) {
231   static const char procdir[] = "/proc/";
232 
233   DIR *dirproc = opendir(procdir);
234   if (dirproc) {
235     struct dirent *direntry = nullptr;
236     const uid_t our_uid = getuid();
237     const lldb::pid_t our_pid = getpid();
238     bool all_users = match_info.GetMatchAllUsers();
239 
240     while ((direntry = readdir(dirproc)) != nullptr) {
241       if (direntry->d_type != DT_DIR || !IsDirNumeric(direntry->d_name))
242         continue;
243 
244       lldb::pid_t pid = atoi(direntry->d_name);
245 
246       // Skip this process.
247       if (pid == our_pid)
248         continue;
249 
250       ::pid_t tracerpid;
251       ProcessState State;
252       ProcessInstanceInfo process_info;
253 
254       if (!GetProcessAndStatInfo(pid, process_info, State, tracerpid))
255         continue;
256 
257       // Skip if process is being debugged.
258       if (tracerpid != 0)
259         continue;
260 
261       if (State == ProcessState::Zombie)
262         continue;
263 
264       // Check for user match if we're not matching all users and not running
265       // as root.
266       if (!all_users && (our_uid != 0) && (process_info.GetUserID() != our_uid))
267         continue;
268 
269       if (match_info.Matches(process_info)) {
270         process_infos.push_back(process_info);
271       }
272     }
273 
274     closedir(dirproc);
275   }
276 
277   return process_infos.size();
278 }
279 
280 bool Host::FindProcessThreads(const lldb::pid_t pid, TidMap &tids_to_attach) {
281   bool tids_changed = false;
282   static const char procdir[] = "/proc/";
283   static const char taskdir[] = "/task/";
284   std::string process_task_dir = procdir + llvm::to_string(pid) + taskdir;
285   DIR *dirproc = opendir(process_task_dir.c_str());
286 
287   if (dirproc) {
288     struct dirent *direntry = nullptr;
289     while ((direntry = readdir(dirproc)) != nullptr) {
290       if (direntry->d_type != DT_DIR || !IsDirNumeric(direntry->d_name))
291         continue;
292 
293       lldb::tid_t tid = atoi(direntry->d_name);
294       TidMap::iterator it = tids_to_attach.find(tid);
295       if (it == tids_to_attach.end()) {
296         tids_to_attach.insert(TidPair(tid, false));
297         tids_changed = true;
298       }
299     }
300     closedir(dirproc);
301   }
302 
303   return tids_changed;
304 }
305 
306 bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) {
307   ::pid_t tracerpid;
308   ProcessState State;
309   return GetProcessAndStatInfo(pid, process_info, State, tracerpid);
310 }
311 
312 Environment Host::GetEnvironment() { return Environment(environ); }
313 
314 Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
315   return Status("unimplemented");
316 }
317 
318 llvm::Optional<lldb::pid_t> lldb_private::getPIDForTID(lldb::pid_t tid) {
319   ::pid_t tracerpid, tgid = LLDB_INVALID_PROCESS_ID;
320   ProcessInstanceInfo process_info;
321   ProcessState state;
322 
323   if (!GetStatusInfo(tid, process_info, state, tracerpid, tgid) ||
324       tgid == LLDB_INVALID_PROCESS_ID)
325     return llvm::None;
326   return tgid;
327 }
328