Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 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" |
Jonas Devlieghere | dd2054d | 2020-12-03 00:26:11 | [diff] [blame] | 10 | #include "Plugins/Platform/MacOSX/PlatformRemoteMacOSX.h" |
Med Ismail Bennani | f32c6b2 | 2024-03-04 19:42:04 | [diff] [blame] | 11 | #include "TestingSupport/TestUtilities.h" |
Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 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" |
Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 20 | #include "gtest/gtest.h" |
| 21 | |
| 22 | using namespace lldb_private; |
Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 23 | using namespace lldb; |
| 24 | |
| 25 | namespace { |
| 26 | class ProcessEventDataTest : public ::testing::Test { |
| 27 | public: |
| 28 | void SetUp() override { |
Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 29 | FileSystem::Initialize(); |
| 30 | HostInfo::Initialize(); |
| 31 | PlatformMacOSX::Initialize(); |
Med Ismail Bennani | f32c6b2 | 2024-03-04 19:42:04 | [diff] [blame] | 32 | std::call_once(TestUtilities::g_debugger_initialize_flag, |
| 33 | []() { Debugger::Initialize(nullptr); }); |
Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 34 | } |
| 35 | void TearDown() override { |
| 36 | PlatformMacOSX::Terminate(); |
| 37 | HostInfo::Terminate(); |
| 38 | FileSystem::Terminate(); |
Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 39 | } |
| 40 | }; |
| 41 | |
| 42 | class DummyProcess : public Process { |
| 43 | public: |
Michał Górny | 9b031d5 | 2022-08-05 17:39:42 | [diff] [blame] | 44 | DummyProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp) |
| 45 | : Process(target_sp, listener_sp) {} |
Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 46 | |
David Blaikie | 6846686 | 2021-01-25 23:03:38 | [diff] [blame] | 47 | bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) override { |
Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 48 | return true; |
| 49 | } |
David Blaikie | 6846686 | 2021-01-25 23:03:38 | [diff] [blame] | 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 { |
Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 54 | return 0; |
| 55 | } |
Walter Erquinigo | 4bb6244 | 2021-01-22 18:22:26 | [diff] [blame] | 56 | bool DoUpdateThreadList(ThreadList &old_thread_list, |
| 57 | ThreadList &new_thread_list) override { |
Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 58 | return false; |
| 59 | } |
Pavel Labath | a3939e1 | 2021-10-15 11:07:39 | [diff] [blame] | 60 | llvm::StringRef GetPluginName() override { return "Dummy"; } |
Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 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: |
Shafik Yaghmour | 28c878a | 2022-03-14 20:32:03 | [diff] [blame] | 85 | DummyStopInfo(Thread &thread, uint64_t value) : StopInfo(thread, value) {} |
Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 86 | |
| 87 | bool ShouldStop(Event *event_ptr) override { return m_should_stop; } |
| 88 | |
| 89 | StopReason GetStopReason() const override { return m_stop_reason; } |
| 90 | |
Shafik Yaghmour | 28c878a | 2022-03-14 20:32:03 | [diff] [blame] | 91 | bool m_should_stop = true; |
| 92 | StopReason m_stop_reason = eStopReasonBreakpoint; |
Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 93 | }; |
| 94 | |
| 95 | class DummyProcessEventData : public Process::ProcessEventData { |
| 96 | public: |
| 97 | DummyProcessEventData(ProcessSP &process_sp, StateType state) |
Shafik Yaghmour | 28c878a | 2022-03-14 20:32:03 | [diff] [blame] | 98 | : ProcessEventData(process_sp, state) {} |
Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 99 | bool ShouldStop(Event *event_ptr, bool &found_valid_stopinfo) override { |
| 100 | m_should_stop_hit_count++; |
| 101 | return false; |
| 102 | } |
| 103 | |
Shafik Yaghmour | 28c878a | 2022-03-14 20:32:03 | [diff] [blame] | 104 | int m_should_stop_hit_count = 0; |
Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 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) { |
Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 112 | PlatformSP platform_sp; |
| 113 | TargetSP target_sp; |
Tatyana Krasnukha | 2634ec6 | 2020-12-11 08:09:39 | [diff] [blame] | 114 | debugger_sp->GetTargetList().CreateTarget( |
Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 115 | *debugger_sp, "", arch, eLoadDependentsNo, platform_sp, target_sp); |
| 116 | |
Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 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 | |
Jim Ingham | a7816c8 | 2024-07-15 23:00:58 | [diff] [blame] | 144 | // Disable this test till I figure out why changing how events are sent |
| 145 | // to Secondary Listeners (44d9692e6a657ec46e98e4912ac56417da67cfee) |
| 146 | // caused this test to fail. It is testing responses to events that are |
| 147 | // not delivered in the way Process events are meant to be delivered, it |
| 148 | // bypasses the private event queue, and I'm not sure is testing real |
| 149 | // behaviors. |
| 150 | #if 0 |
Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 151 | TEST_F(ProcessEventDataTest, DoOnRemoval) { |
| 152 | ArchSpec arch("x86_64-apple-macosx-"); |
| 153 | |
Med Ismail Bennani | d667840 | 2023-01-13 16:51:03 | [diff] [blame] | 154 | Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch)); |
Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 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 | } |
Jim Ingham | a7816c8 | 2024-07-15 23:00:58 | [diff] [blame] | 190 | #endif |
Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 191 | |
| 192 | TEST_F(ProcessEventDataTest, ShouldStop) { |
| 193 | ArchSpec arch("x86_64-apple-macosx-"); |
| 194 | |
Med Ismail Bennani | d667840 | 2023-01-13 16:51:03 | [diff] [blame] | 195 | Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch)); |
Ilya Bukonkin | 3b43f00 | 2020-06-11 21:59:19 | [diff] [blame] | 196 | |
| 197 | DebuggerSP debugger_sp = Debugger::CreateInstance(); |
| 198 | ASSERT_TRUE(debugger_sp); |
| 199 | |
| 200 | TargetSP target_sp = CreateTarget(debugger_sp, arch); |
| 201 | ASSERT_TRUE(target_sp); |
| 202 | |
| 203 | ListenerSP listener_sp(Listener::MakeListener("dummy")); |
| 204 | ProcessSP process_sp = std::make_shared<DummyProcess>(target_sp, listener_sp); |
| 205 | ASSERT_TRUE(process_sp); |
| 206 | |
| 207 | // wants to stop and has valid StopInfo |
| 208 | ThreadSP thread_sp = CreateThread(process_sp, true, true); |
| 209 | |
| 210 | ProcessEventDataSP event_data_sp = |
| 211 | std::make_shared<Process::ProcessEventData>(process_sp, eStateStopped); |
| 212 | EventSP event_sp = std::make_shared<Event>(0, event_data_sp); |
| 213 | /* |
| 214 | Should stop if thread has valid StopInfo and not suspended |
| 215 | */ |
| 216 | bool found_valid_stopinfo = false; |
| 217 | bool should_stop = |
| 218 | event_data_sp->ShouldStop(event_sp.get(), found_valid_stopinfo); |
| 219 | ASSERT_TRUE(should_stop == true && found_valid_stopinfo == true); |
| 220 | |
| 221 | /* |
| 222 | Should not stop if thread has valid StopInfo but was suspended |
| 223 | */ |
| 224 | found_valid_stopinfo = false; |
| 225 | thread_sp->SetResumeState(eStateSuspended); |
| 226 | should_stop = event_data_sp->ShouldStop(event_sp.get(), found_valid_stopinfo); |
| 227 | ASSERT_TRUE(should_stop == false && found_valid_stopinfo == false); |
| 228 | |
| 229 | /* |
| 230 | Should not stop, thread-reason of stop does not want to stop in its |
| 231 | callback and suspended thread who wants to (from previous stop) |
| 232 | must be ignored. |
| 233 | */ |
| 234 | event_data_sp = |
| 235 | std::make_shared<Process::ProcessEventData>(process_sp, eStateStopped); |
| 236 | event_sp = std::make_shared<Event>(0, event_data_sp); |
| 237 | process_sp->GetThreadList().Clear(); |
| 238 | |
| 239 | for (int i = 0; i < 6; i++) { |
| 240 | if (i == 2) { |
| 241 | // Does not want to stop but has valid StopInfo |
| 242 | thread_sp = CreateThread(process_sp, false, true); |
| 243 | } else if (i == 5) { |
| 244 | // Wants to stop and has valid StopInfo |
| 245 | thread_sp = CreateThread(process_sp, true, true); |
| 246 | thread_sp->SetResumeState(eStateSuspended); |
| 247 | } else { |
| 248 | // Thread has no StopInfo i.e is not the reason of stop |
| 249 | thread_sp = CreateThread(process_sp, false, false); |
| 250 | } |
| 251 | } |
| 252 | ASSERT_TRUE(process_sp->GetThreadList().GetSize() == 6); |
| 253 | |
| 254 | found_valid_stopinfo = false; |
| 255 | should_stop = event_data_sp->ShouldStop(event_sp.get(), found_valid_stopinfo); |
| 256 | ASSERT_TRUE(should_stop == false && found_valid_stopinfo == true); |
| 257 | } |