180814287SRaphael Isemann //===-- source/Host/linux/Host.cpp ----------------------------------------===//
23e2a18f6SStephen Wilson //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63e2a18f6SStephen Wilson //
73e2a18f6SStephen Wilson //===----------------------------------------------------------------------===//
83e2a18f6SStephen Wilson
976e47d48SRaphael Isemann #include <cerrno>
1076e47d48SRaphael Isemann #include <cstdio>
1176e47d48SRaphael Isemann #include <cstring>
1225d7eb0dSDaniel Malea #include <dirent.h>
1330213ffcSJohnny Chen #include <fcntl.h>
149d90186dSOleksiy Vyalov #include <sys/stat.h>
159d90186dSOleksiy Vyalov #include <sys/types.h>
169d90186dSOleksiy Vyalov #include <sys/utsname.h>
17b6dbe9a9SPavel Labath #include <unistd.h>
1830213ffcSJohnny Chen
19c5aa63ddSJordan Rupprecht #include "llvm/ADT/StringSwitch.h"
2036e82208SPavel Labath #include "llvm/Object/ELF.h"
211eb0d42aSPavel Labath #include "llvm/Support/ScopedPrinter.h"
2236e82208SPavel Labath
23c34698a8SPavel Labath #include "lldb/Utility/LLDBLog.h"
246f9e6901SZachary Turner #include "lldb/Utility/Log.h"
257523f743SAlexander Kornienko #include "lldb/Utility/ProcessInfo.h"
2697206d57SZachary Turner #include "lldb/Utility/Status.h"
2730213ffcSJohnny Chen
28f8d480b1SJonas Devlieghere #include "lldb/Host/FileSystem.h"
29b9c1b51eSKate Stone #include "lldb/Host/Host.h"
30b9c1b51eSKate Stone #include "lldb/Host/HostInfo.h"
31c8d18cbaSMichał Górny #include "lldb/Host/linux/Host.h"
327e437f8fSPavel Labath #include "lldb/Host/linux/Support.h"
33666cc0b2SZachary Turner #include "lldb/Utility/DataExtractor.h"
343e2a18f6SStephen Wilson
353e2a18f6SStephen Wilson using namespace lldb;
363e2a18f6SStephen Wilson using namespace lldb_private;
373e2a18f6SStephen Wilson
387e437f8fSPavel Labath namespace {
397e437f8fSPavel Labath enum class ProcessState {
407e437f8fSPavel Labath Unknown,
41c5aa63ddSJordan Rupprecht Dead,
427e437f8fSPavel Labath DiskSleep,
43c5aa63ddSJordan Rupprecht Idle,
447e437f8fSPavel Labath Paging,
45c5aa63ddSJordan Rupprecht Parked,
467e437f8fSPavel Labath Running,
477e437f8fSPavel Labath Sleeping,
487e437f8fSPavel Labath TracedOrStopped,
497e437f8fSPavel Labath Zombie,
507e437f8fSPavel Labath };
517e437f8fSPavel Labath }
5225d7eb0dSDaniel Malea
53805e7106SZachary Turner namespace lldb_private {
54805e7106SZachary Turner class ProcessLaunchInfo;
55805e7106SZachary Turner }
56805e7106SZachary Turner
GetStatusInfo(::pid_t Pid,ProcessInstanceInfo & ProcessInfo,ProcessState & State,::pid_t & TracerPid,::pid_t & Tgid)577e437f8fSPavel Labath static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo,
58c8d18cbaSMichał Górny ProcessState &State, ::pid_t &TracerPid,
59c8d18cbaSMichał Górny ::pid_t &Tgid) {
60a007a6d8SPavel Labath Log *log = GetLog(LLDBLog::Host);
61c5aa63ddSJordan Rupprecht
627e437f8fSPavel Labath auto BufferOrError = getProcFile(Pid, "status");
637e437f8fSPavel Labath if (!BufferOrError)
647e437f8fSPavel Labath return false;
6525d7eb0dSDaniel Malea
667e437f8fSPavel Labath llvm::StringRef Rest = BufferOrError.get()->getBuffer();
677e437f8fSPavel Labath while (!Rest.empty()) {
687e437f8fSPavel Labath llvm::StringRef Line;
697e437f8fSPavel Labath std::tie(Line, Rest) = Rest.split('\n');
7025d7eb0dSDaniel Malea
717e437f8fSPavel Labath if (Line.consume_front("Gid:")) {
727e437f8fSPavel Labath // Real, effective, saved set, and file system GIDs. Read the first two.
737e437f8fSPavel Labath Line = Line.ltrim();
747e437f8fSPavel Labath uint32_t RGid, EGid;
757e437f8fSPavel Labath Line.consumeInteger(10, RGid);
767e437f8fSPavel Labath Line = Line.ltrim();
777e437f8fSPavel Labath Line.consumeInteger(10, EGid);
7825d7eb0dSDaniel Malea
797e437f8fSPavel Labath ProcessInfo.SetGroupID(RGid);
807e437f8fSPavel Labath ProcessInfo.SetEffectiveGroupID(EGid);
817e437f8fSPavel Labath } else if (Line.consume_front("Uid:")) {
827e437f8fSPavel Labath // Real, effective, saved set, and file system UIDs. Read the first two.
837e437f8fSPavel Labath Line = Line.ltrim();
847e437f8fSPavel Labath uint32_t RUid, EUid;
857e437f8fSPavel Labath Line.consumeInteger(10, RUid);
867e437f8fSPavel Labath Line = Line.ltrim();
877e437f8fSPavel Labath Line.consumeInteger(10, EUid);
8825d7eb0dSDaniel Malea
897e437f8fSPavel Labath ProcessInfo.SetUserID(RUid);
907e437f8fSPavel Labath ProcessInfo.SetEffectiveUserID(EUid);
917e437f8fSPavel Labath } else if (Line.consume_front("PPid:")) {
927e437f8fSPavel Labath ::pid_t PPid;
937e437f8fSPavel Labath Line.ltrim().consumeInteger(10, PPid);
947e437f8fSPavel Labath ProcessInfo.SetParentProcessID(PPid);
957e437f8fSPavel Labath } else if (Line.consume_front("State:")) {
96c5aa63ddSJordan Rupprecht State = llvm::StringSwitch<ProcessState>(Line.ltrim().take_front(1))
97c5aa63ddSJordan Rupprecht .Case("D", ProcessState::DiskSleep)
98c5aa63ddSJordan Rupprecht .Case("I", ProcessState::Idle)
99c5aa63ddSJordan Rupprecht .Case("R", ProcessState::Running)
100c5aa63ddSJordan Rupprecht .Case("S", ProcessState::Sleeping)
101c5aa63ddSJordan Rupprecht .CaseLower("T", ProcessState::TracedOrStopped)
102c5aa63ddSJordan Rupprecht .Case("W", ProcessState::Paging)
103c5aa63ddSJordan Rupprecht .Case("P", ProcessState::Parked)
104c5aa63ddSJordan Rupprecht .Case("X", ProcessState::Dead)
105c5aa63ddSJordan Rupprecht .Case("Z", ProcessState::Zombie)
106c5aa63ddSJordan Rupprecht .Default(ProcessState::Unknown);
107c5aa63ddSJordan Rupprecht if (State == ProcessState::Unknown) {
108c5aa63ddSJordan Rupprecht LLDB_LOG(log, "Unknown process state {0}", Line);
10925d7eb0dSDaniel Malea }
1107e437f8fSPavel Labath } else if (Line.consume_front("TracerPid:")) {
1117e437f8fSPavel Labath Line = Line.ltrim();
1127e437f8fSPavel Labath Line.consumeInteger(10, TracerPid);
113c8d18cbaSMichał Górny } else if (Line.consume_front("Tgid:")) {
114c8d18cbaSMichał Górny Line = Line.ltrim();
115c8d18cbaSMichał Górny Line.consumeInteger(10, Tgid);
1167e437f8fSPavel Labath }
1177e437f8fSPavel Labath }
11825d7eb0dSDaniel Malea return true;
11925d7eb0dSDaniel Malea }
12025d7eb0dSDaniel Malea
IsDirNumeric(const char * dname)121b9c1b51eSKate Stone static bool IsDirNumeric(const char *dname) {
122b9c1b51eSKate Stone for (; *dname; dname++) {
12325d7eb0dSDaniel Malea if (!isdigit(*dname))
12425d7eb0dSDaniel Malea return false;
12525d7eb0dSDaniel Malea }
12625d7eb0dSDaniel Malea return true;
12725d7eb0dSDaniel Malea }
12825d7eb0dSDaniel Malea
GetELFProcessCPUType(llvm::StringRef exe_path)12936e82208SPavel Labath static ArchSpec GetELFProcessCPUType(llvm::StringRef exe_path) {
130a007a6d8SPavel Labath Log *log = GetLog(LLDBLog::Host);
1317e437f8fSPavel Labath
13287e403aaSJonas Devlieghere auto buffer_sp = FileSystem::Instance().CreateDataBuffer(exe_path, 0x20, 0);
13336e82208SPavel Labath if (!buffer_sp)
13436e82208SPavel Labath return ArchSpec();
13536e82208SPavel Labath
136*a722dea4SJonas Devlieghere uint8_t exe_class =
137*a722dea4SJonas Devlieghere llvm::object::getElfArchType(
138*a722dea4SJonas Devlieghere {reinterpret_cast<const char *>(buffer_sp->GetBytes()),
139f9ac13a8SJonas Devlieghere size_t(buffer_sp->GetByteSize())})
14036e82208SPavel Labath .first;
14136e82208SPavel Labath
14236e82208SPavel Labath switch (exe_class) {
14336e82208SPavel Labath case llvm::ELF::ELFCLASS32:
14436e82208SPavel Labath return HostInfo::GetArchitecture(HostInfo::eArchKind32);
14536e82208SPavel Labath case llvm::ELF::ELFCLASS64:
14636e82208SPavel Labath return HostInfo::GetArchitecture(HostInfo::eArchKind64);
14736e82208SPavel Labath default:
14836e82208SPavel Labath LLDB_LOG(log, "Unknown elf class ({0}) in file {1}", exe_class, exe_path);
14936e82208SPavel Labath return ArchSpec();
1507e437f8fSPavel Labath }
1517e437f8fSPavel Labath }
1527e437f8fSPavel Labath
GetProcessArgs(::pid_t pid,ProcessInstanceInfo & process_info)153d04855f8SWalter Erquinigo static void GetProcessArgs(::pid_t pid, ProcessInstanceInfo &process_info) {
154d04855f8SWalter Erquinigo auto BufferOrError = getProcFile(pid, "cmdline");
1557e437f8fSPavel Labath if (!BufferOrError)
156d04855f8SWalter Erquinigo return;
1577e437f8fSPavel Labath std::unique_ptr<llvm::MemoryBuffer> Cmdline = std::move(*BufferOrError);
1587e437f8fSPavel Labath
159d04855f8SWalter Erquinigo llvm::StringRef Arg0, Rest;
1607e437f8fSPavel Labath std::tie(Arg0, Rest) = Cmdline->getBuffer().split('\0');
1617e437f8fSPavel Labath process_info.SetArg0(Arg0);
1627e437f8fSPavel Labath while (!Rest.empty()) {
1637e437f8fSPavel Labath llvm::StringRef Arg;
1647e437f8fSPavel Labath std::tie(Arg, Rest) = Rest.split('\0');
1657e437f8fSPavel Labath process_info.GetArguments().AppendArgument(Arg);
1667e437f8fSPavel Labath }
167d04855f8SWalter Erquinigo }
168d04855f8SWalter Erquinigo
GetExePathAndArch(::pid_t pid,ProcessInstanceInfo & process_info)169d04855f8SWalter Erquinigo static void GetExePathAndArch(::pid_t pid, ProcessInstanceInfo &process_info) {
170a007a6d8SPavel Labath Log *log = GetLog(LLDBLog::Process);
171d04855f8SWalter Erquinigo std::string ExePath(PATH_MAX, '\0');
172d04855f8SWalter Erquinigo
173d04855f8SWalter Erquinigo // We can't use getProcFile here because proc/[pid]/exe is a symbolic link.
174d04855f8SWalter Erquinigo llvm::SmallString<64> ProcExe;
175d04855f8SWalter Erquinigo (llvm::Twine("/proc/") + llvm::Twine(pid) + "/exe").toVector(ProcExe);
176d04855f8SWalter Erquinigo
177d04855f8SWalter Erquinigo ssize_t len = readlink(ProcExe.c_str(), &ExePath[0], PATH_MAX);
178d04855f8SWalter Erquinigo if (len > 0) {
179d04855f8SWalter Erquinigo ExePath.resize(len);
180d04855f8SWalter Erquinigo } else {
181d04855f8SWalter Erquinigo LLDB_LOG(log, "failed to read link exe link for {0}: {1}", pid,
182d04855f8SWalter Erquinigo Status(errno, eErrorTypePOSIX));
183d04855f8SWalter Erquinigo ExePath.resize(0);
184d04855f8SWalter Erquinigo }
185d04855f8SWalter Erquinigo // If the binary has been deleted, the link name has " (deleted)" appended.
186d04855f8SWalter Erquinigo // Remove if there.
187d04855f8SWalter Erquinigo llvm::StringRef PathRef = ExePath;
188d04855f8SWalter Erquinigo PathRef.consume_back(" (deleted)");
189d04855f8SWalter Erquinigo
190d04855f8SWalter Erquinigo if (!PathRef.empty()) {
191d04855f8SWalter Erquinigo process_info.GetExecutableFile().SetFile(PathRef, FileSpec::Style::native);
192d04855f8SWalter Erquinigo process_info.SetArchitecture(GetELFProcessCPUType(PathRef));
193d04855f8SWalter Erquinigo }
194d04855f8SWalter Erquinigo }
195d04855f8SWalter Erquinigo
GetProcessEnviron(::pid_t pid,ProcessInstanceInfo & process_info)196d04855f8SWalter Erquinigo static void GetProcessEnviron(::pid_t pid, ProcessInstanceInfo &process_info) {
197d04855f8SWalter Erquinigo // Get the process environment.
198d04855f8SWalter Erquinigo auto BufferOrError = getProcFile(pid, "environ");
199d04855f8SWalter Erquinigo if (!BufferOrError)
200d04855f8SWalter Erquinigo return;
201d04855f8SWalter Erquinigo
202d04855f8SWalter Erquinigo std::unique_ptr<llvm::MemoryBuffer> Environ = std::move(*BufferOrError);
203d04855f8SWalter Erquinigo llvm::StringRef Rest = Environ->getBuffer();
204d04855f8SWalter Erquinigo while (!Rest.empty()) {
205d04855f8SWalter Erquinigo llvm::StringRef Var;
206d04855f8SWalter Erquinigo std::tie(Var, Rest) = Rest.split('\0');
207d04855f8SWalter Erquinigo process_info.GetEnvironment().insert(Var);
208d04855f8SWalter Erquinigo }
209d04855f8SWalter Erquinigo }
210d04855f8SWalter Erquinigo
GetProcessAndStatInfo(::pid_t pid,ProcessInstanceInfo & process_info,ProcessState & State,::pid_t & tracerpid)211d04855f8SWalter Erquinigo static bool GetProcessAndStatInfo(::pid_t pid,
212d04855f8SWalter Erquinigo ProcessInstanceInfo &process_info,
213d04855f8SWalter Erquinigo ProcessState &State, ::pid_t &tracerpid) {
214c8d18cbaSMichał Górny ::pid_t tgid;
215d04855f8SWalter Erquinigo tracerpid = 0;
216d04855f8SWalter Erquinigo process_info.Clear();
217d04855f8SWalter Erquinigo
218d04855f8SWalter Erquinigo process_info.SetProcessID(pid);
219d04855f8SWalter Erquinigo
220d04855f8SWalter Erquinigo GetExePathAndArch(pid, process_info);
221d04855f8SWalter Erquinigo GetProcessArgs(pid, process_info);
222d04855f8SWalter Erquinigo GetProcessEnviron(pid, process_info);
223d04855f8SWalter Erquinigo
224d04855f8SWalter Erquinigo // Get User and Group IDs and get tracer pid.
225c8d18cbaSMichał Górny if (!GetStatusInfo(pid, process_info, State, tracerpid, tgid))
226d04855f8SWalter Erquinigo return false;
2277e437f8fSPavel Labath
2287e437f8fSPavel Labath return true;
2297e437f8fSPavel Labath }
2307e437f8fSPavel Labath
FindProcessesImpl(const ProcessInstanceInfoMatch & match_info,ProcessInstanceInfoList & process_infos)2312451cbf0SJonas Devlieghere uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info,
232b9c1b51eSKate Stone ProcessInstanceInfoList &process_infos) {
23325d7eb0dSDaniel Malea static const char procdir[] = "/proc/";
23425d7eb0dSDaniel Malea
23525d7eb0dSDaniel Malea DIR *dirproc = opendir(procdir);
236b9c1b51eSKate Stone if (dirproc) {
237248a1305SKonrad Kleine struct dirent *direntry = nullptr;
23825d7eb0dSDaniel Malea const uid_t our_uid = getuid();
23925d7eb0dSDaniel Malea const lldb::pid_t our_pid = getpid();
24025d7eb0dSDaniel Malea bool all_users = match_info.GetMatchAllUsers();
24125d7eb0dSDaniel Malea
242248a1305SKonrad Kleine while ((direntry = readdir(dirproc)) != nullptr) {
24325d7eb0dSDaniel Malea if (direntry->d_type != DT_DIR || !IsDirNumeric(direntry->d_name))
24425d7eb0dSDaniel Malea continue;
24525d7eb0dSDaniel Malea
24625d7eb0dSDaniel Malea lldb::pid_t pid = atoi(direntry->d_name);
24725d7eb0dSDaniel Malea
24825d7eb0dSDaniel Malea // Skip this process.
24925d7eb0dSDaniel Malea if (pid == our_pid)
25025d7eb0dSDaniel Malea continue;
25125d7eb0dSDaniel Malea
2527e437f8fSPavel Labath ::pid_t tracerpid;
2537e437f8fSPavel Labath ProcessState State;
25425d7eb0dSDaniel Malea ProcessInstanceInfo process_info;
25525d7eb0dSDaniel Malea
2567e437f8fSPavel Labath if (!GetProcessAndStatInfo(pid, process_info, State, tracerpid))
25725d7eb0dSDaniel Malea continue;
25825d7eb0dSDaniel Malea
25925d7eb0dSDaniel Malea // Skip if process is being debugged.
26025d7eb0dSDaniel Malea if (tracerpid != 0)
26125d7eb0dSDaniel Malea continue;
26225d7eb0dSDaniel Malea
2637e437f8fSPavel Labath if (State == ProcessState::Zombie)
26425d7eb0dSDaniel Malea continue;
26525d7eb0dSDaniel Malea
26605097246SAdrian Prantl // Check for user match if we're not matching all users and not running
26705097246SAdrian Prantl // as root.
26825d7eb0dSDaniel Malea if (!all_users && (our_uid != 0) && (process_info.GetUserID() != our_uid))
26925d7eb0dSDaniel Malea continue;
27025d7eb0dSDaniel Malea
271b9c1b51eSKate Stone if (match_info.Matches(process_info)) {
272638b06cfSJonas Devlieghere process_infos.push_back(process_info);
27325d7eb0dSDaniel Malea }
27425d7eb0dSDaniel Malea }
27525d7eb0dSDaniel Malea
27625d7eb0dSDaniel Malea closedir(dirproc);
27725d7eb0dSDaniel Malea }
27825d7eb0dSDaniel Malea
279638b06cfSJonas Devlieghere return process_infos.size();
28025d7eb0dSDaniel Malea }
28125d7eb0dSDaniel Malea
FindProcessThreads(const lldb::pid_t pid,TidMap & tids_to_attach)282b9c1b51eSKate Stone bool Host::FindProcessThreads(const lldb::pid_t pid, TidMap &tids_to_attach) {
283085d6cecSMatt Kopec bool tids_changed = false;
284085d6cecSMatt Kopec static const char procdir[] = "/proc/";
285085d6cecSMatt Kopec static const char taskdir[] = "/task/";
2861eb0d42aSPavel Labath std::string process_task_dir = procdir + llvm::to_string(pid) + taskdir;
287085d6cecSMatt Kopec DIR *dirproc = opendir(process_task_dir.c_str());
288085d6cecSMatt Kopec
289b9c1b51eSKate Stone if (dirproc) {
290248a1305SKonrad Kleine struct dirent *direntry = nullptr;
291248a1305SKonrad Kleine while ((direntry = readdir(dirproc)) != nullptr) {
292085d6cecSMatt Kopec if (direntry->d_type != DT_DIR || !IsDirNumeric(direntry->d_name))
293085d6cecSMatt Kopec continue;
294085d6cecSMatt Kopec
295085d6cecSMatt Kopec lldb::tid_t tid = atoi(direntry->d_name);
296085d6cecSMatt Kopec TidMap::iterator it = tids_to_attach.find(tid);
297b9c1b51eSKate Stone if (it == tids_to_attach.end()) {
298085d6cecSMatt Kopec tids_to_attach.insert(TidPair(tid, false));
299085d6cecSMatt Kopec tids_changed = true;
300085d6cecSMatt Kopec }
301085d6cecSMatt Kopec }
302085d6cecSMatt Kopec closedir(dirproc);
303085d6cecSMatt Kopec }
304085d6cecSMatt Kopec
305085d6cecSMatt Kopec return tids_changed;
306085d6cecSMatt Kopec }
307085d6cecSMatt Kopec
GetProcessInfo(lldb::pid_t pid,ProcessInstanceInfo & process_info)308b9c1b51eSKate Stone bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) {
3097e437f8fSPavel Labath ::pid_t tracerpid;
3107e437f8fSPavel Labath ProcessState State;
3117e437f8fSPavel Labath return GetProcessAndStatInfo(pid, process_info, State, tracerpid);
31225d7eb0dSDaniel Malea }
31325d7eb0dSDaniel Malea
GetEnvironment()31462930e57SPavel Labath Environment Host::GetEnvironment() { return Environment(environ); }
3154ceced3fSTodd Fiala
ShellExpandArguments(ProcessLaunchInfo & launch_info)31697206d57SZachary Turner Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
31797206d57SZachary Turner return Status("unimplemented");
31883a14376SEnrico Granata }
319c8d18cbaSMichał Górny
getPIDForTID(lldb::pid_t tid)320c8d18cbaSMichał Górny llvm::Optional<lldb::pid_t> lldb_private::getPIDForTID(lldb::pid_t tid) {
321c8d18cbaSMichał Górny ::pid_t tracerpid, tgid = LLDB_INVALID_PROCESS_ID;
322c8d18cbaSMichał Górny ProcessInstanceInfo process_info;
323c8d18cbaSMichał Górny ProcessState state;
324c8d18cbaSMichał Górny
325c8d18cbaSMichał Górny if (!GetStatusInfo(tid, process_info, state, tracerpid, tgid) ||
326c8d18cbaSMichał Górny tgid == LLDB_INVALID_PROCESS_ID)
327c8d18cbaSMichał Górny return llvm::None;
328c8d18cbaSMichał Górny return tgid;
329c8d18cbaSMichał Górny }
330