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/posix/Support.h" |
34 | #include "lldb/Utility/DataExtractor.h" |
35 | |
36 | using namespace lldb; |
37 | using namespace lldb_private; |
38 | |
39 | namespace { |
40 | |
41 | enum class ProcessState { |
42 | Unknown, |
43 | Dead, |
44 | DiskSleep, |
45 | Idle, |
46 | Paging, |
47 | Parked, |
48 | Running, |
49 | Sleeping, |
50 | TracedOrStopped, |
51 | Zombie, |
52 | }; |
53 | |
54 | struct StatFields { |
55 | ::pid_t pid = LLDB_INVALID_PROCESS_ID; |
56 | // comm |
57 | char state; |
58 | ::pid_t ppid = LLDB_INVALID_PROCESS_ID; |
59 | ::pid_t pgrp = LLDB_INVALID_PROCESS_ID; |
60 | ::pid_t session = LLDB_INVALID_PROCESS_ID; |
61 | int tty_nr; |
62 | int tpgid; |
63 | unsigned flags; |
64 | long unsigned minflt; |
65 | long unsigned cminflt; |
66 | long unsigned majflt; |
67 | long unsigned cmajflt; |
68 | long unsigned utime; |
69 | long unsigned stime; |
70 | long cutime; |
71 | long cstime; |
72 | // In proc_pid_stat(5) this field is specified as priority |
73 | // but documented as realtime priority. To keep with the adopted |
74 | // nomenclature in ProcessInstanceInfo, we adopt the documented |
75 | // naming here. |
76 | long realtime_priority; |
77 | long priority; |
78 | // .... other things. We don't need them below |
79 | }; |
80 | } |
81 | |
82 | namespace lldb_private { |
83 | class ProcessLaunchInfo; |
84 | } |
85 | |
86 | static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo, |
87 | ProcessState &State, ::pid_t &TracerPid, |
88 | ::pid_t &Tgid) { |
89 | Log *log = GetLog(mask: LLDBLog::Host); |
90 | |
91 | auto BufferOrError = getProcFile(pid: Pid, file: "stat" ); |
92 | if (!BufferOrError) |
93 | return false; |
94 | |
95 | llvm::StringRef Rest = BufferOrError.get()->getBuffer(); |
96 | if (Rest.empty()) |
97 | return false; |
98 | StatFields stat_fields; |
99 | if (sscanf( |
100 | s: Rest.data(), |
101 | format: "%d %*s %c %d %d %d %d %d %u %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld" , |
102 | &stat_fields.pid, /* comm, */ &stat_fields.state, |
103 | &stat_fields.ppid, &stat_fields.pgrp, &stat_fields.session, |
104 | &stat_fields.tty_nr, &stat_fields.tpgid, &stat_fields.flags, |
105 | &stat_fields.minflt, &stat_fields.cminflt, &stat_fields.majflt, |
106 | &stat_fields.cmajflt, &stat_fields.utime, &stat_fields.stime, |
107 | &stat_fields.cutime, &stat_fields.cstime, |
108 | &stat_fields.realtime_priority, &stat_fields.priority) < 0) { |
109 | return false; |
110 | } |
111 | |
112 | auto convert = [sc_clk_ticks = sysconf(_SC_CLK_TCK)](auto time_in_ticks) { |
113 | ProcessInstanceInfo::timespec ts; |
114 | if (sc_clk_ticks <= 0) { |
115 | return ts; |
116 | } |
117 | ts.tv_sec = time_in_ticks / sc_clk_ticks; |
118 | double remainder = |
119 | (static_cast<double>(time_in_ticks) / sc_clk_ticks) - ts.tv_sec; |
120 | ts.tv_usec = |
121 | std::chrono::microseconds{std::lround(x: 1e+6 * remainder)}.count(); |
122 | return ts; |
123 | }; |
124 | |
125 | // Priority (nice) values run from 19 to -20 inclusive (in linux). In the |
126 | // prpsinfo struct pr_nice is a char. |
127 | auto priority_value = static_cast<int8_t>( |
128 | (stat_fields.priority < 0 ? 0x80 : 0x00) | (stat_fields.priority & 0x7f)); |
129 | |
130 | ProcessInfo.SetParentProcessID(stat_fields.ppid); |
131 | ProcessInfo.SetProcessGroupID(stat_fields.pgrp); |
132 | ProcessInfo.SetProcessSessionID(stat_fields.session); |
133 | ProcessInfo.SetUserTime(convert(stat_fields.utime)); |
134 | ProcessInfo.SetSystemTime(convert(stat_fields.stime)); |
135 | ProcessInfo.SetCumulativeUserTime(convert(stat_fields.cutime)); |
136 | ProcessInfo.SetCumulativeSystemTime(convert(stat_fields.cstime)); |
137 | ProcessInfo.SetPriorityValue(priority_value); |
138 | switch (stat_fields.state) { |
139 | case 'R': |
140 | State = ProcessState::Running; |
141 | break; |
142 | case 'S': |
143 | State = ProcessState::Sleeping; |
144 | break; |
145 | case 'D': |
146 | State = ProcessState::DiskSleep; |
147 | break; |
148 | case 'Z': |
149 | State = ProcessState::Zombie; |
150 | break; |
151 | case 'X': |
152 | State = ProcessState::Dead; |
153 | break; |
154 | case 'P': |
155 | State = ProcessState::Parked; |
156 | break; |
157 | case 'W': |
158 | State = ProcessState::Paging; |
159 | break; |
160 | case 'I': |
161 | State = ProcessState::Idle; |
162 | break; |
163 | case 'T': // Stopped on a signal or (before Linux 2.6.33) trace stopped |
164 | [[fallthrough]]; |
165 | case 't': |
166 | State = ProcessState::TracedOrStopped; |
167 | break; |
168 | default: |
169 | State = ProcessState::Unknown; |
170 | break; |
171 | } |
172 | ProcessInfo.SetIsZombie(State == ProcessState::Zombie); |
173 | |
174 | if (State == ProcessState::Unknown) { |
175 | LLDB_LOG(log, "Unknown process state {0}" , stat_fields.state); |
176 | } |
177 | |
178 | BufferOrError = getProcFile(pid: Pid, file: "status" ); |
179 | if (!BufferOrError) |
180 | return false; |
181 | |
182 | Rest = BufferOrError.get()->getBuffer(); |
183 | if (Rest.empty()) |
184 | return false; |
185 | |
186 | while (!Rest.empty()) { |
187 | llvm::StringRef Line; |
188 | std::tie(args&: Line, args&: Rest) = Rest.split(Separator: '\n'); |
189 | |
190 | if (Line.consume_front(Prefix: "Gid:" )) { |
191 | // Real, effective, saved set, and file system GIDs. Read the first two. |
192 | Line = Line.ltrim(); |
193 | uint32_t RGid, EGid; |
194 | Line.consumeInteger(Radix: 10, Result&: RGid); |
195 | Line = Line.ltrim(); |
196 | Line.consumeInteger(Radix: 10, Result&: EGid); |
197 | |
198 | ProcessInfo.SetGroupID(RGid); |
199 | ProcessInfo.SetEffectiveGroupID(EGid); |
200 | } else if (Line.consume_front(Prefix: "Uid:" )) { |
201 | // Real, effective, saved set, and file system UIDs. Read the first two. |
202 | Line = Line.ltrim(); |
203 | uint32_t RUid, EUid; |
204 | Line.consumeInteger(Radix: 10, Result&: RUid); |
205 | Line = Line.ltrim(); |
206 | Line.consumeInteger(Radix: 10, Result&: EUid); |
207 | |
208 | ProcessInfo.SetUserID(RUid); |
209 | ProcessInfo.SetEffectiveUserID(EUid); |
210 | } else if (Line.consume_front(Prefix: "TracerPid:" )) { |
211 | Line = Line.ltrim(); |
212 | Line.consumeInteger(Radix: 10, Result&: TracerPid); |
213 | } else if (Line.consume_front(Prefix: "Tgid:" )) { |
214 | Line = Line.ltrim(); |
215 | Line.consumeInteger(Radix: 10, Result&: Tgid); |
216 | } else if (Line.consume_front(Prefix: "CoreDumping:" )) { |
217 | uint32_t coredumping; |
218 | Line = Line.ltrim(); |
219 | if (!Line.consumeInteger(Radix: 2, Result&: coredumping)) |
220 | ProcessInfo.SetIsCoreDumping(coredumping); |
221 | } |
222 | } |
223 | return true; |
224 | } |
225 | |
226 | static bool IsDirNumeric(const char *dname) { |
227 | for (; *dname; dname++) { |
228 | if (!isdigit(*dname)) |
229 | return false; |
230 | } |
231 | return true; |
232 | } |
233 | |
234 | static ArchSpec GetELFProcessCPUType(llvm::StringRef exe_path) { |
235 | Log *log = GetLog(mask: LLDBLog::Host); |
236 | |
237 | auto buffer_sp = FileSystem::Instance().CreateDataBuffer(path: exe_path, size: 0x20, offset: 0); |
238 | if (!buffer_sp) |
239 | return ArchSpec(); |
240 | |
241 | uint8_t exe_class = |
242 | llvm::object::getElfArchType( |
243 | Object: {reinterpret_cast<const char *>(buffer_sp->GetBytes()), |
244 | size_t(buffer_sp->GetByteSize())}) |
245 | .first; |
246 | |
247 | switch (exe_class) { |
248 | case llvm::ELF::ELFCLASS32: |
249 | return HostInfo::GetArchitecture(arch_kind: HostInfo::eArchKind32); |
250 | case llvm::ELF::ELFCLASS64: |
251 | return HostInfo::GetArchitecture(arch_kind: HostInfo::eArchKind64); |
252 | default: |
253 | LLDB_LOG(log, "Unknown elf class ({0}) in file {1}" , exe_class, exe_path); |
254 | return ArchSpec(); |
255 | } |
256 | } |
257 | |
258 | static void GetProcessArgs(::pid_t pid, ProcessInstanceInfo &process_info) { |
259 | auto BufferOrError = getProcFile(pid, file: "cmdline" ); |
260 | if (!BufferOrError) |
261 | return; |
262 | std::unique_ptr<llvm::MemoryBuffer> Cmdline = std::move(*BufferOrError); |
263 | |
264 | llvm::StringRef Arg0, Rest; |
265 | std::tie(args&: Arg0, args&: Rest) = Cmdline->getBuffer().split(Separator: '\0'); |
266 | process_info.SetArg0(Arg0); |
267 | while (!Rest.empty()) { |
268 | llvm::StringRef Arg; |
269 | std::tie(args&: Arg, args&: Rest) = Rest.split(Separator: '\0'); |
270 | process_info.GetArguments().AppendArgument(arg_str: Arg); |
271 | } |
272 | } |
273 | |
274 | static void GetExePathAndArch(::pid_t pid, ProcessInstanceInfo &process_info) { |
275 | Log *log = GetLog(mask: LLDBLog::Process); |
276 | std::string ExePath(PATH_MAX, '\0'); |
277 | |
278 | // We can't use getProcFile here because proc/[pid]/exe is a symbolic link. |
279 | llvm::SmallString<64> ProcExe; |
280 | (llvm::Twine("/proc/" ) + llvm::Twine(pid) + "/exe" ).toVector(Out&: ProcExe); |
281 | |
282 | ssize_t len = readlink(path: ProcExe.c_str(), buf: &ExePath[0], PATH_MAX); |
283 | if (len > 0) { |
284 | ExePath.resize(n: len); |
285 | } else { |
286 | LLDB_LOG(log, "failed to read link exe link for {0}: {1}" , pid, |
287 | Status(errno, eErrorTypePOSIX)); |
288 | ExePath.resize(n: 0); |
289 | } |
290 | // If the binary has been deleted, the link name has " (deleted)" appended. |
291 | // Remove if there. |
292 | llvm::StringRef PathRef = ExePath; |
293 | PathRef.consume_back(Suffix: " (deleted)" ); |
294 | |
295 | if (!PathRef.empty()) { |
296 | process_info.GetExecutableFile().SetFile(path: PathRef, style: FileSpec::Style::native); |
297 | process_info.SetArchitecture(GetELFProcessCPUType(exe_path: PathRef)); |
298 | } |
299 | } |
300 | |
301 | static void GetProcessEnviron(::pid_t pid, ProcessInstanceInfo &process_info) { |
302 | // Get the process environment. |
303 | auto BufferOrError = getProcFile(pid, file: "environ" ); |
304 | if (!BufferOrError) |
305 | return; |
306 | |
307 | std::unique_ptr<llvm::MemoryBuffer> Environ = std::move(*BufferOrError); |
308 | llvm::StringRef Rest = Environ->getBuffer(); |
309 | while (!Rest.empty()) { |
310 | llvm::StringRef Var; |
311 | std::tie(args&: Var, args&: Rest) = Rest.split(Separator: '\0'); |
312 | process_info.GetEnvironment().insert(KeyEqValue: Var); |
313 | } |
314 | } |
315 | |
316 | static bool GetProcessAndStatInfo(::pid_t pid, |
317 | ProcessInstanceInfo &process_info, |
318 | ProcessState &State, ::pid_t &tracerpid) { |
319 | ::pid_t tgid; |
320 | tracerpid = 0; |
321 | process_info.Clear(); |
322 | |
323 | process_info.SetProcessID(pid); |
324 | |
325 | GetExePathAndArch(pid, process_info); |
326 | GetProcessArgs(pid, process_info); |
327 | GetProcessEnviron(pid, process_info); |
328 | |
329 | // Get User and Group IDs and get tracer pid. |
330 | if (!GetStatusInfo(Pid: pid, ProcessInfo&: process_info, State, TracerPid&: tracerpid, Tgid&: tgid)) |
331 | return false; |
332 | |
333 | return true; |
334 | } |
335 | |
336 | uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info, |
337 | ProcessInstanceInfoList &process_infos) { |
338 | static const char procdir[] = "/proc/" ; |
339 | |
340 | DIR *dirproc = opendir(name: procdir); |
341 | if (dirproc) { |
342 | struct dirent *direntry = nullptr; |
343 | const uid_t our_uid = getuid(); |
344 | const lldb::pid_t our_pid = getpid(); |
345 | bool all_users = match_info.GetMatchAllUsers(); |
346 | |
347 | while ((direntry = readdir(dirp: dirproc)) != nullptr) { |
348 | if (direntry->d_type != DT_DIR || !IsDirNumeric(dname: direntry->d_name)) |
349 | continue; |
350 | |
351 | lldb::pid_t pid = atoi(nptr: direntry->d_name); |
352 | |
353 | // Skip this process. |
354 | if (pid == our_pid) |
355 | continue; |
356 | |
357 | ::pid_t tracerpid; |
358 | ProcessState State; |
359 | ProcessInstanceInfo process_info; |
360 | |
361 | if (!GetProcessAndStatInfo(pid, process_info, State, tracerpid)) |
362 | continue; |
363 | |
364 | // Skip if process is being debugged. |
365 | if (tracerpid != 0) |
366 | continue; |
367 | |
368 | if (State == ProcessState::Zombie) |
369 | continue; |
370 | |
371 | // Check for user match if we're not matching all users and not running |
372 | // as root. |
373 | if (!all_users && (our_uid != 0) && (process_info.GetUserID() != our_uid)) |
374 | continue; |
375 | |
376 | if (match_info.Matches(proc_info: process_info)) { |
377 | process_infos.push_back(x: process_info); |
378 | } |
379 | } |
380 | |
381 | closedir(dirp: dirproc); |
382 | } |
383 | |
384 | return process_infos.size(); |
385 | } |
386 | |
387 | bool Host::FindProcessThreads(const lldb::pid_t pid, TidMap &tids_to_attach) { |
388 | bool tids_changed = false; |
389 | static const char procdir[] = "/proc/" ; |
390 | static const char taskdir[] = "/task/" ; |
391 | std::string process_task_dir = procdir + llvm::to_string(Value: pid) + taskdir; |
392 | DIR *dirproc = opendir(name: process_task_dir.c_str()); |
393 | |
394 | if (dirproc) { |
395 | struct dirent *direntry = nullptr; |
396 | while ((direntry = readdir(dirp: dirproc)) != nullptr) { |
397 | if (direntry->d_type != DT_DIR || !IsDirNumeric(dname: direntry->d_name)) |
398 | continue; |
399 | |
400 | lldb::tid_t tid = atoi(nptr: direntry->d_name); |
401 | TidMap::iterator it = tids_to_attach.find(x: tid); |
402 | if (it == tids_to_attach.end()) { |
403 | tids_to_attach.insert(x: TidPair(tid, false)); |
404 | tids_changed = true; |
405 | } |
406 | } |
407 | closedir(dirp: dirproc); |
408 | } |
409 | |
410 | return tids_changed; |
411 | } |
412 | |
413 | bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) { |
414 | ::pid_t tracerpid; |
415 | ProcessState State; |
416 | return GetProcessAndStatInfo(pid, process_info, State, tracerpid); |
417 | } |
418 | |
419 | Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) { |
420 | return Status::FromErrorString(str: "unimplemented" ); |
421 | } |
422 | |
423 | std::optional<lldb::pid_t> lldb_private::getPIDForTID(lldb::pid_t tid) { |
424 | ::pid_t tracerpid, tgid = LLDB_INVALID_PROCESS_ID; |
425 | ProcessInstanceInfo process_info; |
426 | ProcessState state; |
427 | |
428 | if (!GetStatusInfo(Pid: tid, ProcessInfo&: process_info, State&: state, TracerPid&: tracerpid, Tgid&: tgid) || |
429 | tgid == LLDB_INVALID_PROCESS_ID) |
430 | return std::nullopt; |
431 | return tgid; |
432 | } |
433 | |