[Lldb-commits] [lldb] [lldb] Add Populate Methods for ELFLinuxPrPsInfo and ELFLinuxPrStatus (PR #104109)
via lldb-commits
lldb-commits at lists.llvm.org
Wed Aug 14 11:03:57 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lldb
Author: Fred Grim (feg208)
<details>
<summary>Changes</summary>
To create elf core files there are multiple notes in the core file that contain these structs as the note. These populate methods take a Process and produce fully specified structures that can be used to fill these note sections. The pr also adds tests to ensure these structs are correctly populated.
---
Full diff: https://github.com/llvm/llvm-project/pull/104109.diff
5 Files Affected:
- (modified) lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp (+134)
- (modified) lldb/source/Plugins/Process/elf-core/ThreadElfCore.h (+7)
- (modified) lldb/unittests/Process/CMakeLists.txt (+1)
- (added) lldb/unittests/Process/elf-core/CMakeLists.txt (+15)
- (added) lldb/unittests/Process/elf-core/ThreadElfCoreTest.cpp (+162)
``````````diff
diff --git a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp
index 2a83163884e168..fdb4a5837cd462 100644
--- a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp
+++ b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.cpp
@@ -318,6 +318,32 @@ Status ELFLinuxPrStatus::Parse(const DataExtractor &data,
return error;
}
+static struct compat_timeval
+copy_timespecs(const ProcessInstanceInfo::timespec &oth) {
+ using sec_t = decltype(compat_timeval::tv_sec);
+ using usec_t = decltype(compat_timeval::tv_usec);
+ return {static_cast<sec_t>(oth.tv_sec), static_cast<usec_t>(oth.tv_usec)};
+}
+
+std::optional<ELFLinuxPrStatus>
+ELFLinuxPrStatus::Populate(const lldb::ThreadSP &thread_sp) {
+ ELFLinuxPrStatus prstatus{};
+ prstatus.pr_pid = thread_sp->GetID();
+ lldb::ProcessSP process_sp = thread_sp->GetProcess();
+ ProcessInstanceInfo info;
+ if (!process_sp->GetProcessInfo(info)) {
+ return std::nullopt;
+ }
+ prstatus.pr_ppid = info.GetParentProcessID();
+ prstatus.pr_pgrp = info.GetProcessGroupID();
+ prstatus.pr_sid = info.GetProcessSessionID();
+ prstatus.pr_utime = copy_timespecs(info.GetUserTime());
+ prstatus.pr_stime = copy_timespecs(info.GetSystemTime());
+ prstatus.pr_cutime = copy_timespecs(info.GetCumulativeUserTime());
+ prstatus.pr_cstime = copy_timespecs(info.GetCumulativeSystemTime());
+ return prstatus;
+}
+
// Parse PRPSINFO from NOTE entry
ELFLinuxPrPsInfo::ELFLinuxPrPsInfo() {
memset(this, 0, sizeof(ELFLinuxPrPsInfo));
@@ -394,6 +420,114 @@ Status ELFLinuxPrPsInfo::Parse(const DataExtractor &data,
return error;
}
+std::optional<ELFLinuxPrPsInfo>
+ELFLinuxPrPsInfo::Populate(const lldb::ProcessSP &process_sp) {
+ ELFLinuxPrPsInfo prpsinfo{};
+ prpsinfo.pr_pid = process_sp->GetID();
+ ProcessInstanceInfo info;
+ if (!process_sp->GetProcessInfo(info)) {
+ return std::nullopt;
+ }
+ prpsinfo.pr_nice = info.GetPriorityValue().value_or(0);
+ prpsinfo.pr_zomb = 0;
+ if (auto zombie_opt = info.IsZombie(); zombie_opt.value_or(false)) {
+ prpsinfo.pr_zomb = 1;
+ }
+ /**
+ * In the linux kernel this comes from:
+ * state = READ_ONCE(p->__state);
+ * i = state ? ffz(~state) + 1 : 0;
+ * psinfo->pr_sname = (i > 5) ? '.' : "RSDTZW"[i];
+ *
+ * So we replicate that here. From proc_pid_stats(5)
+ * R = Running
+ * S = Sleeping on uninterrutible wait
+ * D = Waiting on uninterruptable disk sleep
+ * T = Tracing stop
+ * Z = Zombie
+ * W = Paging
+ */
+ lldb::StateType process_state = process_sp->GetState();
+ switch (process_state) {
+ case lldb::StateType::eStateSuspended:
+ prpsinfo.pr_sname = 'S';
+ prpsinfo.pr_state = 1;
+ break;
+ case lldb::StateType::eStateStopped:
+ [[fallthrough]];
+ case lldb::StateType::eStateStepping:
+ prpsinfo.pr_sname = 'T';
+ prpsinfo.pr_state = 3;
+ break;
+ case lldb::StateType::eStateUnloaded:
+ [[fallthrough]];
+ case lldb::StateType::eStateRunning:
+ prpsinfo.pr_sname = 'R';
+ prpsinfo.pr_state = 0;
+ break;
+ default:
+ break;
+ }
+
+ /**
+ * pr_flags is left as 0. The values (in linux) are specific
+ * to the kernel. We recover them from the proc filesystem
+ * but don't put them in ProcessInfo because it would really
+ * become very linux specific and the utility here seems pretty
+ * dubious
+ */
+
+ if (info.EffectiveUserIDIsValid()) {
+ prpsinfo.pr_uid = info.GetUserID();
+ }
+ if (info.EffectiveGroupIDIsValid()) {
+ prpsinfo.pr_gid = info.GetGroupID();
+ }
+
+ if (info.ParentProcessIDIsValid()) {
+ prpsinfo.pr_ppid = info.GetParentProcessID();
+ }
+
+ if (info.ProcessGroupIDIsValid()) {
+ prpsinfo.pr_pgrp = info.GetProcessGroupID();
+ }
+
+ if (info.ProcessSessionIDIsValid()) {
+ prpsinfo.pr_sid = info.GetProcessSessionID();
+ }
+ constexpr size_t fname_len = std::extent_v<decltype(prpsinfo.pr_fname)>;
+ static_assert(fname_len > 0, "This should always be non zero");
+ const llvm::StringRef fname = info.GetNameAsStringRef();
+ auto fname_begin = fname.begin();
+ std::copy(fname_begin,
+ std::next(fname_begin, std::min(fname_len, fname.size())),
+ prpsinfo.pr_fname);
+ prpsinfo.pr_fname[fname_len - 1] = '\0';
+ auto args = info.GetArguments();
+ if (!args.empty()) {
+ constexpr size_t psargs_len = std::extent_v<decltype(prpsinfo.pr_psargs)>;
+ static_assert(psargs_len > 0, "This should always be non zero");
+ size_t i = psargs_len;
+ auto argentry_iterator = std::begin(args);
+ char *psargs = prpsinfo.pr_psargs;
+ while (i > 0 && argentry_iterator != args.end()) {
+ llvm::StringRef argentry = argentry_iterator->ref();
+ size_t len = std::min(i, argentry.size());
+ auto arg_iterator = std::begin(argentry);
+ std::copy(arg_iterator, std::next(arg_iterator, len), psargs);
+ i -= len;
+ psargs += len;
+ if (i > 0) {
+ *(psargs++) = ' ';
+ --i;
+ }
+ ++argentry_iterator;
+ }
+ prpsinfo.pr_psargs[psargs_len - 1] = '\0';
+ }
+ return prpsinfo;
+}
+
// Parse SIGINFO from NOTE entry
ELFLinuxSigInfo::ELFLinuxSigInfo() { memset(this, 0, sizeof(ELFLinuxSigInfo)); }
diff --git a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h
index 2f3ed2a017790a..31602e356850ee 100644
--- a/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h
+++ b/lldb/source/Plugins/Process/elf-core/ThreadElfCore.h
@@ -13,6 +13,7 @@
#include "lldb/Target/Thread.h"
#include "lldb/Utility/DataExtractor.h"
#include "llvm/ADT/DenseMap.h"
+#include <optional>
#include <string>
struct compat_timeval {
@@ -56,6 +57,9 @@ struct ELFLinuxPrStatus {
lldb_private::Status Parse(const lldb_private::DataExtractor &data,
const lldb_private::ArchSpec &arch);
+ static std::optional<ELFLinuxPrStatus>
+ Populate(const lldb::ThreadSP &thread_sp);
+
// Return the bytesize of the structure
// 64 bit - just sizeof
// 32 bit - hardcoded because we are reusing the struct, but some of the
@@ -112,6 +116,9 @@ struct ELFLinuxPrPsInfo {
lldb_private::Status Parse(const lldb_private::DataExtractor &data,
const lldb_private::ArchSpec &arch);
+ static std::optional<ELFLinuxPrPsInfo>
+ Populate(const lldb::ProcessSP &process_sp);
+
// Return the bytesize of the structure
// 64 bit - just sizeof
// 32 bit - hardcoded because we are reusing the struct, but some of the
diff --git a/lldb/unittests/Process/CMakeLists.txt b/lldb/unittests/Process/CMakeLists.txt
index 5fbecfcfa25ebe..d3b37e006fd899 100644
--- a/lldb/unittests/Process/CMakeLists.txt
+++ b/lldb/unittests/Process/CMakeLists.txt
@@ -1,5 +1,6 @@
add_subdirectory(gdb-remote)
if (CMAKE_SYSTEM_NAME MATCHES "Linux|Android")
+ add_subdirectory(elf-core)
add_subdirectory(Linux)
add_subdirectory(POSIX)
endif()
diff --git a/lldb/unittests/Process/elf-core/CMakeLists.txt b/lldb/unittests/Process/elf-core/CMakeLists.txt
new file mode 100644
index 00000000000000..b852a3ffb863c1
--- /dev/null
+++ b/lldb/unittests/Process/elf-core/CMakeLists.txt
@@ -0,0 +1,15 @@
+add_lldb_unittest(ProcessElfCoreTests
+ ThreadElfCoreTest.cpp
+
+ LINK_LIBS
+ lldbCore
+ lldbHost
+ lldbUtilityHelpers
+ lldbPluginProcessElfCore
+ lldbPluginPlatformLinux
+
+ LLVMTestingSupport
+
+ LINK_COMPONENTS
+ Support
+ )
diff --git a/lldb/unittests/Process/elf-core/ThreadElfCoreTest.cpp b/lldb/unittests/Process/elf-core/ThreadElfCoreTest.cpp
new file mode 100644
index 00000000000000..6068c161ba1091
--- /dev/null
+++ b/lldb/unittests/Process/elf-core/ThreadElfCoreTest.cpp
@@ -0,0 +1,162 @@
+//===-- ThreadElfCoreTest.cpp ------------------------------------*- C++
+//-*-===//
+//
+// 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 "Plugins/Process/elf-core/ThreadElfCore.h"
+#include "Plugins/Platform/Linux/PlatformLinux.h"
+#include "TestingSupport/TestUtilities.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/Listener.h"
+#include "llvm/TargetParser/Triple.h"
+#include "gtest/gtest.h"
+
+#include <iostream>
+#include <memory>
+#include <mutex>
+#include <unistd.h>
+
+using namespace lldb_private;
+
+namespace {
+
+struct ElfCoreTest : public testing::Test {
+ static void SetUpTestCase() {
+ FileSystem::Initialize();
+ HostInfo::Initialize();
+ platform_linux::PlatformLinux::Initialize();
+ std::call_once(TestUtilities::g_debugger_initialize_flag,
+ []() { Debugger::Initialize(nullptr); });
+ }
+ static void TearDownTestCase() {
+ platform_linux::PlatformLinux::Terminate();
+ HostInfo::Terminate();
+ FileSystem::Terminate();
+ }
+};
+
+struct DummyProcess : public Process {
+ DummyProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp)
+ : Process(target_sp, listener_sp) {
+ SetID(getpid());
+ }
+
+ bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) override {
+ return true;
+ }
+ Status DoDestroy() override { return {}; }
+ void RefreshStateAfterStop() override {}
+ size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
+ Status &error) override {
+ return 0;
+ }
+ bool DoUpdateThreadList(ThreadList &old_thread_list,
+ ThreadList &new_thread_list) override {
+ return false;
+ }
+ llvm::StringRef GetPluginName() override { return "Dummy"; }
+};
+
+struct DummyThread : public Thread {
+ using Thread::Thread;
+
+ ~DummyThread() override { DestroyThread(); }
+
+ void RefreshStateAfterStop() override {}
+
+ lldb::RegisterContextSP GetRegisterContext() override { return nullptr; }
+
+ lldb::RegisterContextSP
+ CreateRegisterContextForFrame(StackFrame *frame) override {
+ return nullptr;
+ }
+
+ bool CalculateStopInfo() override { return false; }
+};
+
+lldb::TargetSP CreateTarget(lldb::DebuggerSP &debugger_sp, ArchSpec &arch) {
+ lldb::PlatformSP platform_sp;
+ lldb::TargetSP target_sp;
+ debugger_sp->GetTargetList().CreateTarget(
+ *debugger_sp, "", arch, eLoadDependentsNo, platform_sp, target_sp);
+ return target_sp;
+}
+
+lldb::ThreadSP CreateThread(lldb::ProcessSP &process_sp) {
+ lldb::ThreadSP thread_sp =
+ std::make_shared<DummyThread>(*process_sp.get(), gettid());
+ if (thread_sp == nullptr) {
+ return nullptr;
+ }
+ process_sp->GetThreadList().AddThread(thread_sp);
+
+ return thread_sp;
+}
+
+} // namespace
+
+TEST_F(ElfCoreTest, PopulatePrpsInfoTest) {
+ ArchSpec arch{HostInfo::GetTargetTriple()};
+ lldb::DebuggerSP debugger_sp = Debugger::CreateInstance();
+ ASSERT_TRUE(debugger_sp);
+
+ lldb::TargetSP target_sp = CreateTarget(debugger_sp, arch);
+ ASSERT_TRUE(target_sp);
+
+ lldb::ListenerSP listener_sp(Listener::MakeListener("dummy"));
+ lldb::ProcessSP process_sp =
+ std::make_shared<DummyProcess>(target_sp, listener_sp);
+ ASSERT_TRUE(process_sp);
+ auto prpsinfo_opt = ELFLinuxPrPsInfo::Populate(process_sp);
+ ASSERT_TRUE(prpsinfo_opt.has_value());
+ ASSERT_EQ(prpsinfo_opt->pr_pid, getpid());
+ ASSERT_EQ(prpsinfo_opt->pr_state, 0);
+ ASSERT_EQ(prpsinfo_opt->pr_sname, 'R');
+ ASSERT_EQ(prpsinfo_opt->pr_zomb, 0);
+ ASSERT_EQ(prpsinfo_opt->pr_nice, 0);
+ ASSERT_EQ(prpsinfo_opt->pr_flag, 0UL);
+ ASSERT_EQ(prpsinfo_opt->pr_uid, getuid());
+ ASSERT_EQ(prpsinfo_opt->pr_gid, getgid());
+ ASSERT_EQ(prpsinfo_opt->pr_pid, getpid());
+ ASSERT_EQ(prpsinfo_opt->pr_ppid, getppid());
+ ASSERT_EQ(prpsinfo_opt->pr_pgrp, getpgrp());
+ ASSERT_EQ(prpsinfo_opt->pr_sid, getsid(getpid()));
+ ASSERT_EQ(std::string{prpsinfo_opt->pr_fname}, "ProcessElfCoreT");
+ ASSERT_TRUE(std::string{prpsinfo_opt->pr_psargs}.empty());
+}
+
+TEST_F(ElfCoreTest, PopulatePrStatusTest) {
+ ArchSpec arch{HostInfo::GetTargetTriple()};
+ lldb::DebuggerSP debugger_sp = Debugger::CreateInstance();
+ ASSERT_TRUE(debugger_sp);
+
+ lldb::TargetSP target_sp = CreateTarget(debugger_sp, arch);
+ ASSERT_TRUE(target_sp);
+
+ lldb::ListenerSP listener_sp(Listener::MakeListener("dummy"));
+ lldb::ProcessSP process_sp =
+ std::make_shared<DummyProcess>(target_sp, listener_sp);
+ ASSERT_TRUE(process_sp);
+ lldb::ThreadSP thread_sp = CreateThread(process_sp);
+ ASSERT_TRUE(thread_sp);
+ auto prstatus_opt = ELFLinuxPrStatus::Populate(thread_sp);
+ ASSERT_TRUE(prstatus_opt.has_value());
+ ASSERT_EQ(prstatus_opt->si_signo, 0);
+ ASSERT_EQ(prstatus_opt->si_code, 0);
+ ASSERT_EQ(prstatus_opt->si_errno, 0);
+ ASSERT_EQ(prstatus_opt->pr_cursig, 0);
+ ASSERT_EQ(prstatus_opt->pr_sigpend, 0UL);
+ ASSERT_EQ(prstatus_opt->pr_sighold, 0UL);
+ ASSERT_EQ(prstatus_opt->pr_pid, static_cast<uint32_t>(gettid()));
+ ASSERT_EQ(prstatus_opt->pr_ppid, static_cast<uint32_t>(getppid()));
+ ASSERT_EQ(prstatus_opt->pr_pgrp, static_cast<uint32_t>(getpgrp()));
+ ASSERT_EQ(prstatus_opt->pr_sid, static_cast<uint32_t>(getsid(gettid())));
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/104109
More information about the lldb-commits
mailing list