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
22using namespace lldb_private;
23using namespace lldb_private::repro;
24using namespace lldb;
25
26namespace {
27class ProcessEventDataTest : public ::testing::Test {
28public:
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
43class DummyProcess : public Process {
44public:
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
66class DummyThread : public Thread {
67public:
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
84class DummyStopInfo : public StopInfo {
85public:
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
96class DummyProcessEventData : public Process::ProcessEventData {
97public:
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
109typedef std::shared_ptr<Process::ProcessEventData> ProcessEventDataSP;
110typedef std::shared_ptr<Event> EventSP;
111
112TargetSP 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
121ThreadSP 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
145TEST_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
185TEST_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

source code of lldb/unittests/Process/ProcessEventDataTest.cpp