[RFC,6/8] gdb, infrun: handle stop event when selecting a thread

Message ID 20261001102847.576880-7-markus.t.metzger@intel.com
State New
Headers
Series breakpoint handling |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-arm warning Skipped because it is an RFC
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 warning Skipped because it is an RFC

Commit Message

Metzger, Markus T Oct. 1, 2026, 10:28 a.m. UTC
  When debugging a multi-threaded program in all-stop mode, several threads
may have reported an event at the same time.  GDB picks one, switches to
it, and presents it to the user.  When continuing the process, the next
event is picked, GDB switches to the eventing thread, and presents it to
the user.  And so on.

For breakpoint hits that are based on a thread being at the location of a
breakpoint, whether the target actually reported a breakpoint hit or not,
this may be a bit confusing if we switched to a thread that is obviously
at a breakoint location, but the event is still pending.

Check for pending stop events when selecting a thread.

Previous patches already repeat a previous stop notification when
switching threads; they also handle this case.
---
 gdb/infrun.c | 24 ++++++++++++++++++++++++
 gdb/infrun.h |  4 ++++
 gdb/thread.c | 10 ++++++++++
 3 files changed, 38 insertions(+)
  

Patch

diff --git a/gdb/infrun.c b/gdb/infrun.c
index 8ba5937fa73..19eac5c5c8e 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -10624,6 +10624,30 @@  infrun_async_inferior_event_handler (gdb_client_data data)
   inferior_event_handler (INF_REG_EVENT);
 }
 
+void handle_pending_stop (thread_info *tp)
+{
+  gdb_assert (tp->has_pending_waitstatus ());
+
+  target_waitstatus ws = tp->pending_waitstatus ();
+  gdb_assert (ws.kind () == TARGET_WAITKIND_STOPPED);
+
+  /* We need breakpoints inserted for the wait.  */
+  SCOPE_EXIT { maybe_remove_breakpoints (); };
+  insert_breakpoints ();
+
+  ptid_t ptid = do_target_wait_1 (tp->inf, tp->ptid, &ws, TARGET_WNOHANG);
+  if (ptid != tp->ptid)
+    return;
+  if (ws.kind () != TARGET_WAITKIND_STOPPED)
+    return;
+
+  execution_control_state ecs { tp };
+  ecs.target = tp->inf->process_target ();
+  ecs.ws = ws;
+
+  handle_inferior_event (&ecs);
+}
+
 #if GDB_SELF_TEST
 namespace selftests
 {
diff --git a/gdb/infrun.h b/gdb/infrun.h
index 7b2b882dc9a..b4d9461449b 100644
--- a/gdb/infrun.h
+++ b/gdb/infrun.h
@@ -438,4 +438,8 @@  extern void reinstall_readline_callback_handler_cleanup ();
    target_attach_no_wait targets.  */
 extern void set_normal_stop_state_just_attached ();
 
+/* We selected TP with a pending stopped waitstatus.  Handle it so we can
+   show it to the user.  */
+extern void handle_pending_stop (thread_info *tp);
+
 #endif /* GDB_INFRUN_H */
diff --git a/gdb/thread.c b/gdb/thread.c
index bbcd3e61cd1..aa3f8ec783f 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -2116,6 +2116,16 @@  thread_select (const char *tidstr, thread_info *tp)
   /* Since the current thread may have changed, see if there is any
      exited thread we can now delete.  */
   delete_exited_threads (nullptr);
+
+  /* Process any pending stop event that was fetched from the target when
+     that thread was resumed but we decided to present another thread's
+     event to the user.
+
+     Now that we're selecting that thread, we'd want to show the event
+     right away instead of waiting until the thread is resumed again.  */
+  if (tp->has_pending_waitstatus ()
+      && (tp->pending_waitstatus ().kind () == TARGET_WAITKIND_STOPPED))
+    handle_pending_stop (tp);
 }
 
 /* Print thread and frame switch command response.  */