[Lldb-commits] [lldb] [lldb-server] Handle jThreadExtendedInfo (PR #227472)

via lldb-commits lldb-commits at lists.llvm.org
Thu Oct 1 10:56:00 PDT 2026


https://github.com/Nerixyz updated https://github.com/llvm/llvm-project/pull/227472

>From 2631319d78ebda25546cf7e792ddb8ad32815953 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Tue, 29 Sep 2026 22:59:04 +0200
Subject: [PATCH] [lldb-server] Handle jThreadExtendedInfo

---
 .../lldb/Host/common/NativeThreadProtocol.h   |  3 ++
 .../lldb/Utility/StringExtractorGDBRemote.h   |  1 +
 .../Windows/Common/NativeThreadWindows.cpp    |  4 ++
 .../Windows/Common/NativeThreadWindows.h      |  2 +
 .../GDBRemoteCommunicationServerLLGS.cpp      | 49 +++++++++++++++++++
 .../GDBRemoteCommunicationServerLLGS.h        |  2 +
 .../Utility/StringExtractorGDBRemote.cpp      |  2 +
 .../API/windows/thread-extended-info/Makefile |  3 ++
 .../TestThreadExtendedInfo.py                 | 42 ++++++++++++++++
 .../API/windows/thread-extended-info/main.c   |  3 ++
 10 files changed, 111 insertions(+)
 create mode 100644 lldb/test/API/windows/thread-extended-info/Makefile
 create mode 100644 lldb/test/API/windows/thread-extended-info/TestThreadExtendedInfo.py
 create mode 100644 lldb/test/API/windows/thread-extended-info/main.c

diff --git a/lldb/include/lldb/Host/common/NativeThreadProtocol.h b/lldb/include/lldb/Host/common/NativeThreadProtocol.h
index 1bd16cfa26c1e..cb50794249747 100644
--- a/lldb/include/lldb/Host/common/NativeThreadProtocol.h
+++ b/lldb/include/lldb/Host/common/NativeThreadProtocol.h
@@ -13,6 +13,7 @@
 #include <string>
 
 #include "lldb/Host/Debug.h"
+#include "lldb/Utility/StructuredData.h"
 #include "lldb/Utility/UnimplementedError.h"
 #include "lldb/lldb-private-forward.h"
 #include "lldb/lldb-types.h"
@@ -57,6 +58,8 @@ class NativeThreadProtocol {
     return llvm::make_error<UnimplementedError>();
   }
 
+  virtual StructuredData::ObjectSP GetExtendedInfo() const { return nullptr; }
+
 protected:
   void ClearStopInfo() {
     m_stop_info = ThreadStopInfo();
diff --git a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
index fec3c09f225d7..b70372db4b610 100644
--- a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
+++ b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
@@ -178,6 +178,7 @@ class StringExtractorGDBRemote : public StringExtractor {
     eServerPacketType_jAcceleratorPluginInitialize,
     eServerPacketType_jAcceleratorPluginBreakpointHit,
     eServerPacketType_jAcceleratorPluginGetDynamicLoaderLibraryInfo,
+    eServerPacketType_jThreadExtendedInfo,
 
     eServerPacketType_qMemTags, // read memory tags
     eServerPacketType_QMemTags, // write memory tags
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp
index 0842968f25d60..b6363616cf31a 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp
@@ -194,3 +194,7 @@ Status NativeThreadWindows::SetHardwareBreakpoint(lldb::addr_t addr,
 Status NativeThreadWindows::RemoveHardwareBreakpoint(lldb::addr_t addr) {
   return Status::FromErrorString("unimplemented.");
 }
+
+StructuredData::ObjectSP NativeThreadWindows::GetExtendedInfo() const {
+  return m_host_thread.GetNativeThread().GetExtendedInfo();
+}
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.h b/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.h
index 392a5b9a2a7d4..b867489a2fd28 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.h
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.h
@@ -48,6 +48,8 @@ class NativeThreadWindows : public NativeThreadProtocol {
 
   Status RemoveHardwareBreakpoint(lldb::addr_t addr) override;
 
+  StructuredData::ObjectSP GetExtendedInfo() const override;
+
   void SetStopReason(ThreadStopInfo stop_info, std::string description);
 
   const HostThread &GetHostThread() { return m_host_thread; }
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index 516f88aaf6076..af757691359d6 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -228,6 +228,9 @@ void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
   RegisterMemberFunctionHandler(
       StringExtractorGDBRemote::eServerPacketType_jMultiBreakpoint,
       &GDBRemoteCommunicationServerLLGS::Handle_jMultiBreakpoint);
+  RegisterMemberFunctionHandler(
+      StringExtractorGDBRemote::eServerPacketType_jThreadExtendedInfo,
+      &GDBRemoteCommunicationServerLLGS::Handle_jThreadExtendedInfo);
   RegisterMemberFunctionHandler(
       StringExtractorGDBRemote::eServerPacketType_jAcceleratorPluginInitialize,
       &GDBRemoteCommunicationServerLLGS::Handle_jAcceleratorPluginInitialize);
@@ -4732,3 +4735,49 @@ GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::
   return SendErrorResponse(
       Status::FromErrorString("unknown accelerator plugin name"));
 }
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jThreadExtendedInfo(
+    StringExtractorGDBRemote &packet) {
+  llvm::StringRef packet_str = packet.GetStringRef();
+  if (!packet_str.consume_front("jThreadExtendedInfo:"))
+    return SendIllFormedResponse(packet,
+                                 "Invalid jThreadExtendedInfo packet prefix");
+  // Empty packet is sent to check if we support jThreadExtendedInfo.
+  if (packet_str.empty())
+    return SendOKResponse();
+
+  llvm::Expected<llvm::json::Value> parsed = llvm::json::parse(packet_str);
+  if (!parsed) {
+    llvm::consumeError(parsed.takeError());
+    return SendIllFormedResponse(
+        packet, "jThreadExtendedInfo did not contain valid JSON");
+  }
+  llvm::json::Object *request_dict = parsed->getAsObject();
+  if (!request_dict)
+    return SendIllFormedResponse(
+        packet, "jThreadExtendedInfo did not contain a JSON dictionary");
+
+  std::optional<int64_t> thread_id = request_dict->getInteger("thread");
+  if (!thread_id)
+    return SendIllFormedResponse(packet, "jThreadExtendedInfo did not contain "
+                                         "a valid 'breakpoint_requests' field");
+
+  if (!m_current_process)
+    return SendIllFormedResponse(packet, "no current process");
+
+  NativeThreadProtocol *thread = m_current_process->GetThreadByID(*thread_id);
+  if (!thread)
+    return SendIllFormedResponse(packet, "no thread with specified ID");
+
+  StructuredData::ObjectSP ext_info = thread->GetExtendedInfo();
+
+  StreamString stream;
+  if (ext_info)
+    ext_info->Dump(stream, false);
+
+  StringRef response_str = stream.GetString();
+  StreamGDBRemote response;
+  response.PutEscapedBytes(response_str.data(), response_str.size());
+  return SendPacketNoLock(response.GetString());
+}
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
index 873f24d10a627..ac24c69dee0fa 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -313,6 +313,8 @@ class GDBRemoteCommunicationServerLLGS
   PacketResult Handle_jAcceleratorPluginGetDynamicLoaderLibraryInfo(
       StringExtractorGDBRemote &packet);
 
+  PacketResult Handle_jThreadExtendedInfo(StringExtractorGDBRemote &packet);
+
   void SetCurrentThreadID(lldb::tid_t tid);
 
   lldb::tid_t GetCurrentThreadID() const;
diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp b/lldb/source/Utility/StringExtractorGDBRemote.cpp
index a015e7c3c5c8b..ed15926d621bf 100644
--- a/lldb/source/Utility/StringExtractorGDBRemote.cpp
+++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp
@@ -342,6 +342,8 @@ StringExtractorGDBRemote::GetServerPacketType() const {
       return eServerPacketType_jAcceleratorPluginBreakpointHit;
     if (PACKET_STARTS_WITH("jAcceleratorPluginGetDynamicLoaderLibraryInfo:"))
       return eServerPacketType_jAcceleratorPluginGetDynamicLoaderLibraryInfo;
+    if (PACKET_STARTS_WITH("jThreadExtendedInfo:"))
+      return eServerPacketType_jThreadExtendedInfo;
     break;
 
   case 'v':
diff --git a/lldb/test/API/windows/thread-extended-info/Makefile b/lldb/test/API/windows/thread-extended-info/Makefile
new file mode 100644
index 0000000000000..10495940055b6
--- /dev/null
+++ b/lldb/test/API/windows/thread-extended-info/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/windows/thread-extended-info/TestThreadExtendedInfo.py b/lldb/test/API/windows/thread-extended-info/TestThreadExtendedInfo.py
new file mode 100644
index 0000000000000..d530979d1e2c2
--- /dev/null
+++ b/lldb/test/API/windows/thread-extended-info/TestThreadExtendedInfo.py
@@ -0,0 +1,42 @@
+"""
+Test that the extended info of a thread contains the TEB address.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+ at requireWindows
+class TestThreadExtendedInfo(TestBase):
+    def tearDown(self):
+        self.runCmd("settings clear thread-format")
+        return super().tearDown()
+
+    @no_debug_info_test
+    def test(self):
+        self.build()
+
+        target, process, thread, _ = lldbutil.run_to_source_breakpoint(
+            self, "// break here", lldb.SBFileSpec("main.c")
+        )
+
+        err = lldb.SBError()
+        fmt = lldb.SBFormat("${thread.id%%zu};${thread.info.teb_address%%zu}", err)
+        self.assertSuccess(err)
+        stream = lldb.SBStream()
+        self.assertSuccess(thread.GetDescriptionWithFormat(fmt, stream))
+        data = (stream.GetData() or "").split(";")
+        self.assertEqual(len(data), 2)
+        tid = int(data[0])
+        teb_addr = int(data[1])
+        self.assertNotEqual(tid, 0)
+        self.assertNotEqual(teb_addr, 0)
+
+        # Check that the address we got is correct by reading the thread ID out
+        # of the block.
+        tid_off = 0x48 if target.GetAddressByteSize() == 8 else 0x24
+        tid_from_teb = process.ReadUnsignedFromMemory(teb_addr + tid_off, 4, err)
+        self.assertSuccess(err)
+        self.assertEqual(tid, tid_from_teb)
diff --git a/lldb/test/API/windows/thread-extended-info/main.c b/lldb/test/API/windows/thread-extended-info/main.c
new file mode 100644
index 0000000000000..ba45ee316cd42
--- /dev/null
+++ b/lldb/test/API/windows/thread-extended-info/main.c
@@ -0,0 +1,3 @@
+int main() {
+  return 0; // break here
+}



More information about the lldb-commits mailing list