diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index ea89c40ce26..6d8adc62664 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -2153,7 +2153,7 @@ update_watchpoint (struct watchpoint *b, bool reparse) { std::vector val_chain; struct value *v, *result; - struct program_space *frame_pspace; + struct program_space *wp_pspace; fetch_subexp_value (b->exp.get (), b->exp->op.get (), &v, &result, &val_chain, false); @@ -2172,7 +2172,10 @@ update_watchpoint (struct watchpoint *b, bool reparse) b->val_valid = true; } - frame_pspace = get_frame_program_space (get_selected_frame (NULL)); + if (b->exp_valid_block == nullptr) + wp_pspace = current_program_space; + else + wp_pspace = get_frame_program_space (get_selected_frame (NULL)); /* Look at each value on the value chain. */ gdb_assert (!val_chain.empty ()); @@ -2232,7 +2235,7 @@ update_watchpoint (struct watchpoint *b, bool reparse) bp_location *loc = b->allocate_location (); loc->gdbarch = v->type ()->arch (); - loc->pspace = frame_pspace; + loc->pspace = wp_pspace; loc->address = gdbarch_remove_non_address_bits (loc->gdbarch, addr); b->add_location (*loc); @@ -2357,7 +2360,7 @@ update_watchpoint (struct watchpoint *b, bool reparse) bpstat_stop_status requires a location to be able to report stops, so make sure there's at least a dummy one. */ if (b->type == bp_watchpoint && !b->has_locations ()) - add_dummy_location (b, frame_pspace); + add_dummy_location (b, wp_pspace); } else if (!within_current_scope) { diff --git a/gdb/testsuite/gdb.base/watchpoint-running.c b/gdb/testsuite/gdb.base/watchpoint-running.c new file mode 100644 index 00000000000..3ba1cb21dfe --- /dev/null +++ b/gdb/testsuite/gdb.base/watchpoint-running.c @@ -0,0 +1,46 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2023-2024 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 . + +*/ + +#include +#include + +uint64_t global_var; + +/* How long to usleep, in us. The exp file assumes this is lower than + 1s. */ +#define MS_USLEEP 100000 + +/* How many increments per second MS_USLEEP corresponds to. */ +#define INCREMENTS_PER_SEC (1000000 / MS_USLEEP) + +int +main () +{ + while (1) + { + usleep (MS_USLEEP); + global_var++; + + /* Time out after 30s. */ + if (global_var >= (30 * INCREMENTS_PER_SEC)) + return 1; + } + + return 0; +} diff --git a/gdb/testsuite/gdb.base/watchpoint-running.exp b/gdb/testsuite/gdb.base/watchpoint-running.exp new file mode 100644 index 00000000000..d0510f8555c --- /dev/null +++ b/gdb/testsuite/gdb.base/watchpoint-running.exp @@ -0,0 +1,139 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2023-2024 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 . + +# This test verifies that you can set a watchpoint that watches global +# memory, when the selected thread is running. + +#require allow_hw_watchpoint_multi_tests + +set allow_hw_watchpoint_tests_p [allow_hw_watchpoint_tests] + +standard_testfile + +if [build_executable "failed to prepare" $testfile $srcfile {debug}] { + return -1 +} + +# STOP_MODE is either "all-stop" or "non-stop". HW is true if we are +# testing hardware watchpoints, and false if we're testing software +# watchpoints. + +proc test {stop_mode hw} { + + save_vars { ::GDBFLAGS } { + if { $stop_mode == "non-stop" } { + append ::GDBFLAGS " -ex \"set non-stop on\"" + } elseif {[target_info gdb_protocol] == "remote" + || [target_info gdb_protocol]== "extended-remote"} { + # Communicating with the target while the inferior is + # running depends on target running in non-stop mode. + # Force it on for remote targets, until this is the + # default. + append ::GDBFLAGS " -ex \"maint set target-non-stop on\"" + } + clean_restart $::binfile + } + + gdb_test_no_output "set can-use-hw-watchpoints $hw" + + if {![runto_main]} { + return + } + + delete_breakpoints + + # Continue the program in the background. + set test "continue&" + gdb_test_multiple "continue&" $test { + -re "Continuing\\.\r\n$::gdb_prompt " { + pass $test + } + } + + set val1 "" + gdb_test_multiple "print global_var" "global_var once" { + -re -wrap " = ($::decimal)" { + set val1 $expect_out(1,string) + pass "$gdb_test_name" + } + } + + # Sleep for a bit, so the variable is sure to be incremented, if + # indeed we managed to get the target running. + sleep 1 + + set val2 "" + gdb_test_multiple "print global_var" "global_var twice" { + -re -wrap " = ($::decimal)" { + set val2 $expect_out(1,string) + pass "$gdb_test_name" + } + } + + # The variable should have incremented. (Note we didn't give it + # sufficient time to ever wrap around.) + gdb_assert {$val1 != $val2} "values are different" + + set wp_str [expr ($hw)?"Hardware watchpoint":"Watchpoint"] + + # Now set a watchpoint, while the inferior is running. Since + # we're watching a global, and we can read global memory while the + # target is running, this should be able to work. + gdb_test_multiple "watch global_var" "" { + -re "$wp_str $::decimal: global_var\r\n$::gdb_prompt " { + pass $gdb_test_name + } + } + + # Check that the watchpoint triggers. + + save_vars ::timeout { + if {!$hw} { + # This doesn't currently work with software watchpoints. + # GDB should transparently temporarily pause the inferior, + # to force it to single step, but it doesn't, so the + # thread continues running free. + setup_kfail gdb/111111 *-*-* + + # No point waiting too much for output we know isn't + # coming. + set ::timeout 1 + } + set re [multi_line \ + "$wp_str $::decimal: global_var" \ + "" \ + "Old value = $::decimal" \ + "New value = $::decimal"] + gdb_test_multiple "" "watchpoint hit" { + -re $re { + pass $gdb_test_name + } + } + } +} + +foreach hw {0 1} { + if {!$allow_hw_watchpoint_tests_p} { + continue + } + foreach stop_mode {all-stop non-stop} { + set wp_type [expr ($hw)?"hardware":"software"] + with_test_prefix "$stop_mode: $wp_type" { + test $stop_mode $hw + } + } +}