[Lldb-commits] [lldb] [lldb][Windows] Don't synthesise a fake thread name from the executable (PR #199983)

Charles Zablit via lldb-commits lldb-commits at lists.llvm.org
Wed May 27 09:06:46 PDT 2026


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 <c_zablit at apple.com>
Date: Wed, 27 May 2026 13:52:22 +0100
Subject: [PATCH 1/2] [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 <c_zablit at apple.com>
Date: Wed, 27 May 2026 17:06:31 +0100
Subject: [PATCH 2/2] [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



More information about the lldb-commits mailing list