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