detach and breakpoint removal
authorPedro Alves <pedro@palves.net>
Sun, 13 Dec 2020 01:35:05 +0000 (01:35 +0000)
committerPedro Alves <pedro@palves.net>
Wed, 3 Feb 2021 01:15:12 +0000 (01:15 +0000)
A following patch will add a testcase that has a number of threads
constantly stepping over a breakpoint, and then has GDB detach the
process.  That testcase sometimes fails with the inferior crashing
with SIGTRAP after the detach because of the bug fixed by this patch,
when tested with the native target.

The problem is that target_detach removes breakpoints from the target
immediately, and that does not work with the native GNU/Linux target
(and probably no other native target) currently.  The test wouldn't
fail with this issue when testing against gdbserver, because gdbserver
does allow accessing memory while the current thread is running, by
transparently pausing all threads temporarily, without GDB noticing.
Implementing that in gdbserver was a lot of work, so I'm not looking
forward right now to do the same in the native target.  Instead, I
came up with a simpler solution -- push the breakpoints removal down
to the targets.  The Linux target conveniently already pauses all
threads before detaching them, since PTRACE_DETACH only works with
stopped threads, so we move removing breakpoints to after that.  Only
the remote and GNU/Linux targets support support async execution, so
no other target should really need this.

gdb/ChangeLog:

* linux-nat.c (linux_nat_target::detach): Remove breakpoints
here...
* remote.c (remote_target::remote_detach_1): ... and here ...
* target.c (target_detach): ... instead of here.
* target.h (target_ops::detach): Add comment.

gdb/ChangeLog
gdb/linux-nat.c
gdb/remote.c
gdb/target.c
gdb/target.h

index 62aab57972f591e01ee74b63dff8d2d0f7bb6225..4113e3a6284cc3d6e8ecba622d47e350d7294576 100644 (file)
@@ -1,3 +1,11 @@
+2021-02-03  Pedro Alves  <pedro@palves.net>
+
+       * linux-nat.c (linux_nat_target::detach): Remove breakpoints
+       here...
+       * remote.c (remote_target::remote_detach_1): ... and here ...
+       * target.c (target_detach): ... instead of here.
+       * target.h (target_ops::detach): Add comment.
+
 2021-02-03  Pedro Alves  <pedro@palves.net>
 
        * infrun.c (struct wait_one_event): Move higher up.
index 3c7117bff6e616376201988ee77a591244329ebc..10419dc7bb58b5bb589bee59f7d5e37f0b11f047 100644 (file)
@@ -1456,6 +1456,11 @@ linux_nat_target::detach (inferior *inf, int from_tty)
      they're no longer running.  */
   iterate_over_lwps (ptid_t (pid), stop_wait_callback);
 
+  /* We can now safely remove breakpoints.  We don't this in earlier
+     in common code because this target doesn't currently support
+     writing memory while the inferior is running.  */
+  remove_breakpoints_inf (current_inferior ());
+
   iterate_over_lwps (ptid_t (pid), detach_callback);
 
   /* Only the initial process should be left right now.  */
index 512bd9467f6b4928a0305befe07c82c9392120a3..c544fe7fcece9105fd4f4f47877df54e33be8268 100644 (file)
@@ -5800,6 +5800,16 @@ remote_target::remote_detach_1 (inferior *inf, int from_tty)
 
   target_announce_detach (from_tty);
 
+  if (!gdbarch_has_global_breakpoints (target_gdbarch ()))
+    {
+      /* If we're in breakpoints-always-inserted mode, or the inferior
+        is running, we have to remove breakpoints before detaching.
+        We don't do this in common code instead because not all
+        targets support removing breakpoints while the target is
+        running.  The remote target / gdbserver does, though.  */
+      remove_breakpoints_inf (current_inferior ());
+    }
+
   /* Tell the remote target to detach.  */
   remote_detach_pid (pid);
 
index 3a03a0ad530e7c057bdff70ee8d55185503b9e55..9a8473d40e1afebcbc6af935dcc78fc5f1c3e5a6 100644 (file)
@@ -1949,15 +1949,6 @@ target_detach (inferior *inf, int from_tty)
      assertion.  */
   gdb_assert (inf == current_inferior ());
 
-  if (gdbarch_has_global_breakpoints (target_gdbarch ()))
-    /* Don't remove global breakpoints here.  They're removed on
-       disconnection from the target.  */
-    ;
-  else
-    /* If we're in breakpoints-always-inserted mode, have to remove
-       breakpoints before detaching.  */
-    remove_breakpoints_inf (current_inferior ());
-
   prepare_for_detach ();
 
   /* Hold a strong reference because detaching may unpush the
index 917476d16ab65145d4aa93c3d03178f952c33b72..c97ef690e01c167667ed8ddfba962bdfa58ba514 100644 (file)
@@ -470,8 +470,15 @@ struct target_ops
     virtual void attach (const char *, int);
     virtual void post_attach (int)
       TARGET_DEFAULT_IGNORE ();
+
+    /* Detaches from the inferior.  Note that on targets that support
+       async execution (i.e., targets where it is possible to detach
+       from programs with threads running), the target is responsible
+       for removing breakpoints from the program before the actual
+       detach, otherwise the program dies when it hits one.  */
     virtual void detach (inferior *, int)
       TARGET_DEFAULT_IGNORE ();
+
     virtual void disconnect (const char *, int)
       TARGET_DEFAULT_NORETURN (tcomplain ());
     virtual void resume (ptid_t,