1 //===-- ProcessInfo.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 "lldb/Utility/ProcessInfo.h"
10 
11 #include "lldb/Utility/ArchSpec.h"
12 #include "lldb/Utility/ReproducerProvider.h"
13 #include "lldb/Utility/Stream.h"
14 #include "lldb/Utility/StreamString.h"
15 #include "lldb/Utility/UserIDResolver.h"
16 #include "llvm/ADT/SmallString.h"
17 
18 #include <climits>
19 
20 using namespace lldb;
21 using namespace lldb_private;
22 using namespace lldb_private::repro;
23 
24 ProcessInfo::ProcessInfo()
25     : m_executable(), m_arguments(), m_environment(), m_arch() {}
26 
27 ProcessInfo::ProcessInfo(const char *name, const ArchSpec &arch,
28                          lldb::pid_t pid)
29     : m_executable(name), m_arguments(), m_environment(), m_arch(arch),
30       m_pid(pid) {}
31 
32 void ProcessInfo::Clear() {
33   m_executable.Clear();
34   m_arguments.Clear();
35   m_environment.clear();
36   m_uid = UINT32_MAX;
37   m_gid = UINT32_MAX;
38   m_arch.Clear();
39   m_pid = LLDB_INVALID_PROCESS_ID;
40 }
41 
42 const char *ProcessInfo::GetName() const {
43   return m_executable.GetFilename().GetCString();
44 }
45 
46 llvm::StringRef ProcessInfo::GetNameAsStringRef() const {
47   return m_executable.GetFilename().GetStringRef();
48 }
49 
50 void ProcessInfo::Dump(Stream &s, Platform *platform) const {
51   s << "Executable: " << GetName() << "\n";
52   s << "Triple: ";
53   m_arch.DumpTriple(s.AsRawOstream());
54   s << "\n";
55 
56   s << "Arguments:\n";
57   m_arguments.Dump(s);
58 
59   s.Format("Environment:\n{0}", m_environment);
60 }
61 
62 void ProcessInfo::SetExecutableFile(const FileSpec &exe_file,
63                                     bool add_exe_file_as_first_arg) {
64   if (exe_file) {
65     m_executable = exe_file;
66     if (add_exe_file_as_first_arg) {
67       llvm::SmallString<128> filename;
68       exe_file.GetPath(filename);
69       if (!filename.empty())
70         m_arguments.InsertArgumentAtIndex(0, filename);
71     }
72   } else {
73     m_executable.Clear();
74   }
75 }
76 
77 llvm::StringRef ProcessInfo::GetArg0() const { return m_arg0; }
78 
79 void ProcessInfo::SetArg0(llvm::StringRef arg) { m_arg0 = std::string(arg); }
80 
81 void ProcessInfo::SetArguments(char const **argv,
82                                bool first_arg_is_executable) {
83   m_arguments.SetArguments(argv);
84 
85   // Is the first argument the executable?
86   if (first_arg_is_executable) {
87     const char *first_arg = m_arguments.GetArgumentAtIndex(0);
88     if (first_arg) {
89       // Yes the first argument is an executable, set it as the executable in
90       // the launch options. Don't resolve the file path as the path could be a
91       // remote platform path
92       m_executable.SetFile(first_arg, FileSpec::Style::native);
93     }
94   }
95 }
96 
97 void ProcessInfo::SetArguments(const Args &args, bool first_arg_is_executable) {
98   // Copy all arguments
99   m_arguments = args;
100 
101   // Is the first argument the executable?
102   if (first_arg_is_executable) {
103     const char *first_arg = m_arguments.GetArgumentAtIndex(0);
104     if (first_arg) {
105       // Yes the first argument is an executable, set it as the executable in
106       // the launch options. Don't resolve the file path as the path could be a
107       // remote platform path
108       m_executable.SetFile(first_arg, FileSpec::Style::native);
109     }
110   }
111 }
112 
113 void ProcessInstanceInfo::Dump(Stream &s, UserIDResolver &resolver) const {
114   if (m_pid != LLDB_INVALID_PROCESS_ID)
115     s.Printf("    pid = %" PRIu64 "\n", m_pid);
116 
117   if (m_parent_pid != LLDB_INVALID_PROCESS_ID)
118     s.Printf(" parent = %" PRIu64 "\n", m_parent_pid);
119 
120   if (m_executable) {
121     s.Printf("   name = %s\n", m_executable.GetFilename().GetCString());
122     s.PutCString("   file = ");
123     m_executable.Dump(s.AsRawOstream());
124     s.EOL();
125   }
126   const uint32_t argc = m_arguments.GetArgumentCount();
127   if (argc > 0) {
128     for (uint32_t i = 0; i < argc; i++) {
129       const char *arg = m_arguments.GetArgumentAtIndex(i);
130       if (i < 10)
131         s.Printf(" arg[%u] = %s\n", i, arg);
132       else
133         s.Printf("arg[%u] = %s\n", i, arg);
134     }
135   }
136 
137   s.Format("{0}", m_environment);
138 
139   if (m_arch.IsValid()) {
140     s.Printf("   arch = ");
141     m_arch.DumpTriple(s.AsRawOstream());
142     s.EOL();
143   }
144 
145   if (UserIDIsValid()) {
146     s.Format("    uid = {0,-5} ({1})\n", GetUserID(),
147              resolver.GetUserName(GetUserID()).value_or(""));
148   }
149   if (GroupIDIsValid()) {
150     s.Format("    gid = {0,-5} ({1})\n", GetGroupID(),
151              resolver.GetGroupName(GetGroupID()).value_or(""));
152   }
153   if (EffectiveUserIDIsValid()) {
154     s.Format("   euid = {0,-5} ({1})\n", GetEffectiveUserID(),
155              resolver.GetUserName(GetEffectiveUserID()).value_or(""));
156   }
157   if (EffectiveGroupIDIsValid()) {
158     s.Format("   egid = {0,-5} ({1})\n", GetEffectiveGroupID(),
159              resolver.GetGroupName(GetEffectiveGroupID()).value_or(""));
160   }
161 }
162 
163 void ProcessInstanceInfo::DumpTableHeader(Stream &s, bool show_args,
164                                           bool verbose) {
165   const char *label;
166   if (show_args || verbose)
167     label = "ARGUMENTS";
168   else
169     label = "NAME";
170 
171   if (verbose) {
172     s.Printf("PID    PARENT USER       GROUP      EFF USER   EFF GROUP  TRIPLE "
173              "                        %s\n",
174              label);
175     s.PutCString(
176         "====== ====== ========== ========== ========== ========== "
177         "============================== ============================\n");
178   } else {
179     s.Printf("PID    PARENT USER       TRIPLE                         %s\n",
180              label);
181     s.PutCString("====== ====== ========== ============================== "
182                  "============================\n");
183   }
184 }
185 
186 void ProcessInstanceInfo::DumpAsTableRow(Stream &s, UserIDResolver &resolver,
187                                          bool show_args, bool verbose) const {
188   if (m_pid != LLDB_INVALID_PROCESS_ID) {
189     s.Printf("%-6" PRIu64 " %-6" PRIu64 " ", m_pid, m_parent_pid);
190 
191     StreamString arch_strm;
192     if (m_arch.IsValid())
193       m_arch.DumpTriple(arch_strm.AsRawOstream());
194 
195     auto print = [&](bool (ProcessInstanceInfo::*isValid)() const,
196                      uint32_t (ProcessInstanceInfo::*getID)() const,
197                      llvm::Optional<llvm::StringRef> (UserIDResolver::*getName)(
198                          UserIDResolver::id_t id)) {
199       const char *format = "{0,-10} ";
200       if (!(this->*isValid)()) {
201         s.Format(format, "");
202         return;
203       }
204       uint32_t id = (this->*getID)();
205       if (auto name = (resolver.*getName)(id))
206         s.Format(format, *name);
207       else
208         s.Format(format, id);
209     };
210     if (verbose) {
211       print(&ProcessInstanceInfo::UserIDIsValid,
212             &ProcessInstanceInfo::GetUserID, &UserIDResolver::GetUserName);
213       print(&ProcessInstanceInfo::GroupIDIsValid,
214             &ProcessInstanceInfo::GetGroupID, &UserIDResolver::GetGroupName);
215       print(&ProcessInstanceInfo::EffectiveUserIDIsValid,
216             &ProcessInstanceInfo::GetEffectiveUserID,
217             &UserIDResolver::GetUserName);
218       print(&ProcessInstanceInfo::EffectiveGroupIDIsValid,
219             &ProcessInstanceInfo::GetEffectiveGroupID,
220             &UserIDResolver::GetGroupName);
221 
222       s.Printf("%-30s ", arch_strm.GetData());
223     } else {
224       print(&ProcessInstanceInfo::EffectiveUserIDIsValid,
225             &ProcessInstanceInfo::GetEffectiveUserID,
226             &UserIDResolver::GetUserName);
227       s.Printf("%-30s ", arch_strm.GetData());
228     }
229 
230     if (verbose || show_args) {
231       s.PutCString(m_arg0);
232       const uint32_t argc = m_arguments.GetArgumentCount();
233       for (uint32_t i = 0; i < argc; i++) {
234         s.PutChar(' ');
235         s.PutCString(m_arguments.GetArgumentAtIndex(i));
236       }
237     } else {
238       s.PutCString(GetName());
239     }
240 
241     s.EOL();
242   }
243 }
244 
245 bool ProcessInstanceInfoMatch::ArchitectureMatches(
246     const ArchSpec &arch_spec) const {
247   return !m_match_info.GetArchitecture().IsValid() ||
248          m_match_info.GetArchitecture().IsCompatibleMatch(arch_spec);
249 }
250 
251 bool ProcessInstanceInfoMatch::NameMatches(const char *process_name) const {
252   if (m_name_match_type == NameMatch::Ignore)
253     return true;
254   const char *match_name = m_match_info.GetName();
255   if (!match_name)
256     return true;
257 
258   return lldb_private::NameMatches(process_name, m_name_match_type, match_name);
259 }
260 
261 bool ProcessInstanceInfoMatch::ProcessIDsMatch(
262     const ProcessInstanceInfo &proc_info) const {
263   if (m_match_info.ProcessIDIsValid() &&
264       m_match_info.GetProcessID() != proc_info.GetProcessID())
265     return false;
266 
267   if (m_match_info.ParentProcessIDIsValid() &&
268       m_match_info.GetParentProcessID() != proc_info.GetParentProcessID())
269     return false;
270   return true;
271 }
272 
273 bool ProcessInstanceInfoMatch::UserIDsMatch(
274     const ProcessInstanceInfo &proc_info) const {
275   if (m_match_info.UserIDIsValid() &&
276       m_match_info.GetUserID() != proc_info.GetUserID())
277     return false;
278 
279   if (m_match_info.GroupIDIsValid() &&
280       m_match_info.GetGroupID() != proc_info.GetGroupID())
281     return false;
282 
283   if (m_match_info.EffectiveUserIDIsValid() &&
284       m_match_info.GetEffectiveUserID() != proc_info.GetEffectiveUserID())
285     return false;
286 
287   if (m_match_info.EffectiveGroupIDIsValid() &&
288       m_match_info.GetEffectiveGroupID() != proc_info.GetEffectiveGroupID())
289     return false;
290   return true;
291 }
292 bool ProcessInstanceInfoMatch::Matches(
293     const ProcessInstanceInfo &proc_info) const {
294   return ArchitectureMatches(proc_info.GetArchitecture()) &&
295          ProcessIDsMatch(proc_info) && UserIDsMatch(proc_info) &&
296          NameMatches(proc_info.GetName());
297 }
298 
299 bool ProcessInstanceInfoMatch::MatchAllProcesses() const {
300   if (m_name_match_type != NameMatch::Ignore)
301     return false;
302 
303   if (m_match_info.ProcessIDIsValid())
304     return false;
305 
306   if (m_match_info.ParentProcessIDIsValid())
307     return false;
308 
309   if (m_match_info.UserIDIsValid())
310     return false;
311 
312   if (m_match_info.GroupIDIsValid())
313     return false;
314 
315   if (m_match_info.EffectiveUserIDIsValid())
316     return false;
317 
318   if (m_match_info.EffectiveGroupIDIsValid())
319     return false;
320 
321   if (m_match_info.GetArchitecture().IsValid())
322     return false;
323 
324   if (m_match_all_users)
325     return false;
326 
327   return true;
328 }
329 
330 void ProcessInstanceInfoMatch::Clear() {
331   m_match_info.Clear();
332   m_name_match_type = NameMatch::Ignore;
333   m_match_all_users = false;
334 }
335 
336 void llvm::yaml::MappingTraits<ProcessInstanceInfo>::mapping(
337     IO &io, ProcessInstanceInfo &Info) {
338   io.mapRequired("executable", Info.m_executable);
339   io.mapRequired("arg0", Info.m_arg0);
340   io.mapRequired("args", Info.m_arguments);
341   io.mapRequired("arch", Info.m_arch);
342   io.mapRequired("uid", Info.m_uid);
343   io.mapRequired("gid", Info.m_gid);
344   io.mapRequired("pid", Info.m_pid);
345   io.mapRequired("effective-uid", Info.m_euid);
346   io.mapRequired("effective-gid", Info.m_egid);
347   io.mapRequired("parent-pid", Info.m_parent_pid);
348 }
349 
350 
351 llvm::Optional<ProcessInstanceInfoList>
352 repro::GetReplayProcessInstanceInfoList() {
353   static std::unique_ptr<repro::MultiLoader<repro::ProcessInfoProvider>>
354       loader = repro::MultiLoader<repro::ProcessInfoProvider>::Create(
355           repro::Reproducer::Instance().GetLoader());
356 
357   if (!loader)
358     return {};
359 
360   llvm::Optional<std::string> nextfile = loader->GetNextFile();
361   if (!nextfile)
362     return {};
363 
364   auto error_or_file = llvm::MemoryBuffer::getFile(*nextfile);
365   if (std::error_code err = error_or_file.getError())
366     return {};
367 
368   ProcessInstanceInfoList infos;
369   llvm::yaml::Input yin((*error_or_file)->getBuffer());
370   yin >> infos;
371 
372   if (auto err = yin.error())
373     return {};
374 
375   return infos;
376 }
377