//===-- 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 #include #include #include #ifndef HAVE_GETTID #include pid_t gettid() { return ((pid_t)syscall(SYS_gettid)); } #endif 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(*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(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); int priority = getpriority(PRIO_PROCESS, getpid()); if (priority == -1) ASSERT_EQ(errno, 0); ASSERT_EQ(prpsinfo_opt->pr_nice, priority); 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(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(gettid())); ASSERT_EQ(prstatus_opt->pr_ppid, static_cast(getppid())); ASSERT_EQ(prstatus_opt->pr_pgrp, static_cast(getpgrp())); ASSERT_EQ(prstatus_opt->pr_sid, static_cast(getsid(gettid()))); }