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