| # Copyright 2014-2021 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 the Zx breakpoint/watchpoint packets are idempotent. |
| |
| # GDBserver used to not treat Zx breakpoints other than Z0 as |
| # idempotent, although it must, to avoid problems with |
| # retransmissions. Even without spurious transport problems, if the |
| # target supports target conditions or commands, GDB re-inserts Zx |
| # breakpoints even if they are already inserted, to update the |
| # target-side condition/commands. E.g., simply when a duplicate |
| # breakpoint is created, or when a shared library load causes a |
| # re-set, which creates duplicate locations while breakpoints are |
| # inserted, or when the condition is really changed while breakpoints |
| # are inserted. To make the test not depend on shared library support |
| # or on details of the breakpoint re-set implementation, or on GDB |
| # optimizing out re-sends if the condition hasn't actually changed, we |
| # force always-inserted on, and really change the breakpoint's |
| # condition. For good measure, test with both always-inserted "on" |
| # and "off" modes. |
| |
| # The test is written in black-box style, and doesn't actually use |
| # anything target remote specific, so let it run on all targets. |
| |
| standard_testfile |
| |
| # Force a breakpoint re-set in GDB. Currently this is done by |
| # reloading symbols with the "file" command. |
| |
| proc force_breakpoint_re_set {} { |
| global binfile gdb_prompt |
| |
| set test "file \$binfile" |
| gdb_test_multiple "file $binfile" $test { |
| -re "Are you sure you want to change the file. .*y or n. $" { |
| send_gdb "y\n" optional |
| exp_continue |
| } |
| -re "Load new symbol table from \".*\".*y or n. $" { |
| send_gdb "y\n" optional |
| exp_continue |
| } |
| -re "Reading symbols from.*$gdb_prompt $" { |
| pass $test |
| } |
| } |
| } |
| |
| # Set a break/hbreak/watch/rwatch/awatch. |
| |
| proc set_breakpoint { break_command } { |
| global gdb_prompt srcfile |
| |
| if { $break_command == "break" } { |
| gdb_test "$break_command foo" "Breakpoint.*at.* file .*$srcfile, line.*" |
| } elseif { $break_command == "hbreak" } { |
| set test "$break_command foo" |
| gdb_test_multiple $test $test { |
| -re "No hardware breakpoint support in the target.*$gdb_prompt $" { |
| unsupported $test |
| } |
| -re "Hardware breakpoints used exceeds limit.*$gdb_prompt $" { |
| unsupported $test |
| } |
| -re "Cannot insert hardware breakpoint.*$gdb_prompt $" { |
| unsupported $test |
| } |
| -re "Hardware assisted breakpoint.*at.* file .*$srcfile, line.*$gdb_prompt $" { |
| pass $test |
| } |
| } |
| } elseif { [string first "watch" $break_command] != -1 } { |
| set test "$break_command global" |
| gdb_test_multiple $test $test { |
| -re "Target does not support this type of hardware watchpoint\\.\r\n$gdb_prompt $" { |
| unsupported $test |
| } |
| -re "Could not insert hardware watchpoint.*$gdb_prompt $" { |
| unsupported $test |
| } |
| -re "atchpoint \[0-9\]+: global\r\n$gdb_prompt $" { |
| pass $test |
| } |
| } |
| } else { |
| error "unhandled command: $break_command" |
| } |
| } |
| |
| # Run the test proper. ALWAYS_INSERT determines whether |
| # always-inserted mode is on/off, and BREAK_COMMAND is the |
| # break/watch/etc. command being tested. |
| # |
| proc test_break { always_inserted break_command } { |
| set cmd [lindex [split "$break_command"] 0] |
| |
| with_test_prefix "$cmd" { |
| delete_breakpoints |
| |
| if ![runto_main] then { |
| return |
| } |
| |
| gdb_test_no_output "set breakpoint always-inserted $always_inserted" |
| |
| # Set breakpoints/watchpoints twice. With always-inserted on, |
| # GDB reinserts the exact same Z breakpoint twice... Do this |
| # to make sure the stub pays attention to idempotency even |
| # when the condition doesn't change. If GDB end up optimizing |
| # out exact duplicate packets, we should come up with a way to |
| # keep testing this case. |
| foreach iter { "once" "twice" } { |
| with_test_prefix $iter { |
| set_breakpoint $break_command |
| } |
| } |
| |
| # Force a breakpoint re-set. In always-inserted mode, this |
| # makes GDB re-send Z packets too... |
| force_breakpoint_re_set |
| |
| # Now really change the condition, which forces a reinsert by |
| # design. |
| gdb_test "condition \$bpnum cond_global == 0" ".*" |
| |
| # Now delete breakpoints, and let the program execute the |
| # address where the breakpoint used to be set. If the target |
| # doesn't treat insertions an idempotent way, we'll get a |
| # spurious SIGTRAP. |
| delete_breakpoints |
| gdb_test "b bar" "Breakpoint .* at .*" |
| gdb_test "continue" "Breakpoint .*, bar .*" |
| } |
| } |
| |
| # The testcase uses the "file" command to force breakpoint re-set in |
| # GDB. Test both with and without PIE, as GDB used to mishandle |
| # breakpoint re-set when reloading PIEs. |
| foreach_with_prefix pie { "nopie" "pie" } { |
| |
| set opts {debug} |
| lappend opts $pie |
| |
| set binfile [standard_output_file $testfile-$pie] |
| |
| if {[prepare_for_testing "failed to prepare" $binfile $srcfile $opts]} { |
| continue |
| } |
| |
| if [is_remote host] { |
| set arg [remote_download host $binfile] |
| if { $arg == "" } { |
| untested "download failed" |
| continue |
| } |
| } |
| |
| foreach_with_prefix always_inserted { "off" "on" } { |
| test_break $always_inserted "break" |
| |
| if {![skip_hw_breakpoint_tests]} { |
| test_break $always_inserted "hbreak" |
| } |
| |
| if {![skip_hw_watchpoint_tests]} { |
| test_break $always_inserted "watch" |
| } |
| |
| if {![skip_hw_watchpoint_access_tests] |
| && ![skip_hw_watchpoint_multi_tests]} { |
| test_break $always_inserted "rwatch" |
| test_break $always_inserted "awatch" |
| } |
| } |
| } |