This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
user breakpoints on top of step-resume breakpoints
- From: Pedro Alves <pedro at codesourcery dot com>
- To: gdb-patches at sourceware dot org
- Date: Thu, 26 May 2011 16:01:35 +0100
- Subject: user breakpoints on top of step-resume breakpoints
step-resume breakpoints currently have the highest priority
over all breakpoints, even user breakpoints. This is
causing trouble for reverse execution.
In e.g., reverse-next, we set a step-resume breakpoint
at the function's entry, and once that's reached, we
do an extra step. Trouble is, if there's a user breakpoint
that should cause a stop, set at the same address as
the step-resume breakpoint ends up, it gets ignored,
because step-resume breakpoints always have higher priority.
See a new test pasted at the end, along with a patch.
(gdb) b *callee
Breakpoint 2 at 0x400574: file ../../../src/gdb/testsuite/gdb.reverse/step-reverse.c, line 25.
(gdb) reverse-next
infrun: clear_proceed_status_thread (process 4071)
infrun: proceed (addr=0xffffffffffffffff, signal=144, step=1)
infrun: resume (step=1, signal=0), trap_expected=0, current thread [process 4071] at 0x4005ea
infrun: wait_for_inferior ()
infrun: target_wait (-1, status) =
infrun: 4071 [process 4071],
infrun: status->kind = stopped, signal = SIGTRAP
infrun: infwait_normal_state
infrun: TARGET_WAITKIND_STOPPED
infrun: stop_pc = 0x40058d
infrun: stepped into subroutine
infrun: inserting step-resume breakpoint at 0x400574
infrun: resume (step=0, signal=0), trap_expected=0, current thread [process 4071] at 0x40058d
infrun: prepare_to_wait
infrun: target_wait (-1, status) =
infrun: 4071 [process 4071],
infrun: status->kind = stopped, signal = SIGTRAP
infrun: infwait_normal_state
infrun: TARGET_WAITKIND_STOPPED
infrun: stop_pc = 0x400574
infrun: BPSTAT_WHAT_STEP_RESUME
infrun: resume (step=1, signal=0), trap_expected=1, current thread [process 4071] at 0x400574
infrun: prepare_to_wait
infrun: target_wait (-1, status) =
infrun: 4071 [process 4071],
infrun: status->kind = stopped, signal = SIGTRAP
infrun: infwait_normal_state
infrun: TARGET_WAITKIND_STOPPED
infrun: stop_pc = 0x4005e5
infrun: keep going
infrun: resume (step=1, signal=0), trap_expected=0, current thread [process 4071] at 0x4005e5
infrun: prepare_to_wait
infrun: target_wait (-1, status) =
infrun: 4071 [process 4071],
infrun: status->kind = stopped, signal = SIGTRAP
infrun: infwait_normal_state
infrun: TARGET_WAITKIND_STOPPED
infrun: stop_pc = 0x4005e0
infrun: stepping inside range [0x4005e0-0x4005ea]
infrun: stop_stepping
55 callee(); /* NEXT OVER THIS CALL */
(gdb)
Notice that breakpoint 2 didn't trigger, although
it is set at the same address as the step-resume
breakpoint. The program should have stopped at
the "calleed" entry point (breakpoint 2), rather
than at the caller, but we ignored the breakpoint,
and issued a step anyway.
The reason this works in forward execution, is that
after hitting the step resume breakpoint, we'll either
detect we've stepped to a different line, and stop,
or we keep going, and do another step with the user
breakpoint in place, which ends up re-hitting that same
breakpoint, and eventually causing a stop. In both these
cases the user breakpoint ends up in the bpstat chain, so
we do end up reporting the hit to the CLI.
(gdb) n
infrun: clear_proceed_status_thread (Thread 0x7ffff7028700 (LWP 697))
infrun: clear_proceed_status_thread (Thread 0x7ffff7829700 (LWP 696))
infrun: clear_proceed_status_thread (Thread 0x7ffff7fce720 (LWP 693))
infrun: proceed (addr=0xffffffffffffffff, signal=144, step=1)
infrun: resume (step=1, signal=0), trap_expected=0, current thread [Thread 0x7ffff7028700 (LWP 697)] at 0x400676
infrun: wait_for_inferior ()
infrun: target_wait (-1, status) =
infrun: 693 [Thread 0x7ffff7028700 (LWP 697)],
infrun: status->kind = stopped, signal = SIGTRAP
infrun: infwait_normal_state
infrun: TARGET_WAITKIND_STOPPED
infrun: stop_pc = 0x40062a
infrun: stepped into subroutine
infrun: inserting step-resume breakpoint at 0x40067b
infrun: resume (step=0, signal=0), trap_expected=0, current thread [Thread 0x7ffff7028700 (LWP 697)] at 0x40062a
infrun: prepare_to_wait
infrun: target_wait (-1, status) =
infrun: 693 [Thread 0x7ffff7028700 (LWP 697)],
infrun: status->kind = stopped, signal = SIGTRAP
infrun: infwait_normal_state
infrun: TARGET_WAITKIND_STOPPED
infrun: stop_pc = 0x40067b
infrun: BPSTAT_WHAT_STEP_RESUME
infrun: stepped to a different line
infrun: stop_stepping
Breakpoint 3, thread1 (arg=0x1) at loop.c:75
75 }
(gdb)
In MI, that translates into a "end-stepping-range"
*stopped notification rather than a "breakpoint-hit".
Even it is weren't for the reverse execution troubles,
this looks a bit fragile, and unnecessary. If we hit
a breakpoint that should cause a stop at the same time
as a step-resume, we should just stop for the stopping
breakpoint. This suggests lowering step-resume's priority
below BPSTAT_WHAT_STOP_SILENT/BPSTAT_WHAT_STOP_NOISY.
There's one case where we do need to ignore other
breakpoints set at the step-resume location, and give
the step-resume the highest priority. That is when
a signal arrives while stepping, and we need to skip
over a signal handler. We set a step-resume at the
current PC, and set the inferior running until it gets
back to PC. In this case, we don't want to consider
a breakpoint at PC as a new hit, we just want to move
the inferior past it.
So this leads to the patch below. It lowers the regular
step-resume breakpoint's priority, and adds a new
high-priority step-resume breakpoint kind, along
with a new corresponding bpstat_what_main_action to be
used in the skip-signal-handler's case.
It also adds a reverse-next testcase that would
fail without the patch. We now get the expected:
(gdb) b *callee
Breakpoint 2 at 0x400574: file ../../../src/gdb/testsuite/gdb.reverse/step-reverse.c, line 25.
(gdb) reverse-next
infrun: clear_proceed_status_thread (process 5058)
infrun: proceed (addr=0xffffffffffffffff, signal=144, step=1)
infrun: resume (step=1, signal=0), trap_expected=0, current thread [process 5058] at 0x4005ea
infrun: wait_for_inferior ()
infrun: target_wait (-1, status) =
infrun: 5058 [process 5058],
infrun: status->kind = stopped, signal = SIGTRAP
infrun: infwait_normal_state
infrun: TARGET_WAITKIND_STOPPED
infrun: stop_pc = 0x40058d
infrun: stepped into subroutine
infrun: inserting step-resume breakpoint at 0x400574
infrun: resume (step=0, signal=0), trap_expected=0, current thread [process 5058] at 0x40058d
infrun: prepare_to_wait
infrun: target_wait (-1, status) =
infrun: 5058 [process 5058],
infrun: status->kind = stopped, signal = SIGTRAP
infrun: infwait_normal_state
infrun: TARGET_WAITKIND_STOPPED
infrun: stop_pc = 0x400574
infrun: BPSTAT_WHAT_STOP_NOISY
infrun: stop_stepping
Breakpoint 2, callee () at ../../../src/gdb/testsuite/gdb.reverse/step-reverse.c:25
25 int callee() { /* ENTER CALLEE */
(gdb)
Tested on x86_64-linux, and applied.
Pedro Alves
2011-05-26 Pedro Alves <pedro@codesourcery.com>
gdb/
* breakpoint.h (enum bptype) <bp_hp_step_resume>: New.
(enum bpstat_what_main_action): Move BPSTAT_WHAT_STEP_RESUME
before BPSTAT_WHAT_STOP_SILENT. Add BPSTAT_WHAT_HP_STEP_RESUME
at the end.
* breakpoint.c (update_breakpoints_after_exec): Also delete hp
step-resume breakpoints.
(print_it_typical): Handle bp_hp_step_resume.
(bpstat_what): Ditto.
(bptype_string): Ditto.
(print_one_breakpoint_location): Ditto.
(allocate_bp_location): Ditto.
(mention): Ditto.
(breakpoint_re_set_one): Ditto.
* infrun.c (handle_inferior_event): Adjust. Split
BPSTAT_WHAT_STEP_RESUME handling in BPSTAT_WHAT_STEP_RESUME and
BPSTAT_WHAT_HP_STEP_RESUME.
(insert_step_resume_breakpoint_at_sal): Rename to ...
(insert_step_resume_breakpoint_at_sal_1): ... this. Add bptype
parameter. Handle it.
(insert_step_resume_breakpoint_at_sal): Reimplement on top of
insert_step_resume_breakpoint_at_sal_1.
(insert_step_resume_breakpoint_at_frame): Rename to ...
(insert_hp_step_resume_breakpoint_at_frame): ... this. Adjust to
set a high-priority step-resume breakpoint.
(insert_step_resume_breakpoint_at_frame): Adjust comment.
(insert_step_resume_breakpoint_at_caller): Ditto.
gdb/testsuite/
* gdb.reverse/next-reverse-bkpt-over-sr.exp: New test.
---
gdb/breakpoint.c | 17 +++
gdb/breakpoint.h | 20 +++
gdb/infrun.c | 81 +++++++++-------
gdb/testsuite/gdb.reverse/next-reverse-bkpt-over-sr.exp | 58 +++++++++++
4 files changed, 140 insertions(+), 36 deletions(-)
Index: src/gdb/breakpoint.h
===================================================================
--- src.orig/gdb/breakpoint.h 2011-05-26 15:50:30.641255579 +0100
+++ src/gdb/breakpoint.h 2011-05-26 15:51:33.001255558 +0100
@@ -68,10 +68,13 @@ enum bptype
bp_exception_resume,
/* Used by wait_for_inferior for stepping over subroutine calls,
- for stepping over signal handlers, and for skipping
- prologues. */
+ and for skipping prologues. */
bp_step_resume,
+ /* Used by wait_for_inferior for stepping over signal
+ handlers. */
+ bp_hp_step_resume,
+
/* Used to detect when a watchpoint expression has gone out of
scope. These breakpoints are usually not visible to the user.
@@ -721,6 +724,9 @@ enum bpstat_what_main_action
BPSTAT_WHAT_KEEP_CHECKING. */
BPSTAT_WHAT_CLEAR_LONGJMP_RESUME,
+ /* Clear step resume breakpoint, and keep checking. */
+ BPSTAT_WHAT_STEP_RESUME,
+
/* Rather than distinguish between noisy and silent stops here, it
might be cleaner to have bpstat_print make that decision (also
taking into account stop_print_frame and source_only). But the
@@ -733,8 +739,14 @@ enum bpstat_what_main_action
/* Stop and print. */
BPSTAT_WHAT_STOP_NOISY,
- /* Clear step resume breakpoint, and keep checking. */
- BPSTAT_WHAT_STEP_RESUME,
+ /* Clear step resume breakpoint, and keep checking. High-priority
+ step-resume breakpoints are used when even if there's a user
+ breakpoint at the current PC when we set the step-resume
+ breakpoint, we don't want to re-handle any breakpoint other
+ than the step-resume when it's hit; instead we want to move
+ past the breakpoint. This is used in the case of skipping
+ signal handlers. */
+ BPSTAT_WHAT_HP_STEP_RESUME,
};
/* An enum indicating the kind of "stack dummy" stop. This is a bit
Index: src/gdb/breakpoint.c
===================================================================
--- src.orig/gdb/breakpoint.c 2011-05-26 15:50:59.101255569 +0100
+++ src/gdb/breakpoint.c 2011-05-26 15:51:33.001255558 +0100
@@ -2415,7 +2415,7 @@ update_breakpoints_after_exec (void)
}
/* Step-resume breakpoints are meaningless after an exec(). */
- if (b->type == bp_step_resume)
+ if (b->type == bp_step_resume || b->type == bp_hp_step_resume)
{
delete_breakpoint (b);
continue;
@@ -3503,6 +3503,7 @@ print_it_typical (bpstat bs)
case bp_exception:
case bp_exception_resume:
case bp_step_resume:
+ case bp_hp_step_resume:
case bp_watchpoint_scope:
case bp_call_dummy:
case bp_std_terminate:
@@ -4489,6 +4490,15 @@ bpstat_what (bpstat bs_head)
this_action = BPSTAT_WHAT_SINGLE;
}
break;
+ case bp_hp_step_resume:
+ if (bs->stop)
+ this_action = BPSTAT_WHAT_HP_STEP_RESUME;
+ else
+ {
+ /* It is for the wrong frame. */
+ this_action = BPSTAT_WHAT_SINGLE;
+ }
+ break;
case bp_watchpoint_scope:
case bp_thread_event:
case bp_overlay_event:
@@ -4757,6 +4767,7 @@ bptype_string (enum bptype type)
{bp_exception, "exception"},
{bp_exception_resume, "exception resume"},
{bp_step_resume, "step resume"},
+ {bp_hp_step_resume, "high-priority step resume"},
{bp_watchpoint_scope, "watchpoint scope"},
{bp_call_dummy, "call dummy"},
{bp_std_terminate, "std::terminate"},
@@ -4892,6 +4903,7 @@ print_one_breakpoint_location (struct br
case bp_exception:
case bp_exception_resume:
case bp_step_resume:
+ case bp_hp_step_resume:
case bp_watchpoint_scope:
case bp_call_dummy:
case bp_std_terminate:
@@ -5701,6 +5713,7 @@ allocate_bp_location (struct breakpoint
case bp_exception:
case bp_exception_resume:
case bp_step_resume:
+ case bp_hp_step_resume:
case bp_watchpoint_scope:
case bp_call_dummy:
case bp_std_terminate:
@@ -7227,6 +7240,7 @@ mention (struct breakpoint *b)
case bp_exception:
case bp_exception_resume:
case bp_step_resume:
+ case bp_hp_step_resume:
case bp_call_dummy:
case bp_std_terminate:
case bp_watchpoint_scope:
@@ -11459,6 +11473,7 @@ breakpoint_re_set_one (void *bint)
case bp_call_dummy:
case bp_std_terminate:
case bp_step_resume:
+ case bp_hp_step_resume:
case bp_longjmp:
case bp_longjmp_resume:
case bp_exception:
Index: src/gdb/infrun.c
===================================================================
--- src.orig/gdb/infrun.c 2011-05-26 15:50:30.641255579 +0100
+++ src/gdb/infrun.c 2011-05-26 15:51:33.011255558 +0100
@@ -99,7 +99,7 @@ void _initialize_infrun (void);
void nullify_last_target_wait_ptid (void);
-static void insert_step_resume_breakpoint_at_frame (struct frame_info *);
+static void insert_hp_step_resume_breakpoint_at_frame (struct frame_info *);
static void insert_step_resume_breakpoint_at_caller (struct frame_info *);
@@ -1778,7 +1778,7 @@ a command like `return' or `jump' to con
original breakpoint is hit. */
if (tp->control.step_resume_breakpoint == NULL)
{
- insert_step_resume_breakpoint_at_frame (get_current_frame ());
+ insert_hp_step_resume_breakpoint_at_frame (get_current_frame ());
tp->step_after_step_resume_breakpoint = 1;
}
@@ -4173,7 +4173,7 @@ process_event_stop_test:
"infrun: signal arrived while stepping over "
"breakpoint\n");
- insert_step_resume_breakpoint_at_frame (frame);
+ insert_hp_step_resume_breakpoint_at_frame (frame);
ecs->event_thread->step_after_step_resume_breakpoint = 1;
/* Reset trap_expected to ensure breakpoints are re-inserted. */
ecs->event_thread->control.trap_expected = 0;
@@ -4203,7 +4203,7 @@ process_event_stop_test:
"infrun: signal may take us out of "
"single-step range\n");
- insert_step_resume_breakpoint_at_frame (frame);
+ insert_hp_step_resume_breakpoint_at_frame (frame);
/* Reset trap_expected to ensure breakpoints are re-inserted. */
ecs->event_thread->control.trap_expected = 0;
keep_going (ecs);
@@ -4349,6 +4349,24 @@ process_event_stop_test:
where we are stepping and step out of the right range. */
break;
+ case BPSTAT_WHAT_STEP_RESUME:
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_STEP_RESUME\n");
+
+ delete_step_resume_breakpoint (ecs->event_thread);
+ if (stop_pc == ecs->stop_func_start
+ && execution_direction == EXEC_REVERSE)
+ {
+ /* We are stepping over a function call in reverse, and
+ just hit the step-resume breakpoint at the start
+ address of the function. Go back to single-stepping,
+ which should take us back to the function call. */
+ ecs->event_thread->stepping_over_breakpoint = 1;
+ keep_going (ecs);
+ return;
+ }
+ break;
+
case BPSTAT_WHAT_STOP_NOISY:
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_STOP_NOISY\n");
@@ -4371,9 +4389,9 @@ process_event_stop_test:
stop_stepping (ecs);
return;
- case BPSTAT_WHAT_STEP_RESUME:
+ case BPSTAT_WHAT_HP_STEP_RESUME:
if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_STEP_RESUME\n");
+ fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_HP_STEP_RESUME\n");
delete_step_resume_breakpoint (ecs->event_thread);
if (ecs->event_thread->step_after_step_resume_breakpoint)
@@ -4386,17 +4404,6 @@ process_event_stop_test:
keep_going (ecs);
return;
}
- if (stop_pc == ecs->stop_func_start
- && execution_direction == EXEC_REVERSE)
- {
- /* We are stepping over a function call in reverse, and
- just hit the step-resume breakpoint at the start
- address of the function. Go back to single-stepping,
- which should take us back to the function call. */
- ecs->event_thread->stepping_over_breakpoint = 1;
- keep_going (ecs);
- return;
- }
break;
case BPSTAT_WHAT_KEEP_CHECKING:
@@ -5208,14 +5215,16 @@ handle_step_into_function_backward (stru
This is used to both functions and to skip over code. */
static void
-insert_step_resume_breakpoint_at_sal (struct gdbarch *gdbarch,
- struct symtab_and_line sr_sal,
- struct frame_id sr_id)
+insert_step_resume_breakpoint_at_sal_1 (struct gdbarch *gdbarch,
+ struct symtab_and_line sr_sal,
+ struct frame_id sr_id,
+ enum bptype sr_type)
{
/* There should never be more than one step-resume or longjmp-resume
breakpoint per thread, so we should never be setting a new
step_resume_breakpoint when one is already active. */
gdb_assert (inferior_thread ()->control.step_resume_breakpoint == NULL);
+ gdb_assert (sr_type == bp_step_resume || sr_type == bp_hp_step_resume);
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog,
@@ -5223,18 +5232,28 @@ insert_step_resume_breakpoint_at_sal (st
paddress (gdbarch, sr_sal.pc));
inferior_thread ()->control.step_resume_breakpoint
- = set_momentary_breakpoint (gdbarch, sr_sal, sr_id, bp_step_resume);
+ = set_momentary_breakpoint (gdbarch, sr_sal, sr_id, sr_type);
+}
+
+static void
+insert_step_resume_breakpoint_at_sal (struct gdbarch *gdbarch,
+ struct symtab_and_line sr_sal,
+ struct frame_id sr_id)
+{
+ insert_step_resume_breakpoint_at_sal_1 (gdbarch,
+ sr_sal, sr_id,
+ bp_step_resume);
}
-/* Insert a "step-resume breakpoint" at RETURN_FRAME.pc. This is used
- to skip a potential signal handler.
+/* Insert a "high-priority step-resume breakpoint" at RETURN_FRAME.pc.
+ This is used to skip a potential signal handler.
This is called with the interrupted function's frame. The signal
handler, when it returns, will resume the interrupted function at
RETURN_FRAME.pc. */
static void
-insert_step_resume_breakpoint_at_frame (struct frame_info *return_frame)
+insert_hp_step_resume_breakpoint_at_frame (struct frame_info *return_frame)
{
struct symtab_and_line sr_sal;
struct gdbarch *gdbarch;
@@ -5247,14 +5266,14 @@ insert_step_resume_breakpoint_at_frame (
sr_sal.section = find_pc_overlay (sr_sal.pc);
sr_sal.pspace = get_frame_program_space (return_frame);
- insert_step_resume_breakpoint_at_sal (gdbarch, sr_sal,
- get_stack_frame_id (return_frame));
+ insert_step_resume_breakpoint_at_sal_1 (gdbarch, sr_sal,
+ get_stack_frame_id (return_frame),
+ bp_hp_step_resume);
}
-/* Similar to insert_step_resume_breakpoint_at_frame, except
- but a breakpoint at the previous frame's PC. This is used to
- skip a function after stepping into it (for "next" or if the called
- function has no debugging information).
+/* Insert a "step-resume breakpoint" at the previous frame's PC. This
+ is used to skip a function after stepping into it (for "next" or if
+ the called function has no debugging information).
The current function has almost always been reached by single
stepping a call or return instruction. NEXT_FRAME belongs to the
@@ -5262,7 +5281,7 @@ insert_step_resume_breakpoint_at_frame (
resume address.
This is a separate function rather than reusing
- insert_step_resume_breakpoint_at_frame in order to avoid
+ insert_hp_step_resume_breakpoint_at_frame in order to avoid
get_prev_frame, which may stop prematurely (see the implementation
of frame_unwind_caller_id for an example). */
Index: src/gdb/testsuite/gdb.reverse/next-reverse-bkpt-over-sr.exp
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.reverse/next-reverse-bkpt-over-sr.exp 2011-05-26 15:51:33.011255558 +0100
@@ -0,0 +1,58 @@
+# Copyright 2008, 2009, 2010, 2011 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/>. */
+
+# This file is part of the GDB testsuite. It tests reverse stepping.
+# Lots of code borrowed from "step-test.exp".
+
+#
+# reverse-next over a function call sets a step-resume breakpoint at
+# callee's entry point, runs to it, and then does an extra single-step
+# to get at the callee's caller. Test that a user breakpoint set at
+# the same location as the step-resume breakpoint isn't ignored.
+#
+
+if ![target_info exists gdb,can_reverse] {
+ return
+}
+
+set testfile "next-reverse-bkpt-over-sr"
+set srcfile step-reverse.c
+
+if { [prepare_for_testing $testfile.exp $testfile $srcfile] } {
+ return -1
+}
+
+if ![runto_main] then {
+ fail "Can't run to main"
+ return 0
+}
+
+if [target_info exists gdb,use_precord] {
+ # Activate process record/replay
+ gdb_test_no_output "record" "Turn on process record"
+}
+
+set lineno [gdb_get_line_number "STEP INTO THIS CALL"]
+gdb_test "advance $lineno" ".*STEP INTO THIS CALL.*" "get past callee call"
+
+gdb_test "b \*callee" "" "set breakpoint at callee's entry"
+
+gdb_test "reverse-next" \
+ "Breakpoint.*, callee.*ENTER CALLEE.*" \
+ "reverse-next over call trips user breakpoint at function entry"
+
+gdb_test "up" \
+ ".*NEXT OVER THIS CALL.*" \
+ "stopped at the right callee call"