* [RFC PATCH 1/8] gdb, infrun: do not discard a step-completed pending waitstatus
2026-10-01 10:28 [RFC PATCH 0/8] breakpoint handling Markus Metzger
@ 2026-10-01 10:28 ` Markus Metzger
2026-10-01 10:28 ` [RFC PATCH 2/8] gdb, infrun: track last waitstatus per thread Markus Metzger
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Markus Metzger @ 2026-10-01 10:28 UTC (permalink / raw)
To: gdb-patches; +Cc: Guinevere Larsen
Consider a scenario with two breakpoints on adjacent instructions:
bp1 at 0xf00
bp2 at 0xf01
as well as two threads in all-stop-on-top-of-non-stop mode.
Assume that threads A hits bp1 and we report the breakpoint hit to the
user. When the user continues, we start a step-over for thread A at 0xf00.
Assume that thread B now hits bp1 and we report the breakpoint hit to the
user. We stop all threads to report the event. Meanwhile, the step-over
of thread A completes, so we save the pending waitstatus (stop_pc=0xf01,
currently_stepping=1) of thread A.
When the user continues, clear_proceed_status_thread() discards the
pending step completed waitstatus of thread A, and proceed() starts
another step-over for thread A at 0xf01.
We skip bp2 for thread A.
Remove the code in clear_proceed_status_thread() that discards a step
completed waitstatus and let it get handled normally. This involves
calling finish_step_over() to clean up displaced stepping state.
This introduces an additional step completed stop event for thread A that
results in two user-visible changes in behavior when switching to thread A
and resuming that thread:
- stepi/nexti completes without making progress
- adding a breakpoint at the current location before resuming hits that
breakpoint
Fixes gdb/34380.
Tested-By: Guinevere Larsen <guinevere@redhat.com>
---
gdb/infrun.c | 25 ++-----
gdb/testsuite/gdb.threads/adjacent-bp.c | 48 +++++++++++++
gdb/testsuite/gdb.threads/adjacent-bp.exp | 86 +++++++++++++++++++++++
3 files changed, 139 insertions(+), 20 deletions(-)
create mode 100644 gdb/testsuite/gdb.threads/adjacent-bp.c
create mode 100644 gdb/testsuite/gdb.threads/adjacent-bp.exp
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 47966cf72db..a70d88cd4a7 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -3100,28 +3100,13 @@ clear_proceed_status_thread (struct thread_info *tp)
infrun_debug_printf ("%s", tp->ptid.to_string ().c_str ());
gdb_assert (tp->internal_state () != THREAD_INT_RUNNING);
- /* If we're starting a new sequence, then the previous finished
- single-step is no longer relevant. */
if (tp->has_pending_waitstatus ())
{
- if (tp->stop_reason () == TARGET_STOPPED_BY_SINGLE_STEP)
- {
- infrun_debug_printf ("pending event of %s was a finished step. "
- "Discarding.",
- tp->ptid.to_string ().c_str ());
-
- tp->set_internal_state (THREAD_INT_STOPPED);
- tp->clear_pending_waitstatus ();
- tp->set_stop_reason (TARGET_STOPPED_BY_NO_REASON);
- }
- else
- {
- infrun_debug_printf
- ("thread %s has pending wait status %s (currently_stepping=%d).",
- tp->ptid.to_string ().c_str (),
- tp->pending_waitstatus ().to_string ().c_str (),
- tp->control.currently_stepping);
- }
+ infrun_debug_printf
+ ("thread %s has pending wait status %s (currently_stepping=%d).",
+ tp->ptid.to_string ().c_str (),
+ tp->pending_waitstatus ().to_string ().c_str (),
+ tp->control.currently_stepping);
}
/* If this signal should not be seen by program, give it zero.
diff --git a/gdb/testsuite/gdb.threads/adjacent-bp.c b/gdb/testsuite/gdb.threads/adjacent-bp.c
new file mode 100644
index 00000000000..e72e674f52a
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/adjacent-bp.c
@@ -0,0 +1,48 @@
+/* Copyright 2026 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <pthread.h>
+#include <unistd.h>
+
+static pthread_barrier_t barrier;
+
+static void *
+test (void *arg)
+{
+ pthread_barrier_wait (&barrier);
+ int a = 0; /* break here. */
+ int b = 0;
+ int c = 0;
+ return arg;
+}
+
+int
+main ()
+{
+ pthread_t th;
+
+ alarm (500);
+
+ pthread_barrier_init (&barrier, NULL, 2);
+ pthread_create (&th, NULL, test, NULL);
+ test (NULL);
+
+ pthread_join (th, NULL);
+ pthread_barrier_destroy (&barrier);
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.threads/adjacent-bp.exp b/gdb/testsuite/gdb.threads/adjacent-bp.exp
new file mode 100644
index 00000000000..9e7782c01c2
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/adjacent-bp.exp
@@ -0,0 +1,86 @@
+# Copyright 2026 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Test that GDB does not skip a breakpoint when a step-over completes
+# while another event leads to a stop.
+
+standard_testfile
+
+if {[prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \
+ {debug pthreads}]} {
+ return
+}
+
+if {![runto_main]} {
+ return
+}
+
+# Find a sequence of adjacent instructions.
+set bp_line [gdb_get_line_number "break here"]
+set pcs {}
+gdb_test_multiple "info line $bp_line" "" {
+ -re -wrap "starts at address ($hex).*" {
+ pass $gdb_test_name
+
+ set line "\\s+($hex) \[^\r\n\]+"
+ gdb_test_multiple "x/3i $expect_out(1,string)" "disassemble" {
+ -re -wrap "$line\r\n$line\r\n$line.*" {
+ pass $gdb_test_name
+
+ lappend pcs $expect_out(1,string)
+ lappend pcs $expect_out(2,string)
+ lappend pcs $expect_out(3,string)
+ }
+ -re -wrap "" {
+ fail $gdb_test_name
+ }
+ }
+ }
+ -re -wrap "" {
+ fail $gdb_test_name
+ }
+}
+
+# Set breakpoints on adjacent instructions.
+foreach pc $pcs {
+ gdb_breakpoint "\*$pc"
+}
+
+# Continue from breakpoint to breakpoint.
+set hits [dict create]
+set iter 0
+gdb_test_multiple "continue" "" {
+ -re -wrap "hit Breakpoint.*" {
+ dict incr hits [get_hexadecimal_valueof "\$pc" invalid "stop $iter"]
+ incr iter
+ send_gdb "continue\n"
+ exp_continue
+ }
+ -re -wrap "$inferior_exited_re normally.*" {
+ pass "$gdb_test_name"
+ }
+}
+
+# We expect all breakpoints to be hit by both threads.
+foreach pc $pcs {
+ if {[dict exists $hits $pc]} {
+ gdb_assert {[dict get $hits $pc] eq 2} "breakpoint at $pc"
+ dict unset hits $pc
+ } else {
+ fail "breakpoint at $pc"
+ }
+}
+# And no unrelated stops.
+gdb_assert {[dict size $hits] eq 0} "no extra stops"
--
2.53.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 9+ messages in thread* [RFC PATCH 2/8] gdb, infrun: track last waitstatus per thread
2026-10-01 10:28 [RFC PATCH 0/8] breakpoint handling Markus Metzger
2026-10-01 10:28 ` [RFC PATCH 1/8] gdb, infrun: do not discard a step-completed pending waitstatus Markus Metzger
@ 2026-10-01 10:28 ` Markus Metzger
2026-10-01 10:28 ` [RFC PATCH 3/8] gdb, infrun: simplify print_stop_location() Markus Metzger
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Markus Metzger @ 2026-10-01 10:28 UTC (permalink / raw)
To: gdb-patches
---
gdb/gdbthread.h | 3 +++
gdb/infrun.c | 11 +++++++++--
2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 51e9ae1b3f0..fdb6b749387 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -225,6 +225,9 @@ struct thread_control_state
/* Nonzero if stopped due to a step command. */
int stop_step = 0;
+ /* The last waitstatus for this thread. */
+ target_waitstatus stop_ws {};
+
/* Chain containing status of breakpoint(s) the thread stopped
at. */
bpstat *stop_bpstat = nullptr;
diff --git a/gdb/infrun.c b/gdb/infrun.c
index a70d88cd4a7..b347459f335 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -3134,6 +3134,7 @@ clear_proceed_status_thread (struct thread_info *tp)
/* Discard any remaining commands or status from previous stop. */
bpstat_clear (&tp->control.stop_bpstat);
+ tp->control.stop_ws.set_ignore ();
}
/* Notify the current interpreter and observers that the target is about to
@@ -4169,7 +4170,8 @@ do_target_wait_1 (inferior *inf, ptid_t ptid,
}
tp->set_stop_reason (TARGET_STOPPED_BY_NO_REASON);
- *status = tp->pending_waitstatus ();
+ tp->control.stop_ws = tp->pending_waitstatus ();
+ *status = tp->control.stop_ws;
tp->set_internal_state (THREAD_INT_STOPPED);
tp->clear_pending_waitstatus ();
@@ -4187,7 +4189,12 @@ do_target_wait_1 (inferior *inf, ptid_t ptid,
if (!target_can_async_p ())
options &= ~TARGET_WNOHANG;
- return target_wait (ptid, status, options);
+ ptid_t event_ptid = target_wait (ptid, status, options);
+ tp = inf->find_thread (event_ptid);
+ if (tp != nullptr)
+ tp->control.stop_ws = *status;
+
+ return event_ptid;
}
/* Wrapper for target_wait that first checks whether threads have
--
2.53.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 9+ messages in thread* [RFC PATCH 3/8] gdb, infrun: simplify print_stop_location()
2026-10-01 10:28 [RFC PATCH 0/8] breakpoint handling Markus Metzger
2026-10-01 10:28 ` [RFC PATCH 1/8] gdb, infrun: do not discard a step-completed pending waitstatus Markus Metzger
2026-10-01 10:28 ` [RFC PATCH 2/8] gdb, infrun: track last waitstatus per thread Markus Metzger
@ 2026-10-01 10:28 ` Markus Metzger
2026-10-01 10:28 ` [RFC PATCH 4/8] gdb, thread: simplify print_selected_thread_frame() Markus Metzger
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Markus Metzger @ 2026-10-01 10:28 UTC (permalink / raw)
To: gdb-patches
print_stop_location() expects its argument waitstatus to match
inferior_thread()'s control.stop_bpstat.
Instead, use inferior_thread()'s last waitstatus that the parent patch
stored in control.stop_ws.
This removes the need for a waitstatus parameter. Change it to take the
thread to print the stop location for.
---
gdb/infrun.c | 15 +++++----------
1 file changed, 5 insertions(+), 10 deletions(-)
diff --git a/gdb/infrun.c b/gdb/infrun.c
index b347459f335..8ba5937fa73 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -9441,14 +9441,14 @@ print_no_history_reason (struct ui_out *uiout)
based on the event(s) that just occurred. */
static void
-print_stop_location (const target_waitstatus &ws)
+print_stop_location (thread_info *tp)
{
int bpstat_ret;
enum print_what source_flag;
int do_frame_printing = 1;
- struct thread_info *tp = inferior_thread ();
- bpstat_ret = bpstat_print (tp->control.stop_bpstat, ws.kind ());
+ bpstat_ret = bpstat_print (tp->control.stop_bpstat,
+ tp->control.stop_ws.kind ());
/* Function bpstat_print selects the frame to print. Typically, that is the
stop frame, in other words get_current_frame (). But bpstat_print may
select a different frame, see for instance ada_catchpoint::print_it. */
@@ -9516,22 +9516,17 @@ print_stop_location (const target_waitstatus &ws)
static void
do_print_stop_event (struct ui_out *uiout, bool displays)
{
- struct target_waitstatus last;
- struct thread_info *tp;
-
- get_last_target_status (nullptr, nullptr, &last);
-
+ struct thread_info *tp = inferior_thread ();
{
scoped_restore save_uiout = make_scoped_restore (¤t_uiout, uiout);
- print_stop_location (last);
+ print_stop_location (tp);
/* Display the auto-display expressions. */
if (displays)
do_displays ();
}
- tp = inferior_thread ();
if (tp->thread_fsm () != nullptr
&& tp->thread_fsm ()->finished_p ())
{
--
2.53.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 9+ messages in thread* [RFC PATCH 4/8] gdb, thread: simplify print_selected_thread_frame()
2026-10-01 10:28 [RFC PATCH 0/8] breakpoint handling Markus Metzger
` (2 preceding siblings ...)
2026-10-01 10:28 ` [RFC PATCH 3/8] gdb, infrun: simplify print_stop_location() Markus Metzger
@ 2026-10-01 10:28 ` Markus Metzger
2026-10-01 10:28 ` [RFC PATCH 5/8] gdb, interp: repeat stop event when switching threads Markus Metzger
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Markus Metzger @ 2026-10-01 10:28 UTC (permalink / raw)
To: gdb-patches
Handle selections one by one in separate blocks.
This fixes an issue with forgetting a newline at the end of the output if
USER_SELECTED_THREAD is specified, USER_SELECTED_FRAME is not specified,
and the thread is not running.
Since the function is never called with just USER_SELECTED_THREAD, this
does not occur in practice. This will change with a subsequent commit.
---
gdb/thread.c | 17 +++++++----------
1 file changed, 7 insertions(+), 10 deletions(-)
diff --git a/gdb/thread.c b/gdb/thread.c
index 0f86f6efe38..bbcd3e61cd1 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -2141,19 +2141,16 @@ print_selected_thread_frame (struct ui_out *uiout,
uiout->text (target_pid_to_str (inferior_ptid));
uiout->text (")]");
}
- }
- if (tp->state () == THREAD_RUNNING)
- {
- if (selection & USER_SELECTED_THREAD)
- uiout->text ("(running)\n");
+ if (tp->state () == THREAD_RUNNING)
+ uiout->text ("(running)");
+
+ uiout->text ("\n");
}
- else if (selection & USER_SELECTED_FRAME)
- {
- if (selection & USER_SELECTED_THREAD)
- uiout->text ("\n");
- if (has_stack_frames ())
+ if (selection & USER_SELECTED_FRAME)
+ {
+ if ((tp->state () != THREAD_RUNNING) && has_stack_frames ())
print_stack_frame_to_uiout (uiout, get_selected_frame (),
1, SRC_AND_LOC, 1);
}
--
2.53.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 9+ messages in thread* [RFC PATCH 5/8] gdb, interp: repeat stop event when switching threads
2026-10-01 10:28 [RFC PATCH 0/8] breakpoint handling Markus Metzger
` (3 preceding siblings ...)
2026-10-01 10:28 ` [RFC PATCH 4/8] gdb, thread: simplify print_selected_thread_frame() Markus Metzger
@ 2026-10-01 10:28 ` Markus Metzger
2026-10-01 10:28 ` [RFC PATCH 6/8] gdb, infrun: handle stop event when selecting a thread Markus Metzger
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Markus Metzger @ 2026-10-01 10:28 UTC (permalink / raw)
To: gdb-patches
When switching threads back and forth and inspecting the state of various
threads, one needs to note down the original stop reason or search for it
in the console log when switching back to the eventing thread.
GDB knows the stop reason, so why not print it again when switching back
to the thread?
We currently only print breakpoint stops. When the stop reason is
printed, the location is printed as part of the stop reason without the
frame level, whereas otherwise, the frame level would be printed.
The MI interpreter printed
thread-selected,id=<global thread number>
when either USER_SELECTED_THREAD or USER_SELECTED_FRAME is specified. So
it would print this thread information when only USER_SELECTED_FRAME is
specified, which seems wrong to me. This behavior is covered by
gdb.mi/user-selected-context-sync.exp, so I preserve it.
It also printed the stack frame without checking USER_SELECTED_FRAME if
the thread isn't running, which also seems wrong.
---
gdb/cli/cli-interp.c | 29 ++++++++++++++++++++++++++---
gdb/mi/mi-interp.c | 42 ++++++++++++++++++++++++++++++++----------
2 files changed, 58 insertions(+), 13 deletions(-)
diff --git a/gdb/cli/cli-interp.c b/gdb/cli/cli-interp.c
index 55892d2ce28..20b2ee906c3 100644
--- a/gdb/cli/cli-interp.c
+++ b/gdb/cli/cli-interp.c
@@ -166,9 +166,32 @@ cli_interp_base::on_user_selected_context_changed (user_selected_what selection)
if (selection & USER_SELECTED_INFERIOR)
print_selected_inferior (this->interp_ui_out ());
- if (tp != nullptr
- && ((selection & (USER_SELECTED_THREAD | USER_SELECTED_FRAME))))
- print_selected_thread_frame (this->interp_ui_out (), selection);
+ if (tp == nullptr)
+ return;
+
+ bool print_stop = false;
+ if (selection & USER_SELECTED_THREAD)
+ {
+ print_selected_thread_frame (this->interp_ui_out (),
+ USER_SELECTED_THREAD);
+
+ if (selection & USER_SELECTED_FRAME)
+ {
+ /* Repeat any previous breakpoint stop notification. */
+ print_stop = (bpstat_what (tp->control.stop_bpstat).main_action
+ == BPSTAT_WHAT_STOP_NOISY);
+ if (print_stop)
+ print_stop_event (this->interp_ui_out ());
+ }
+ }
+
+ if (selection & USER_SELECTED_FRAME)
+ {
+ /* Printing the stop notification already prints the frame. */
+ if (!print_stop)
+ print_selected_thread_frame (this->interp_ui_out (),
+ USER_SELECTED_FRAME);
+ }
}
/* pre_command_loop implementation. */
diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c
index 207b1fc97ff..ccf66f7b58a 100644
--- a/gdb/mi/mi-interp.c
+++ b/gdb/mi/mi-interp.c
@@ -850,19 +850,41 @@ mi_interp::on_user_selected_context_changed (user_selected_what selection)
if (selection & USER_SELECTED_INFERIOR)
print_selected_inferior (this->cli_uiout);
- if (tp != NULL
- && (selection & (USER_SELECTED_THREAD | USER_SELECTED_FRAME)))
+ if (tp != NULL)
{
- print_selected_thread_frame (this->cli_uiout, selection);
-
- gdb_printf (this->event_channel, "thread-selected,id=\"%d\"",
- tp->global_num);
+ if (selection & USER_SELECTED_THREAD)
+ {
+ print_selected_thread_frame (this->cli_uiout,
+ USER_SELECTED_THREAD);
+
+ if (selection & USER_SELECTED_FRAME)
+ {
+ /* Repeat any previous breakpoint stop notification. */
+ if (bpstat_what (tp->control.stop_bpstat).main_action
+ == BPSTAT_WHAT_STOP_NOISY)
+ print_stop_event (this->cli_uiout);
+ else
+ print_selected_thread_frame (this->cli_uiout,
+ USER_SELECTED_FRAME);
+ }
+
+ gdb_printf (this->event_channel, "thread-selected,id=\"%d\"",
+ tp->global_num);
+ }
- if (tp->state () != THREAD_RUNNING)
+ if (selection & USER_SELECTED_FRAME)
{
- if (has_stack_frames ())
- print_stack_frame_to_uiout (mi_uiout, get_selected_frame (),
- 1, SRC_AND_LOC, 1);
+ if (!(selection & USER_SELECTED_THREAD))
+ {
+ print_selected_thread_frame (this->cli_uiout,
+ USER_SELECTED_FRAME);
+
+ gdb_printf (this->event_channel, "thread-selected,id=\"%d\"",
+ tp->global_num);
+ }
+
+ print_selected_thread_frame (mi_uiout,
+ USER_SELECTED_FRAME);
}
}
--
2.53.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 9+ messages in thread* [RFC PATCH 6/8] gdb, infrun: handle stop event when selecting a thread
2026-10-01 10:28 [RFC PATCH 0/8] breakpoint handling Markus Metzger
` (4 preceding siblings ...)
2026-10-01 10:28 ` [RFC PATCH 5/8] gdb, interp: repeat stop event when switching threads Markus Metzger
@ 2026-10-01 10:28 ` Markus Metzger
2026-10-01 10:28 ` [RFC PATCH 7/8] gdb, infrun: inline init_thread_stepping_state() Markus Metzger
2026-10-01 10:28 ` [RFC PATCH 8/8] gdb, infrun: only step over breakpoints that were hit Markus Metzger
7 siblings, 0 replies; 9+ messages in thread
From: Markus Metzger @ 2026-10-01 10:28 UTC (permalink / raw)
To: gdb-patches
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(+)
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. */
--
2.53.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 9+ messages in thread* [RFC PATCH 7/8] gdb, infrun: inline init_thread_stepping_state()
2026-10-01 10:28 [RFC PATCH 0/8] breakpoint handling Markus Metzger
` (5 preceding siblings ...)
2026-10-01 10:28 ` [RFC PATCH 6/8] gdb, infrun: handle stop event when selecting a thread Markus Metzger
@ 2026-10-01 10:28 ` Markus Metzger
2026-10-01 10:28 ` [RFC PATCH 8/8] gdb, infrun: only step over breakpoints that were hit Markus Metzger
7 siblings, 0 replies; 9+ messages in thread
From: Markus Metzger @ 2026-10-01 10:28 UTC (permalink / raw)
To: gdb-patches
There remains only a single call to init_thread_stepping_state() in
proceed(). Inline it.
---
gdb/infrun.c | 18 ++++--------------
1 file changed, 4 insertions(+), 14 deletions(-)
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 19eac5c5c8e..44c41be1baa 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -459,8 +459,6 @@ set_normal_stop_state_just_attached ()
set_last_target_status (nullptr, minus_one_ptid, status);
}
-void init_thread_stepping_state (struct thread_info *tss);
-
static const char follow_fork_mode_child[] = "child";
static const char follow_fork_mode_parent[] = "parent";
@@ -3663,7 +3661,10 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
pc = regcache_read_pc_protected (regcache);
/* Fill in with reasonable starting values. */
- init_thread_stepping_state (cur_thr);
+ cur_thr->stepped_breakpoint = 0;
+ cur_thr->stepping_over_breakpoint = 0;
+ cur_thr->stepping_over_watchpoint = 0;
+ cur_thr->step_after_step_resume_breakpoint = 0;
gdb_assert (!thread_is_in_step_over_chain (cur_thr));
@@ -4942,17 +4943,6 @@ set_step_info (thread_info *tp, const frame_info_ptr &frame,
tp->control.step_stack_frame_id.to_string ().c_str ());
}
-/* Clear context switchable stepping state. */
-
-void
-init_thread_stepping_state (struct thread_info *tss)
-{
- tss->stepped_breakpoint = 0;
- tss->stepping_over_breakpoint = 0;
- tss->stepping_over_watchpoint = 0;
- tss->step_after_step_resume_breakpoint = 0;
-}
-
/* See infrun.h. */
void
--
2.53.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 9+ messages in thread* [RFC PATCH 8/8] gdb, infrun: only step over breakpoints that were hit
2026-10-01 10:28 [RFC PATCH 0/8] breakpoint handling Markus Metzger
` (6 preceding siblings ...)
2026-10-01 10:28 ` [RFC PATCH 7/8] gdb, infrun: inline init_thread_stepping_state() Markus Metzger
@ 2026-10-01 10:28 ` Markus Metzger
7 siblings, 0 replies; 9+ messages in thread
From: Markus Metzger @ 2026-10-01 10:28 UTC (permalink / raw)
To: gdb-patches
When a thread hits a breakpoint, we note that this thread will need to
step over the breakpoint when it is next resumed, e.g.
case BPSTAT_WHAT_STOP_NOISY:
infrun_debug_printf ("BPSTAT_WHAT_STOP_NOISY");
stop_print_frame = true;
/* Assume the thread stopped for a breakpoint. We'll still check
whether a/the breakpoint is there when the thread is next
resumed. */
ecs->event_thread->stepping_over_breakpoint = 1;
stop_waiting (ecs);
return;
in process_event_stop_test().
In addition, there is a special check whether there is a breakpoint at the
current location in proceed():
if (cur_thr->stop_pc_p ()
&& pc == cur_thr->stop_pc ()
&& breakpoint_here_p (aspace, pc) == ordinary_breakpoint_here
&& execution_direction != EXEC_REVERSE)
/* There is a breakpoint at the address we will resume at,
step one instruction before inserting breakpoints so that
we do not stop right away (and report a second hit at this
breakpoint).
Note, we don't do this in reverse, because we won't
actually be executing the breakpoint insn anyway.
We'll be (un-)executing the previous instruction. */
cur_thr->stepping_over_breakpoint = 1;
This does not consider whether we actually hit the breakpoint. So, when
setting $pc to a location with a breakpoint and continuing, we will skip
it, but when jumping to that same location, we will hit the breakpoint.
Prior to the parent patch, when stepping onto the breakpoint location in
some other thread that was resumed while stepping the current thread, and
then switching to that thread, we had skipped that breakpoint.
There even is some special code in other locations to avoid the special
handling in proceed():
/* Start the target running. Do not use -1 continuation as it would skip
breakpoint right at the entry point. */
proceed (regcache_read_pc (get_thread_regcache (inferior_thread ())),
GDB_SIGNAL_0);
Removing this special case in proceed() makes GDB behave more consistently.
It does introduce potentially undesired behavior when using the break
command without arguments, though. E.g.
(gdb) break
(gdb) continue
will now hit the newly inserted breakpoint that used to be skipped.
On the other hand, any other thread at the same location would have hit
the breakpoint, even though the current thread skipped it. E.g.
(gdb) break
(gdb) thread <some other thread>
(gdb) continue
would have hit the breakpoint (assuming no scheduler-locking).
---
gdb/infcmd.c | 10 ++++++----
gdb/infrun.c | 20 +-------------------
2 files changed, 7 insertions(+), 23 deletions(-)
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index d7544168362..043f5e890bf 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -654,10 +654,8 @@ run_command_1 (const char *args, int from_tty, enum run_how run_how)
prior to a proceed call. */
clear_proceed_status (0);
- /* Start the target running. Do not use -1 continuation as it would skip
- breakpoint right at the entry point. */
- proceed (regcache_read_pc (get_thread_regcache (inferior_thread ())),
- GDB_SIGNAL_0);
+ /* Start the target running. */
+ proceed ((CORE_ADDR) -1, GDB_SIGNAL_0);
/* Since there was no error, there's no need to finish the thread
states here. */
@@ -1312,6 +1310,10 @@ jump_command (const char *arg, int from_tty)
gdb_printf (".\n");
}
+ /* Any pending step-over is cancelled by the jump. */
+ thread_info *tp = inferior_thread ();
+ tp->stepping_over_breakpoint = 0;
+
clear_proceed_status (0);
proceed (addr, GDB_SIGNAL_0);
}
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 44c41be1baa..2450394e3d1 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -3634,7 +3634,6 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
INFRUN_SCOPED_DEBUG_ENTER_EXIT;
struct gdbarch *gdbarch;
- CORE_ADDR pc;
/* If we're stopped at a fork/vfork, switch to either the parent or child
thread as defined by the "set follow-fork-mode" command, or, if both
@@ -3658,11 +3657,9 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
regcache *regcache = get_thread_regcache (cur_thr);
gdbarch = regcache->arch ();
- pc = regcache_read_pc_protected (regcache);
/* Fill in with reasonable starting values. */
cur_thr->stepped_breakpoint = 0;
- cur_thr->stepping_over_breakpoint = 0;
cur_thr->stepping_over_watchpoint = 0;
cur_thr->step_after_step_resume_breakpoint = 0;
@@ -3677,22 +3674,7 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
if (addr == (CORE_ADDR) -1)
{
- const address_space *aspace = cur_thr->inf->aspace.get ();
-
- if (cur_thr->stop_pc_p ()
- && pc == cur_thr->stop_pc ()
- && breakpoint_here_p (aspace, pc) == ordinary_breakpoint_here
- && execution_direction != EXEC_REVERSE)
- /* There is a breakpoint at the address we will resume at,
- step one instruction before inserting breakpoints so that
- we do not stop right away (and report a second hit at this
- breakpoint).
-
- Note, we don't do this in reverse, because we won't
- actually be executing the breakpoint insn anyway.
- We'll be (un-)executing the previous instruction. */
- cur_thr->stepping_over_breakpoint = 1;
- else if (gdbarch_single_step_through_delay_p (gdbarch)
+ if (gdbarch_single_step_through_delay_p (gdbarch)
&& gdbarch_single_step_through_delay (gdbarch,
get_current_frame ()))
/* We stepped onto an instruction that needs to be stepped
--
2.53.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 9+ messages in thread