This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Second patch ping


Zack Weinberg <zack@codesourcery.com> writes:

> Here's how I would do it: Have an opaque object that encapsulates all
> the state of the chain of processes being executed.  This is created
> with one call, say pex_init().  That takes a set of flags: the current
> two are PEX_USE_PIPES and PEX_RECORD_TIMES; both are requests, which
> will not be honored if the necessary capabilities are not available.
> (PEX_RECORD_TIMES perhaps should print a warning message in that
> case.)
> 
> Then we cycle through calling pex_invoke(object, done, executable, argv)
> which automatically connects the stdout of the first process to the
> stdin of the next, using whatever means are appropriate (pipes or
> temporary files).  The only flag we need here is 'done' which is a
> boolean: is this the last process in the chain?  Note that that's to
> be false in the case where the parent wants to read the final output.
> pex_invoke waits for the child, and squirrels away the exit status, if
> that's the only way it can be done or if pipes are not requested.
> 
> pex_read_output(object) returns a FILE* which the parent may read to
> receive the output of the last process in the chain.
> 
> pex_get_status(object, count, vector) writes a vector of exit statuses
> (requested length COUNT - that's just a sanity check) into vector,
> which is an int*.  It blocks until all children are done executing if
> they aren't already.
> 
> pex_get_times(object, count, vector) is the same but the vector is a
> vector of rusage structures.  (Or we make up our own if that structure
> isn't universal, which I suspect it is.)  This one will return an
> error code if PEX_RECORD_TIMES wasn't set earlier.
> 
> Finally, pex_destroy() tears down the object.  It has no other
> effects.

Sounds reasonable for gcc.c.  Seems like overkill for collect2.c, but
I suppose we can shoehorn that in as well.

(I hope you're kidding about thinking that struct rusage is universal;
it's not even POSIX.)

Here is a sample Unix only implementation, followed by the patch to
other gcc files.  This is not yet well tested, but I'd like to hear
feedback before I put a lot of time into it.

Ian

/* Utilities to execute a program in a subprocess (possibly linked by pipes
   with other subprocesses), and wait for it.  Generic Unix version
   (also used for UWIN and VMS).
   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004
   Free Software Foundation, Inc.

This file is part of the libiberty library.
Libiberty is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.

Libiberty is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with libiberty; see the file COPYING.LIB.  If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

#include "config.h"
#include "libiberty.h"
#include "pex-common.h"

#include <errno.h>
#ifdef NEED_DECLARATION_ERRNO
extern int errno;
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#ifdef HAVE_GETRUSAGE
#include <sys/time.h>
#include <sys/resource.h>
#endif

#include <sys/types.h>

#ifdef vfork /* Autoconf may define this to fork for us. */
# define VFORK_STRING "fork"
#else
# define VFORK_STRING "vfork"
#endif
#ifdef HAVE_VFORK_H
#include <vfork.h>
#endif
#ifdef VMS
#define vfork() (decc$$alloc_vfork_blocks() >= 0 ? \
               lib$get_current_invo_context(decc$$get_vfork_jmpbuf()) : -1)
#endif /* VMS */

/* Get the exit status of a particular process, and optionally get the
   time that it took.  This is simple if we have wait4, slightly
   harder if we have waitpid, and is a pain if we only have wait.  */

static pid_t pex_wait PARAMS ((pid_t, int *, struct pex_time *));

#ifdef HAVE_WAIT4

static pid_t
pex_wait (pid, status, time)
     pid_t pid;
     int *status;
     struct pex_time *time;
{
  pid_t ret;
  struct rusage r;

#ifdef HAVE_WAITPID
  if (time == NULL)
    return waitpid (pid, status, 0);
#endif

  ret = wait4 (pid, status, 0, &r);

  if (time != NULL)
    {
      time->user_seconds = r.ru_utime.tv_sec;
      time->user_microseconds= r.ru_utime.tv_usec;
      time->system_seconds = r.ru_stime.tv_sec;
      time->system_microseconds= r.ru_stime.tv_usec;
    }

  return ret;
}

#else /* ! defined (HAVE_WAIT4) */

#ifdef HAVE_WAITPID

#ifndef HAVE_GETRUSAGE

static pid_t
pex_wait (pid, status, time)
     pid_t pid;
     int *status;
     struct pex_time *time;
{
  if (time != NULL)
    memset (time, 0, sizeof (struct pex_time));
  return waitpid (pid, status, 0);
}

#else /* defined (HAVE_GETRUSAGE) */

static pid_t
pex_wait (pid, status, time)
     pid_t pid;
     int *status;
     struct pex_time *time;
{
  struct rusage r1, r2;
  pid_t ret;

  if (time == NULL)
    return waitpid (pid, status, 0);

  getrusage (RUSAGE_CHILDREN, &r1);

  ret = waitpid (pid, status, 0);

  getrusage (RUSAGE_CHILDREN, &r2);

  time->user_seconds = r2.ru_utime.tv_sec - r1.ru_utime.tv_sec;
  time->user_microseconds = r2.ru_utime.tv_usec - r1.ru_utime.tv_usec;
  if (time->user_microseconds < 0)
    {
      --time->user_seconds;
      time->user_microseconds += 1000000;
    }

  time->system_seconds = r2.ru_stime.tv_sec - r1.ru_stime.tv_sec;
  time->system_microseconds = r2.ru_stime.tv_usec - r1.ru_stime.tv_usec;
  if (time->system_microseconds < 0)
    {
      --time->system_seconds;
      time->system_microseconds += 1000000;
    }

  return ret;
}

#endif /* defined (HAVE_GETRUSAGE) */

#else /* ! defined (HAVE_WAITPID) */

struct status_list
{
  struct status_list *next;
  pid_t pid;
  int status;
  struct pex_time time;
};

static struct status_list *sl;

static pid_t
pex_wait (pid, status, time)
     pid_t pid;
     int *status;
     struct pex_time *time;
{
  struct status_list **pp;

  for (pp = &sl; *pp != NULL; pp = &(*pp)->next)
    {
      if ((*pp)->pid == pid)
	{
	  struct status_list *p;

	  p = *pp;
	  *status = p->status;
	  if (time != NULL)
	    *time = p->time;
	  *pp = p->next;
	  free (p);
	  return pid;
	}
    }

  while (1)
    {
      pid_t cpid;
      struct status_list *psl;
      struct pex_time pt;
#ifdef HAVE_GETRUSAGE
      struct rusage r1, r2;
#endif

      if (time != NULL)
	{
#ifdef HAVE_GETRUSAGE
	  getrusage (RUSAGE_CHILDREN, &r1);
#else
	  memset (&pt, 0, sizeof (struct pex_time));
#endif
	}

      cpid = wait (status);

#ifdef HAVE_GETRUSAGE
      if (time != NULL)
	{
	  getrusage (RUSAGE_CHILDREN, &r2);

	  pt.user_seconds = r2.ru_utime.tv_sec - r1.ru_utime.tv_sec;
	  pt.user_microseconds = r2.ru_utime.tv_usec - r1.ru_utime.tv_usec;
	  if (pt.user_microseconds < 0)
	    {
	      --pt.user_seconds;
	      pt.user_microseconds += 1000000;
	    }

	  pt.system_seconds = r2.ru_stime.tv_sec - r1.ru_stime.tv_sec;
	  pt.system_microseconds = r2.ru_stime.tv_usec - r1.ru_stime.tv_usec;
	  if (pt.system_microseconds < 0)
	    {
	      --pt.system_seconds;
	      pt.system_microseconds += 1000000;
	    }
	}
#endif

      if (cpid < 0 || cpid == pid)
	{
	  if (time != NULL)
	    *time = pt;
	  return cpid;
	}

      /* ??? Space leak here if we come up with a PID we weren't
	 supposed to wait for, but in that case we probably have
	 bigger problems anyhow.  */
      psl = xmalloc (sizeof (struct status_list));
      psl->pid = cpid;
      psl->status = *status;
      if (time != NULL)
	psl->time = pt;
      psl->next = sl;
      sl = psl;
    }
}

#endif /* ! defined (HAVE_WAITPID) */
#endif /* ! defined (HAVE_WAIT4) */

struct pex_obj
{
  /* Flags.  */
  int flags;
  /* Name of calling program, for error messages.  */
  const char *pname;
  /* Pipe to use as stdin for next process.  */
  int next_input;
  /* Number of child processes.  */
  int count;
  /* PIDs of child processes.  */
  pid_t *children;
  /* Exit statuses of child processes.  */
  int *status;
  /* Time used by child processes.  */
  struct pex_time *time;
  /* FILE created by pex_read_output.  */
  FILE *read_output;
};

static void pex_get_status_and_time PARAMS ((struct pex_obj *));

struct pex_obj *
pex_init (flags, pname)
     int flags;
     const char *pname;
{
  struct pex_obj *ret;

  ret = xmalloc (sizeof *ret);
  ret->flags = flags;
  ret->pname = pname;
  ret->next_input = STDIN_FILE_NO;
  ret->count = 0;
  ret->children = NULL;
  ret->status = NULL;
  ret->time = NULL;
  ret->read_output = NULL;
  return ret;
}

const char *
pex_run (obj, flags, executable, argv)
     struct pex_obj *obj;
     int flags;
     const char *executable;
     char * const * argv;
{
  int p[2];
  int in;
  int out;
  int tout;
  pid_t pid;
  /* We declare these to be volatile to avoid warnings from gcc about
     them being clobbered by vfork.  */
  volatile int sleep_interval;
  volatile int retries;

  in = obj->next_input;
  if (in < 0)
    return "pipeline already complete";

  if ((flags & PEX_LAST) != 0)
    {
      tout = STDOUT_FILE_NO;
      obj->next_input = -1;
    }
  else
    {
      if (pipe (p) < 0)
	return "pipe";
      tout = p[WRITE_PORT];
      obj->next_input = p[READ_PORT];
    }

  out = tout;

  sleep_interval = 1;
  pid = -1;
  for (retries = 0; retries < 4; ++retries)
    {
      pid = vfork ();
      if (pid >= 0)
	break;
      sleep (sleep_interval);
      sleep_interval *= 2;
    }

  switch (pid)
    {
    case -1:
      return VFORK_STRING;

    case 0:
      /* Child process.  */
      if (in != STDIN_FILE_NO)
	{
	  if (dup2 (in, STDIN_FILE_NO) < 0)
	    return "dup2";
	  if (close (in) < 0)
	    return "close";
	}
      if (out != STDOUT_FILE_NO)
	{
	  if (dup2 (out, STDOUT_FILE_NO) < 0)
	    return "dup2";
	  if (close (out) < 0)
	    return "close";
	}

      if ((flags & PEX_LAST) == 0)
	{
	  if (close (p[READ_PORT]) < 0)
	    return "close";
	}

      if ((flags & PEX_SEARCH) != 0)
	execvp (executable, argv);
      else
	execv (executable, argv);

      /* We don't want to call fprintf after vfork.  */
#define writeerr(s) write (STDERR_FILE_NO, s, strlen (s))
      writeerr (obj->pname);
      writeerr (": ");
      writeerr ("installation problem, cannot exec '");
      writeerr (executable);
      writeerr ("': ");
      writeerr (xstrerror (errno));
      writeerr ("\n");
      _exit (-1);
      /* NOTREACHED */
      return 0;

    default:
      if (in != STDIN_FILE_NO)
	{
	  if (close (in) < 0)
	    return "close";
	}
      if (out != STDOUT_FILE_NO)
	{
	  if (close (out) < 0)
	    return "close";
	}

      ++obj->count;
      obj->children = xrealloc (obj->children, obj->count * sizeof (pid_t));
      obj->children[obj->count - 1] = pid;

      return NULL;
    }
}

FILE *
pex_read_output (obj)
     struct pex_obj *obj;
{
  int o;

  o = obj->next_input;
  if (o < 0)
    return NULL;
  obj->next_input = -1;
  return fdopen (o, "r");
}

/* Get the exit status and, if requested, the resource time for all
   the child processes.  */

static void
pex_get_status_and_time (obj)
     struct pex_obj *obj;
{
  int i;

  obj->status = xmalloc (obj->count * sizeof (int));
  if ((obj->flags & PEX_RECORD_TIMES) != 0)
    obj->time = xmalloc (obj->count * sizeof (struct pex_time));

  for (i = 0; i < obj->count; ++i)
    {
#ifdef EINTR
	interrupted1:
#endif

      if (pex_wait (obj->children[i], &obj->status[i],
		    obj->time == NULL ? NULL : &obj->time[i])
	  < 0)
	{
	  /* Any error other than EINTR should be impossible.  The
	     other plausible error returns are ECHILD, EFAULT, and
	     EINVAL.  We don't have a way to return EINTR up to the
	     caller, so we loop.  */
#ifdef EINTR
	  if (errno == EINTR)
	    goto interrupted1;
#endif

	  abort ();
	}
    }
}

void
pex_get_status (obj, count, vector)
     struct pex_obj *obj;
     int count;
     int *vector;
{
  if (obj->status == NULL)
    pex_get_status_and_time (obj);
  if (count > obj->count)
    count = obj->count;
  memcpy (vector, obj->status, count * sizeof (int));
}

void
pex_get_times (obj, count, vector)
     struct pex_obj *obj;
     int count;
     struct pex_time *vector;
{
  if (obj->status == NULL)
    pex_get_status_and_time (obj);
  if (count > obj->count)
    count = obj->count;
  memcpy (vector, obj->time, count * sizeof (struct pex_time));
}

void
pex_free (obj)
     struct pex_obj *obj;
{
  /* If the caller forgot to wait for the children, we do it here, to
     avoid zombies.  We could have a flag to detach the process, but
     on the other hand the caller can just avoid calling pex_free.  */
  if (obj->status == NULL)
    {
      obj->flags &= ~ PEX_RECORD_TIMES;
      pex_get_status_and_time (obj);
    }

  if (obj->children != NULL)
    free (obj->children);
  if (obj->status != NULL)
    free (obj->status);
  if (obj->time != NULL)
    free (obj->time);
  if (obj->read_output != NULL)
    fclose (obj->read_output);
  free (obj);
}

Index: gcc/collect2.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/collect2.c,v
retrieving revision 1.162
diff -p -u -r1.162 collect2.c
--- gcc/collect2.c	10 Mar 2004 00:17:34 -0000	1.162
+++ gcc/collect2.c	15 Apr 2004 04:23:41 -0000
@@ -35,19 +35,6 @@ Software Foundation, 59 Temple Place - S
 #  define SIGCHLD SIGCLD
 #endif
 
-#ifdef vfork /* Autoconf may define this to fork for us.  */
-# define VFORK_STRING "fork"
-#else
-# define VFORK_STRING "vfork"
-#endif
-#ifdef HAVE_VFORK_H
-#include <vfork.h>
-#endif
-#ifdef VMS
-#define vfork() (decc$$alloc_vfork_blocks() >= 0 ? \
-               lib$get_current_invo_context(decc$$get_vfork_jmpbuf()) : -1)
-#endif /* VMS */
-
 #ifndef LIBRARY_PATH_ENV
 #define LIBRARY_PATH_ENV "LIBRARY_PATH"
 #endif
@@ -254,7 +241,7 @@ static char *find_a_file (struct path_pr
 static void add_prefix (struct path_prefix *, const char *);
 static void prefix_from_env (const char *, struct path_prefix *);
 static void prefix_from_string (const char *, struct path_prefix *);
-static void do_wait (const char *);
+static void do_wait (struct pex_obj *, const char *);
 static void fork_execute (const char *, char **);
 static void maybe_unlink (const char *);
 static void add_to_list (struct head *, const char *);
@@ -1458,11 +1445,13 @@ main (int argc, char **argv)
 /* Wait for a process to finish, and exit if a nonzero status is found.  */
 
 int
-collect_wait (const char *prog)
+collect_wait (struct pex_obj *pex, const char *prog)
 {
   int status;
 
-  pwait (pid, &status, 0);
+  pex_get_status (pex, 1, &status);
+  pex_free (pex);
+
   if (status)
     {
       if (WIFSIGNALED (status))
@@ -1481,9 +1470,9 @@ collect_wait (const char *prog)
 }
 
 static void
-do_wait (const char *prog)
+do_wait (struct pex_obj *pex, const char *prog)
 {
-  int ret = collect_wait (prog);
+  int ret = collect_wait (pex, prog);
   if (ret != 0)
     {
       error ("%s returned %d exit status", prog, ret);
@@ -1494,11 +1483,11 @@ do_wait (const char *prog)
 
 /* Execute a program, and wait for the reply.  */
 
-void
+struct pex_obj *
 collect_execute (const char *prog, char **argv, const char *redir)
 {
-  char *errmsg_fmt;
-  char *errmsg_arg;
+  struct pex_obj *pex;
+  const char *errmsg;
   int redir_handle = -1;
   int stdout_save = -1;
   int stderr_save = -1;
@@ -1547,8 +1536,9 @@ collect_execute (const char *prog, char 
       dup2 (redir_handle, STDERR_FILENO);
     }
 
-  pid = pexecute (argv[0], argv, argv[0], NULL, &errmsg_fmt, &errmsg_arg,
-		  (PEXECUTE_FIRST | PEXECUTE_LAST | PEXECUTE_SEARCH));
+  pex = pex_init (0, "collect2");
+
+  errmsg = pex_run (pex, PEX_SEARCH | PEX_LAST, argv[0], argv);
 
   if (redir)
     {
@@ -1560,15 +1550,19 @@ collect_execute (const char *prog, char 
       close (redir_handle);
     }
 
- if (pid == -1)
-   fatal_perror (errmsg_fmt, errmsg_arg);
+  if (errmsg != NULL)
+    fatal_perror (errmsg);
+
+  return pex;
 }
 
 static void
 fork_execute (const char *prog, char **argv)
 {
-  collect_execute (prog, argv, NULL);
-  do_wait (prog);
+  struct pex_obj *pex;
+
+  pex = collect_execute (prog, argv, NULL);
+  do_wait (pex, prog);
 }
 
 /* Unlink a file unless we are debugging.  */
@@ -1994,7 +1988,8 @@ scan_prog_file (const char *prog_name, e
   char *real_nm_argv[4];
   const char **nm_argv = (const char **) real_nm_argv;
   int argc = 0;
-  int pipe_fd[2];
+  struct pex_obj *pex;
+  const char *errmsg;
   char *p, buf[1024];
   FILE *inf;
 
@@ -2012,13 +2007,6 @@ scan_prog_file (const char *prog_name, e
   nm_argv[argc++] = prog_name;
   nm_argv[argc++] = (char *) 0;
 
-  if (pipe (pipe_fd) < 0)
-    fatal_perror ("pipe");
-
-  inf = fdopen (pipe_fd[0], "r");
-  if (inf == (FILE *) 0)
-    fatal_perror ("fdopen");
-
   /* Trace if needed.  */
   if (vflag)
     {
@@ -2034,36 +2022,21 @@ scan_prog_file (const char *prog_name, e
   fflush (stdout);
   fflush (stderr);
 
-  /* Spawn child nm on pipe.  */
-  pid = vfork ();
-  if (pid == -1)
-    fatal_perror (VFORK_STRING);
-
-  if (pid == 0)			/* child context */
-    {
-      /* setup stdout */
-      if (dup2 (pipe_fd[1], 1) < 0)
-	fatal_perror ("dup2 %d 1", pipe_fd[1]);
-
-      if (close (pipe_fd[0]) < 0)
-	fatal_perror ("close %d", pipe_fd[0]);
+  pex = pex_init (0, "collect2");
 
-      if (close (pipe_fd[1]) < 0)
-	fatal_perror ("close %d", pipe_fd[1]);
+  errmsg = pex_run (pex, 0, nm_file_name, real_nm_argv);
+  if (errmsg != NULL)
+    fatal_perror (errmsg);
+
+  inf = pex_read_output (pex);
+  if (inf == NULL)
+    fatal_perror ("pex_read_output");
 
-      execv (nm_file_name, real_nm_argv);
-      fatal_perror ("execv %s", nm_file_name);
-    }
-
-  /* Parent context from here on.  */
   int_handler  = (void (*) (int)) signal (SIGINT,  SIG_IGN);
 #ifdef SIGQUIT
   quit_handler = (void (*) (int)) signal (SIGQUIT, SIG_IGN);
 #endif
 
-  if (close (pipe_fd[1]) < 0)
-    fatal_perror ("close %d", pipe_fd[1]);
-
   if (debug)
     fprintf (stderr, "\nnm output with constructors/destructors.\n");
 
@@ -2136,10 +2109,7 @@ scan_prog_file (const char *prog_name, e
   if (debug)
     fprintf (stderr, "\n");
 
-  if (fclose (inf) != 0)
-    fatal_perror ("fclose");
-
-  do_wait (nm_file_name);
+  do_wait (pex, nm_file_name);
 
   signal (SIGINT,  int_handler);
 #ifdef SIGQUIT
@@ -2163,7 +2133,8 @@ scan_libraries (const char *prog_name)
   char *real_ldd_argv[4];
   const char **ldd_argv = (const char **) real_ldd_argv;
   int argc = 0;
-  int pipe_fd[2];
+  struct pex_obj *pex;
+  const char *errmsg;
   char buf[1024];
   FILE *inf;
 
@@ -2178,13 +2149,6 @@ scan_libraries (const char *prog_name)
   ldd_argv[argc++] = prog_name;
   ldd_argv[argc++] = (char *) 0;
 
-  if (pipe (pipe_fd) < 0)
-    fatal_perror ("pipe");
-
-  inf = fdopen (pipe_fd[0], "r");
-  if (inf == (FILE *) 0)
-    fatal_perror ("fdopen");
-
   /* Trace if needed.  */
   if (vflag)
     {
@@ -2200,36 +2164,21 @@ scan_libraries (const char *prog_name)
   fflush (stdout);
   fflush (stderr);
 
-  /* Spawn child ldd on pipe.  */
-  pid = vfork ();
-  if (pid == -1)
-    fatal_perror (VFORK_STRING);
+  pex = pex_init (0, "collect2");
 
-  if (pid == 0)			/* child context */
-    {
-      /* setup stdout */
-      if (dup2 (pipe_fd[1], 1) < 0)
-	fatal_perror ("dup2 %d 1", pipe_fd[1]);
+  errmsg = pex_run (pex, 0, ldd_file_name, real_ldd_argv);
+  if (errmsg != NULL)
+    fatal_perror (errmsg);
+
+  inf = pex_read_output (pex);
+  if (inf == NULL)
+    fatal_perror ("pex_read_output");
 
-      if (close (pipe_fd[0]) < 0)
-	fatal_perror ("close %d", pipe_fd[0]);
-
-      if (close (pipe_fd[1]) < 0)
-	fatal_perror ("close %d", pipe_fd[1]);
-
-      execv (ldd_file_name, real_ldd_argv);
-      fatal_perror ("execv %s", ldd_file_name);
-    }
-
-  /* Parent context from here on.  */
   int_handler  = (void (*) (int)) signal (SIGINT,  SIG_IGN);
 #ifdef SIGQUIT
   quit_handler = (void (*) (int)) signal (SIGQUIT, SIG_IGN);
 #endif
 
-  if (close (pipe_fd[1]) < 0)
-    fatal_perror ("close %d", pipe_fd[1]);
-
   if (debug)
     notice ("\nldd output with constructors/destructors.\n");
 
@@ -2266,10 +2215,7 @@ scan_libraries (const char *prog_name)
   if (debug)
     fprintf (stderr, "\n");
 
-  if (fclose (inf) != 0)
-    fatal_perror ("fclose");
-
-  do_wait (ldd_file_name);
+  do_wait (pex, ldd_file_name);
 
   signal (SIGINT,  int_handler);
 #ifdef SIGQUIT
Index: gcc/collect2.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/collect2.h,v
retrieving revision 1.8
diff -p -u -r1.8 collect2.h
--- gcc/collect2.h	29 Jun 2003 12:07:06 -0000	1.8
+++ gcc/collect2.h	15 Apr 2004 04:23:41 -0000
@@ -23,11 +23,11 @@ Software Foundation, 59 Temple Place - S
 
 extern void do_tlink (char **, char **);
 
-extern void collect_execute (const char *, char **, const char *);
+extern struct pex_obj *collect_execute (const char *, char **, const char *);
 
 extern void collect_exit (int) ATTRIBUTE_NORETURN;
 
-extern int collect_wait (const char *);
+extern int collect_wait (struct pex_obj *, const char *);
 
 extern void dump_file (const char *);
 
Index: gcc/gcc.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/gcc.c,v
retrieving revision 1.415
diff -p -u -r1.415 gcc.c
--- gcc/gcc.c	9 Apr 2004 00:38:36 -0000	1.415
+++ gcc/gcc.c	15 Apr 2004 04:23:45 -0000
@@ -85,13 +85,6 @@ compilation is specified by a string cal
 #include "gcc.h"
 #include "flags.h"
 
-#ifdef HAVE_SYS_RESOURCE_H
-#include <sys/resource.h>
-#endif
-#if defined (HAVE_DECL_GETRUSAGE) && !HAVE_DECL_GETRUSAGE
-extern int getrusage (int, struct rusage *);
-#endif
-
 /* By default there is no special suffix for target executables.  */
 /* FIXME: when autoconf is fixed, remove the host check - dj */
 #if defined(TARGET_EXECUTABLE_SUFFIX) && defined(HOST_EXECUTABLE_SUFFIX)
@@ -282,12 +275,6 @@ static struct obstack obstack;
 
 static struct obstack collect_obstack;
 
-/* These structs are used to collect resource usage information for
-   subprocesses.  */
-#ifdef HAVE_GETRUSAGE
-static struct rusage rus, prus;
-#endif
-
 /* Forward declaration for prototypes.  */
 struct path_prefix;
 
@@ -338,7 +325,6 @@ static int default_arg (const char *, in
 static void set_multilib_dir (void);
 static void print_multilib_info (void);
 static void perror_with_name (const char *);
-static void pfatal_pexecute (const char *, const char *) ATTRIBUTE_NORETURN;
 static void notice (const char *, ...) ATTRIBUTE_PRINTF_1;
 static void display_help (void);
 static void add_preprocessor_option (const char *, int);
@@ -2634,10 +2620,10 @@ execute (void)
   {
     const char *prog;		/* program name.  */
     const char **argv;		/* vector of args.  */
-    int pid;			/* pid of process for this command.  */
   };
 
   struct command *commands;	/* each command buffer with above info.  */
+  struct pex_obj *pex;
 
   if (processing_spec_function)
     abort ();
@@ -2768,24 +2754,21 @@ execute (void)
 
   /* Run each piped subprocess.  */
 
+  pex = pex_init (report_times ? PEX_RECORD_TIMES : 0, programname);
+
   for (i = 0; i < n_commands; i++)
     {
-      char *errmsg_fmt, *errmsg_arg;
       const char *string = commands[i].argv[0];
+      const char *errmsg;
 
-      /* For some bizarre reason, the second argument of execvp() is
-	 char *const *, not const char *const *.  */
-      commands[i].pid = pexecute (string, (char *const *) commands[i].argv,
-				  programname, temp_filename,
-				  &errmsg_fmt, &errmsg_arg,
-				  ((i == 0 ? PEXECUTE_FIRST : 0)
-				   | (i + 1 == n_commands ? PEXECUTE_LAST : 0)
-				   | (string == commands[i].prog
-				      ? PEXECUTE_SEARCH : 0)
-				   | (verbose_flag ? PEXECUTE_VERBOSE : 0)));
+      errmsg = pex_run (pex,
+			((i + 1 == n_commands ? PEX_LAST : 0)
+			 | (string == commands[i].prog ? PEX_SEARCH : 0)),
+			string,
+			(char * const *) commands[i].argv);
 
-      if (commands[i].pid == -1)
-	pfatal_pexecute (errmsg_fmt, errmsg_arg);
+      if (errmsg != NULL)
+	pfatal_with_name (errmsg);
 
       if (string != commands[i].prog)
 	free ((void *) string);
@@ -2793,89 +2776,70 @@ execute (void)
 
   execution_count++;
 
-  /* Wait for all the subprocesses to finish.
-     We don't care what order they finish in;
-     we know that N_COMMANDS waits will get them all.
-     Ignore subprocesses that we don't know about,
-     since they can be spawned by the process that exec'ed us.  */
-
   {
     int ret_code = 0;
-#ifdef HAVE_GETRUSAGE
-    struct timeval d;
-    double ut = 0.0, st = 0.0;
-#endif
+    int *statuses;
+    struct pex_time *time = NULL;
+
+    statuses = alloca (n_commands * sizeof (int));
+    pex_get_status (pex, n_commands, statuses);
 
-    for (i = 0; i < n_commands;)
+    if (report_times)
       {
-	int j;
-	int status;
-	int pid;
-
-	pid = pwait (commands[i].pid, &status, 0);
-	if (pid < 0)
-	  abort ();
+	time = alloca (n_commands * sizeof (struct pex_time));
+	pex_get_times (pex, n_commands, time);
+      }
 
-#ifdef HAVE_GETRUSAGE
-	if (report_times)
-	  {
-	    /* getrusage returns the total resource usage of all children
-	       up to now.  Copy the previous values into prus, get the
-	       current statistics, then take the difference.  */
-
-	    prus = rus;
-	    getrusage (RUSAGE_CHILDREN, &rus);
-	    d.tv_sec = rus.ru_utime.tv_sec - prus.ru_utime.tv_sec;
-	    d.tv_usec = rus.ru_utime.tv_usec - prus.ru_utime.tv_usec;
-	    ut = (double) d.tv_sec + (double) d.tv_usec / 1.0e6;
-
-	    d.tv_sec = rus.ru_stime.tv_sec - prus.ru_stime.tv_sec;
-	    d.tv_usec = rus.ru_stime.tv_usec - prus.ru_stime.tv_usec;
-	    st = (double) d.tv_sec + (double) d.tv_usec / 1.0e6;
-	  }
-#endif
+    for (i = 0; i < n_commands; i++)
+      {
+	int status = statuses[i];
 
-	for (j = 0; j < n_commands; j++)
-	  if (commands[j].pid == pid)
-	    {
-	      i++;
-	      if (WIFSIGNALED (status))
-		{
+	if (WIFSIGNALED (status))
+	  {
 #ifdef SIGPIPE
-		  /* SIGPIPE is a special case.  It happens in -pipe mode
-		     when the compiler dies before the preprocessor is
-		     done, or the assembler dies before the compiler is
-		     done.  There's generally been an error already, and
-		     this is just fallout.  So don't generate another error
-		     unless we would otherwise have succeeded.  */
-		  if (WTERMSIG (status) == SIGPIPE
-		      && (signal_count || greatest_status >= MIN_FATAL_STATUS))
-		    ;
-		  else
+	    /* SIGPIPE is a special case.  It happens in -pipe mode
+	       when the compiler dies before the preprocessor is done,
+	       or the assembler dies before the compiler is done.
+	       There's generally been an error already, and this is
+	       just fallout.  So don't generate another error unless
+	       we would otherwise have succeeded.  */
+	    if (WTERMSIG (status) == SIGPIPE
+		&& (signal_count || greatest_status >= MIN_FATAL_STATUS))
+	      ;
+	    else
 #endif
-		    fatal ("\
+	      fatal ("\
 Internal error: %s (program %s)\n\
 Please submit a full bug report.\n\
 See %s for instructions.",
-			   strsignal (WTERMSIG (status)), commands[j].prog,
-			   bug_report_url);
-		  signal_count++;
-		  ret_code = -1;
-		}
-	      else if (WIFEXITED (status)
-		       && WEXITSTATUS (status) >= MIN_FATAL_STATUS)
-		{
-		  if (WEXITSTATUS (status) > greatest_status)
-		    greatest_status = WEXITSTATUS (status);
-		  ret_code = -1;
-		}
-#ifdef HAVE_GETRUSAGE
-	      if (report_times && ut + st != 0)
-		notice ("# %s %.2f %.2f\n", commands[j].prog, ut, st);
-#endif
-	      break;
-	    }
+		     strsignal (WTERMSIG (status)), commands[i].prog,
+		     bug_report_url);
+	    signal_count++;
+	    ret_code = -1;
+	  }
+	else if (WIFEXITED (status)
+		 && WEXITSTATUS (status) >= MIN_FATAL_STATUS)
+	  {
+	    if (WEXITSTATUS (status) > greatest_status)
+	      greatest_status = WEXITSTATUS (status);
+	    ret_code = -1;
+	  }
+
+	if (report_times)
+	  {
+	    double ut, st;
+
+	    ut = ((double) time[i].user_seconds
+		  + (double) time[i].user_microseconds / 1.0e6);
+	    st = ((double) time[i].system_seconds
+		  + (double) time[i].system_microseconds / 1.0e6);
+
+	    notice ("# %s %.2f %.2f\n", commands[i].prog, ut, st);
+	  }
       }
+
+    pex_free (pex);
+
     return ret_code;
   }
 }
@@ -3760,16 +3724,10 @@ warranty; not even for MERCHANTABILITY o
 	}
     }
 
-  if ((save_temps_flag || report_times) && use_pipes)
+  if (save_temps_flag && use_pipes)
     {
       /* -save-temps overrides -pipe, so that temp files are produced */
-      if (save_temps_flag)
-	error ("warning: -pipe ignored because -save-temps specified");
-      /* -time overrides -pipe because we can't get correct stats when
-	 multiple children are running at once.  */
-      else if (report_times)
-	error ("warning: -pipe ignored because -time specified");
-
+      error ("warning: -pipe ignored because -save-temps specified");
       use_pipes = 0;
     }
 
@@ -6666,24 +6624,6 @@ static void
 perror_with_name (const char *name)
 {
   error ("%s: %s", name, xstrerror (errno));
-}
-
-static void
-pfatal_pexecute (const char *errmsg_fmt, const char *errmsg_arg)
-{
-  if (errmsg_arg)
-    {
-      int save_errno = errno;
-
-      /* Space for trailing '\0' is in %s.  */
-      char *msg = xmalloc (strlen (errmsg_fmt) + strlen (errmsg_arg));
-      sprintf (msg, errmsg_fmt, errmsg_arg);
-      errmsg_fmt = msg;
-
-      errno = save_errno;
-    }
-
-  pfatal_with_name (errmsg_fmt);
 }
 
 /* Output an error message and exit.  */
Index: gcc/tlink.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tlink.c,v
retrieving revision 1.55
diff -p -u -r1.55 tlink.c
--- gcc/tlink.c	19 Jul 2003 14:47:14 -0000	1.55
+++ gcc/tlink.c	15 Apr 2004 04:23:45 -0000
@@ -277,8 +277,10 @@ tlink_init (void)
 static int
 tlink_execute (const char *prog, char **argv, const char *redir)
 {
-  collect_execute (prog, argv, redir);
-  return collect_wait (prog);
+  struct pex_obj *pex;
+
+  pex = collect_execute (prog, argv, redir);
+  return collect_wait (pex, prog);
 }
 
 static char *
Index: include/libiberty.h
===================================================================
RCS file: /cvs/gcc/gcc/include/libiberty.h,v
retrieving revision 1.35
diff -p -u -r1.35 libiberty.h
--- include/libiberty.h	15 May 2003 19:02:12 -0000	1.35
+++ include/libiberty.h	15 Apr 2004 04:23:57 -0000
@@ -48,6 +48,8 @@ extern "C" {
 #include <stdarg.h>
 #endif
 
+#include <stdio.h>
+
 /* Build an argument vector from a string.  Allocates memory using
    malloc.  Use freeargv to free the vector.  */
 
@@ -261,22 +263,64 @@ extern void hex_init PARAMS ((void));
    the argument being performed exactly once.  */
 #define hex_value(c)	((unsigned int) _hex_value[(unsigned char) (c)])
 
-/* Definitions used by the pexecute routine.  */
+/* Flags for pex_init.  */
+
+/* Record subprocess times if possible.  */
+#define PEX_RECORD_TIMES   (1)
+
+/* Prepare to execute one or more programs.  */
+
+extern struct pex_obj *pex_init PARAMS ((int flags, const char *pname));
+
+/* Flags for pex_run.  */
+
+/* Last program in pipeline.  */
+#define PEX_LAST           (1)
+
+/* Search for program.  */
+#define PEX_SEARCH         (2)
+
+/* Execute one program.  Returns NULL on success, or an error message
+   on error.  */
+
+extern const char *pex_run PARAMS ((struct pex_obj *, int flags,
+				    const char *executable,
+				    char * const *argv));
+
+/* Read the standard output of the last program to be executed.  */
+
+extern FILE *pex_read_output PARAMS ((struct pex_obj *));
+
+/* Return exit status of all programs.  */
+
+extern void pex_get_status PARAMS ((struct pex_obj *, int count, int *vector));
+
+/* Return times of all programs.  This is really just struct timeval,
+   but that is not portable to all systems.  */
+
+struct pex_time
+{
+  unsigned long user_seconds;
+  unsigned long user_microseconds;
+  unsigned long system_seconds;
+  unsigned long system_microseconds;
+};
 
-#define PEXECUTE_FIRST   1
-#define PEXECUTE_LAST    2
-#define PEXECUTE_ONE     (PEXECUTE_FIRST + PEXECUTE_LAST)
-#define PEXECUTE_SEARCH  4
-#define PEXECUTE_VERBOSE 8
+extern void pex_get_times PARAMS ((struct pex_obj *, int count,
+				   struct pex_time *vector));
 
-/* Execute a program.  */
+/* Clean up a pex_obj.  */
 
-extern int pexecute PARAMS ((const char *, char * const *, const char *,
-			    const char *, char **, char **, int));
+extern void pex_free PARAMS ((struct pex_obj *));
 
-/* Wait for pexecute to finish.  */
+/* Just execute one program, without worrying about anything else.  On
+   success, returns NULL and sets *status to the exit status.  On
+   failure, returns an error message.  */
 
-extern int pwait PARAMS ((int, int *, int));
+extern const char *pex_one PARAMS ((const char *executable,
+				    char * const *argv,
+				    const char *pname,
+				    int *status));
 
 #if !HAVE_DECL_ASPRINTF
 /* Like sprintf but provides a pointer to malloc'd storage, which must
Index: libiberty/configure.ac
===================================================================
RCS file: /cvs/gcc/gcc/libiberty/configure.ac,v
retrieving revision 1.4
diff -p -u -r1.4 configure.ac
--- libiberty/configure.ac	9 Mar 2004 23:02:47 -0000	1.4
+++ libiberty/configure.ac	15 Apr 2004 04:24:00 -0000
@@ -229,7 +229,7 @@ vars="sys_errlist sys_nerr sys_siglist"
 
 checkfuncs="getrusage on_exit psignal strerror strsignal sysconf times sbrk gettimeofday"
 checkfuncs="$checkfuncs realpath canonicalize_file_name pstat_getstatic pstat_getdynamic sysmp"
-checkfuncs="$checkfuncs getsysinfo table sysctl"
+checkfuncs="$checkfuncs getsysinfo table sysctl wait3 wait4"
 
 # These are neither executed nor required, but they help keep
 # autoheader happy without adding a bunch of text to acconfig.h.
@@ -241,7 +241,7 @@ if test "x" = "y"; then
   strtod strtol strtoul tmpnam vasprintf vfprintf vprintf \
   vsprintf waitpid getrusage on_exit psignal strerror strsignal \
   sysconf times sbrk gettimeofday ffs snprintf vsnprintf \
-  pstat_getstatic pstat_getdynamic sysmp getsysinfo table sysctl \
+  pstat_getstatic pstat_getdynamic sysmp getsysinfo table sysctl wait3 wait4 \
   realpath canonicalize_file_name)
   AC_DEFINE(HAVE_SYS_ERRLIST, 1, [Define if you have the sys_errlist variable.])
   AC_DEFINE(HAVE_SYS_NERR,    1, [Define if you have the sys_nerr variable.])


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]