Fix attaching to cloned processes. This fixes PR gdb/61.
authorMark Kettenis <kettenis@gnu.org>
Sun, 14 Oct 2001 11:30:37 +0000 (11:30 +0000)
committerMark Kettenis <kettenis@gnu.org>
Sun, 14 Oct 2001 11:30:37 +0000 (11:30 +0000)
* lin-lwp.c (struct lwp_info): Add new member `cloned'.
(is_cloned) Removed.
(lin_lwp_attach_lwp): Don't call stop_wait_callback.  Instead call
waitpid explicitly.  Mark the LWP as cloned if waitpid fails and
retry with __WCLONE flag.
(lin_lwp_attach): Likewise.  Warn if attaching to a cloned process.
(detach_callback): Replace use of is_cloned with explicit check on
LWP id and process id.
(stop_wait_callback): Replace use of is_cloned with check if LWP
is marked as cloned.
[CHILD_WAIT] (child_wait): New function.
(lin_lwp_wait): Replace use of is_cloned with check if LWP is
marked as cloned.  Mark newly detected LWPs as cloned if detected
by waitpid with __WCLONE flag.
(kill_wait_callback): Replace use of is_cloned with check if LWP
is marked as cloned.
* config/i386/nm-linux.h (struct target_waitstatus): Add forward
declaration.
(child_wait): Add prototype.
(CHILD_WAIT): Define.

gdb/ChangeLog
gdb/config/i386/nm-linux.h
gdb/lin-lwp.c

index 40e45e93d6aa97a22fe3e2e8e6dc84f8c694a185..6cefdb22e791157fbc8de2a4a22334df4cd0102f 100644 (file)
@@ -1,3 +1,27 @@
+2001-10-14  Mark Kettenis  <kettenis@gnu.org>
+
+       Fix attaching to cloned processes.  This fixes PR gdb/61.
+       * lin-lwp.c (struct lwp_info): Add new member `cloned'.
+       (is_cloned) Removed.
+       (lin_lwp_attach_lwp): Don't call stop_wait_callback.  Instead call
+       waitpid explicitly.  Mark the LWP as cloned if waitpid fails and
+       retry with __WCLONE flag.
+       (lin_lwp_attach): Likewise.  Warn if attaching to a cloned process.
+       (detach_callback): Replace use of is_cloned with explicit check on
+       LWP id and process id.
+       (stop_wait_callback): Replace use of is_cloned with check if LWP
+       is marked as cloned.
+       [CHILD_WAIT] (child_wait): New function.
+       (lin_lwp_wait): Replace use of is_cloned with check if LWP is
+       marked as cloned.  Mark newly detected LWPs as cloned if detected
+       by waitpid with __WCLONE flag.
+       (kill_wait_callback): Replace use of is_cloned with check if LWP
+       is marked as cloned.
+       * config/i386/nm-linux.h (struct target_waitstatus): Add forward
+       declaration.
+       (child_wait): Add prototype.
+       (CHILD_WAIT): Define.
+
 2001-10-13  Andrew Cagney  <ac131313@redhat.com>
 
        S/390 31 & 64 bit target and GNU/Linux native support.
index ab42ee59e56e2c8cc15be74a33be5ca564e42abc..089cb1ba47c61c9bab75eb404afc8961641596c9 100644 (file)
@@ -87,6 +87,11 @@ extern int cannot_store_register (int regno);
 /* Override child_resume in `infptrace.c'.  */
 #define CHILD_RESUME
 
+/* Override child_wait in `inftarg.c'.  */
+struct target_waitstatus;
+extern ptid_t child_wait (ptid_t ptid, struct target_waitstatus *ourstatus);
+#define CHILD_WAIT
+
 /* FIXME: kettenis/2000-09-03: This should be moved to ../nm-linux.h
    once we have converted all Linux targets to use the new threads
    stuff (without the #undef of course).  */
index 385b235f37efd9713de256ab66953ed461fe62b9..22d9d74796c58a57350f60a91a6b284f3a5fbfb1 100644 (file)
@@ -75,6 +75,11 @@ struct lwp_info
      and overall process id.  */
   ptid_t ptid;
 
+  /* Non-zero if this LWP is cloned.  In this context "cloned" means
+     that the LWP is reporting to its parent using a signal other than
+     SIGCHLD.  */
+  int cloned;
+
   /* Non-zero if we sent this LWP a SIGSTOP (but the LWP didn't report
      it back yet).  */
   int signalled;
@@ -115,8 +120,6 @@ static int threaded;
 #define is_lwp(ptid)           (GET_LWP (ptid) != 0)
 #define BUILD_LWP(lwp, pid)    ptid_build (pid, lwp, 0)
 
-#define is_cloned(pid) (GET_LWP (pid) != GET_PID (pid))
-
 /* If the last reported event was a SIGTRAP, this variable is set to
    the process id of the LWP/thread that got it.  */
 ptid_t trap_ptid;
@@ -352,18 +355,33 @@ lin_lwp_attach_lwp (ptid_t ptid, int verbose)
   if (verbose)
     printf_filtered ("[New %s]\n", target_pid_to_str (ptid));
 
-  /* We assume that we're already tracing the initial process.  */
-  if (is_cloned (ptid) && ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0)
-    error ("Can't attach %s: %s", target_pid_to_str (ptid), strerror (errno));
-
   lp = find_lwp_pid (ptid);
   if (lp == NULL)
     lp = add_lwp (ptid);
 
-  if (is_cloned (ptid))
+  /* We assume that we're already attached to any LWP that has an
+     id equal to the overall process id.  */
+  if (GET_LWP (ptid) != GET_PID (ptid))
     {
-      lp->signalled = 1;
-      stop_wait_callback (lp, NULL);
+      pid_t pid;
+      int status;
+
+      if (ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0)
+       error ("Can't attach %s: %s", target_pid_to_str (ptid),
+              strerror (errno));
+
+      pid = waitpid (GET_LWP (ptid), &status, 0);
+      if (pid == -1 && errno == ECHILD)
+       {
+         /* Try again with __WCLONE to check cloned processes.  */
+         pid = waitpid (GET_LWP (ptid), &status, __WCLONE);
+         lp->cloned = 1;
+       }
+
+      gdb_assert (pid == GET_LWP (ptid)
+                 && WIFSTOPPED (status) && WSTOPSIG (status));
+
+      lp->stopped = 1;
     }
 }
 
@@ -371,20 +389,33 @@ static void
 lin_lwp_attach (char *args, int from_tty)
 {
   struct lwp_info *lp;
+  pid_t pid;
+  int status;
 
   /* FIXME: We should probably accept a list of process id's, and
      attach all of them.  */
   child_ops.to_attach (args, from_tty);
 
   /* Add the initial process as the first LWP to the list.  */
-  lp = add_lwp (BUILD_LWP (PIDGET (inferior_ptid), PIDGET (inferior_ptid)));
+  lp = add_lwp (BUILD_LWP (GET_PID (inferior_ptid), GET_PID (inferior_ptid)));
 
   /* Make sure the initial process is stopped.  The user-level threads
      layer might want to poke around in the inferior, and that won't
      work if things haven't stabilized yet.  */
-  lp->signalled = 1;
-  stop_wait_callback (lp, NULL);
-  gdb_assert (lp->status == 0);
+  pid = waitpid (GET_PID (inferior_ptid), &status, 0);
+  if (pid == -1 && errno == ECHILD)
+    {
+      warning ("%s is a cloned process", target_pid_to_str (inferior_ptid));
+
+      /* Try again with __WCLONE to check cloned processes.  */
+      pid = waitpid (GET_PID (inferior_ptid), &status, __WCLONE);
+      lp->cloned = 1;
+    }
+
+  gdb_assert (pid == GET_PID (inferior_ptid)
+             && WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP);
+
+  lp->stopped = 1;
 
   /* Fake the SIGSTOP that core GDB expects.  */
   lp->status = W_STOPCODE (SIGSTOP);
@@ -415,7 +446,9 @@ detach_callback (struct lwp_info *lp, void *data)
       gdb_assert (lp->status == 0 || WIFSTOPPED (lp->status));
     }
 
-  if (is_cloned (lp->ptid))
+  /* We don't actually detach from the LWP that has an id equal to the
+     overall process id just yet.  */
+  if (GET_LWP (lp->ptid) != GET_PID (lp->ptid))
     {
       if (ptrace (PTRACE_DETACH, GET_LWP (lp->ptid), 0,
                  WSTOPSIG (lp->status)) < 0)
@@ -433,7 +466,7 @@ lin_lwp_detach (char *args, int from_tty)
 {
   iterate_over_lwps (detach_callback, NULL);
 
-  /* Only the initial (uncloned) process should be left right now.  */
+  /* Only the initial process should be left right now.  */
   gdb_assert (num_lwps == 1);
 
   trap_ptid = null_ptid;
@@ -610,8 +643,7 @@ stop_wait_callback (struct lwp_info *lp, void *data)
 
       gdb_assert (lp->status == 0);
 
-      pid = waitpid (GET_LWP (lp->ptid), &status,
-                    is_cloned (lp->ptid) ? __WCLONE : 0);
+      pid = waitpid (GET_LWP (lp->ptid), &status, lp->cloned ? __WCLONE : 0);
       if (pid == -1 && errno == ECHILD)
        /* OK, the proccess has disappeared.  We'll catch the actual
           exit event in lin_lwp_wait.  */
@@ -888,6 +920,55 @@ resumed_callback (struct lwp_info *lp, void *data)
   return lp->resumed;
 }
 
+#ifdef CHILD_WAIT
+
+/* We need to override child_wait to support attaching to cloned
+   processes, since a normal wait (as done by the default version)
+   ignores those processes.  */
+
+/* Wait for child PTID to do something.  Return id of the child,
+   minus_one_ptid in case of error; store status into *OURSTATUS.  */
+
+ptid_t
+child_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
+{
+  int save_errno;
+  int status;
+  pid_t pid;
+
+  do
+    {
+      set_sigint_trap ();      /* Causes SIGINT to be passed on to the
+                                  attached process.  */
+      set_sigio_trap ();
+
+      pid = waitpid (GET_PID (ptid), &status, 0);
+      if (pid == -1 && errno == ECHILD)
+       /* Try again with __WCLONE to check cloned processes.  */
+       pid = waitpid (GET_PID (ptid), &status, __WCLONE);
+      save_errno = errno;
+
+      clear_sigio_trap ();
+      clear_sigint_trap ();
+    }
+  while (pid == -1 && errno == EINTR);
+
+  if (pid == -1)
+    {
+      warning ("Child process unexpectedly missing: %s", strerror (errno));
+
+      /* Claim it exited with unknown signal.  */
+      ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
+      ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN;
+      return minus_one_ptid;
+    }
+
+  store_waitstatus (ourstatus, status);
+  return pid_to_ptid (pid);
+}
+
+#endif
+
 static ptid_t
 lin_lwp_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
 {
@@ -954,7 +1035,7 @@ lin_lwp_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
       /* If we have to wait, take into account whether PID is a cloned
          process or not.  And we have to convert it to something that
          the layer beneath us can understand.  */
-      options = is_cloned (lp->ptid) ? __WCLONE : 0;
+      options = lp->cloned ? __WCLONE : 0;
       pid = GET_LWP (ptid);
     }
 
@@ -997,6 +1078,9 @@ lin_lwp_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
          if (! lp)
            {
              lp = add_lwp (BUILD_LWP (lwpid, GET_PID (inferior_ptid)));
+             if (options & __WCLONE)
+               lp->cloned = 1;
+
              if (threaded)
                {
                  gdb_assert (WIFSTOPPED (status)
@@ -1189,7 +1273,7 @@ kill_wait_callback (struct lwp_info *lp, void *data)
   /* For cloned processes we must check both with __WCLONE and
      without, since the exit status of a cloned process isn't reported
      with __WCLONE.  */
-  if (is_cloned (lp->ptid))
+  if (lp->cloned)
     {
       do
        {