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