1 //===-- ProcessEventDataTest.cpp ------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "Plugins/Platform/MacOSX/PlatformMacOSX.h" 10 #include "lldb/Core/Debugger.h" 11 #include "lldb/Host/FileSystem.h" 12 #include "lldb/Host/HostInfo.h" 13 #include "lldb/Target/Process.h" 14 #include "lldb/Target/StopInfo.h" 15 #include "lldb/Target/Thread.h" 16 #include "lldb/Utility/ArchSpec.h" 17 #include "lldb/Utility/Event.h" 18 #include "lldb/Utility/Reproducer.h" 19 #include "gtest/gtest.h" 20 21 using namespace lldb_private; 22 using namespace lldb_private::repro; 23 using namespace lldb; 24 25 namespace { 26 class ProcessEventDataTest : public ::testing::Test { 27 public: 28 void SetUp() override { 29 llvm::cantFail(Reproducer::Initialize(ReproducerMode::Off, llvm::None)); 30 FileSystem::Initialize(); 31 HostInfo::Initialize(); 32 PlatformMacOSX::Initialize(); 33 } 34 void TearDown() override { 35 PlatformMacOSX::Terminate(); 36 HostInfo::Terminate(); 37 FileSystem::Terminate(); 38 Reproducer::Terminate(); 39 } 40 }; 41 42 class DummyProcess : public Process { 43 public: 44 using Process::Process; 45 46 virtual bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) { 47 return true; 48 } 49 virtual Status DoDestroy() { return {}; } 50 virtual void RefreshStateAfterStop() {} 51 virtual size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, 52 Status &error) { 53 return 0; 54 } 55 virtual bool UpdateThreadList(ThreadList &old_thread_list, 56 ThreadList &new_thread_list) { 57 return false; 58 } 59 virtual ConstString GetPluginName() { return ConstString("Dummy"); } 60 virtual uint32_t GetPluginVersion() { return 0; } 61 62 ProcessModID &GetModIDNonConstRef() { return m_mod_id; } 63 }; 64 65 class DummyThread : public Thread { 66 public: 67 using Thread::Thread; 68 69 ~DummyThread() override { DestroyThread(); } 70 71 void RefreshStateAfterStop() override {} 72 73 lldb::RegisterContextSP GetRegisterContext() override { return nullptr; } 74 75 lldb::RegisterContextSP 76 CreateRegisterContextForFrame(StackFrame *frame) override { 77 return nullptr; 78 } 79 80 bool CalculateStopInfo() override { return false; } 81 }; 82 83 class DummyStopInfo : public StopInfo { 84 public: 85 DummyStopInfo(Thread &thread, uint64_t value) 86 : StopInfo(thread, value), m_should_stop(true), 87 m_stop_reason(eStopReasonBreakpoint) {} 88 89 bool ShouldStop(Event *event_ptr) override { return m_should_stop; } 90 91 StopReason GetStopReason() const override { return m_stop_reason; } 92 93 bool m_should_stop; 94 StopReason m_stop_reason; 95 }; 96 97 class DummyProcessEventData : public Process::ProcessEventData { 98 public: 99 DummyProcessEventData(ProcessSP &process_sp, StateType state) 100 : ProcessEventData(process_sp, state), m_should_stop_hit_count(0) {} 101 bool ShouldStop(Event *event_ptr, bool &found_valid_stopinfo) override { 102 m_should_stop_hit_count++; 103 return false; 104 } 105 106 int m_should_stop_hit_count; 107 }; 108 } // namespace 109 110 typedef std::shared_ptr<Process::ProcessEventData> ProcessEventDataSP; 111 typedef std::shared_ptr<Event> EventSP; 112 113 TargetSP CreateTarget(DebuggerSP &debugger_sp, ArchSpec &arch) { 114 Status error; 115 PlatformSP platform_sp; 116 TargetSP target_sp; 117 error = debugger_sp->GetTargetList().CreateTarget( 118 *debugger_sp, "", arch, eLoadDependentsNo, platform_sp, target_sp); 119 120 if (target_sp) { 121 debugger_sp->GetTargetList().SetSelectedTarget(target_sp.get()); 122 } 123 124 return target_sp; 125 } 126 127 ThreadSP CreateThread(ProcessSP &process_sp, bool should_stop, 128 bool has_valid_stopinfo) { 129 ThreadSP thread_sp = std::make_shared<DummyThread>(*process_sp.get(), 0); 130 if (thread_sp == nullptr) { 131 return nullptr; 132 } 133 134 if (has_valid_stopinfo) { 135 StopInfoSP stopinfo_sp = 136 std::make_shared<DummyStopInfo>(*thread_sp.get(), 0); 137 static_cast<DummyStopInfo *>(stopinfo_sp.get())->m_should_stop = 138 should_stop; 139 if (stopinfo_sp == nullptr) { 140 return nullptr; 141 } 142 143 thread_sp->SetStopInfo(stopinfo_sp); 144 } 145 146 process_sp->GetThreadList().AddThread(thread_sp); 147 148 return thread_sp; 149 } 150 151 TEST_F(ProcessEventDataTest, DoOnRemoval) { 152 ArchSpec arch("x86_64-apple-macosx-"); 153 154 Platform::SetHostPlatform(PlatformMacOSX::CreateInstance(true, &arch)); 155 156 DebuggerSP debugger_sp = Debugger::CreateInstance(); 157 ASSERT_TRUE(debugger_sp); 158 159 TargetSP target_sp = CreateTarget(debugger_sp, arch); 160 ASSERT_TRUE(target_sp); 161 162 ListenerSP listener_sp(Listener::MakeListener("dummy")); 163 ProcessSP process_sp = std::make_shared<DummyProcess>(target_sp, listener_sp); 164 ASSERT_TRUE(process_sp); 165 166 /* 167 Should hit ShouldStop if state is eStateStopped 168 */ 169 ProcessEventDataSP event_data_sp = 170 std::make_shared<DummyProcessEventData>(process_sp, eStateStopped); 171 EventSP event_sp = std::make_shared<Event>(0, event_data_sp); 172 event_data_sp->SetUpdateStateOnRemoval(event_sp.get()); 173 event_data_sp->DoOnRemoval(event_sp.get()); 174 bool result = static_cast<DummyProcessEventData *>(event_data_sp.get()) 175 ->m_should_stop_hit_count == 1; 176 ASSERT_TRUE(result); 177 178 /* 179 Should not hit ShouldStop if state is not eStateStopped 180 */ 181 event_data_sp = 182 std::make_shared<DummyProcessEventData>(process_sp, eStateStepping); 183 event_sp = std::make_shared<Event>(0, event_data_sp); 184 event_data_sp->SetUpdateStateOnRemoval(event_sp.get()); 185 event_data_sp->DoOnRemoval(event_sp.get()); 186 result = static_cast<DummyProcessEventData *>(event_data_sp.get()) 187 ->m_should_stop_hit_count == 0; 188 ASSERT_TRUE(result); 189 } 190 191 TEST_F(ProcessEventDataTest, ShouldStop) { 192 ArchSpec arch("x86_64-apple-macosx-"); 193 194 Platform::SetHostPlatform(PlatformMacOSX::CreateInstance(true, &arch)); 195 196 DebuggerSP debugger_sp = Debugger::CreateInstance(); 197 ASSERT_TRUE(debugger_sp); 198 199 TargetSP target_sp = CreateTarget(debugger_sp, arch); 200 ASSERT_TRUE(target_sp); 201 202 ListenerSP listener_sp(Listener::MakeListener("dummy")); 203 ProcessSP process_sp = std::make_shared<DummyProcess>(target_sp, listener_sp); 204 ASSERT_TRUE(process_sp); 205 206 // wants to stop and has valid StopInfo 207 ThreadSP thread_sp = CreateThread(process_sp, true, true); 208 209 ProcessEventDataSP event_data_sp = 210 std::make_shared<Process::ProcessEventData>(process_sp, eStateStopped); 211 EventSP event_sp = std::make_shared<Event>(0, event_data_sp); 212 /* 213 Should stop if thread has valid StopInfo and not suspended 214 */ 215 bool found_valid_stopinfo = false; 216 bool should_stop = 217 event_data_sp->ShouldStop(event_sp.get(), found_valid_stopinfo); 218 ASSERT_TRUE(should_stop == true && found_valid_stopinfo == true); 219 220 /* 221 Should not stop if thread has valid StopInfo but was suspended 222 */ 223 found_valid_stopinfo = false; 224 thread_sp->SetResumeState(eStateSuspended); 225 should_stop = event_data_sp->ShouldStop(event_sp.get(), found_valid_stopinfo); 226 ASSERT_TRUE(should_stop == false && found_valid_stopinfo == false); 227 228 /* 229 Should not stop, thread-reason of stop does not want to stop in its 230 callback and suspended thread who wants to (from previous stop) 231 must be ignored. 232 */ 233 event_data_sp = 234 std::make_shared<Process::ProcessEventData>(process_sp, eStateStopped); 235 event_sp = std::make_shared<Event>(0, event_data_sp); 236 process_sp->GetThreadList().Clear(); 237 238 for (int i = 0; i < 6; i++) { 239 if (i == 2) { 240 // Does not want to stop but has valid StopInfo 241 thread_sp = CreateThread(process_sp, false, true); 242 } else if (i == 5) { 243 // Wants to stop and has valid StopInfo 244 thread_sp = CreateThread(process_sp, true, true); 245 thread_sp->SetResumeState(eStateSuspended); 246 } else { 247 // Thread has no StopInfo i.e is not the reason of stop 248 thread_sp = CreateThread(process_sp, false, false); 249 } 250 } 251 ASSERT_TRUE(process_sp->GetThreadList().GetSize() == 6); 252 253 found_valid_stopinfo = false; 254 should_stop = event_data_sp->ShouldStop(event_sp.get(), found_valid_stopinfo); 255 ASSERT_TRUE(should_stop == false && found_valid_stopinfo == true); 256 } 257