[PING] [PATCH v2] [gdb/breakpoints] Fix gdb.base/scope-hw-watch-disable.exp on arm-linux
Tom de Vries
tdevries@suse.de
Wed Sep 4 07:38:53 GMT 2024
On 8/21/24 13:09, Tom de Vries wrote:
> 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.
>
Ping.
Thanks,
- Tom
> Tested on x86_64-linux.
>
> PR breakpoints/31860
> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31860
> ---
> gdb/breakpoint.c | 100 ++++++++++--------
> .../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 17bd627f867..6d8610d14e7 100644
> --- a/gdb/breakpoint.c
> +++ b/gdb/breakpoint.c
> @@ -10504,45 +10504,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
> @@ -10613,14 +10574,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);
>
> @@ -10628,7 +10581,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 61137703e5c..29eb682df47 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
>
> base-commit: 28c3bf55f0f9aca8619c6d01be34a02a887c5577
More information about the Gdb-patches
mailing list