//===-- ProcessEventDataTest.cpp ------------------------------------------===// // // 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/Platform/MacOSX/PlatformMacOSX.h" #include "Plugins/Platform/MacOSX/PlatformRemoteMacOSX.h" #include "lldb/Core/Debugger.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/HostInfo.h" #include "lldb/Target/Process.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/ArchSpec.h" #include "lldb/Utility/Event.h" #include "lldb/Utility/Reproducer.h" #include "gtest/gtest.h" using namespace lldb_private; using namespace lldb_private::repro; using namespace lldb; namespace { class ProcessEventDataTest : public ::testing::Test { public: void SetUp() override { llvm::cantFail(Reproducer::Initialize(ReproducerMode::Off, llvm::None)); FileSystem::Initialize(); HostInfo::Initialize(); PlatformMacOSX::Initialize(); } void TearDown() override { PlatformMacOSX::Terminate(); HostInfo::Terminate(); FileSystem::Terminate(); Reproducer::Terminate(); } }; class DummyProcess : public Process { public: using Process::Process; virtual bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) { return true; } virtual Status DoDestroy() { return {}; } virtual void RefreshStateAfterStop() {} virtual size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Status &error) { return 0; } virtual bool UpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) { return false; } virtual ConstString GetPluginName() { return ConstString("Dummy"); } virtual uint32_t GetPluginVersion() { return 0; } ProcessModID &GetModIDNonConstRef() { return m_mod_id; } }; class DummyThread : public Thread { public: 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; } }; class DummyStopInfo : public StopInfo { public: DummyStopInfo(Thread &thread, uint64_t value) : StopInfo(thread, value), m_should_stop(true), m_stop_reason(eStopReasonBreakpoint) {} bool ShouldStop(Event *event_ptr) override { return m_should_stop; } StopReason GetStopReason() const override { return m_stop_reason; } bool m_should_stop; StopReason m_stop_reason; }; class DummyProcessEventData : public Process::ProcessEventData { public: DummyProcessEventData(ProcessSP &process_sp, StateType state) : ProcessEventData(process_sp, state), m_should_stop_hit_count(0) {} bool ShouldStop(Event *event_ptr, bool &found_valid_stopinfo) override { m_should_stop_hit_count++; return false; } int m_should_stop_hit_count; }; } // namespace typedef std::shared_ptr ProcessEventDataSP; typedef std::shared_ptr EventSP; TargetSP CreateTarget(DebuggerSP &debugger_sp, ArchSpec &arch) { Status error; PlatformSP platform_sp; TargetSP target_sp; error = debugger_sp->GetTargetList().CreateTarget( *debugger_sp, "", arch, eLoadDependentsNo, platform_sp, target_sp); if (target_sp) { debugger_sp->GetTargetList().SetSelectedTarget(target_sp.get()); } return target_sp; } ThreadSP CreateThread(ProcessSP &process_sp, bool should_stop, bool has_valid_stopinfo) { ThreadSP thread_sp = std::make_shared(*process_sp.get(), 0); if (thread_sp == nullptr) { return nullptr; } if (has_valid_stopinfo) { StopInfoSP stopinfo_sp = std::make_shared(*thread_sp.get(), 0); static_cast(stopinfo_sp.get())->m_should_stop = should_stop; if (stopinfo_sp == nullptr) { return nullptr; } thread_sp->SetStopInfo(stopinfo_sp); } process_sp->GetThreadList().AddThread(thread_sp); return thread_sp; } TEST_F(ProcessEventDataTest, DoOnRemoval) { ArchSpec arch("x86_64-apple-macosx-"); Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch)); DebuggerSP debugger_sp = Debugger::CreateInstance(); ASSERT_TRUE(debugger_sp); TargetSP target_sp = CreateTarget(debugger_sp, arch); ASSERT_TRUE(target_sp); ListenerSP listener_sp(Listener::MakeListener("dummy")); ProcessSP process_sp = std::make_shared(target_sp, listener_sp); ASSERT_TRUE(process_sp); /* Should hit ShouldStop if state is eStateStopped */ ProcessEventDataSP event_data_sp = std::make_shared(process_sp, eStateStopped); EventSP event_sp = std::make_shared(0, event_data_sp); event_data_sp->SetUpdateStateOnRemoval(event_sp.get()); event_data_sp->DoOnRemoval(event_sp.get()); bool result = static_cast(event_data_sp.get()) ->m_should_stop_hit_count == 1; ASSERT_TRUE(result); /* Should not hit ShouldStop if state is not eStateStopped */ event_data_sp = std::make_shared(process_sp, eStateStepping); event_sp = std::make_shared(0, event_data_sp); event_data_sp->SetUpdateStateOnRemoval(event_sp.get()); event_data_sp->DoOnRemoval(event_sp.get()); result = static_cast(event_data_sp.get()) ->m_should_stop_hit_count == 0; ASSERT_TRUE(result); } TEST_F(ProcessEventDataTest, ShouldStop) { ArchSpec arch("x86_64-apple-macosx-"); Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch)); DebuggerSP debugger_sp = Debugger::CreateInstance(); ASSERT_TRUE(debugger_sp); TargetSP target_sp = CreateTarget(debugger_sp, arch); ASSERT_TRUE(target_sp); ListenerSP listener_sp(Listener::MakeListener("dummy")); ProcessSP process_sp = std::make_shared(target_sp, listener_sp); ASSERT_TRUE(process_sp); // wants to stop and has valid StopInfo ThreadSP thread_sp = CreateThread(process_sp, true, true); ProcessEventDataSP event_data_sp = std::make_shared(process_sp, eStateStopped); EventSP event_sp = std::make_shared(0, event_data_sp); /* Should stop if thread has valid StopInfo and not suspended */ bool found_valid_stopinfo = false; bool should_stop = event_data_sp->ShouldStop(event_sp.get(), found_valid_stopinfo); ASSERT_TRUE(should_stop == true && found_valid_stopinfo == true); /* Should not stop if thread has valid StopInfo but was suspended */ found_valid_stopinfo = false; thread_sp->SetResumeState(eStateSuspended); should_stop = event_data_sp->ShouldStop(event_sp.get(), found_valid_stopinfo); ASSERT_TRUE(should_stop == false && found_valid_stopinfo == false); /* Should not stop, thread-reason of stop does not want to stop in its callback and suspended thread who wants to (from previous stop) must be ignored. */ event_data_sp = std::make_shared(process_sp, eStateStopped); event_sp = std::make_shared(0, event_data_sp); process_sp->GetThreadList().Clear(); for (int i = 0; i < 6; i++) { if (i == 2) { // Does not want to stop but has valid StopInfo thread_sp = CreateThread(process_sp, false, true); } else if (i == 5) { // Wants to stop and has valid StopInfo thread_sp = CreateThread(process_sp, true, true); thread_sp->SetResumeState(eStateSuspended); } else { // Thread has no StopInfo i.e is not the reason of stop thread_sp = CreateThread(process_sp, false, false); } } ASSERT_TRUE(process_sp->GetThreadList().GetSize() == 6); found_valid_stopinfo = false; should_stop = event_data_sp->ShouldStop(event_sp.get(), found_valid_stopinfo); ASSERT_TRUE(should_stop == false && found_valid_stopinfo == true); }