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:
29   void SetUp() override {
30     llvm::cantFail(Reproducer::Initialize(ReproducerMode::Off, llvm::None));
31     FileSystem::Initialize();
32     HostInfo::Initialize();
33     PlatformMacOSX::Initialize();
34   }
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 
47   bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) override {
48     return true;
49   }
50   Status DoDestroy() override { return {}; }
51   void RefreshStateAfterStop() override {}
52   size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
53                       Status &error) override {
54     return 0;
55   }
56   bool DoUpdateThreadList(ThreadList &old_thread_list,
57                           ThreadList &new_thread_list) override {
58     return false;
59   }
60   llvm::StringRef GetPluginName() override { return "Dummy"; }
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) : StopInfo(thread, value) {}
86 
87   bool ShouldStop(Event *event_ptr) override { return m_should_stop; }
88 
89   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:
97   DummyProcessEventData(ProcessSP &process_sp, StateType state)
98       : ProcessEventData(process_sp, state) {}
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 
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 
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 
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 
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