| //===-- ProcessInfo.cpp ---------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "lldb/Utility/ProcessInfo.h" | |
| #include "lldb/Utility/ArchSpec.h" | |
| #include "lldb/Utility/ScriptedMetadata.h" | |
| #include "lldb/Utility/Stream.h" | |
| #include "lldb/Utility/StreamString.h" | |
| #include "lldb/Utility/UserIDResolver.h" | |
| #include "llvm/ADT/SmallString.h" | |
| #include <climits> | |
| #include <optional> | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| ProcessInfo::ProcessInfo() | |
| : m_executable(), m_arguments(), m_environment(), m_arch(), m_listener_sp(), | |
| m_hijack_listener_sp(), m_shadow_listener_sp() {} | |
| ProcessInfo::ProcessInfo(const char *name, const ArchSpec &arch, | |
| lldb::pid_t pid) | |
| : m_executable(name), m_arguments(), m_environment(), m_arch(arch), | |
| m_pid(pid), m_listener_sp(), m_hijack_listener_sp(), | |
| m_shadow_listener_sp() {} | |
| void ProcessInfo::Clear() { | |
| m_executable.Clear(); | |
| m_arguments.Clear(); | |
| m_environment.clear(); | |
| m_uid = UINT32_MAX; | |
| m_gid = UINT32_MAX; | |
| m_arch.Clear(); | |
| m_pid = LLDB_INVALID_PROCESS_ID; | |
| m_scripted_metadata_sp.reset(); | |
| } | |
| llvm::StringRef ProcessInfo::GetName() const { | |
| return m_executable.GetFilename(); | |
| } | |
| void ProcessInfo::Dump(Stream &s, Platform *platform) const { | |
| s << "Executable: " << GetName() << "\n"; | |
| s << "Triple: "; | |
| m_arch.DumpTriple(s.AsRawOstream()); | |
| s << "\n"; | |
| s << "Arguments:\n"; | |
| m_arguments.Dump(s); | |
| s.Format("Environment:\n{0}", m_environment); | |
| } | |
| void ProcessInfo::SetExecutableFile(const FileSpec &exe_file, | |
| bool add_exe_file_as_first_arg) { | |
| if (exe_file) { | |
| m_executable = exe_file; | |
| if (add_exe_file_as_first_arg) { | |
| llvm::SmallString<128> filename; | |
| exe_file.GetPath(filename); | |
| if (!filename.empty()) | |
| m_arguments.InsertArgumentAtIndex(0, filename); | |
| } | |
| } else { | |
| m_executable.Clear(); | |
| } | |
| } | |
| llvm::StringRef ProcessInfo::GetArg0() const { return m_arg0; } | |
| void ProcessInfo::SetArg0(llvm::StringRef arg) { m_arg0 = std::string(arg); } | |
| void ProcessInfo::SetArguments(char const **argv, | |
| bool first_arg_is_executable) { | |
| m_arguments.SetArguments(argv); | |
| // Is the first argument the executable? | |
| if (first_arg_is_executable) { | |
| const char *first_arg = m_arguments.GetArgumentAtIndex(0); | |
| if (first_arg) { | |
| // Yes the first argument is an executable, set it as the executable in | |
| // the launch options. Don't resolve the file path as the path could be a | |
| // remote platform path | |
| m_executable.SetFile(first_arg, FileSpec::Style::native); | |
| } | |
| } | |
| } | |
| void ProcessInfo::SetArguments(const Args &args, bool first_arg_is_executable) { | |
| // Copy all arguments | |
| m_arguments = args; | |
| // Is the first argument the executable? | |
| if (first_arg_is_executable) { | |
| const char *first_arg = m_arguments.GetArgumentAtIndex(0); | |
| if (first_arg) { | |
| // Yes the first argument is an executable, set it as the executable in | |
| // the launch options. Don't resolve the file path as the path could be a | |
| // remote platform path | |
| m_executable.SetFile(first_arg, FileSpec::Style::native); | |
| } | |
| } | |
| } | |
| bool ProcessInfo::IsScriptedProcess() const { | |
| return m_scripted_metadata_sp && *m_scripted_metadata_sp; | |
| } | |
| void ProcessInstanceInfo::Dump(Stream &s, UserIDResolver &resolver) const { | |
| if (m_pid != LLDB_INVALID_PROCESS_ID) | |
| s.Printf(" pid = %" PRIu64 "\n", m_pid); | |
| if (ParentProcessIDIsValid()) | |
| s.Printf(" parent = %" PRIu64 "\n", GetParentProcessID()); | |
| if (m_executable) { | |
| s.Format(" name = {0}\n", m_executable.GetFilename()); | |
| s.PutCString(" file = "); | |
| m_executable.Dump(s.AsRawOstream()); | |
| s.EOL(); | |
| } | |
| const uint32_t argc = m_arguments.GetArgumentCount(); | |
| if (argc > 0) { | |
| for (uint32_t i = 0; i < argc; i++) { | |
| const char *arg = m_arguments.GetArgumentAtIndex(i); | |
| if (i < 10) | |
| s.Printf(" arg[%u] = %s\n", i, arg); | |
| else | |
| s.Printf("arg[%u] = %s\n", i, arg); | |
| } | |
| } | |
| s.Format("{0}", m_environment); | |
| if (m_arch.IsValid()) { | |
| s.PutCString(" arch = "); | |
| m_arch.DumpTriple(s.AsRawOstream()); | |
| s.EOL(); | |
| } | |
| if (UserIDIsValid()) { | |
| s.Format(" uid = {0,-5} ({1})\n", GetUserID(), | |
| resolver.GetUserName(GetUserID()).value_or("")); | |
| } | |
| if (GroupIDIsValid()) { | |
| s.Format(" gid = {0,-5} ({1})\n", GetGroupID(), | |
| resolver.GetGroupName(GetGroupID()).value_or("")); | |
| } | |
| if (EffectiveUserIDIsValid()) { | |
| s.Format(" euid = {0,-5} ({1})\n", GetEffectiveUserID(), | |
| resolver.GetUserName(GetEffectiveUserID()).value_or("")); | |
| } | |
| if (EffectiveGroupIDIsValid()) { | |
| s.Format(" egid = {0,-5} ({1})\n", GetEffectiveGroupID(), | |
| resolver.GetGroupName(GetEffectiveGroupID()).value_or("")); | |
| } | |
| } | |
| void ProcessInstanceInfo::DumpTableHeader(Stream &s, bool show_args, | |
| bool verbose) { | |
| const char *label; | |
| if (show_args || verbose) | |
| label = "ARGUMENTS"; | |
| else | |
| label = "NAME"; | |
| if (verbose) { | |
| s.Printf("PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE " | |
| " %s\n", | |
| label); | |
| s.PutCString( | |
| "====== ====== ========== ========== ========== ========== " | |
| "============================== ============================\n"); | |
| } else { | |
| s.Printf("PID PARENT USER TRIPLE %s\n", | |
| label); | |
| s.PutCString("====== ====== ========== ============================== " | |
| "============================\n"); | |
| } | |
| } | |
| void ProcessInstanceInfo::DumpAsTableRow(Stream &s, UserIDResolver &resolver, | |
| bool show_args, bool verbose) const { | |
| if (m_pid != LLDB_INVALID_PROCESS_ID) { | |
| s.Printf("%-6" PRIu64 " %-6" PRIu64 " ", m_pid, | |
| (ParentProcessIDIsValid()) ? GetParentProcessID() : 0); | |
| StreamString arch_strm; | |
| if (m_arch.IsValid()) | |
| m_arch.DumpTriple(arch_strm.AsRawOstream()); | |
| auto print = [&](bool (ProcessInstanceInfo::*isValid)() const, | |
| uint32_t (ProcessInstanceInfo::*getID)() const, | |
| std::optional<llvm::StringRef> (UserIDResolver::*getName)( | |
| UserIDResolver::id_t id)) { | |
| const char *format = "{0,-10} "; | |
| if (!(this->*isValid)()) { | |
| s.Format(format, ""); | |
| return; | |
| } | |
| uint32_t id = (this->*getID)(); | |
| if (auto name = (resolver.*getName)(id)) | |
| s.Format(format, *name); | |
| else | |
| s.Format(format, id); | |
| }; | |
| if (verbose) { | |
| print(&ProcessInstanceInfo::UserIDIsValid, | |
| &ProcessInstanceInfo::GetUserID, &UserIDResolver::GetUserName); | |
| print(&ProcessInstanceInfo::GroupIDIsValid, | |
| &ProcessInstanceInfo::GetGroupID, &UserIDResolver::GetGroupName); | |
| print(&ProcessInstanceInfo::EffectiveUserIDIsValid, | |
| &ProcessInstanceInfo::GetEffectiveUserID, | |
| &UserIDResolver::GetUserName); | |
| print(&ProcessInstanceInfo::EffectiveGroupIDIsValid, | |
| &ProcessInstanceInfo::GetEffectiveGroupID, | |
| &UserIDResolver::GetGroupName); | |
| s.Printf("%-30s ", arch_strm.GetData()); | |
| } else { | |
| print(&ProcessInstanceInfo::EffectiveUserIDIsValid, | |
| &ProcessInstanceInfo::GetEffectiveUserID, | |
| &UserIDResolver::GetUserName); | |
| s.Printf("%-30s ", arch_strm.GetData()); | |
| } | |
| if (verbose || show_args) { | |
| s.PutCString(m_arg0); | |
| const uint32_t argc = m_arguments.GetArgumentCount(); | |
| for (uint32_t i = 0; i < argc; i++) { | |
| s.PutChar(' '); | |
| s.PutCString(m_arguments.GetArgumentAtIndex(i)); | |
| } | |
| } else { | |
| s.PutCString(GetName()); | |
| } | |
| s.EOL(); | |
| } | |
| } | |
| bool ProcessInstanceInfoMatch::ArchitectureMatches( | |
| const ArchSpec &arch_spec) const { | |
| return !m_match_info.GetArchitecture().IsValid() || | |
| m_match_info.GetArchitecture().IsCompatibleMatch(arch_spec); | |
| } | |
| bool ProcessInstanceInfoMatch::NameMatches(llvm::StringRef process_name) const { | |
| if (m_name_match_type == NameMatch::Ignore) | |
| return true; | |
| llvm::StringRef match_name = m_match_info.GetName(); | |
| if (match_name.empty()) | |
| return true; | |
| return lldb_private::NameMatches(process_name, m_name_match_type, match_name); | |
| } | |
| bool ProcessInstanceInfoMatch::ProcessIDsMatch( | |
| const ProcessInstanceInfo &proc_info) const { | |
| if (m_match_info.ProcessIDIsValid() && | |
| m_match_info.GetProcessID() != proc_info.GetProcessID()) | |
| return false; | |
| if (m_match_info.ParentProcessIDIsValid() && | |
| m_match_info.GetParentProcessID() != proc_info.GetParentProcessID()) | |
| return false; | |
| return true; | |
| } | |
| bool ProcessInstanceInfoMatch::UserIDsMatch( | |
| const ProcessInstanceInfo &proc_info) const { | |
| if (m_match_info.UserIDIsValid() && | |
| m_match_info.GetUserID() != proc_info.GetUserID()) | |
| return false; | |
| if (m_match_info.GroupIDIsValid() && | |
| m_match_info.GetGroupID() != proc_info.GetGroupID()) | |
| return false; | |
| if (m_match_info.EffectiveUserIDIsValid() && | |
| m_match_info.GetEffectiveUserID() != proc_info.GetEffectiveUserID()) | |
| return false; | |
| if (m_match_info.EffectiveGroupIDIsValid() && | |
| m_match_info.GetEffectiveGroupID() != proc_info.GetEffectiveGroupID()) | |
| return false; | |
| return true; | |
| } | |
| bool ProcessInstanceInfoMatch::Matches( | |
| const ProcessInstanceInfo &proc_info) const { | |
| return ArchitectureMatches(proc_info.GetArchitecture()) && | |
| ProcessIDsMatch(proc_info) && UserIDsMatch(proc_info) && | |
| NameMatches(proc_info.GetName()); | |
| } | |
| bool ProcessInstanceInfoMatch::MatchAllProcesses() const { | |
| if (m_name_match_type != NameMatch::Ignore) | |
| return false; | |
| if (m_match_info.ProcessIDIsValid()) | |
| return false; | |
| if (m_match_info.ParentProcessIDIsValid()) | |
| return false; | |
| if (m_match_info.UserIDIsValid()) | |
| return false; | |
| if (m_match_info.GroupIDIsValid()) | |
| return false; | |
| if (m_match_info.EffectiveUserIDIsValid()) | |
| return false; | |
| if (m_match_info.EffectiveGroupIDIsValid()) | |
| return false; | |
| if (m_match_info.GetArchitecture().IsValid()) | |
| return false; | |
| if (m_match_all_users) | |
| return false; | |
| return true; | |
| } | |
| void ProcessInstanceInfoMatch::Clear() { | |
| m_match_info.Clear(); | |
| m_name_match_type = NameMatch::Ignore; | |
| m_match_all_users = false; | |
| } |