Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [rfc] Fix linux_test_for_tracefork for newer kernels
@ 2004-11-12 22:59 Daniel Jacobowitz
  2004-11-13 22:25 ` Daniel Jacobowitz
  0 siblings, 1 reply; 3+ messages in thread
From: Daniel Jacobowitz @ 2004-11-12 22:59 UTC (permalink / raw)
  To: gdb-patches

This patch makes the run-time test for fork tracing a little more robust.
In particular, it fixes two things:
  - If the test failed in an unexpected way, then two stopped processes
    could be left around.
  - Waitpid can be interrupted by EINTR.  Kernel 2.6.10-rc1 has some
    changes to wait and signal processing, which cause the SIGCHLD
    to be received before wait returns.  I don't think there's any
    actual bug here, since reissuing the wait works fine.

These changes let my testsuite runs finish without leaving hordes of stopped
processes around.  I won't check it in yet, since I haven't tested it on a
system where this runtime check should fail; in the meantime, I'm just
posting it for comments.  Hopefully I got it right this time!

-- 
Daniel Jacobowitz

2004-11-12  Daniel Jacobowitz  <dan@debian.org>

	* linux-nat.c (my_waitpid): New function.
	(linux_test_for_tracefork): Make more robust and verbose.

Index: gdb-6.3/gdb/linux-nat.c
===================================================================
--- gdb-6.3.orig/gdb/linux-nat.c	2004-10-08 16:29:47.000000000 -0400
+++ gdb-6.3/gdb/linux-nat.c	2004-11-12 17:05:30.000000000 -0500
@@ -150,6 +150,21 @@ linux_tracefork_child (void)
   exit (0);
 }
 
+/* Wrapper function for waitpid which handles EINTR.  */
+
+static int
+my_waitpid (int pid, int *status, int flags)
+{
+  int ret;
+  do
+    {
+      ret = waitpid (pid, status, flags);
+    }
+  while (ret == -1 && errno == EINTR);
+
+  return ret;
+}
+
 /* Determine if PTRACE_O_TRACEFORK can be used to follow fork events.  We
    create a child process, attach to it, use PTRACE_SETOPTIONS to enable
    fork tracing, and let it fork.  If the process exits, we assume that
@@ -169,7 +184,7 @@ linux_test_for_tracefork (void)
   if (child_pid == 0)
     linux_tracefork_child ();
 
-  ret = waitpid (child_pid, &status, 0);
+  ret = my_waitpid (child_pid, &status, 0);
   if (ret == -1)
     perror_with_name ("linux_test_for_tracefork: waitpid");
   else if (ret != child_pid)
@@ -182,8 +197,17 @@ linux_test_for_tracefork (void)
   ret = ptrace (PTRACE_SETOPTIONS, child_pid, 0, PTRACE_O_TRACEFORK);
   if (ret != 0)
     {
-      ptrace (PTRACE_KILL, child_pid, 0, 0);
-      waitpid (child_pid, &status, 0);
+      ret = ptrace (PTRACE_KILL, child_pid, 0, 0);
+      if (ret != 0)
+	{
+	  warning ("linux_test_for_tracefork: failed to kill child");
+	  return;
+	}
+
+      ret = my_waitpid (child_pid, &status, 0);
+      if (ret != 0)
+	warning ("linux_test_for_tracefork: failed to wait for killed child");
+
       return;
     }
 
@@ -192,8 +216,12 @@ linux_test_for_tracefork (void)
 		PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORKDONE);
   linux_supports_tracevforkdone_flag = (ret == 0);
 
-  ptrace (PTRACE_CONT, child_pid, 0, 0);
-  ret = waitpid (child_pid, &status, 0);
+  ret = ptrace (PTRACE_CONT, child_pid, 0, 0);
+  if (ret != 0)
+    warning ("linux_test_for_tracefork: failed to resume child");
+
+  ret = my_waitpid (child_pid, &status, 0);
+
   if (ret == child_pid && WIFSTOPPED (status)
       && status >> 16 == PTRACE_EVENT_FORK)
     {
@@ -204,16 +232,20 @@ linux_test_for_tracefork (void)
 	  int second_status;
 
 	  linux_supports_tracefork_flag = 1;
-	  waitpid (second_pid, &second_status, 0);
-	  ptrace (PTRACE_DETACH, second_pid, 0, 0);
+	  my_waitpid (second_pid, &second_status, 0);
+	  ret = ptrace (PTRACE_KILL, second_pid, 0, 0);
+	  if (ret != 0)
+	    warning ("linux_test_for_tracefork: failed to kill second child");
 	}
     }
+  else
+    warning ("linux_test_for_tracefork: unexpected result from waitpid "
+	     "(%d, status 0x%x)", ret, status);
 
-  if (WIFSTOPPED (status))
-    {
-      ptrace (PTRACE_DETACH, child_pid, 0, 0);
-      waitpid (child_pid, &status, 0);
-    }
+  ret = ptrace (PTRACE_KILL, child_pid, 0, 0);
+  if (ret != 0)
+    warning ("linux_test_for_tracefork: failed to kill child");
+  my_waitpid (child_pid, &status, 0);
 }
 
 /* Return non-zero iff we have tracefork functionality available.


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [rfc] Fix linux_test_for_tracefork for newer kernels
  2004-11-12 22:59 [rfc] Fix linux_test_for_tracefork for newer kernels Daniel Jacobowitz
@ 2004-11-13 22:25 ` Daniel Jacobowitz
  2004-11-21 20:10   ` Daniel Jacobowitz
  0 siblings, 1 reply; 3+ messages in thread
From: Daniel Jacobowitz @ 2004-11-13 22:25 UTC (permalink / raw)
  To: gdb-patches

On Fri, Nov 12, 2004 at 05:59:41PM -0500, Daniel Jacobowitz wrote:
> This patch makes the run-time test for fork tracing a little more robust.
> In particular, it fixes two things:
>   - If the test failed in an unexpected way, then two stopped processes
>     could be left around.
>   - Waitpid can be interrupted by EINTR.  Kernel 2.6.10-rc1 has some
>     changes to wait and signal processing, which cause the SIGCHLD
>     to be received before wait returns.  I don't think there's any
>     actual bug here, since reissuing the wait works fine.
> 
> These changes let my testsuite runs finish without leaving hordes of stopped
> processes around.  I won't check it in yet, since I haven't tested it on a
> system where this runtime check should fail; in the meantime, I'm just
> posting it for comments.  Hopefully I got it right this time!

Now I've tested it in a couple of other systems.  Here's a revised
version, with a spurious warning fixed, and one additional improvement:
on most systems lacking the support, we don't need to fork for the
runtime test.  This fixes some problems in user-mode-linux, which does
the most disgusting things with ptrace I have ever encountered.  Wow!

Barring comments, I'll check this in in a few days.

-- 
Daniel Jacobowitz

2004-11-12  Daniel Jacobowitz  <dan@debian.org>

	* linux-nat.c (my_waitpid): New function.
	(linux_test_for_tracefork): Make more robust and verbose.  Take
	an ORIGINAL_PID argument and test for PTRACE_SETOPTIONS first.
	(linux_supports_tracefork, linux_supports_tracevforkdone): Take a PID
	argument.  Update calls to linux_test_for_tracefork.
	(linux_enable_event_reporting, child_follow_fork)
	(child_insert_fork_catchpoint, child_insert_vfork_catchpoint)
	(child_insert_exec_catchpoint): Update calls to
	linux_supports_tracefork and linux_supports_tracevforkdone.

Index: gdb-6.3/gdb/linux-nat.c
===================================================================
--- gdb-6.3.orig/gdb/linux-nat.c	2004-10-08 16:29:47.000000000 -0400
+++ gdb-6.3/gdb/linux-nat.c	2004-11-13 16:41:51.368720845 -0500
@@ -150,18 +150,47 @@ linux_tracefork_child (void)
   exit (0);
 }
 
-/* Determine if PTRACE_O_TRACEFORK can be used to follow fork events.  We
+/* Wrapper function for waitpid which handles EINTR.  */
+
+static int
+my_waitpid (int pid, int *status, int flags)
+{
+  int ret;
+  do
+    {
+      ret = waitpid (pid, status, flags);
+    }
+  while (ret == -1 && errno == EINTR);
+
+  return ret;
+}
+
+/* Determine if PTRACE_O_TRACEFORK can be used to follow fork events.
+
+   First, we try to enable fork tracing on ORIGINAL_PID.  If this fails,
+   we know that the feature is not available.  This may change the tracing
+   options for ORIGINAL_PID, but we'll be setting them shortly anyway.
+
+   However, if it succeeds, we don't know for sure that the feature is
+   available; old versions of PTRACE_SETOPTIONS ignored unknown options.  We
    create a child process, attach to it, use PTRACE_SETOPTIONS to enable
-   fork tracing, and let it fork.  If the process exits, we assume that
-   we can't use TRACEFORK; if we get the fork notification, and we can
-   extract the new child's PID, then we assume that we can.  */
+   fork tracing, and let it fork.  If the process exits, we assume that we
+   can't use TRACEFORK; if we get the fork notification, and we can extract
+   the new child's PID, then we assume that we can.  */
 
 static void
-linux_test_for_tracefork (void)
+linux_test_for_tracefork (int original_pid)
 {
   int child_pid, ret, status;
   long second_pid;
 
+  linux_supports_tracefork_flag = 0;
+  linux_supports_tracevforkdone_flag = 0;
+
+  ret = ptrace (PTRACE_SETOPTIONS, original_pid, 0, PTRACE_O_TRACEFORK);
+  if (ret != 0)
+    return;
+
   child_pid = fork ();
   if (child_pid == -1)
     perror_with_name ("linux_test_for_tracefork: fork");
@@ -169,7 +198,7 @@ linux_test_for_tracefork (void)
   if (child_pid == 0)
     linux_tracefork_child ();
 
-  ret = waitpid (child_pid, &status, 0);
+  ret = my_waitpid (child_pid, &status, 0);
   if (ret == -1)
     perror_with_name ("linux_test_for_tracefork: waitpid");
   else if (ret != child_pid)
@@ -177,13 +206,23 @@ linux_test_for_tracefork (void)
   if (! WIFSTOPPED (status))
     error ("linux_test_for_tracefork: waitpid: unexpected status %d.", status);
 
-  linux_supports_tracefork_flag = 0;
-
   ret = ptrace (PTRACE_SETOPTIONS, child_pid, 0, PTRACE_O_TRACEFORK);
   if (ret != 0)
     {
-      ptrace (PTRACE_KILL, child_pid, 0, 0);
-      waitpid (child_pid, &status, 0);
+      ret = ptrace (PTRACE_KILL, child_pid, 0, 0);
+      if (ret != 0)
+	{
+	  warning ("linux_test_for_tracefork: failed to kill child");
+	  return;
+	}
+
+      ret = my_waitpid (child_pid, &status, 0);
+      if (ret != child_pid)
+	warning ("linux_test_for_tracefork: failed to wait for killed child");
+      else if (!WIFSIGNALED (status))
+	warning ("linux_test_for_tracefork: unexpected wait status 0x%x from "
+		 "killed child", status);
+
       return;
     }
 
@@ -192,8 +231,12 @@ linux_test_for_tracefork (void)
 		PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORKDONE);
   linux_supports_tracevforkdone_flag = (ret == 0);
 
-  ptrace (PTRACE_CONT, child_pid, 0, 0);
-  ret = waitpid (child_pid, &status, 0);
+  ret = ptrace (PTRACE_CONT, child_pid, 0, 0);
+  if (ret != 0)
+    warning ("linux_test_for_tracefork: failed to resume child");
+
+  ret = my_waitpid (child_pid, &status, 0);
+
   if (ret == child_pid && WIFSTOPPED (status)
       && status >> 16 == PTRACE_EVENT_FORK)
     {
@@ -204,34 +247,38 @@ linux_test_for_tracefork (void)
 	  int second_status;
 
 	  linux_supports_tracefork_flag = 1;
-	  waitpid (second_pid, &second_status, 0);
-	  ptrace (PTRACE_DETACH, second_pid, 0, 0);
+	  my_waitpid (second_pid, &second_status, 0);
+	  ret = ptrace (PTRACE_KILL, second_pid, 0, 0);
+	  if (ret != 0)
+	    warning ("linux_test_for_tracefork: failed to kill second child");
 	}
     }
+  else
+    warning ("linux_test_for_tracefork: unexpected result from waitpid "
+	     "(%d, status 0x%x)", ret, status);
 
-  if (WIFSTOPPED (status))
-    {
-      ptrace (PTRACE_DETACH, child_pid, 0, 0);
-      waitpid (child_pid, &status, 0);
-    }
+  ret = ptrace (PTRACE_KILL, child_pid, 0, 0);
+  if (ret != 0)
+    warning ("linux_test_for_tracefork: failed to kill child");
+  my_waitpid (child_pid, &status, 0);
 }
 
 /* Return non-zero iff we have tracefork functionality available.
    This function also sets linux_supports_tracefork_flag.  */
 
 static int
-linux_supports_tracefork (void)
+linux_supports_tracefork (int pid)
 {
   if (linux_supports_tracefork_flag == -1)
-    linux_test_for_tracefork ();
+    linux_test_for_tracefork (pid);
   return linux_supports_tracefork_flag;
 }
 
 static int
-linux_supports_tracevforkdone (void)
+linux_supports_tracevforkdone (int pid)
 {
   if (linux_supports_tracefork_flag == -1)
-    linux_test_for_tracefork ();
+    linux_test_for_tracefork (pid);
   return linux_supports_tracevforkdone_flag;
 }
 
@@ -242,12 +289,12 @@ linux_enable_event_reporting (ptid_t pti
   int pid = ptid_get_pid (ptid);
   int options;
 
-  if (! linux_supports_tracefork ())
+  if (! linux_supports_tracefork (pid))
     return;
 
   options = PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEEXEC
     | PTRACE_O_TRACECLONE;
-  if (linux_supports_tracevforkdone ())
+  if (linux_supports_tracevforkdone (pid))
     options |= PTRACE_O_TRACEVFORKDONE;
 
   /* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to support
@@ -308,7 +355,8 @@ child_follow_fork (int follow_child)
 
       if (has_vforked)
 	{
-	  if (linux_supports_tracevforkdone ())
+	  gdb_assert (linux_supports_tracefork_flag >= 0);
+	  if (linux_supports_tracevforkdone (0))
 	    {
 	      int status;
 
@@ -476,7 +524,7 @@ linux_handle_extended_wait (int pid, int
 int
 child_insert_fork_catchpoint (int pid)
 {
-  if (! linux_supports_tracefork ())
+  if (! linux_supports_tracefork (pid))
     error ("Your system does not support fork catchpoints.");
 
   return 0;
@@ -485,7 +533,7 @@ child_insert_fork_catchpoint (int pid)
 int
 child_insert_vfork_catchpoint (int pid)
 {
-  if (!linux_supports_tracefork ())
+  if (!linux_supports_tracefork (pid))
     error ("Your system does not support vfork catchpoints.");
 
   return 0;
@@ -494,7 +542,7 @@ child_insert_vfork_catchpoint (int pid)
 int
 child_insert_exec_catchpoint (int pid)
 {
-  if (!linux_supports_tracefork ())
+  if (!linux_supports_tracefork (pid))
     error ("Your system does not support exec catchpoints.");
 
   return 0;


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [rfc] Fix linux_test_for_tracefork for newer kernels
  2004-11-13 22:25 ` Daniel Jacobowitz
@ 2004-11-21 20:10   ` Daniel Jacobowitz
  0 siblings, 0 replies; 3+ messages in thread
From: Daniel Jacobowitz @ 2004-11-21 20:10 UTC (permalink / raw)
  To: gdb-patches

On Sat, Nov 13, 2004 at 05:25:06PM -0500, Daniel Jacobowitz wrote:
> On Fri, Nov 12, 2004 at 05:59:41PM -0500, Daniel Jacobowitz wrote:
> > This patch makes the run-time test for fork tracing a little more robust.
> > In particular, it fixes two things:
> >   - If the test failed in an unexpected way, then two stopped processes
> >     could be left around.
> >   - Waitpid can be interrupted by EINTR.  Kernel 2.6.10-rc1 has some
> >     changes to wait and signal processing, which cause the SIGCHLD
> >     to be received before wait returns.  I don't think there's any
> >     actual bug here, since reissuing the wait works fine.
> > 
> > These changes let my testsuite runs finish without leaving hordes of stopped
> > processes around.  I won't check it in yet, since I haven't tested it on a
> > system where this runtime check should fail; in the meantime, I'm just
> > posting it for comments.  Hopefully I got it right this time!
> 
> Now I've tested it in a couple of other systems.  Here's a revised
> version, with a spurious warning fixed, and one additional improvement:
> on most systems lacking the support, we don't need to fork for the
> runtime test.  This fixes some problems in user-mode-linux, which does
> the most disgusting things with ptrace I have ever encountered.  Wow!
> 
> Barring comments, I'll check this in in a few days.

> 2004-11-12  Daniel Jacobowitz  <dan@debian.org>
> 
> 	* linux-nat.c (my_waitpid): New function.
> 	(linux_test_for_tracefork): Make more robust and verbose.  Take
> 	an ORIGINAL_PID argument and test for PTRACE_SETOPTIONS first.
> 	(linux_supports_tracefork, linux_supports_tracevforkdone): Take a PID
> 	argument.  Update calls to linux_test_for_tracefork.
> 	(linux_enable_event_reporting, child_follow_fork)
> 	(child_insert_fork_catchpoint, child_insert_vfork_catchpoint)
> 	(child_insert_exec_catchpoint): Update calls to
> 	linux_supports_tracefork and linux_supports_tracevforkdone.

I have checked this in.  It fixes a couple of hangs when running the
testsuite, for tests which expect GDB to exit, and it fixes the huge
number of zombies and stopped processes that my system has been
collecting every test run.

-- 
Daniel Jacobowitz


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2004-11-21 20:10 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-11-12 22:59 [rfc] Fix linux_test_for_tracefork for newer kernels Daniel Jacobowitz
2004-11-13 22:25 ` Daniel Jacobowitz
2004-11-21 20:10   ` Daniel Jacobowitz

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox