ef370185fc
with the following code... 12 Nested; -- break #1 13 return I; -- break #2 14 end; (line 12 is a call to function Nested) ... we have noticed the following errorneous behavior on ppc-aix, where, after having inserted a breakpoint at line 12 and line 13, and continuing from the breakpoint at line 12, the program never stops at line 13, running away until the program terminates: % gdb -q func (gdb) b func.adb:12 Breakpoint 1 at 0x10000a24: file func.adb, line 12. (gdb) b func.adb:13 Breakpoint 2 at 0x10000a28: file func.adb, line 13. (gdb) run Starting program: /[...]/func Breakpoint 1, func () at func.adb:12 12 Nested; -- break #1 (gdb) c Continuing. [Inferior 1 (process 4128872) exited with code 02] When resuming from the first breakpoint, GDB first tries to step out of that first breakpoint. We rely on software single-stepping on this platform, and it just so happens that the address of the first software single-step breakpoint is the same as the user's breakpoint #2 (0x10000a28). So, with infrun and target traces turned on (but uninteresting traces snip'ed off), the "continue" operation looks like this: (gdb) c ### First, we insert the user breakpoints (the second one is an internal ### breakpoint on __pthread_init). The first user breakpoint is not ### inserted as we need to step out of it first. target_insert_breakpoint (0x0000000010000a28, xxx) = 0 target_insert_breakpoint (0x00000000d03f3800, xxx) = 0 ### Then we proceed with the step-out-of-breakpoint... infrun: resume (step=1, signal=GDB_SIGNAL_0), trap_expected=1, current thread [process 15335610] at 0x10000a24 ### That's when we insert the SSS breakpoints... target_insert_breakpoint (0x0000000010000a28, xxx) = 0 target_insert_breakpoint (0x00000000100009ac, xxx) = 0 ### ... then let the inferior resume... target_resume (15335610, continue, 0) infrun: wait_for_inferior () target_wait (-1, status, options={}) = 15335610, status->kind = stopped, signal = GDB_SIGNAL_TRAP infrun: target_wait (-1, status) = infrun: 15335610 [process 15335610], infrun: status->kind = stopped, signal = GDB_SIGNAL_TRAP infrun: infwait_normal_state infrun: TARGET_WAITKIND_STOPPED infrun: stop_pc = 0x100009ac ### At this point, we stopped at the second SSS breakpoint... target_stopped_by_watchpoint () = 0 ### We remove the SSS breakpoints... target_remove_breakpoint (0x0000000010000a28, xxx) = 0 target_remove_breakpoint (0x00000000100009ac, xxx) = 0 target_stopped_by_watchpoint () = 0 ### We find that we're not done, so we resume.... infrun: no stepping, continue ### And thus insert the user breakpoints again, except we're not ### inserting the second breakpoint?!? target_insert_breakpoint (0x0000000010000a24, xxx) = 0 infrun: resume (step=0, signal=GDB_SIGNAL_0), trap_expected=0, current thread [process 15335610] at 0x100009ac target_resume (-1, continue, 0) infrun: prepare_to_wait target_wait (-1, status, options={}) = 15335610, status->kind = exited, status = 2 What happens is that the removal of the software single-step breakpoints effectively removed the breakpoint instruction from inferior memory. But because such breakpoints are inserted directly as raw breakpoints rather than through the normal chain of breakpoints, we fail to notice that one of the user breakpoints points to the same address and that this user breakpoint is therefore effectively un-inserted. When resuming after the single-step, GDB thinks that the user breakpoint is still inserted and therefore does not need to insert it again. This patch teaches the insert and remove routines of both regular and raw breakpoints to be aware of each other. Special care needs to be applied in case the target supports evaluation of breakpoint conditions or commands. gdb/ChangeLog: PR breakpoints/17000 * breakpoint.c (find_non_raw_software_breakpoint_inserted_here): New function, extracted from software_breakpoint_inserted_here_p. (software_breakpoint_inserted_here_p): Replace factored out code by call to find_non_raw_software_breakpoint_inserted_here. (bp_target_info_copy_insertion_state): New function. (bkpt_insert_location): Handle the case of a single-step breakpoint already inserted at the same address. (bkpt_remove_location): Handle the case of a single-step breakpoint still inserted at the same address. (deprecated_insert_raw_breakpoint): Handle the case of non-raw breakpoint already inserted at the same address. (deprecated_remove_raw_breakpoint): Handle the case of a non-raw breakpoint still inserted at the same address. (find_single_step_breakpoint): New function, extracted from single_step_breakpoint_inserted_here_p. (find_single_step_breakpoint): New function, factored out from single_step_breakpoint_inserted_here_p. (single_step_breakpoint_inserted_here_p): Reimplement. gdb/testsuite/ChangeLog: PR breakpoints/17000 * gdb.base/sss-bp-on-user-bp.exp: Remove kfail. * gdb.base/sss-bp-on-user-bp-2.exp: Remove kfail. Tested on ppc-aix with AdaCore's testsuite. Tested on x86_64-linux, (native and gdbserver) with the official testsuite. Also tested on x86_64-linux through Pedro's branch enabling software single-stepping on that platform (native and gdbserver).
147 lines
4.4 KiB
Text
147 lines
4.4 KiB
Text
# Copyright (C) 2014 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 doesn't get confused in the following scenario
|
|
# (PR breakpoints/17000). Say, we have this program:
|
|
#
|
|
# => 0xff000001 INSN1
|
|
# 0xff000002 INSN2
|
|
#
|
|
# The PC currently points at INSN1.
|
|
#
|
|
# 1 - User sets a breakpoint at 0xff000002 (INSN2).
|
|
#
|
|
# 2 - User steps. On software single-step archs, this sets a software
|
|
# single-step breakpoint at 0xff000002 (INSN2) too.
|
|
#
|
|
# 3 - User deletes breakpoint (INSN2) before the single-step finishes.
|
|
#
|
|
# 4 - The single-step finishes, and GDB removes the single-step
|
|
# breakpoint.
|
|
|
|
standard_testfile
|
|
|
|
if {[prepare_for_testing "failed to prepare" $testfile $srcfile debug]} {
|
|
return -1
|
|
}
|
|
|
|
if ![runto_main] {
|
|
fail "Can't run to main"
|
|
return 0
|
|
}
|
|
|
|
delete_breakpoints
|
|
|
|
# With the all-stop RSP, we can't talk to the target while it's
|
|
# running, until we get back the stop reply. If not using single-step
|
|
# breakpoints, then the "del" in stepi_del_break below will try to
|
|
# delete the user breakpoint from the target, which will fail, with
|
|
# "Cannot execute this command while the target is running.". On
|
|
# software single-step targets, that del shouldn't trigger any RSP
|
|
# traffic. Just skip the test if testing against a remove target and
|
|
# not using software single-stepping. IOW, skip the test if we see a
|
|
# 'vCont;s' or 's' in the RSP traffic.
|
|
|
|
gdb_test_no_output "set debug remote 1"
|
|
|
|
set rsp_hardware_step 0
|
|
|
|
# Probe for software single-step breakpoint use.
|
|
set test "probe RSP hardware step"
|
|
gdb_test_multiple "si" $test {
|
|
-re "\\\$vCont;s.*$gdb_prompt $" {
|
|
set rsp_hardware_step 1
|
|
pass $test
|
|
}
|
|
-re "\\\$s#.*$gdb_prompt $" {
|
|
set rsp_hardware_step 1
|
|
pass $test
|
|
}
|
|
-re "$gdb_prompt $" {
|
|
pass $test
|
|
}
|
|
}
|
|
|
|
if { $rsp_hardware_step } {
|
|
unsupported "remote target doesn't use software single-stepping"
|
|
return
|
|
}
|
|
|
|
gdb_test_no_output "set debug remote 0"
|
|
|
|
set line_re "\[^\r\n\]*"
|
|
|
|
gdb_test "b test:label" "Breakpoint .*"
|
|
gdb_continue_to_breakpoint "run past setup"
|
|
delete_breakpoints
|
|
|
|
# So we can precisely control breakpoint insertion order.
|
|
gdb_test_no_output "set breakpoint always-inserted on"
|
|
|
|
# Capture disassembly output. PREFIX is used as test prefix. The
|
|
# current instruction indicator (=>) is stripped away.
|
|
proc disassemble { prefix } {
|
|
with_test_prefix "$prefix" {
|
|
set output [capture_command_output "disassemble test" ""]
|
|
return [string map {"=>" " "} $output]
|
|
}
|
|
}
|
|
|
|
# Issue a stepi and immediately delete the user breakpoint that is set
|
|
# at the same address as the software single-step breakpoint. Do this
|
|
# in a user defined command, so that the stepi's trap doesn't have a
|
|
# chance to be handled before further input is processed. We then
|
|
# compare before/after disassembly. GDB should be able to handle
|
|
# deleting the user breakpoint before deleting the single-step
|
|
# breakpoint. E.g., we shouldn't see breakpoint instructions in the
|
|
# disassembly.
|
|
|
|
set disasm_before [disassemble "before"]
|
|
|
|
gdb_test "b test:label2" ".*" "set breakpoint where si will land"
|
|
|
|
set test "define stepi_del_break"
|
|
gdb_test_multiple $test $test {
|
|
-re "Type commands for definition of \"stepi_del_break\".\r\nEnd with a line saying just \"end\".\r\n>$" {
|
|
gdb_test "si&\ndel \$bpnum\nend" "" $test
|
|
}
|
|
}
|
|
|
|
set command "stepi_del_break"
|
|
set test $command
|
|
gdb_test_multiple $command $test {
|
|
-re "^$command\r\n$gdb_prompt " {
|
|
# Note no end anchor, because "si&" finishes and prints the
|
|
# current frame/line after the prompt is printed.
|
|
pass $test
|
|
}
|
|
}
|
|
|
|
# Now consume the output of the finished "si&".
|
|
set test "si& finished"
|
|
gdb_test_multiple "" $test {
|
|
-re "must be a single line \\\*/\r\n" {
|
|
pass $test
|
|
}
|
|
}
|
|
|
|
set disasm_after [disassemble "after"]
|
|
|
|
set test "before/after disassembly matches"
|
|
if ![string compare $disasm_before $disasm_after] {
|
|
pass $test
|
|
} else {
|
|
fail $test
|
|
}
|