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