1 //===-- ThreadTest.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 "lldb/Target/Thread.h" 10 #include "Plugins/Platform/Linux/PlatformLinux.h" 11 #include <thread> 12 #ifdef _WIN32 13 #include "lldb/Host/windows/HostThreadWindows.h" 14 #include "lldb/Host/windows/windows.h" 15 16 #include "Plugins/Platform/Windows/PlatformWindows.h" 17 #include "Plugins/Process/Windows/Common/LocalDebugDelegate.h" 18 #include "Plugins/Process/Windows/Common/ProcessWindows.h" 19 #include "Plugins/Process/Windows/Common/TargetThreadWindows.h" 20 #endif 21 #include "lldb/Core/Debugger.h" 22 #include "lldb/Host/FileSystem.h" 23 #include "lldb/Host/HostInfo.h" 24 #include "lldb/Host/HostThread.h" 25 #include "lldb/Target/Process.h" 26 #include "lldb/Target/StopInfo.h" 27 #include "lldb/Utility/ArchSpec.h" 28 #include "gtest/gtest.h" 29 30 using namespace lldb_private; 31 using namespace lldb_private::repro; 32 using namespace lldb; 33 34 namespace { 35 36 #ifdef _WIN32 37 using SetThreadDescriptionFunctionPtr = HRESULT 38 WINAPI (*)(HANDLE hThread, PCWSTR lpThreadDescription); 39 40 static SetThreadDescriptionFunctionPtr SetThreadName; 41 #endif 42 43 class ThreadTest : public ::testing::Test { 44 public: 45 void SetUp() override { 46 FileSystem::Initialize(); 47 HostInfo::Initialize(); 48 #ifdef _WIN32 49 HMODULE hModule = ::LoadLibraryW(L"Kernel32.dll"); 50 if (hModule) { 51 SetThreadName = reinterpret_cast<SetThreadDescriptionFunctionPtr>( 52 ::GetProcAddress(hModule, "SetThreadDescription")); 53 } 54 PlatformWindows::Initialize(); 55 #endif 56 platform_linux::PlatformLinux::Initialize(); 57 } 58 void TearDown() override { 59 #ifdef _WIN32 60 PlatformWindows::Terminate(); 61 #endif 62 platform_linux::PlatformLinux::Terminate(); 63 HostInfo::Terminate(); 64 FileSystem::Terminate(); 65 } 66 }; 67 68 class DummyProcess : public Process { 69 public: 70 DummyProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp) 71 : Process(target_sp, listener_sp) {} 72 73 bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) override { 74 return true; 75 } 76 Status DoDestroy() override { return {}; } 77 void RefreshStateAfterStop() override {} 78 size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, 79 Status &error) override { 80 return 0; 81 } 82 bool DoUpdateThreadList(ThreadList &old_thread_list, 83 ThreadList &new_thread_list) override { 84 return false; 85 } 86 llvm::StringRef GetPluginName() override { return "Dummy"; } 87 88 ProcessModID &GetModIDNonConstRef() { return m_mod_id; } 89 }; 90 91 class DummyThread : public Thread { 92 public: 93 using Thread::Thread; 94 95 ~DummyThread() override { DestroyThread(); } 96 97 void RefreshStateAfterStop() override {} 98 99 lldb::RegisterContextSP GetRegisterContext() override { return nullptr; } 100 101 lldb::RegisterContextSP 102 CreateRegisterContextForFrame(StackFrame *frame) override { 103 return nullptr; 104 } 105 106 bool CalculateStopInfo() override { return false; } 107 108 bool IsStillAtLastBreakpointHit() override { return true; } 109 }; 110 } // namespace 111 112 TargetSP CreateTarget(DebuggerSP &debugger_sp, ArchSpec &arch) { 113 PlatformSP platform_sp; 114 TargetSP target_sp; 115 debugger_sp->GetTargetList().CreateTarget( 116 *debugger_sp, "", arch, eLoadDependentsNo, platform_sp, target_sp); 117 118 return target_sp; 119 } 120 121 #ifdef _WIN32 122 std::shared_ptr<TargetThreadWindows> 123 CreateWindowsThread(const ProcessWindowsSP &process_sp, std::thread &t) { 124 HostThread host_thread((lldb::thread_t)t.native_handle()); 125 ThreadSP thread_sp = 126 std::make_shared<TargetThreadWindows>(*process_sp.get(), host_thread); 127 return std::static_pointer_cast<TargetThreadWindows>(thread_sp); 128 } 129 130 TEST_F(ThreadTest, GetThreadDescription) { 131 if (!SetThreadName) 132 return; 133 134 ArchSpec arch(HostInfo::GetArchitecture()); 135 Platform::SetHostPlatform(PlatformWindows::CreateInstance(true, &arch)); 136 137 DebuggerSP debugger_sp = Debugger::CreateInstance(); 138 ASSERT_TRUE(debugger_sp); 139 140 TargetSP target_sp = CreateTarget(debugger_sp, arch); 141 ASSERT_TRUE(target_sp); 142 143 ListenerSP listener_sp(Listener::MakeListener("dummy")); 144 auto process_sp = std::static_pointer_cast<ProcessWindows>( 145 ProcessWindows::CreateInstance(target_sp, listener_sp, nullptr, false)); 146 ASSERT_TRUE(process_sp); 147 148 std::thread t([]() {}); 149 auto thread_sp = CreateWindowsThread(process_sp, t); 150 DWORD tid = thread_sp->GetHostThread().GetNativeThread().GetThreadId(); 151 HANDLE hThread = ::OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, tid); 152 ASSERT_TRUE(hThread); 153 154 SetThreadName(hThread, L"thread name"); 155 ::CloseHandle(hThread); 156 ASSERT_STREQ(thread_sp->GetName(), "thread name"); 157 158 t.join(); 159 } 160 #endif 161 162 TEST_F(ThreadTest, SetStopInfo) { 163 ArchSpec arch("powerpc64-pc-linux"); 164 165 Platform::SetHostPlatform( 166 platform_linux::PlatformLinux::CreateInstance(true, &arch)); 167 168 DebuggerSP debugger_sp = Debugger::CreateInstance(); 169 ASSERT_TRUE(debugger_sp); 170 171 TargetSP target_sp = CreateTarget(debugger_sp, arch); 172 ASSERT_TRUE(target_sp); 173 174 ListenerSP listener_sp(Listener::MakeListener("dummy")); 175 ProcessSP process_sp = std::make_shared<DummyProcess>(target_sp, listener_sp); 176 ASSERT_TRUE(process_sp); 177 178 DummyProcess *process = static_cast<DummyProcess *>(process_sp.get()); 179 180 ThreadSP thread_sp = std::make_shared<DummyThread>(*process_sp.get(), 0); 181 ASSERT_TRUE(thread_sp); 182 183 StopInfoSP stopinfo_sp = 184 StopInfo::CreateStopReasonWithBreakpointSiteID(*thread_sp.get(), 0); 185 ASSERT_TRUE(stopinfo_sp->IsValid() == true); 186 187 /* 188 Should make stopinfo valid. 189 */ 190 process->GetModIDNonConstRef().BumpStopID(); 191 ASSERT_TRUE(stopinfo_sp->IsValid() == false); 192 193 thread_sp->SetStopInfo(stopinfo_sp); 194 ASSERT_TRUE(stopinfo_sp->IsValid() == true); 195 } 196 197 TEST_F(ThreadTest, GetPrivateStopInfo) { 198 ArchSpec arch("powerpc64-pc-linux"); 199 200 Platform::SetHostPlatform( 201 platform_linux::PlatformLinux::CreateInstance(true, &arch)); 202 203 DebuggerSP debugger_sp = Debugger::CreateInstance(); 204 ASSERT_TRUE(debugger_sp); 205 206 TargetSP target_sp = CreateTarget(debugger_sp, arch); 207 ASSERT_TRUE(target_sp); 208 209 ListenerSP listener_sp(Listener::MakeListener("dummy")); 210 ProcessSP process_sp = std::make_shared<DummyProcess>(target_sp, listener_sp); 211 ASSERT_TRUE(process_sp); 212 213 DummyProcess *process = static_cast<DummyProcess *>(process_sp.get()); 214 215 ThreadSP thread_sp = std::make_shared<DummyThread>(*process_sp.get(), 0); 216 ASSERT_TRUE(thread_sp); 217 218 StopInfoSP stopinfo_sp = 219 StopInfo::CreateStopReasonWithBreakpointSiteID(*thread_sp.get(), 0); 220 ASSERT_TRUE(stopinfo_sp); 221 222 thread_sp->SetStopInfo(stopinfo_sp); 223 224 /* 225 Should make stopinfo valid if thread is at last breakpoint hit. 226 */ 227 process->GetModIDNonConstRef().BumpStopID(); 228 ASSERT_TRUE(stopinfo_sp->IsValid() == false); 229 StopInfoSP new_stopinfo_sp = thread_sp->GetPrivateStopInfo(); 230 ASSERT_TRUE(new_stopinfo_sp && stopinfo_sp->IsValid() == true); 231 } 232