[RFC,6/8] gdb, infrun: handle stop event when selecting a thread
Checks
Commit Message
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(+)
@@ -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
{
@@ -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 */
@@ -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. */