Skip to content

adds additional information to the ProcessInfo object for elf processes #88995

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions lldb/include/lldb/Utility/ProcessInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ class ProcessInfo {
// to that process.
class ProcessInstanceInfo : public ProcessInfo {
public:
struct timespec {
time_t tv_sec = 0;
long int tv_usec = 0;
};

ProcessInstanceInfo() = default;

ProcessInstanceInfo(const char *name, const ArchSpec &arch, lldb::pid_t pid)
Expand Down Expand Up @@ -172,6 +177,66 @@ class ProcessInstanceInfo : public ProcessInfo {
return m_parent_pid != LLDB_INVALID_PROCESS_ID;
}

lldb::pid_t GetProcessGroupID() const { return m_process_group_id; }

void SetProcessGroupID(lldb::pid_t pgrp) { m_process_group_id = pgrp; }

bool ProcessGroupIDIsValid() const {
return m_process_group_id != LLDB_INVALID_PROCESS_ID;
}

lldb::pid_t GetProcessSessionID() const { return m_process_session_id; }

void SetProcessSessionID(lldb::pid_t session) {
m_process_session_id = session;
}

bool ProcessSessionIDIsValid() const {
return m_process_session_id != LLDB_INVALID_PROCESS_ID;
}

struct timespec GetUserTime() const { return m_user_time; }

void SetUserTime(struct timespec utime) { m_user_time = utime; }

bool UserTimeIsValid() const {
return m_user_time.tv_sec > 0 || m_user_time.tv_usec > 0;
}

struct timespec GetSystemTime() const { return m_system_time; }

void SetSystemTime(struct timespec stime) { m_system_time = stime; }

bool SystemTimeIsValid() const {
return m_system_time.tv_sec > 0 || m_system_time.tv_usec > 0;
}

struct timespec GetCumulativeUserTime() const {
return m_cumulative_user_time;
}

void SetCumulativeUserTime(struct timespec cutime) {
m_cumulative_user_time = cutime;
}

bool CumulativeUserTimeIsValid() const {
return m_cumulative_user_time.tv_sec > 0 ||
m_cumulative_user_time.tv_usec > 0;
}

struct timespec GetCumulativeSystemTime() const {
return m_cumulative_system_time;
}

void SetCumulativeSystemTime(struct timespec cstime) {
m_cumulative_system_time = cstime;
}

bool CumulativeSystemTimeIsValid() const {
return m_cumulative_system_time.tv_sec > 0 ||
m_cumulative_system_time.tv_sec > 0;
}

void Dump(Stream &s, UserIDResolver &resolver) const;

static void DumpTableHeader(Stream &s, bool show_args, bool verbose);
Expand All @@ -183,6 +248,12 @@ class ProcessInstanceInfo : public ProcessInfo {
uint32_t m_euid = UINT32_MAX;
uint32_t m_egid = UINT32_MAX;
lldb::pid_t m_parent_pid = LLDB_INVALID_PROCESS_ID;
lldb::pid_t m_process_group_id = LLDB_INVALID_PROCESS_ID;
lldb::pid_t m_process_session_id = LLDB_INVALID_PROCESS_ID;
struct timespec m_user_time {};
struct timespec m_system_time {};
struct timespec m_cumulative_user_time {};
struct timespec m_cumulative_system_time {};
};

typedef std::vector<ProcessInstanceInfo> ProcessInstanceInfoList;
Expand Down
125 changes: 105 additions & 20 deletions lldb/source/Host/linux/Host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,29 @@ enum class ProcessState {
TracedOrStopped,
Zombie,
};

constexpr int task_comm_len = 16;

struct StatFields {
::pid_t pid = LLDB_INVALID_PROCESS_ID;
char comm[task_comm_len];
char state;
::pid_t ppid = LLDB_INVALID_PROCESS_ID;
::pid_t pgrp = LLDB_INVALID_PROCESS_ID;
::pid_t session = LLDB_INVALID_PROCESS_ID;
int tty_nr;
int tpgid;
unsigned flags;
long unsigned minflt;
long unsigned cminflt;
long unsigned majflt;
long unsigned cmajflt;
long unsigned utime;
long unsigned stime;
long cutime;
long cstime;
// .... other things. We don't need them below
};
}

namespace lldb_private {
Expand All @@ -60,11 +83,92 @@ static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo,
::pid_t &Tgid) {
Log *log = GetLog(LLDBLog::Host);

auto BufferOrError = getProcFile(Pid, "status");
auto BufferOrError = getProcFile(Pid, "stat");
if (!BufferOrError)
return false;

llvm::StringRef Rest = BufferOrError.get()->getBuffer();
if (Rest.empty())
return false;
StatFields stat_fields;
if (sscanf(Rest.data(),
"%d %s %c %d %d %d %d %d %u %lu %lu %lu %lu %lu %lu %ld %ld",
&stat_fields.pid, stat_fields.comm, &stat_fields.state,
&stat_fields.ppid, &stat_fields.pgrp, &stat_fields.session,
&stat_fields.tty_nr, &stat_fields.tpgid, &stat_fields.flags,
&stat_fields.minflt, &stat_fields.cminflt, &stat_fields.majflt,
&stat_fields.cmajflt, &stat_fields.utime, &stat_fields.stime,
&stat_fields.cutime, &stat_fields.cstime) < 0) {
return false;
}

auto convert = [sc_clk_ticks = sysconf(_SC_CLK_TCK)](auto time_in_ticks) {
ProcessInstanceInfo::timespec ts;
if (sc_clk_ticks <= 0) {
return ts;
}
ts.tv_sec = time_in_ticks / sc_clk_ticks;
double remainder =
(static_cast<double>(time_in_ticks) / sc_clk_ticks) - ts.tv_sec;
ts.tv_usec =
std::chrono::microseconds{std::lround(1e+6 * remainder)}.count();
return ts;
};

ProcessInfo.SetParentProcessID(stat_fields.ppid);
ProcessInfo.SetProcessGroupID(stat_fields.pgrp);
ProcessInfo.SetProcessSessionID(stat_fields.session);
ProcessInfo.SetUserTime(convert(stat_fields.utime));
ProcessInfo.SetSystemTime(convert(stat_fields.stime));
ProcessInfo.SetCumulativeUserTime(convert(stat_fields.cutime));
ProcessInfo.SetCumulativeSystemTime(convert(stat_fields.cstime));
switch (stat_fields.state) {
case 'R':
State = ProcessState::Running;
break;
case 'S':
State = ProcessState::Sleeping;
break;
case 'D':
State = ProcessState::DiskSleep;
break;
case 'Z':
State = ProcessState::Zombie;
break;
case 'X':
State = ProcessState::Dead;
break;
case 'P':
State = ProcessState::Parked;
break;
case 'W':
State = ProcessState::Paging;
break;
case 'I':
State = ProcessState::Idle;
break;
case 'T': // Stopped on a signal or (before Linux 2.6.33) trace stopped
[[fallthrough]];
case 't':
State = ProcessState::TracedOrStopped;
break;
default:
State = ProcessState::Unknown;
break;
}

if (State == ProcessState::Unknown) {
LLDB_LOG(log, "Unknown process state {0}", stat_fields.state);
}

BufferOrError = getProcFile(Pid, "status");
if (!BufferOrError)
return false;

Rest = BufferOrError.get()->getBuffer();
if (Rest.empty())
return false;

while (!Rest.empty()) {
llvm::StringRef Line;
std::tie(Line, Rest) = Rest.split('\n');
Expand All @@ -89,25 +193,6 @@ static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo,

ProcessInfo.SetUserID(RUid);
ProcessInfo.SetEffectiveUserID(EUid);
} else if (Line.consume_front("PPid:")) {
::pid_t PPid;
Line.ltrim().consumeInteger(10, PPid);
ProcessInfo.SetParentProcessID(PPid);
} else if (Line.consume_front("State:")) {
State = llvm::StringSwitch<ProcessState>(Line.ltrim().take_front(1))
.Case("D", ProcessState::DiskSleep)
.Case("I", ProcessState::Idle)
.Case("R", ProcessState::Running)
.Case("S", ProcessState::Sleeping)
.CaseLower("T", ProcessState::TracedOrStopped)
.Case("W", ProcessState::Paging)
.Case("P", ProcessState::Parked)
.Case("X", ProcessState::Dead)
.Case("Z", ProcessState::Zombie)
.Default(ProcessState::Unknown);
if (State == ProcessState::Unknown) {
LLDB_LOG(log, "Unknown process state {0}", Line);
}
} else if (Line.consume_front("TracerPid:")) {
Line = Line.ltrim();
Line.consumeInteger(10, TracerPid);
Expand Down
23 changes: 23 additions & 0 deletions lldb/unittests/Host/linux/HostTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ class HostTest : public testing::Test {
} // namespace

TEST_F(HostTest, GetProcessInfo) {
llvm::Triple triple = HostInfo::GetTargetTriple();

ASSERT_TRUE(
(triple.getOS() == llvm::Triple::OSType::Linux) ||
(triple.hasEnvironment() &&
triple.getEnvironment() == llvm::Triple::EnvironmentType::Android));

ProcessInstanceInfo Info;
ASSERT_FALSE(Host::GetProcessInfo(0, Info));

Expand All @@ -40,6 +47,12 @@ TEST_F(HostTest, GetProcessInfo) {
ASSERT_TRUE(Info.ParentProcessIDIsValid());
EXPECT_EQ(lldb::pid_t(getppid()), Info.GetParentProcessID());

ASSERT_TRUE(Info.ProcessGroupIDIsValid());
EXPECT_EQ(lldb::pid_t(getpgrp()), Info.GetProcessGroupID());

ASSERT_TRUE(Info.ProcessSessionIDIsValid());
EXPECT_EQ(lldb::pid_t(getsid(getpid())), Info.GetProcessSessionID());

ASSERT_TRUE(Info.EffectiveUserIDIsValid());
EXPECT_EQ(geteuid(), Info.GetEffectiveUserID());

Expand All @@ -55,4 +68,14 @@ TEST_F(HostTest, GetProcessInfo) {
EXPECT_TRUE(Info.GetArchitecture().IsValid());
EXPECT_EQ(HostInfo::GetArchitecture(HostInfo::eArchKindDefault),
Info.GetArchitecture());
// Test timings
ASSERT_TRUE(Host::GetProcessInfo(getpid(), Info));
ProcessInstanceInfo::timespec user_time = Info.GetUserTime();
for (unsigned i = 0; i < 10'000'000; i++) {
__asm__ __volatile__("" : "+g"(i) : :);
}
ASSERT_TRUE(Host::GetProcessInfo(getpid(), Info));
ProcessInstanceInfo::timespec next_user_time = Info.GetUserTime();
ASSERT_TRUE(user_time.tv_sec < next_user_time.tv_sec ||
user_time.tv_usec < next_user_time.tv_usec);
}