Skip to content

Commit 4681079

Browse files
authored
adds additional information to the ProcessInfo object for elf processes (#88995)
This adds some additional bits into a ProcessInfo structure that will be of use in filling structs in an elf core file. This is a demand for implementing process save-core
1 parent c37c472 commit 4681079

File tree

3 files changed

+199
-20
lines changed

3 files changed

+199
-20
lines changed

lldb/include/lldb/Utility/ProcessInfo.h

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ class ProcessInfo {
139139
// to that process.
140140
class ProcessInstanceInfo : public ProcessInfo {
141141
public:
142+
struct timespec {
143+
time_t tv_sec = 0;
144+
long int tv_usec = 0;
145+
};
146+
142147
ProcessInstanceInfo() = default;
143148

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

180+
lldb::pid_t GetProcessGroupID() const { return m_process_group_id; }
181+
182+
void SetProcessGroupID(lldb::pid_t pgrp) { m_process_group_id = pgrp; }
183+
184+
bool ProcessGroupIDIsValid() const {
185+
return m_process_group_id != LLDB_INVALID_PROCESS_ID;
186+
}
187+
188+
lldb::pid_t GetProcessSessionID() const { return m_process_session_id; }
189+
190+
void SetProcessSessionID(lldb::pid_t session) {
191+
m_process_session_id = session;
192+
}
193+
194+
bool ProcessSessionIDIsValid() const {
195+
return m_process_session_id != LLDB_INVALID_PROCESS_ID;
196+
}
197+
198+
struct timespec GetUserTime() const { return m_user_time; }
199+
200+
void SetUserTime(struct timespec utime) { m_user_time = utime; }
201+
202+
bool UserTimeIsValid() const {
203+
return m_user_time.tv_sec > 0 || m_user_time.tv_usec > 0;
204+
}
205+
206+
struct timespec GetSystemTime() const { return m_system_time; }
207+
208+
void SetSystemTime(struct timespec stime) { m_system_time = stime; }
209+
210+
bool SystemTimeIsValid() const {
211+
return m_system_time.tv_sec > 0 || m_system_time.tv_usec > 0;
212+
}
213+
214+
struct timespec GetCumulativeUserTime() const {
215+
return m_cumulative_user_time;
216+
}
217+
218+
void SetCumulativeUserTime(struct timespec cutime) {
219+
m_cumulative_user_time = cutime;
220+
}
221+
222+
bool CumulativeUserTimeIsValid() const {
223+
return m_cumulative_user_time.tv_sec > 0 ||
224+
m_cumulative_user_time.tv_usec > 0;
225+
}
226+
227+
struct timespec GetCumulativeSystemTime() const {
228+
return m_cumulative_system_time;
229+
}
230+
231+
void SetCumulativeSystemTime(struct timespec cstime) {
232+
m_cumulative_system_time = cstime;
233+
}
234+
235+
bool CumulativeSystemTimeIsValid() const {
236+
return m_cumulative_system_time.tv_sec > 0 ||
237+
m_cumulative_system_time.tv_sec > 0;
238+
}
239+
175240
void Dump(Stream &s, UserIDResolver &resolver) const;
176241

177242
static void DumpTableHeader(Stream &s, bool show_args, bool verbose);
@@ -183,6 +248,12 @@ class ProcessInstanceInfo : public ProcessInfo {
183248
uint32_t m_euid = UINT32_MAX;
184249
uint32_t m_egid = UINT32_MAX;
185250
lldb::pid_t m_parent_pid = LLDB_INVALID_PROCESS_ID;
251+
lldb::pid_t m_process_group_id = LLDB_INVALID_PROCESS_ID;
252+
lldb::pid_t m_process_session_id = LLDB_INVALID_PROCESS_ID;
253+
struct timespec m_user_time {};
254+
struct timespec m_system_time {};
255+
struct timespec m_cumulative_user_time {};
256+
struct timespec m_cumulative_system_time {};
186257
};
187258

188259
typedef std::vector<ProcessInstanceInfo> ProcessInstanceInfoList;

lldb/source/Host/linux/Host.cpp

Lines changed: 105 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,29 @@ enum class ProcessState {
4949
TracedOrStopped,
5050
Zombie,
5151
};
52+
53+
constexpr int task_comm_len = 16;
54+
55+
struct StatFields {
56+
::pid_t pid = LLDB_INVALID_PROCESS_ID;
57+
char comm[task_comm_len];
58+
char state;
59+
::pid_t ppid = LLDB_INVALID_PROCESS_ID;
60+
::pid_t pgrp = LLDB_INVALID_PROCESS_ID;
61+
::pid_t session = LLDB_INVALID_PROCESS_ID;
62+
int tty_nr;
63+
int tpgid;
64+
unsigned flags;
65+
long unsigned minflt;
66+
long unsigned cminflt;
67+
long unsigned majflt;
68+
long unsigned cmajflt;
69+
long unsigned utime;
70+
long unsigned stime;
71+
long cutime;
72+
long cstime;
73+
// .... other things. We don't need them below
74+
};
5275
}
5376

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

63-
auto BufferOrError = getProcFile(Pid, "status");
86+
auto BufferOrError = getProcFile(Pid, "stat");
6487
if (!BufferOrError)
6588
return false;
6689

6790
llvm::StringRef Rest = BufferOrError.get()->getBuffer();
91+
if (Rest.empty())
92+
return false;
93+
StatFields stat_fields;
94+
if (sscanf(Rest.data(),
95+
"%d %s %c %d %d %d %d %d %u %lu %lu %lu %lu %lu %lu %ld %ld",
96+
&stat_fields.pid, stat_fields.comm, &stat_fields.state,
97+
&stat_fields.ppid, &stat_fields.pgrp, &stat_fields.session,
98+
&stat_fields.tty_nr, &stat_fields.tpgid, &stat_fields.flags,
99+
&stat_fields.minflt, &stat_fields.cminflt, &stat_fields.majflt,
100+
&stat_fields.cmajflt, &stat_fields.utime, &stat_fields.stime,
101+
&stat_fields.cutime, &stat_fields.cstime) < 0) {
102+
return false;
103+
}
104+
105+
auto convert = [sc_clk_ticks = sysconf(_SC_CLK_TCK)](auto time_in_ticks) {
106+
ProcessInstanceInfo::timespec ts;
107+
if (sc_clk_ticks <= 0) {
108+
return ts;
109+
}
110+
ts.tv_sec = time_in_ticks / sc_clk_ticks;
111+
double remainder =
112+
(static_cast<double>(time_in_ticks) / sc_clk_ticks) - ts.tv_sec;
113+
ts.tv_usec =
114+
std::chrono::microseconds{std::lround(1e+6 * remainder)}.count();
115+
return ts;
116+
};
117+
118+
ProcessInfo.SetParentProcessID(stat_fields.ppid);
119+
ProcessInfo.SetProcessGroupID(stat_fields.pgrp);
120+
ProcessInfo.SetProcessSessionID(stat_fields.session);
121+
ProcessInfo.SetUserTime(convert(stat_fields.utime));
122+
ProcessInfo.SetSystemTime(convert(stat_fields.stime));
123+
ProcessInfo.SetCumulativeUserTime(convert(stat_fields.cutime));
124+
ProcessInfo.SetCumulativeSystemTime(convert(stat_fields.cstime));
125+
switch (stat_fields.state) {
126+
case 'R':
127+
State = ProcessState::Running;
128+
break;
129+
case 'S':
130+
State = ProcessState::Sleeping;
131+
break;
132+
case 'D':
133+
State = ProcessState::DiskSleep;
134+
break;
135+
case 'Z':
136+
State = ProcessState::Zombie;
137+
break;
138+
case 'X':
139+
State = ProcessState::Dead;
140+
break;
141+
case 'P':
142+
State = ProcessState::Parked;
143+
break;
144+
case 'W':
145+
State = ProcessState::Paging;
146+
break;
147+
case 'I':
148+
State = ProcessState::Idle;
149+
break;
150+
case 'T': // Stopped on a signal or (before Linux 2.6.33) trace stopped
151+
[[fallthrough]];
152+
case 't':
153+
State = ProcessState::TracedOrStopped;
154+
break;
155+
default:
156+
State = ProcessState::Unknown;
157+
break;
158+
}
159+
160+
if (State == ProcessState::Unknown) {
161+
LLDB_LOG(log, "Unknown process state {0}", stat_fields.state);
162+
}
163+
164+
BufferOrError = getProcFile(Pid, "status");
165+
if (!BufferOrError)
166+
return false;
167+
168+
Rest = BufferOrError.get()->getBuffer();
169+
if (Rest.empty())
170+
return false;
171+
68172
while (!Rest.empty()) {
69173
llvm::StringRef Line;
70174
std::tie(Line, Rest) = Rest.split('\n');
@@ -89,25 +193,6 @@ static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo,
89193

90194
ProcessInfo.SetUserID(RUid);
91195
ProcessInfo.SetEffectiveUserID(EUid);
92-
} else if (Line.consume_front("PPid:")) {
93-
::pid_t PPid;
94-
Line.ltrim().consumeInteger(10, PPid);
95-
ProcessInfo.SetParentProcessID(PPid);
96-
} else if (Line.consume_front("State:")) {
97-
State = llvm::StringSwitch<ProcessState>(Line.ltrim().take_front(1))
98-
.Case("D", ProcessState::DiskSleep)
99-
.Case("I", ProcessState::Idle)
100-
.Case("R", ProcessState::Running)
101-
.Case("S", ProcessState::Sleeping)
102-
.CaseLower("T", ProcessState::TracedOrStopped)
103-
.Case("W", ProcessState::Paging)
104-
.Case("P", ProcessState::Parked)
105-
.Case("X", ProcessState::Dead)
106-
.Case("Z", ProcessState::Zombie)
107-
.Default(ProcessState::Unknown);
108-
if (State == ProcessState::Unknown) {
109-
LLDB_LOG(log, "Unknown process state {0}", Line);
110-
}
111196
} else if (Line.consume_front("TracerPid:")) {
112197
Line = Line.ltrim();
113198
Line.consumeInteger(10, TracerPid);

lldb/unittests/Host/linux/HostTest.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ class HostTest : public testing::Test {
2929
} // namespace
3030

3131
TEST_F(HostTest, GetProcessInfo) {
32+
llvm::Triple triple = HostInfo::GetTargetTriple();
33+
34+
ASSERT_TRUE(
35+
(triple.getOS() == llvm::Triple::OSType::Linux) ||
36+
(triple.hasEnvironment() &&
37+
triple.getEnvironment() == llvm::Triple::EnvironmentType::Android));
38+
3239
ProcessInstanceInfo Info;
3340
ASSERT_FALSE(Host::GetProcessInfo(0, Info));
3441

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

50+
ASSERT_TRUE(Info.ProcessGroupIDIsValid());
51+
EXPECT_EQ(lldb::pid_t(getpgrp()), Info.GetProcessGroupID());
52+
53+
ASSERT_TRUE(Info.ProcessSessionIDIsValid());
54+
EXPECT_EQ(lldb::pid_t(getsid(getpid())), Info.GetProcessSessionID());
55+
4356
ASSERT_TRUE(Info.EffectiveUserIDIsValid());
4457
EXPECT_EQ(geteuid(), Info.GetEffectiveUserID());
4558

@@ -55,4 +68,14 @@ TEST_F(HostTest, GetProcessInfo) {
5568
EXPECT_TRUE(Info.GetArchitecture().IsValid());
5669
EXPECT_EQ(HostInfo::GetArchitecture(HostInfo::eArchKindDefault),
5770
Info.GetArchitecture());
71+
// Test timings
72+
ASSERT_TRUE(Host::GetProcessInfo(getpid(), Info));
73+
ProcessInstanceInfo::timespec user_time = Info.GetUserTime();
74+
for (unsigned i = 0; i < 10'000'000; i++) {
75+
__asm__ __volatile__("" : "+g"(i) : :);
76+
}
77+
ASSERT_TRUE(Host::GetProcessInfo(getpid(), Info));
78+
ProcessInstanceInfo::timespec next_user_time = Info.GetUserTime();
79+
ASSERT_TRUE(user_time.tv_sec < next_user_time.tv_sec ||
80+
user_time.tv_usec < next_user_time.tv_usec);
5881
}

0 commit comments

Comments
 (0)