Extended-remote follow vfork
authorDon Breazeal <donb@codesourcery.com>
Tue, 12 May 2015 16:52:45 +0000 (09:52 -0700)
committerDon Breazeal <donb@codesourcery.com>
Tue, 12 May 2015 16:52:45 +0000 (09:52 -0700)
This patch implements follow-fork for vfork on extended-remote Linux targets.

The implementation follows the native implementation as much as possible.
Most of the work is done on the GDB side in the existing code now in
infrun.c.  GDBserver just has to report the events and do a little
bookkeeping.

Implementation includes:

 * enabling VFORK events by adding ptrace options for VFORK and VFORK_DONE
   to linux-low.c:linux_low_ptrace_options.

 * handling VFORK and VFORK_DONE events in linux-low.c:handle_extended_wait
   and reporting them to GDB.

 * including VFORK and VFORK_DONE events in the predicate
   linux-low.c:extended_event_reported.

 * adding support for VFORK and VFORK_DONE events in RSP by adding stop
   reasons "vfork" and "vforkdone" to the 'T' Stop Reply Packet in both
   gdbserver/remote-utils.c and gdb/remote.c.

Tested on x64 Ubuntu Lucid, native, remote, extended-remote.

gdb/gdbserver/ChangeLog:

        * linux-low.c (handle_extended_wait): Handle PTRACE_EVENT_FORK and
        PTRACE_EVENT_VFORK_DONE.
        (linux_low_ptrace_options, extended_event_reported): Add vfork
        events.
        * remote-utils.c (prepare_resume_reply): New stop reasons "vfork"
        and "vforkdone" for RSP 'T' Stop Reply Packet.
        * server.h (report_vfork_events): Declare
        global variable.

gdb/ChangeLog:

        * remote.c (remove_vfork_event_p): New function.
        (remote_follow_fork): Add vfork event type to event checking.
        (remote_parse_stop_reply): New stop reasons "vfork" and
        "vforkdone" for RSP 'T' Stop Reply Packet.

gdb/ChangeLog
gdb/gdbserver/ChangeLog
gdb/gdbserver/linux-low.c
gdb/gdbserver/remote-utils.c
gdb/gdbserver/server.h
gdb/remote.c

index c9c2e62eecffd1337db6c2460281dfe5874d7827..e84f23faf84fd372f6ac7408ed35a9f8e11dfdb2 100644 (file)
@@ -1,3 +1,10 @@
+2015-05-12  Don Breazeal  <donb@codesourcery.com>
+
+       * remote.c (remove_vfork_event_p): New function.
+       (remote_follow_fork): Add vfork event type to event checking.
+       (remote_parse_stop_reply): New stop reasons "vfork" and
+       "vforkdone" for RSP 'T' Stop Reply Packet.
+
 2015-05-12  Don Breazeal  <donb@codesourcery.com>
 
        * linux-nat.c (linux_nat_ptrace_options): New function.
index 0bdcdf36c368d29202e2011397d5c67ae79123f6..1e20a36cacbefe1e1c51f1bbab5e3ea36ddde6cf 100644 (file)
@@ -1,3 +1,14 @@
+2015-05-12  Don Breazeal  <donb@codesourcery.com>
+
+       * linux-low.c (handle_extended_wait): Handle PTRACE_EVENT_FORK and
+       PTRACE_EVENT_VFORK_DONE.
+       (linux_low_ptrace_options, extended_event_reported): Add vfork
+       events.
+       * remote-utils.c (prepare_resume_reply): New stop reasons "vfork"
+       and "vforkdone" for RSP 'T' Stop Reply Packet.
+       * server.h (report_vfork_events): Declare
+       global variable.
+
 2015-05-12  Don Breazeal  <donb@codesourcery.com>
 
        * linux-aarch64-low.c (aarch64_linux_new_fork): New function.
index d9053ad6048e000a501f0a3869bceb946e8a283d..9f3ea4881ff4f0ba015fe998eb14e022c6b573b1 100644 (file)
@@ -425,7 +425,8 @@ handle_extended_wait (struct lwp_info *event_lwp, int wstat)
   struct thread_info *event_thr = get_lwp_thread (event_lwp);
   struct lwp_info *new_lwp;
 
-  if ((event == PTRACE_EVENT_FORK) || (event == PTRACE_EVENT_CLONE))
+  if ((event == PTRACE_EVENT_FORK) || (event == PTRACE_EVENT_VFORK)
+      || (event == PTRACE_EVENT_CLONE))
     {
       ptid_t ptid;
       unsigned long new_pid;
@@ -451,7 +452,7 @@ handle_extended_wait (struct lwp_info *event_lwp, int wstat)
            warning ("wait returned unexpected status 0x%x", status);
        }
 
-      if (event == PTRACE_EVENT_FORK)
+      if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK)
        {
          struct process_info *parent_proc;
          struct process_info *child_proc;
@@ -494,8 +495,13 @@ handle_extended_wait (struct lwp_info *event_lwp, int wstat)
            the_low_target.new_fork (parent_proc, child_proc);
 
          /* Save fork info in the parent thread.  */
-         event_lwp->waitstatus.kind = TARGET_WAITKIND_FORKED;
+         if (event == PTRACE_EVENT_FORK)
+           event_lwp->waitstatus.kind = TARGET_WAITKIND_FORKED;
+         else if (event == PTRACE_EVENT_VFORK)
+           event_lwp->waitstatus.kind = TARGET_WAITKIND_VFORKED;
+
          event_lwp->waitstatus.value.related_pid = ptid;
+
          /* The status_pending field contains bits denoting the
             extended event, so when the pending event is handled,
             the handler will look at lwp->waitstatus.  */
@@ -538,6 +544,13 @@ handle_extended_wait (struct lwp_info *event_lwp, int wstat)
       /* Don't report the event.  */
       return 1;
     }
+  else if (event == PTRACE_EVENT_VFORK_DONE)
+    {
+      event_lwp->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE;
+
+      /* Report the event.  */
+      return 0;
+    }
 
   internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
 }
@@ -2010,6 +2023,9 @@ linux_low_ptrace_options (int attached)
   if (report_fork_events)
     options |= PTRACE_O_TRACEFORK;
 
+  if (report_vfork_events)
+    options |= (PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE);
+
   return options;
 }
 
@@ -2714,7 +2730,9 @@ extended_event_reported (const struct target_waitstatus *waitstatus)
   if (waitstatus == NULL)
     return 0;
 
-  return (waitstatus->kind == TARGET_WAITKIND_FORKED);
+  return (waitstatus->kind == TARGET_WAITKIND_FORKED
+         || waitstatus->kind == TARGET_WAITKIND_VFORKED
+         || waitstatus->kind == TARGET_WAITKIND_VFORK_DONE);
 }
 
 /* Wait for process, returns status.  */
index 60783489b8a1fdebc0dddb9099bef58cf4db540b..bb3145683bbea5fb0c461a9d1ed90b25dbdc8b34 100644 (file)
@@ -1115,16 +1115,20 @@ prepare_resume_reply (char *buf, ptid_t ptid,
     {
     case TARGET_WAITKIND_STOPPED:
     case TARGET_WAITKIND_FORKED:
+    case TARGET_WAITKIND_VFORKED:
       {
        struct thread_info *saved_thread;
        const char **regp;
        struct regcache *regcache;
 
-       if (status->kind == TARGET_WAITKIND_FORKED && report_fork_events)
+       if ((status->kind == TARGET_WAITKIND_FORKED && report_fork_events)
+           || (status->kind == TARGET_WAITKIND_VFORKED && report_vfork_events))
          {
            enum gdb_signal signal = GDB_SIGNAL_TRAP;
+           const char *event = (status->kind == TARGET_WAITKIND_FORKED
+                                ? "fork" : "vfork");
 
-           sprintf (buf, "T%02xfork:", signal);
+           sprintf (buf, "T%02x%s:", signal, event);
            buf += strlen (buf);
            buf = write_ptid (buf, status->value.related_pid);
            strcat (buf, ";");
@@ -1244,6 +1248,16 @@ prepare_resume_reply (char *buf, ptid_t ptid,
       else
        sprintf (buf, "X%02x", status->value.sig);
       break;
+    case TARGET_WAITKIND_VFORK_DONE:
+      if (report_vfork_events)
+       {
+         enum gdb_signal signal = GDB_SIGNAL_TRAP;
+
+         sprintf (buf, "T%02xvforkdone:;", signal);
+       }
+      else
+       sprintf (buf, "T%02x", GDB_SIGNAL_0);
+      break;
     default:
       error ("unhandled waitkind");
       break;
index 696a24e8c4ac85021d608989f825c3f288b45cf6..09a562455755ea8934539c732a88a17d4c302be6 100644 (file)
@@ -85,6 +85,7 @@ extern int disable_packet_qfThreadInfo;
 extern int run_once;
 extern int multi_process;
 extern int report_fork_events;
+extern int report_vfork_events;
 extern int non_stop;
 
 /* True if the "swbreak+" feature is active.  In that case, GDB wants
index 390d30db82e6c61f159aed4b619ba268a6e1e7ed..df8fca3513cdc5b2264dea5829ffac6bbdb648e8 100644 (file)
@@ -1470,6 +1470,14 @@ remote_fork_event_p (struct remote_state *rs)
   return packet_support (PACKET_fork_event_feature) == PACKET_ENABLE;
 }
 
+/* Returns true if vfork events are supported.  */
+
+static int
+remote_vfork_event_p (struct remote_state *rs)
+{
+  return packet_support (PACKET_vfork_event_feature) == PACKET_ENABLE;
+}
+
 /* Tokens for use by the asynchronous signal handlers for SIGINT.  */
 static struct async_signal_handler *async_sigint_remote_twice_token;
 static struct async_signal_handler *async_sigint_remote_token;
@@ -4537,8 +4545,10 @@ remote_follow_fork (struct target_ops *ops, int follow_child,
                    int detach_fork)
 {
   struct remote_state *rs = get_remote_state ();
+  enum target_waitkind kind = inferior_thread ()->pending_follow.kind;
 
-  if (remote_fork_event_p (rs))
+  if ((kind == TARGET_WAITKIND_FORKED && remote_fork_event_p (rs))
+      || (kind == TARGET_WAITKIND_VFORKED && remote_vfork_event_p (rs)))
     {
       /* When following the parent and detaching the child, we detach
         the child here.  For the case of following the child and
@@ -5727,6 +5737,16 @@ Packet: '%s'\n"),
              event->ws.value.related_pid = read_ptid (++p1, &p);
              event->ws.kind = TARGET_WAITKIND_FORKED;
            }
+         else if (strncmp (p, "vfork", p1 - p) == 0)
+           {
+             event->ws.value.related_pid = read_ptid (++p1, &p);
+             event->ws.kind = TARGET_WAITKIND_VFORKED;
+           }
+         else if (strncmp (p, "vforkdone", p1 - p) == 0)
+           {
+             event->ws.kind = TARGET_WAITKIND_VFORK_DONE;
+             p = skip_to_semicolon (p1 + 1);
+           }
          else
            {
              ULONGEST pnum;