[gdb/breakpoints] Fix gdb.base/scope-hw-watch-disable.exp on arm-linux

On arm-linux, with test-case gdb.base/scope-hw-watch-disable.exp I run into:
...
(gdb) awatch a^M
Can't set read/access watchpoint when hardware watchpoints are disabled.^M
(gdb) PASS: $exp: unsuccessful attempt to create an access watchpoint
rwatch b^M
Can't set read/access watchpoint when hardware watchpoints are disabled.^M
(gdb) PASS: $exp: unsuccessful attempt to create a read watchpoint
continue^M
Continuing.^M
^M
Program received signal SIGSEGV, Segmentation fault.^M
0xf7ec82c8 in ?? () from /lib/arm-linux-gnueabihf/libc.so.6^M
(gdb) FAIL: $exp: continue until exit
...

Using "maint info break", we can see that the two failed attempts to set a
watchpoint each left behind a stale "watchpoint scope" breakpoint:
...
-5      watchpoint scope del  y   0xf7ec569a  inf 1
-5.1                          y   0xf7ec569a  inf 1
	stop only in stack frame at 0xfffef4f8
-6      watchpoint scope del  y   0xf7ec569a  inf 1
-6.1                          y   0xf7ec569a  inf 1
	stop only in stack frame at 0xfffef4f8
...

The SIGSEGV is a consequence of the stale "watchpoint scope" breakpoint: the
same happens if we:
- have can-use-hw-watchpoints == 1,
- set one of the watchpoints, and
- continue to exit.
The problem is missing symbol info on libc which is supposed to tell which
code is thumb.  After doing "set arm fallback-mode thumb" the SIGSEGV
disappears.

Extend the test-case to check the "maint info break" command before and after
the two failed attempts, to make sure that we catch the stale
"watchpoint scope" breakpoints also on x86_64-linux.

Fix this in watch_command_1 by moving creation of the "watchpoint scope"
breakpoint after the call to update_watchpoint.

Tested on x86_64-linux.

PR breakpoints/31860
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31860
This commit is contained in:
Tom de Vries
2024-10-10 12:41:40 +02:00
parent a98a6fa2d8
commit f04b2702fa
2 changed files with 71 additions and 47 deletions

View File

@@ -10480,45 +10480,6 @@ watch_command_1 (const char *arg, int accessflag, int from_tty,
'wp_frame'. */
frame_id watchpoint_frame = get_frame_id (wp_frame);
/* If the expression is "local", then set up a "watchpoint scope"
breakpoint at the point where we've left the scope of the watchpoint
expression. Create the scope breakpoint before the watchpoint, so
that we will encounter it first in bpstat_stop_status. */
if (exp_valid_block != NULL && wp_frame != NULL)
{
frame_id caller_frame_id = frame_unwind_caller_id (wp_frame);
if (frame_id_p (caller_frame_id))
{
gdbarch *caller_arch = frame_unwind_caller_arch (wp_frame);
CORE_ADDR caller_pc = frame_unwind_caller_pc (wp_frame);
scope_breakpoint
= create_internal_breakpoint (caller_arch, caller_pc,
bp_watchpoint_scope);
/* create_internal_breakpoint could invalidate WP_FRAME. */
wp_frame = NULL;
scope_breakpoint->enable_state = bp_enabled;
/* Automatically delete the breakpoint when it hits. */
scope_breakpoint->disposition = disp_del;
/* Only break in the proper frame (help with recursion). */
scope_breakpoint->frame_id = caller_frame_id;
/* Set the address at which we will stop. */
bp_location &loc = scope_breakpoint->first_loc ();
loc.gdbarch = caller_arch;
loc.requested_address = caller_pc;
loc.address
= adjust_breakpoint_address (loc.gdbarch, loc.requested_address,
scope_breakpoint->type,
current_program_space);
}
}
/* Now set up the breakpoint. We create all watchpoints as hardware
watchpoints here even if hardware watchpoints are turned off, a call
to update_watchpoint later in this function will cause the type to
@@ -10589,14 +10550,6 @@ watch_command_1 (const char *arg, int accessflag, int from_tty,
w->watchpoint_thread = null_ptid;
}
if (scope_breakpoint != NULL)
{
/* The scope breakpoint is related to the watchpoint. We will
need to act on them together. */
w->related_breakpoint = scope_breakpoint;
scope_breakpoint->related_breakpoint = w.get ();
}
if (!just_location)
value_free_to_mark (mark);
@@ -10604,7 +10557,60 @@ watch_command_1 (const char *arg, int accessflag, int from_tty,
that should be inserted. */
update_watchpoint (w.get (), true /* reparse */);
/* If the expression is "local", then set up a "watchpoint scope"
breakpoint at the point where we've left the scope of the watchpoint
expression. Create the scope breakpoint before the watchpoint, so
that we will encounter it first in bpstat_stop_status. */
if (exp_valid_block != nullptr && wp_frame != nullptr)
{
frame_id caller_frame_id = frame_unwind_caller_id (wp_frame);
if (frame_id_p (caller_frame_id))
{
gdbarch *caller_arch = frame_unwind_caller_arch (wp_frame);
CORE_ADDR caller_pc = frame_unwind_caller_pc (wp_frame);
scope_breakpoint
= create_internal_breakpoint (caller_arch, caller_pc,
bp_watchpoint_scope);
/* create_internal_breakpoint could invalidate WP_FRAME. */
wp_frame = nullptr;
scope_breakpoint->enable_state = bp_enabled;
/* Automatically delete the breakpoint when it hits. */
scope_breakpoint->disposition = disp_del;
/* Only break in the proper frame (help with recursion). */
scope_breakpoint->frame_id = caller_frame_id;
/* Set the address at which we will stop. */
bp_location &loc = scope_breakpoint->first_loc ();
loc.gdbarch = caller_arch;
loc.requested_address = caller_pc;
loc.address
= adjust_breakpoint_address (loc.gdbarch, loc.requested_address,
scope_breakpoint->type,
current_program_space);
}
}
if (scope_breakpoint != nullptr)
{
/* The scope breakpoint is related to the watchpoint. We will
need to act on them together. */
w->related_breakpoint = scope_breakpoint;
scope_breakpoint->related_breakpoint = w.get ();
}
/* Verify that the scope breakpoint comes before the watchpoint in the
breakpoint chain. */
gdb_assert (scope_breakpoint == nullptr
|| &breakpoint_chain.back () == scope_breakpoint);
watchpoint *watchpoint_ptr = w.get ();
install_breakpoint (internal, std::move (w), 1);
gdb_assert (&breakpoint_chain.back () == watchpoint_ptr);
}
/* Return count of debug registers needed to watch the given expression.

View File

@@ -29,6 +29,15 @@ if {![runto_main]} {
return -1
}
gdb_test_multiple "maint info break" "maint info break before" {
-re -wrap "watchpoint.*" {
fail $gdb_test_name
}
-re -wrap "" {
pass $gdb_test_name
}
}
gdb_test "awatch a" \
"Can't set read/access watchpoint when hardware watchpoints are disabled." \
"unsuccessful attempt to create an access watchpoint"
@@ -36,5 +45,14 @@ gdb_test "rwatch b" \
"Can't set read/access watchpoint when hardware watchpoints are disabled." \
"unsuccessful attempt to create a read watchpoint"
gdb_test_multiple "maint info break" "maint info break after" {
-re -wrap "watchpoint.*" {
fail $gdb_test_name
}
-re -wrap "" {
pass $gdb_test_name
}
}
# The program continues until termination.
gdb_continue_to_end