diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index ebe97940f54..47f35c6b0e5 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -2143,6 +2143,21 @@ update_watchpoint (struct watchpoint *b, bool reparse) } } + /* Helper function to bundle possibly emitting a warning along with + changing the type of B to bp_watchpoint. */ + auto change_type_to_bp_watchpoint = [] (breakpoint *bp) + { + /* Only warn for breakpoints that have been assigned a +ve number, + anything else is either an internal watchpoint (which we don't + currently create) or has not yet been finalized, in which case + this change of type will be occurring before the user is told + the type of this watchpoint. */ + if (bp->type == bp_hardware_watchpoint && bp->number > 0) + warning (_("watchpoint %d downgraded to software watchpoint"), + bp->number); + bp->type = bp_watchpoint; + }; + /* Change the type of breakpoint between hardware assisted or an ordinary watchpoint depending on the hardware support and free hardware slots. Recheck the number of free hardware slots @@ -2200,7 +2215,7 @@ update_watchpoint (struct watchpoint *b, bool reparse) "resources for this watchpoint.")); /* Downgrade to software watchpoint. */ - b->type = bp_watchpoint; + change_type_to_bp_watchpoint (b); } else { @@ -2221,7 +2236,7 @@ update_watchpoint (struct watchpoint *b, bool reparse) "read/access watchpoint.")); } else - b->type = bp_watchpoint; + change_type_to_bp_watchpoint (b); loc_type = (b->type == bp_watchpoint? bp_loc_software_watchpoint : bp_loc_hardware_watchpoint); diff --git a/gdb/testsuite/gdb.arch/amd64-watchpoint-downgrade.c b/gdb/testsuite/gdb.arch/amd64-watchpoint-downgrade.c new file mode 100644 index 00000000000..c2f5f2cc78e --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-watchpoint-downgrade.c @@ -0,0 +1,29 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2023 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 . */ + +struct struct_type +{ + unsigned long long array[100]; +}; + +struct struct_type global_var; + +int +main () +{ + return 0; +} diff --git a/gdb/testsuite/gdb.arch/amd64-watchpoint-downgrade.exp b/gdb/testsuite/gdb.arch/amd64-watchpoint-downgrade.exp new file mode 100644 index 00000000000..bc579a93e2c --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-watchpoint-downgrade.exp @@ -0,0 +1,67 @@ +# Copyright 2023 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 . + +# Ask GDB to watch a large structure before the inferior has started, +# GDB will assume it can place a hardware watchpoint. +# +# Once the inferior starts GDB realises that it is not able to watch +# such a large structure and downgrades to a software watchpoint. +# +# This test checks that GDB emits a warnings about this downgrade, as +# a software watchpoint will be significantly slower than a hardware +# watchpoint, and the user probably wants to know about this. + +require target_can_use_run_cmd is_x86_64_m64_target + +# The remote/extended-remote target has its own set of flags to +# control the use of s/w vs h/w watchpoints, this test isn't about +# those, so skip the test in these cases. +if {[target_info gdb_protocol] == "remote" + || [target_info gdb_protocol] == "extended-remote"} { + unsupported "using [target_info gdb_protocol] protocol" + return -1 +} + +standard_testfile + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \ + { debug }] } { + return -1 +} + +# Insert the watchpoint, it should default to a h/w watchpoint. +gdb_test "watch global_var" \ + "Hardware watchpoint $decimal: global_var" +set num [get_integer_valueof "\$bpnum" "*UNKNOWN*" \ + "get watchpoint number"] + +# Watchpoint should initially show as a h/w watchpoint. +gdb_test "info watchpoints" \ + "\r\n$num\\s+hw watchpoint\\s+keep\\s+y\\s+global_var" \ + "check info watchpoints before starting" + +# Start the inferior, GDB should emit a warning that the watchpoint +# type has changed. +gdb_test "starti" \ + [multi_line \ + "warning: watchpoint $num downgraded to software watchpoint" \ + "" \ + "Program stopped\\." \ + ".*"] + +# Watchpoint should now have downgraded to a s/w watchpoint. +gdb_test "info watchpoints" \ + "\r\n$num\\s+watchpoint\\s+keep\\s+y\\s+global_var" \ + "check info watchpoints after starting" diff --git a/gdb/testsuite/gdb.base/watchpoint.exp b/gdb/testsuite/gdb.base/watchpoint.exp index 513964ebf86..28276681afc 100644 --- a/gdb/testsuite/gdb.base/watchpoint.exp +++ b/gdb/testsuite/gdb.base/watchpoint.exp @@ -273,7 +273,8 @@ proc test_stepping {} { global gdb_prompt if {[runto marker1]} { - gdb_test "watch ival2" ".*\[Ww\]atchpoint \[0-9\]*: ival2" + gdb_test "watch ival2" \ + "^watch ival2\r\n\[^\r\n\]*\[Ww\]atchpoint \[0-9\]*: ival2" # Well, let's not be too mundane. It should be a *bit* of a challenge gdb_test "break func2 if 0" "Breakpoint.*at.*" @@ -432,7 +433,8 @@ proc test_complex_watchpoint {} { global gdb_prompt if {[runto marker4]} { - gdb_test "watch ptr1->val" ".*\[Ww\]atchpoint \[0-9\]*: ptr1->val" + gdb_test "watch ptr1->val" \ + "^watch ptr1->val\r\n\[^\r\n\]*\[Ww\]atchpoint \[0-9\]*: ptr1->val" gdb_test "break marker5" ".*Breakpoint.*" gdb_test "cont" "Continuing.*\[Ww\]atchpoint.*ptr1->val.*Old value = 1.*New value = 2.*" "test complex watchpoint" @@ -458,7 +460,9 @@ proc test_complex_watchpoint {} { # is the function we're now in. This should auto-delete when # execution exits the scope of the watchpoint. # - gdb_test "watch local_a" ".*\[Ww\]atchpoint \[0-9\]*: local_a" "set local watch" + gdb_test "watch local_a" \ + "^watch local_a\r\n\[^\r\n\]*\[Ww\]atchpoint \[0-9\]*: local_a" \ + "set local watch" gdb_test "cont" "\[Ww\]atchpoint.*local_a.*" "trigger local watch" set test "self-delete local watch" @@ -487,7 +491,8 @@ proc test_complex_watchpoint {} { # something whose scope is larger than this invocation # of "func2". This should also auto-delete. # - gdb_test "watch local_a + ival5" ".*\[Ww\]atchpoint \[0-9\]*: local_a . ival5" \ + gdb_test "watch local_a + ival5" \ + "^watch local_a \\+ ival5\r\n\[^\r\n\]*\[Ww\]atchpoint \[0-9\]*: local_a . ival5" \ "set partially local watch" gdb_test "cont" "Continuing.*\[Ww\]atchpoint .*: local_a . ival5.*" \ "trigger1 partially local watch" @@ -502,7 +507,8 @@ proc test_complex_watchpoint {} { # delete. # gdb_continue_to_breakpoint "func2 breakpoint here, third time" - gdb_test "watch static_b" ".*\[Ww\]atchpoint \[0-9\]*: static_b" \ + gdb_test "watch static_b" \ + "^watch static_b\r\n\[^\r\n\]*\[Ww\]atchpoint \[0-9\]*: static_b" \ "set static local watch" gdb_test "cont" "Continuing.*\[Ww\]atchpoint .*: static_b.*" \ "trigger static local watch" @@ -521,7 +527,8 @@ proc test_complex_watchpoint {} { gdb_test "tbreak recurser" ".*breakpoint.*" gdb_test "cont" "Continuing.*recurser.*" gdb_test "next" "if \\(x > 0.*" "next past local_x initialization" - gdb_test "watch local_x" ".*\[Ww\]atchpoint \[0-9\]*: local_x" \ + gdb_test "watch local_x" \ + "^watch local_x\r\n\[^\r\n\]*\[Ww\]atchpoint \[0-9\]*: local_x" \ "set local watch in recursive call" gdb_test "cont" "Continuing.*\[Ww\]atchpoint .*: local_x.*New value = 2.*" \ "trigger local watch in recursive call" @@ -536,7 +543,8 @@ proc test_complex_watchpoint {} { gdb_test "tbreak recurser" ".*breakpoint.*" gdb_test "cont" "Continuing.*recurser.*" "continue to recurser" gdb_test "next" "if \\(x > 0.*" "next past local_x initialization" - gdb_test "watch recurser::local_x" ".*\[Ww\]atchpoint \[0-9\]*: recurser::local_x" \ + gdb_test "watch recurser::local_x" \ + "^watch recurser::local_x\r\n\[^\r\n\]*\[Ww\]atchpoint \[0-9\]*: recurser::local_x" \ "set local watch in recursive call with explicit scope" gdb_test "cont" "Continuing.*\[Ww\]atchpoint .*: recurser::local_x.*New value = 2.*" \ "trigger local watch with explicit scope in recursive call" @@ -562,7 +570,8 @@ proc test_watchpoint_and_breakpoint {} { if {[runto func3]} { gdb_breakpoint [gdb_get_line_number "second x assignment"] gdb_continue_to_breakpoint "second x assignment" - gdb_test "watch x" ".*atchpoint \[0-9\]+: x" + gdb_test "watch x" \ + "^watch x\r\n\[^\r\n\]*\[Ww\]atchpoint \[0-9\]+: x" gdb_test "next" \ ".*atchpoint \[0-9\]+: x\r\n\r\nOld value = 0\r\nNew value = 1\r\n.*" \ "next after watch x" @@ -579,7 +588,8 @@ proc test_constant_watchpoint {} { "marker1 is constant" gdb_test "watch count + 6" ".*atchpoint \[0-9\]+: count \\+ 6" gdb_test_no_output "delete \$bpnum" "delete watchpoint `count + 6'" - gdb_test "watch 7 + count" ".*atchpoint \[0-9\]+: 7 \\+ count" + gdb_test "watch 7 + count" \ + "^watch 7 \\+ count\r\n\[^\r\n\]*atchpoint \[0-9\]+: 7 \\+ count" gdb_test_no_output "delete \$bpnum" "delete watchpoint `7 + count'" } @@ -588,7 +598,7 @@ proc test_disable_enable_software_watchpoint {} { # for software watchpoints. # Watch something not memory to force a software watchpoint. - gdb_test {watch $pc} ".*atchpoint \[0-9\]+: .pc" + gdb_test {watch $pc} "^watch \\\$pc\r\n\[^\r\n\]*\[Ww\]atchpoint \[0-9\]+: .pc" gdb_test_no_output "disable \$bpnum" "disable watchpoint `\$pc'" gdb_test_no_output "enable \$bpnum" "reenable watchpoint `\$pc'" @@ -822,7 +832,7 @@ proc test_no_hw_watchpoints {} { "show disable fast watches" gdb_test "watch ival3 if count > 1" \ - "Watchpoint \[0-9\]*: ival3.*" \ + "^watch ival3 if count > 1\r\nWatchpoint \[0-9\]*: ival3.*" \ "set slow conditional watch" gdb_test "continue" \ @@ -832,7 +842,7 @@ proc test_no_hw_watchpoints {} { gdb_test_no_output "delete \$bpnum" "delete watch ival3" gdb_test "watch ival3 if count > 1 thread 1 " \ - "Watchpoint \[0-9\]*: ival3.*" \ + "watch ival3 if count > 1 thread 1 \r\nWatchpoint \[0-9\]*: ival3.*" \ "set slow condition watch w/thread" gdb_test_no_output "delete \$bpnum" "delete watch w/condition and thread"