-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[lldb] Add Populate Methods for ELFLinuxPrPsInfo and ELFLinuxPrStatus #104109
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
Changes from all commits
8f2f842
db97ae5
0655878
809efb3
379a842
8be18b9
e2587be
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
add_lldb_unittest(ProcessElfCoreTests | ||
ThreadElfCoreTest.cpp | ||
|
||
LINK_LIBS | ||
lldbCore | ||
lldbHost | ||
lldbUtilityHelpers | ||
lldbPluginProcessElfCore | ||
lldbPluginPlatformLinux | ||
|
||
LLVMTestingSupport | ||
|
||
LINK_COMPONENTS | ||
Support | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
//===-- 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 <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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would be easier to test if the API was like If you want you can still have an overload which takes a process (and calls this function), but given that it will likely only ever have a single caller, it could just be inlined into it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah this is a good idea. I'll alter the interface and add more testing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
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()); | ||
lldb_private::ProcessInstanceInfo info; | ||
ASSERT_TRUE(process_sp->GetProcessInfo(info)); | ||
const char *args[] = {"a.out", "--foo=bar", "--baz=boo", nullptr}; | ||
info.SetArguments(args, true); | ||
prpsinfo_opt = | ||
ELFLinuxPrPsInfo::Populate(info, lldb::StateType::eStateStopped); | ||
ASSERT_TRUE(prpsinfo_opt.has_value()); | ||
ASSERT_EQ(prpsinfo_opt->pr_pid, getpid()); | ||
ASSERT_EQ(prpsinfo_opt->pr_state, 3); | ||
ASSERT_EQ(prpsinfo_opt->pr_sname, 'T'); | ||
ASSERT_EQ(std::string{prpsinfo_opt->pr_fname}, "a.out"); | ||
ASSERT_FALSE(std::string{prpsinfo_opt->pr_psargs}.empty()); | ||
ASSERT_EQ(std::string{prpsinfo_opt->pr_psargs}, "a.out --foo=bar --baz=boo"); | ||
} | ||
|
||
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()))); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be be using a
thread_sp
to populate process info? We would need to ensure we call this with the main thread only right? Can we just pass in a ProcessSP instead so there can be no error?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes we should. In linux (and I guess in FreeBSD as well) a prstatus object is created for each thread which you can see in ProcessELFCore
The tid is the pid. So in SaveCore we'll need to iterate over all the threads to build each struct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good.