https://github.com/charles-zablit created 
https://github.com/llvm/llvm-project/pull/203525

The client kept the previously-selected thread across stops, ignoring the 
primary tid from the server in `T<sig>thread:<tid>`.
On Windows, lldb-server halts the inferior by injecting a thread called 
`DbgUiRemoteBreakin` whose only job is to execute an int 3. After an interrupt, 
the process has `N+1` threads and the new one is what caused the stop. The 
server reports that thread as primary, but the client ignored it and stayed on 
whichever thread was selected before. In `TestExpressionInSyscall` that thread 
is main, paused just past `NtDelayExecution`'s syscall with a garbage value in 
the RSI register. Evaluating an expression there crashes the JIT trampoline.

This patch makes `ProcessGDBRemote::RefreshStateAfterStop` record the primary 
tid from the most recent T-packet's `thread:<tid>` and select it after applying 
per thread stop infos.

Fixes `TestBreakpointSetRestart` and `TestExpressionInSyscall` with 
LLDB_USE_LLDB_SERVER=1.

>From ea720471e242838d0c8150596eaab3bf45a6c07b Mon Sep 17 00:00:00 2001
From: Charles Zablit <[email protected]>
Date: Fri, 12 Jun 2026 14:06:40 +0100
Subject: [PATCH] [lldb][Windows] Honor server's primary stop thread on
 gdb-remote stops

---
 .../Plugins/Process/gdb-remote/ProcessGDBRemote.cpp       | 8 ++++++++
 lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h | 3 +++
 2 files changed, 11 insertions(+)

diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp 
b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 2fc6dbb546f79..754666665ae07 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -2621,6 +2621,8 @@ StateType 
ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) {
 
     SetAddressableBitMasks(addressable_bits);
 
+    m_last_stop_primary_tid = tid;
+
     ThreadSP thread_sp = SetThreadStopInfo(
         tid, expedited_register_map, signo, thread_name, reason, description,
         exc_type, exc_data, thread_dispatch_qaddr, queue_vars_valid,
@@ -2669,7 +2671,13 @@ void ProcessGDBRemote::RefreshStateAfterStop() {
   if (m_initial_tid != LLDB_INVALID_THREAD_ID) {
     m_thread_list.SetSelectedThreadByID(m_initial_tid);
     m_initial_tid = LLDB_INVALID_THREAD_ID;
+  } else if (m_last_stop_primary_tid != LLDB_INVALID_THREAD_ID) {
+    if (ThreadSP primary_thread_sp =
+            m_thread_list.FindThreadByProtocolID(m_last_stop_primary_tid,
+                                                 /*can_update=*/false))
+      m_thread_list.SetSelectedThreadByID(primary_thread_sp->GetID());
   }
+  m_last_stop_primary_tid = LLDB_INVALID_THREAD_ID;
 
   // Let all threads recover from stopping and do any clean up based on the
   // previous thread state (if any).
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h 
b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index 0a3386082c388..7a030cd966a18 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -322,6 +322,9 @@ class ProcessGDBRemote : public Process,
   lldb::CommandObjectSP m_command_sp;
   int64_t m_breakpoint_pc_offset;
   lldb::tid_t m_initial_tid; // The initial thread ID, given by stub on attach
+  lldb::tid_t m_last_stop_primary_tid =
+      LLDB_INVALID_THREAD_ID; // Thread ID extracted from the most recent
+                              // T-packet's "thread:<tid>" key.
   bool m_use_g_packet_for_reading;
 
   bool m_allow_flash_writes;

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to