# Copyright 2014-2018 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 . */ # 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 if {[prepare_for_testing "failed to prepare" $testfile $srcfile debug]} { return -1 } if ![runto_main] then { fail "can't run to main" return 0 } if [is_remote host] { set arg [remote_download host $binfile] if { $arg == "" } { perror "download failed" return -1 } } # 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" exp_continue } -re "Load new symbol table from \".*\".*y or n. $" { send_gdb "y\n" exp_continue } -re "Reading symbols from.*done.*$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 "always-inserted $always_inserted: $cmd" { delete_breakpoints if ![runto_main] then { fail "can't run to main" 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 .*" } } foreach 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" } }