https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/199983
>From a0aed4b7998ad8717e80d417c13dc33149f7f147 Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Wed, 27 May 2026 13:52:22 +0100 Subject: [PATCH 1/3] [lldb][Windows] Don't synthesise a fake thread name from the executable --- .../Process/Windows/Common/NativeThreadWindows.cpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp index 442af86af40d2..598bbdf6c9eed 100644 --- a/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp @@ -99,17 +99,8 @@ Status NativeThreadWindows::DoResume(lldb::StateType resume_state) { } std::string NativeThreadWindows::GetName() { - if (!m_name.empty()) - return m_name; - - // Name is not a property of the Windows thread. Create one with the - // process's. - NativeProcessProtocol &process = GetProcess(); - ProcessInstanceInfo process_info; - if (Host::GetProcessInfo(process.GetID(), process_info)) { - std::string process_name(process_info.GetName()); - m_name = process_name; - } + // Windows threads only have a name when the inferior calls + // SetThreadDescription explicitly. return m_name; } >From 38f0ce3d5bdce2066b0567a78d2757db72460665 Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Wed, 27 May 2026 17:06:31 +0100 Subject: [PATCH 2/3] [lldb-server][Windows] add support for thread name --- lldb/include/lldb/Host/windows/LazyImport.h | 46 +++++++++++++++++++ .../Windows/Common/NativeThreadWindows.cpp | 23 +++++++++- .../Windows/Common/TargetThreadWindows.cpp | 19 +++----- lldb/test/API/windows/thread/Makefile | 3 ++ .../test/API/windows/thread/TestThreadName.py | 38 +++++++++++++++ lldb/test/API/windows/thread/main.c | 16 +++++++ 6 files changed, 130 insertions(+), 15 deletions(-) create mode 100644 lldb/include/lldb/Host/windows/LazyImport.h create mode 100644 lldb/test/API/windows/thread/Makefile create mode 100644 lldb/test/API/windows/thread/TestThreadName.py create mode 100644 lldb/test/API/windows/thread/main.c diff --git a/lldb/include/lldb/Host/windows/LazyImport.h b/lldb/include/lldb/Host/windows/LazyImport.h new file mode 100644 index 0000000000000..9be0fb73da4ca --- /dev/null +++ b/lldb/include/lldb/Host/windows/LazyImport.h @@ -0,0 +1,46 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_HOST_WINDOWS_LAZYIMPORT_H +#define LLDB_HOST_WINDOWS_LAZYIMPORT_H + +#include "lldb/Host/windows/windows.h" + +namespace lldb_private { + +template <typename FnPtr> class LazyImport { +public: + constexpr LazyImport(const wchar_t *dll, const char *symbol) + : m_dll(dll), m_symbol(symbol) {} + + /// Returns the resolved function pointer, or nullptr if the DLL or symbol + /// is unavailable on this system. Resolution happens once. + FnPtr get() const { + static FnPtr resolved = Resolve(m_dll, m_symbol); + return resolved; + } + + explicit operator bool() const { return get() != nullptr; } + FnPtr operator*() const { return get(); } + +private: + static FnPtr Resolve(const wchar_t *dll, const char *symbol) { + HMODULE module = ::LoadLibraryW(dll); + if (!module) + return nullptr; + return reinterpret_cast<FnPtr>( + reinterpret_cast<void *>(::GetProcAddress(module, symbol))); + } + + const wchar_t *m_dll; + const char *m_symbol; +}; + +} // namespace lldb_private + +#endif // LLDB_HOST_WINDOWS_LAZYIMPORT_H \ No newline at end of file diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp index 598bbdf6c9eed..302b1c347b5ca 100644 --- a/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp @@ -11,6 +11,7 @@ #include "lldb/Host/HostThread.h" #include "lldb/Host/windows/HostThreadWindows.h" +#include "lldb/Host/windows/LazyImport.h" #include "lldb/Host/windows/windows.h" #include "lldb/Target/Process.h" #include "lldb/Utility/LLDBLog.h" @@ -18,6 +19,7 @@ #include "lldb/Utility/State.h" #include "lldb/lldb-forward.h" +#include <llvm/Support/ConvertUTF.h> using namespace lldb; using namespace lldb_private; @@ -99,8 +101,25 @@ Status NativeThreadWindows::DoResume(lldb::StateType resume_state) { } std::string NativeThreadWindows::GetName() { - // Windows threads only have a name when the inferior calls - // SetThreadDescription explicitly. + Log *log = GetLog(LLDBLog::Thread); + static LazyImport<HRESULT(WINAPI *)(HANDLE, PWSTR *)> kGetThreadDescription{ + L"Kernel32.dll", "GetThreadDescription"}; + if (!kGetThreadDescription) + return m_name; + auto GetThreadDescription = *kGetThreadDescription; + + PWSTR pszThreadName; + if (SUCCEEDED(GetThreadDescription( + m_host_thread.GetNativeThread().GetSystemHandle(), &pszThreadName))) { + LLDB_LOGF(log, "GetThreadDescription: %ls", pszThreadName); + m_name.clear(); + llvm::convertUTF16ToUTF8String( + llvm::ArrayRef(reinterpret_cast<char *>(pszThreadName), + wcslen(pszThreadName) * sizeof(wchar_t)), + m_name); + ::LocalFree(pszThreadName); + } + return m_name; } diff --git a/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp index bd6db55446cd6..f04b0a7eb217b 100644 --- a/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/TargetThreadWindows.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "lldb/Host/HostInfo.h" +#include "lldb/Host/windows/LazyImport.h" #include "lldb/Target/Unwind.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" @@ -29,9 +30,6 @@ 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()), @@ -177,17 +175,12 @@ Status TargetThreadWindows::DoResume() { const char *TargetThreadWindows::GetName() { Log *log = GetLog(LLDBLog::Thread); - static GetThreadDescriptionFunctionPtr GetThreadDescription = []() { - HMODULE hModule = ::LoadLibraryW(L"Kernel32.dll"); - return hModule - ? reinterpret_cast<GetThreadDescriptionFunctionPtr>( - (void *)::GetProcAddress(hModule, "GetThreadDescription")) - : nullptr; - }(); - LLDB_LOGF(log, "GetProcAddress: %p", - reinterpret_cast<void *>(GetThreadDescription)); - if (!GetThreadDescription) + static LazyImport<HRESULT(WINAPI *)(HANDLE, PWSTR *)> kGetThreadDescription{ + L"Kernel32.dll", "GetThreadDescription"}; + if (!kGetThreadDescription) return m_name.c_str(); + auto GetThreadDescription = *kGetThreadDescription; + PWSTR pszThreadName; if (SUCCEEDED(GetThreadDescription( m_host_thread.GetNativeThread().GetSystemHandle(), &pszThreadName))) { diff --git a/lldb/test/API/windows/thread/Makefile b/lldb/test/API/windows/thread/Makefile new file mode 100644 index 0000000000000..10495940055b6 --- /dev/null +++ b/lldb/test/API/windows/thread/Makefile @@ -0,0 +1,3 @@ +C_SOURCES := main.c + +include Makefile.rules diff --git a/lldb/test/API/windows/thread/TestThreadName.py b/lldb/test/API/windows/thread/TestThreadName.py new file mode 100644 index 0000000000000..b8169ab0bdd33 --- /dev/null +++ b/lldb/test/API/windows/thread/TestThreadName.py @@ -0,0 +1,38 @@ +""" +Test that lldb reports the Windows thread description set via SetThreadDescription. +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestThreadName(TestBase): + @skipUnlessWindows + @skipIfWindows(windows_version=["<", "10.0.14393"]) + def test_with_thread_description(self): + """SBThread.GetName() reflects SetThreadDescription on Windows.""" + self.build() + source = lldb.SBFileSpec("main.c") + + target, process, thread, bkpt = lldbutil.run_to_source_breakpoint( + self, "// break here", source + ) + self.assertEqual( + bkpt.GetNumLocations(), 2, + "expected breakpoints at both '// break here' markers", + ) + + # No thread name yet. + self.assertFalse( + thread.GetName(), + "thread should have no name before SetThreadDescription", + ) + + process.Continue() + self.assertEqual(process.GetState(), lldb.eStateStopped) + + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) + self.assertTrue(thread.IsValid(), "no thread stopped at breakpoint") + self.assertEqual(thread.GetName(), "ThreadName") \ No newline at end of file diff --git a/lldb/test/API/windows/thread/main.c b/lldb/test/API/windows/thread/main.c new file mode 100644 index 0000000000000..e9384c4bcdddf --- /dev/null +++ b/lldb/test/API/windows/thread/main.c @@ -0,0 +1,16 @@ +#include <processthreadsapi.h> +#include <stdio.h> +#include <windows.h> + +int main() { + // break here + HANDLE thread = GetCurrentThread(); + HRESULT hr = SetThreadDescription(thread, L"ThreadName"); + if (FAILED(hr)) { + fprintf(stderr, "SetThreadDescription failed: 0x%08lx\n", hr); + return 1; + } + + printf("Thread name set successfully.\n"); // break here + return 0; +} \ No newline at end of file >From 7cde91b206a125504062bd2052e2dac0475dfdbe Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Wed, 27 May 2026 17:10:53 +0100 Subject: [PATCH 3/3] fixup! [lldb-server][Windows] add support for thread name --- lldb/test/API/windows/thread/TestThreadName.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lldb/test/API/windows/thread/TestThreadName.py b/lldb/test/API/windows/thread/TestThreadName.py index b8169ab0bdd33..590b936191a22 100644 --- a/lldb/test/API/windows/thread/TestThreadName.py +++ b/lldb/test/API/windows/thread/TestThreadName.py @@ -16,11 +16,12 @@ def test_with_thread_description(self): self.build() source = lldb.SBFileSpec("main.c") - target, process, thread, bkpt = lldbutil.run_to_source_breakpoint( + _, process, thread, breakpoint = lldbutil.run_to_source_breakpoint( self, "// break here", source ) self.assertEqual( - bkpt.GetNumLocations(), 2, + breakpoint.GetNumLocations(), + 2, "expected breakpoints at both '// break here' markers", ) @@ -35,4 +36,4 @@ def test_with_thread_description(self): thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint) self.assertTrue(thread.IsValid(), "no thread stopped at breakpoint") - self.assertEqual(thread.GetName(), "ThreadName") \ No newline at end of file + self.assertEqual(thread.GetName(), "ThreadName") _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
