[Lldb-commits] [lldb] adds additional information to the ProcessInfo object for elf processes (PR #88995)

Fred Grim via lldb-commits lldb-commits at lists.llvm.org
Tue Apr 16 14:55:07 PDT 2024


https://github.com/feg208 created https://github.com/llvm/llvm-project/pull/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

>From 9b8ec4d0c31ad1b228add56bc27cd79457e515c7 Mon Sep 17 00:00:00 2001
From: Fred Grim <fgrim at apple.com>
Date: Tue, 16 Apr 2024 14:46:37 -0700
Subject: [PATCH] adds additional information to the ProcessInfo object for elf
 processes

---
 lldb/include/lldb/Utility/ProcessInfo.h |  71 ++++++++++++++
 lldb/source/Host/linux/Host.cpp         | 125 ++++++++++++++++++++----
 lldb/unittests/Host/linux/HostTest.cpp  |   6 ++
 3 files changed, 182 insertions(+), 20 deletions(-)

diff --git a/lldb/include/lldb/Utility/ProcessInfo.h b/lldb/include/lldb/Utility/ProcessInfo.h
index 7fb5b37be0f48f..e9fe71e1b851d1 100644
--- a/lldb/include/lldb/Utility/ProcessInfo.h
+++ b/lldb/include/lldb/Utility/ProcessInfo.h
@@ -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)
@@ -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);
@@ -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;
diff --git a/lldb/source/Host/linux/Host.cpp b/lldb/source/Host/linux/Host.cpp
index 6c57384aa38a13..c6490f2fc9e2f5 100644
--- a/lldb/source/Host/linux/Host.cpp
+++ b/lldb/source/Host/linux/Host.cpp
@@ -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 {
@@ -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');
@@ -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);
diff --git a/lldb/unittests/Host/linux/HostTest.cpp b/lldb/unittests/Host/linux/HostTest.cpp
index 78bbe470d69531..32b1a3678e1fa4 100644
--- a/lldb/unittests/Host/linux/HostTest.cpp
+++ b/lldb/unittests/Host/linux/HostTest.cpp
@@ -40,6 +40,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());
 



More information about the lldb-commits mailing list