llvm-project/lldb/source/Utility/ProcessInfo.cpp

432 lines
13 KiB
C++

//===-- 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";