From 95e5839e06fdffd278499257c6e7679bba3d6868 Mon Sep 17 00:00:00 2001 From: oltolm Date: Thu, 21 Dec 2023 13:42:22 +0100 Subject: [PATCH] [lldb] add support for thread names on Windows (#74731) This PR adds support for thread names in lldb on Windows. ``` (lldb) thr list Process 2960 stopped thread #53: tid = 0x03a0, 0x00007ff84582db34 ntdll.dll`NtWaitForMultipleObjects + 20 thread #29: tid = 0x04ec, 0x00007ff845830a14 ntdll.dll`NtWaitForAlertByThreadId + 20, name = 'SPUW.6' thread #89: tid = 0x057c, 0x00007ff845830a14 ntdll.dll`NtWaitForAlertByThreadId + 20, name = 'PPU[0x1000019] physics[main]' thread #3: tid = 0x0648, 0x00007ff843c2cafe combase.dll`InternalDoATClassCreate + 39518 thread #93: tid = 0x0688, 0x00007ff845830a14 ntdll.dll`NtWaitForAlertByThreadId + 20, name = 'PPU[0x100501d] uMovie::StreamingThread' thread #1: tid = 0x087c, 0x00007ff842e7a104 win32u.dll`NtUserMsgWaitForMultipleObjectsEx + 20 thread #96: tid = 0x0890, 0x00007ff845830a14 ntdll.dll`NtWaitForAlertByThreadId + 20, name = 'PPU[0x1002020] HLE Video Decoder' <...> ``` --- .../Windows/Common/TargetThreadWindows.cpp | 37 ++++++++-- .../Windows/Common/TargetThreadWindows.h | 2 + lldb/unittests/Thread/CMakeLists.txt | 2 + lldb/unittests/Thread/ThreadTest.cpp | 71 +++++++++++++++++++ 4 files changed, 106 insertions(+), 6 deletions(-) diff --git a/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp index 37dc8f6d6d14a..ad67e764fe10f 100644 --- a/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp @@ -7,18 +7,14 @@ //===----------------------------------------------------------------------===// #include "lldb/Host/HostInfo.h" -#include "lldb/Host/HostNativeThreadBase.h" -#include "lldb/Host/windows/HostThreadWindows.h" -#include "lldb/Host/windows/windows.h" -#include "lldb/Target/RegisterContext.h" #include "lldb/Target/Unwind.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" -#include "lldb/Utility/State.h" #include "ProcessWindows.h" -#include "ProcessWindowsLog.h" #include "TargetThreadWindows.h" +#include "lldb/Host/windows/HostThreadWindows.h" +#include #if defined(__x86_64__) || defined(_M_AMD64) #include "x64/RegisterContextWindows_x64.h" @@ -33,6 +29,9 @@ using namespace lldb; using namespace lldb_private; +using GetThreadDescriptionFunctionPtr = HRESULT +WINAPI (*)(HANDLE hThread, PWSTR *ppszThreadDescription); + TargetThreadWindows::TargetThreadWindows(ProcessWindows &process, const HostThread &thread) : Thread(process, thread.GetNativeThread().GetThreadId()), @@ -175,3 +174,29 @@ Status TargetThreadWindows::DoResume() { return Status(); } + +const char *TargetThreadWindows::GetName() { + Log *log = GetLog(LLDBLog::Thread); + static GetThreadDescriptionFunctionPtr GetThreadDescription = []() { + HMODULE hModule = ::LoadLibraryW(L"Kernel32.dll"); + return hModule ? reinterpret_cast( + ::GetProcAddress(hModule, "GetThreadDescription")) + : nullptr; + }(); + LLDB_LOGF(log, "GetProcAddress: %p", + reinterpret_cast(GetThreadDescription)); + if (!GetThreadDescription) + return m_name.c_str(); + PWSTR pszThreadName; + if (SUCCEEDED(GetThreadDescription( + m_host_thread.GetNativeThread().GetSystemHandle(), &pszThreadName))) { + LLDB_LOGF(log, "GetThreadDescription: %ls", pszThreadName); + llvm::convertUTF16ToUTF8String( + llvm::ArrayRef(reinterpret_cast(pszThreadName), + wcslen(pszThreadName) * sizeof(wchar_t)), + m_name); + ::LocalFree(pszThreadName); + } + + return m_name.c_str(); +} diff --git a/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.h b/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.h index 2845847738f60..07e1db464ad59 100644 --- a/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.h +++ b/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.h @@ -34,6 +34,7 @@ class TargetThreadWindows : public lldb_private::Thread { lldb::RegisterContextSP CreateRegisterContextForFrame(StackFrame *frame) override; bool CalculateStopInfo() override; + const char *GetName() override; Status DoResume(); @@ -42,6 +43,7 @@ class TargetThreadWindows : public lldb_private::Thread { private: lldb::RegisterContextSP m_thread_reg_ctx_sp; HostThread m_host_thread; + std::string m_name; }; } // namespace lldb_private diff --git a/lldb/unittests/Thread/CMakeLists.txt b/lldb/unittests/Thread/CMakeLists.txt index d6e365adac5dd..f6c8795f349a5 100644 --- a/lldb/unittests/Thread/CMakeLists.txt +++ b/lldb/unittests/Thread/CMakeLists.txt @@ -11,5 +11,7 @@ add_lldb_unittest(ThreadTests lldbInterpreter lldbBreakpoint lldbPluginPlatformLinux + lldbPluginPlatformWindows + lldbPluginProcessWindowsCommon ) diff --git a/lldb/unittests/Thread/ThreadTest.cpp b/lldb/unittests/Thread/ThreadTest.cpp index bd8cdce99f172..4c660e9815c3e 100644 --- a/lldb/unittests/Thread/ThreadTest.cpp +++ b/lldb/unittests/Thread/ThreadTest.cpp @@ -8,9 +8,20 @@ #include "lldb/Target/Thread.h" #include "Plugins/Platform/Linux/PlatformLinux.h" +#include +#ifdef _WIN32 +#include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/windows.h" + +#include "Plugins/Platform/Windows/PlatformWindows.h" +#include "Plugins/Process/Windows/Common/LocalDebugDelegate.h" +#include "Plugins/Process/Windows/Common/ProcessWindows.h" +#include "Plugins/Process/Windows/Common/TargetThreadWindows.h" +#endif #include "lldb/Core/Debugger.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/HostInfo.h" +#include "lldb/Host/HostThread.h" #include "lldb/Target/Process.h" #include "lldb/Target/StopInfo.h" #include "lldb/Utility/ArchSpec.h" @@ -21,14 +32,33 @@ using namespace lldb_private::repro; using namespace lldb; namespace { + +#ifdef _WIN32 +using SetThreadDescriptionFunctionPtr = HRESULT +WINAPI (*)(HANDLE hThread, PCWSTR lpThreadDescription); + +static SetThreadDescriptionFunctionPtr SetThreadName; +#endif + class ThreadTest : public ::testing::Test { public: void SetUp() override { FileSystem::Initialize(); HostInfo::Initialize(); +#ifdef _WIN32 + HMODULE hModule = ::LoadLibraryW(L"Kernel32.dll"); + if (hModule) { + SetThreadName = reinterpret_cast( + ::GetProcAddress(hModule, "SetThreadDescription")); + } + PlatformWindows::Initialize(); +#endif platform_linux::PlatformLinux::Initialize(); } void TearDown() override { +#ifdef _WIN32 + PlatformWindows::Terminate(); +#endif platform_linux::PlatformLinux::Terminate(); HostInfo::Terminate(); FileSystem::Terminate(); @@ -88,6 +118,47 @@ TargetSP CreateTarget(DebuggerSP &debugger_sp, ArchSpec &arch) { return target_sp; } +#ifdef _WIN32 +std::shared_ptr +CreateWindowsThread(const ProcessWindowsSP &process_sp, std::thread &t) { + HostThread host_thread((lldb::thread_t)t.native_handle()); + ThreadSP thread_sp = + std::make_shared(*process_sp.get(), host_thread); + return std::static_pointer_cast(thread_sp); +} + +TEST_F(ThreadTest, GetThreadDescription) { + if (!SetThreadName) + return; + + ArchSpec arch(HostInfo::GetArchitecture()); + Platform::SetHostPlatform(PlatformWindows::CreateInstance(true, &arch)); + + DebuggerSP debugger_sp = Debugger::CreateInstance(); + ASSERT_TRUE(debugger_sp); + + TargetSP target_sp = CreateTarget(debugger_sp, arch); + ASSERT_TRUE(target_sp); + + ListenerSP listener_sp(Listener::MakeListener("dummy")); + auto process_sp = std::static_pointer_cast( + ProcessWindows::CreateInstance(target_sp, listener_sp, nullptr, false)); + ASSERT_TRUE(process_sp); + + std::thread t([]() {}); + auto thread_sp = CreateWindowsThread(process_sp, t); + DWORD tid = thread_sp->GetHostThread().GetNativeThread().GetThreadId(); + HANDLE hThread = ::OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, tid); + ASSERT_TRUE(hThread); + + SetThreadName(hThread, L"thread name"); + ::CloseHandle(hThread); + ASSERT_STREQ(thread_sp->GetName(), "thread name"); + + t.join(); +} +#endif + TEST_F(ThreadTest, SetStopInfo) { ArchSpec arch("powerpc64-pc-linux");