Handle reinsert breakpoints for vforked child
authorYao Qi <yao.qi@linaro.org>
Fri, 17 Jun 2016 09:25:13 +0000 (10:25 +0100)
committerYao Qi <yao.qi@linaro.org>
Fri, 17 Jun 2016 09:38:55 +0000 (10:38 +0100)
When a thread is doing step-over with reinsert breakpoint, and the
instruction executed is a syscall doing vfork, both parent and child
share the memory, so the reinsert breakpoint in the space is visible
to both of them.  Also, removing the reinsert breakpoints from the
child will effectively remove them from the parent.  We should
carefully manipulate reinsert breakpoints for both processes.

What we are doing here is that

 - uninsert reinsert breakpoints from the parent before cloning the
   breakpoint list.  We use "uninsert" instead of "remove", because
   we need to "reinsert" them back after vfork is done.  In fact,
   "uninsert" removes them from both child and parent process space.
 - reinsert breakpoints in parent process are still copied to child's
   breakpoint list,
 - remove them from child's breakpoint list as what we did for fork,
   at this point, reinsert breakpoints are removed from the child and
   the parent, but they are still tracked by the parent's breakpoint
   list,
 - once vfork is done, "reinsert" them back to the parent,

gdb/gdbserver:

2016-06-17  Yao Qi  <yao.qi@linaro.org>

* linux-low.c (handle_extended_wait): Call
uninsert_reinsert_breakpoints for the parent process.  Remove
reinsert breakpoints from the child process.  Reinsert them to
the parent process when vfork is done.
* mem-break.c (uninsert_reinsert_breakpoints): New function.
(reinsert_reinsert_breakpoints): New function.
* mem-break.h (uninsert_reinsert_breakpoints): Declare
(reinsert_reinsert_breakpoints): Declare.

gdb/gdbserver/ChangeLog
gdb/gdbserver/linux-low.c
gdb/gdbserver/mem-break.c
gdb/gdbserver/mem-break.h

index 734d70dd656177f1eb5e1ce68b12017508b1c556..6983e19a704e17e75d9880e0fc9eb1f2e0ad3de6 100644 (file)
@@ -1,3 +1,14 @@
+2016-06-17  Yao Qi  <yao.qi@linaro.org>
+
+       * linux-low.c (handle_extended_wait): Call
+       uninsert_reinsert_breakpoints for the parent process.  Remove
+       reinsert breakpoints from the child process.  Reinsert them to
+       the parent process when vfork is done.
+       * mem-break.c (uninsert_reinsert_breakpoints): New function.
+       (reinsert_reinsert_breakpoints): New function.
+       * mem-break.h (uninsert_reinsert_breakpoints): Declare
+       (reinsert_reinsert_breakpoints): Declare.
+
 2016-06-17  Yao Qi  <yao.qi@linaro.org>
 
        * linux-low.c (handle_extended_wait): If the parent is doing
index 3f2d08eea5accbc7e4f84a4219ca208df3dcc658..dd92e7814af99d8e91b1d7168321a0cedb6a3eb9 100644 (file)
@@ -543,6 +543,22 @@ handle_extended_wait (struct lwp_info **orig_event_lwp, int wstat)
 
          parent_proc = get_thread_process (event_thr);
          child_proc->attached = parent_proc->attached;
+
+         if (event_lwp->bp_reinsert != 0
+             && can_software_single_step ()
+             && event == PTRACE_EVENT_VFORK)
+           {
+             struct thread_info *saved_thread = current_thread;
+
+             current_thread = event_thr;
+             /* If we leave reinsert breakpoints there, child will
+                hit it, so uninsert reinsert breakpoints from parent
+                (and child).  Once vfork child is done, reinsert
+                them back to parent.  */
+             uninsert_reinsert_breakpoints ();
+             current_thread = saved_thread;
+           }
+
          clone_all_breakpoints (&child_proc->breakpoints,
                                 &child_proc->raw_breakpoints,
                                 parent_proc->breakpoints);
@@ -570,12 +586,12 @@ handle_extended_wait (struct lwp_info **orig_event_lwp, int wstat)
          event_lwp->status_pending = wstat;
 
          /* If the parent thread is doing step-over with reinsert
-            breakpoints, the reinsert breakpoints are still in forked
-            child's process space and cloned to its breakpoint list
-            from the parent's.  Remove them from the child process.  */
+            breakpoints, the list of reinsert breakpoints are cloned
+            from the parent's.  Remove them from the child process.
+            In case of vfork, we'll reinsert them back once vforked
+            child is done.  */
          if (event_lwp->bp_reinsert != 0
-             && can_software_single_step ()
-             && event == PTRACE_EVENT_FORK)
+             && can_software_single_step ())
            {
              struct thread_info *saved_thread = current_thread;
 
@@ -639,6 +655,18 @@ handle_extended_wait (struct lwp_info **orig_event_lwp, int wstat)
     {
       event_lwp->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE;
 
+      if (event_lwp->bp_reinsert != 0 && can_software_single_step ())
+       {
+         struct thread_info *saved_thread = current_thread;
+         struct process_info *proc = get_thread_process (event_thr);
+
+         current_thread = event_thr;
+         reinsert_reinsert_breakpoints ();
+         current_thread = saved_thread;
+
+         gdb_assert (has_reinsert_breakpoints (proc));
+       }
+
       /* Report the event.  */
       return 0;
     }
index c27e803027ce44b1bd0acee51a10f5b2127dc31d..5c73326ebb547a5c93bbae09ca4011c1a0ecd182 100644 (file)
@@ -1509,6 +1509,26 @@ uninsert_all_breakpoints (void)
       uninsert_raw_breakpoint (bp);
 }
 
+void
+uninsert_reinsert_breakpoints (void)
+{
+  struct process_info *proc = current_process ();
+  struct breakpoint *bp;
+
+  for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
+    {
+    if (bp->type == reinsert_breakpoint)
+      {
+       gdb_assert (bp->raw->inserted > 0);
+
+       /* Only uninsert the raw breakpoint if it only belongs to a
+          reinsert breakpoint.  */
+       if (bp->raw->refcount == 1)
+         uninsert_raw_breakpoint (bp->raw);
+      }
+    }
+}
+
 static void
 reinsert_raw_breakpoint (struct raw_breakpoint *bp)
 {
@@ -1588,6 +1608,24 @@ reinsert_all_breakpoints (void)
       reinsert_raw_breakpoint (bp);
 }
 
+void
+reinsert_reinsert_breakpoints (void)
+{
+  struct process_info *proc = current_process ();
+  struct breakpoint *bp;
+
+  for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
+    {
+      if (bp->type == reinsert_breakpoint)
+       {
+         gdb_assert (bp->raw->inserted > 0);
+
+         if (bp->raw->refcount == 1)
+           reinsert_raw_breakpoint (bp->raw);
+       }
+    }
+}
+
 void
 check_breakpoints (CORE_ADDR stop_pc)
 {
index b84dc1e2415f5eab6e6ed7cd5e27e2aea96bb72c..6a06c0c0f7c8fde6fb4ab10e6fd730159712f536 100644 (file)
@@ -158,6 +158,15 @@ void set_reinsert_breakpoint (CORE_ADDR stop_at);
 
 void delete_reinsert_breakpoints (void);
 
+/* Reinsert all reinsert breakpoints of the current process.  */
+
+void reinsert_reinsert_breakpoints (void);
+
+/* Uninsert all reinsert breakpoints of the current process.  This
+   still leaves the reinsert breakpoints in the table.  */
+
+void uninsert_reinsert_breakpoints (void);
+
 /* Reinsert breakpoints at WHERE (and change their status to
    inserted).  */