diff options
author | Tom de Vries <tdevries@suse.de> | 2024-10-10 12:41:40 +0200 |
---|---|---|
committer | Tom de Vries <tdevries@suse.de> | 2024-10-10 12:41:40 +0200 |
commit | f04b2702faf409b0d54a1dacd4ef4beb4272c35d (patch) | |
tree | 9f1109770ed7728e2af2fb6982f2cc42c5197aa9 | |
parent | a98a6fa2d8ef5eb61534b07db80850dcdf07bdb4 (diff) | |
download | gdb-f04b2702faf409b0d54a1dacd4ef4beb4272c35d.zip gdb-f04b2702faf409b0d54a1dacd4ef4beb4272c35d.tar.gz gdb-f04b2702faf409b0d54a1dacd4ef4beb4272c35d.tar.bz2 |
[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
-rw-r--r-- | gdb/breakpoint.c | 100 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/scope-hw-watch-disable.exp | 18 |
2 files changed, 71 insertions, 47 deletions
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 10e2ef3..4cf6857 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -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. diff --git a/gdb/testsuite/gdb.base/scope-hw-watch-disable.exp b/gdb/testsuite/gdb.base/scope-hw-watch-disable.exp index 6113770..29eb682 100644 --- a/gdb/testsuite/gdb.base/scope-hw-watch-disable.exp +++ b/gdb/testsuite/gdb.base/scope-hw-watch-disable.exp @@ -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 |