]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
[gdb/breakpoints] Fix gdb.base/scope-hw-watch-disable.exp on arm-linux
authorTom de Vries <tdevries@suse.de>
Thu, 10 Oct 2024 10:41:40 +0000 (12:41 +0200)
committerTom de Vries <tdevries@suse.de>
Thu, 10 Oct 2024 10:41:40 +0000 (12:41 +0200)
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

gdb/breakpoint.c
gdb/testsuite/gdb.base/scope-hw-watch-disable.exp

index 10e2ef3852395c8c2887ae5cbe8bc1b5681b3986..4cf6857b9d947c6929323325864a893d01ca54a3 100644 (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.
index 61137703e5c6f8d2439406069063cbd9dcfec077..29eb682df47e4708c5ffe52f64f553150b4230dd 100644 (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