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