| //===-- 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/Stream.h" |
| #include "lldb/Utility/StreamString.h" |
| #include "lldb/Utility/UserIDResolver.h" |
| #include "llvm/ADT/SmallString.h" |
| |
| #include <climits> |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| using namespace lldb_private::repro; |
| |
| ProcessInfo::ProcessInfo() |
| : m_executable(), m_arguments(), m_environment(), m_uid(UINT32_MAX), |
| m_gid(UINT32_MAX), m_arch(), m_pid(LLDB_INVALID_PROCESS_ID) {} |
| |
| ProcessInfo::ProcessInfo(const char *name, const ArchSpec &arch, |
| lldb::pid_t pid) |
| : m_executable(name), m_arguments(), m_environment(), m_uid(UINT32_MAX), |
| m_gid(UINT32_MAX), m_arch(arch), m_pid(pid) {} |
| |
| 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; |
| } |
| |
| const char *ProcessInfo::GetName() const { |
| return m_executable.GetFilename().GetCString(); |
| } |
| |
| llvm::StringRef ProcessInfo::GetNameAsStringRef() const { |
| return m_executable.GetFilename().GetStringRef(); |
| } |
| |
| 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); |
| } |
| } |
| } |
| |
| void ProcessInstanceInfo::Dump(Stream &s, UserIDResolver &resolver) const { |
| if (m_pid != LLDB_INVALID_PROCESS_ID) |
| s.Printf(" pid = %" PRIu64 "\n", m_pid); |
| |
| if (m_parent_pid != LLDB_INVALID_PROCESS_ID) |
| s.Printf(" parent = %" PRIu64 "\n", m_parent_pid); |
| |
| if (m_executable) { |
| s.Printf(" name = %s\n", m_executable.GetFilename().GetCString()); |
| 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.Printf(" arch = "); |
| m_arch.DumpTriple(s.AsRawOstream()); |
| s.EOL(); |
| } |
| |
| if (UserIDIsValid()) { |
| s.Format(" uid = {0,-5} ({1})\n", GetUserID(), |
| resolver.GetUserName(GetUserID()).getValueOr("")); |
| } |
| if (GroupIDIsValid()) { |
| s.Format(" gid = {0,-5} ({1})\n", GetGroupID(), |
| resolver.GetGroupName(GetGroupID()).getValueOr("")); |
| } |
| if (EffectiveUserIDIsValid()) { |
| s.Format(" euid = {0,-5} ({1})\n", GetEffectiveUserID(), |
| resolver.GetUserName(GetEffectiveUserID()).getValueOr("")); |
| } |
| if (EffectiveGroupIDIsValid()) { |
| s.Format(" egid = {0,-5} ({1})\n", GetEffectiveGroupID(), |
| resolver.GetGroupName(GetEffectiveGroupID()).getValueOr("")); |
| } |
| } |
| |
| 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, m_parent_pid); |
| |
| StreamString arch_strm; |
| if (m_arch.IsValid()) |
| m_arch.DumpTriple(arch_strm.AsRawOstream()); |
| |
| auto print = [&](bool (ProcessInstanceInfo::*isValid)() const, |
| uint32_t (ProcessInstanceInfo::*getID)() const, |
| llvm::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(const char *process_name) const { |
| if (m_name_match_type == NameMatch::Ignore) |
| return true; |
| const char *match_name = m_match_info.GetName(); |
| if (!match_name) |
| 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; |
| } |
| |
| void llvm::yaml::MappingTraits<ProcessInstanceInfo>::mapping( |
| IO &io, ProcessInstanceInfo &Info) { |
| io.mapRequired("executable", Info.m_executable); |
| io.mapRequired("arg0", Info.m_arg0); |
| io.mapRequired("args", Info.m_arguments); |
| io.mapRequired("arch", Info.m_arch); |
| io.mapRequired("uid", Info.m_uid); |
| io.mapRequired("gid", Info.m_gid); |
| io.mapRequired("pid", Info.m_pid); |
| io.mapRequired("effective-uid", Info.m_euid); |
| io.mapRequired("effective-gid", Info.m_egid); |
| io.mapRequired("parent-pid", Info.m_parent_pid); |
| } |
| |
| llvm::Expected<std::unique_ptr<ProcessInfoRecorder>> |
| ProcessInfoRecorder::Create(const FileSpec &filename) { |
| std::error_code ec; |
| auto recorder = |
| std::make_unique<ProcessInfoRecorder>(std::move(filename), ec); |
| if (ec) |
| return llvm::errorCodeToError(ec); |
| return std::move(recorder); |
| } |
| |
| void ProcessInfoProvider::Keep() { |
| std::vector<std::string> files; |
| for (auto &recorder : m_process_info_recorders) { |
| recorder->Stop(); |
| files.push_back(recorder->GetFilename().GetPath()); |
| } |
| |
| FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file); |
| std::error_code ec; |
| llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text); |
| if (ec) |
| return; |
| llvm::yaml::Output yout(os); |
| yout << files; |
| } |
| |
| void ProcessInfoProvider::Discard() { m_process_info_recorders.clear(); } |
| |
| ProcessInfoRecorder *ProcessInfoProvider::GetNewProcessInfoRecorder() { |
| std::size_t i = m_process_info_recorders.size() + 1; |
| std::string filename = (llvm::Twine(Info::name) + llvm::Twine("-") + |
| llvm::Twine(i) + llvm::Twine(".yaml")) |
| .str(); |
| auto recorder_or_error = ProcessInfoRecorder::Create( |
| GetRoot().CopyByAppendingPathComponent(filename)); |
| if (!recorder_or_error) { |
| llvm::consumeError(recorder_or_error.takeError()); |
| return nullptr; |
| } |
| |
| m_process_info_recorders.push_back(std::move(*recorder_or_error)); |
| return m_process_info_recorders.back().get(); |
| } |
| |
| void ProcessInfoRecorder::Record(const ProcessInstanceInfoList &process_infos) { |
| if (!m_record) |
| return; |
| llvm::yaml::Output yout(m_os); |
| yout << const_cast<ProcessInstanceInfoList &>(process_infos); |
| m_os.flush(); |
| } |
| |
| llvm::Optional<ProcessInstanceInfoList> |
| repro::GetReplayProcessInstanceInfoList() { |
| static std::unique_ptr<repro::MultiLoader<repro::ProcessInfoProvider>> |
| loader = repro::MultiLoader<repro::ProcessInfoProvider>::Create( |
| repro::Reproducer::Instance().GetLoader()); |
| |
| if (!loader) |
| return {}; |
| |
| llvm::Optional<std::string> nextfile = loader->GetNextFile(); |
| if (!nextfile) |
| return {}; |
| |
| auto error_or_file = llvm::MemoryBuffer::getFile(*nextfile); |
| if (std::error_code err = error_or_file.getError()) |
| return {}; |
| |
| ProcessInstanceInfoList infos; |
| llvm::yaml::Input yin((*error_or_file)->getBuffer()); |
| yin >> infos; |
| |
| if (auto err = yin.error()) |
| return {}; |
| |
| return infos; |
| } |
| |
| char ProcessInfoProvider::ID = 0; |
| const char *ProcessInfoProvider::Info::file = "process-info.yaml"; |
| const char *ProcessInfoProvider::Info::name = "process-info"; |