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