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]

fork-proofing -ftest-coverage / gcov


I had to update this patch so that it can apply to the current sources;
this also required patching cp/optimize.c .

The testcase situation is still unchanged; I know how to test it by hand,
but not how to express this in terms of c-torture or gcc.dg .

phal:/x/egcs-x86-stage1/gcc$ uname -a
Linux phal 2.2.10 #4 SMP Tue Jul 20 18:57:42 BST 1999 i686 unknown
phal:/x/egcs-x86-stage1/gcc$ cat f2.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main()
{
  pid_t new_pid;
  int i;

  for (i = 4; i >= 0; i--)
    {
      new_pid = fork();
      if (new_pid == 0) {
        printf("In child\n");
      } else if (new_pid == -1) {
        printf ("Fork failed\n");
      } else {
        printf("In Parent\n");
      }
    }
  return 0;
}
phal:/x/egcs-x86-stage1/gcc$ ./xgcc -B./ -ftest-coverage -fprofile-arcs f2.c
phal:/x/egcs-x86-stage1/gcc$ rm -f f2.da
phal:/x/egcs-x86-stage1/gcc$ ./a.out | sort | uniq -c
     80 In Parent
     80 In child
phal:/x/egcs-x86-stage1/gcc$ gcov f2.c
 84.62% of 13 source lines executed in file f2.c
Creating f2.c.gcov.
phal:/x/egcs-x86-stage1/gcc$ cat f2.c.gcov
                #include <stdio.h>
                #include <unistd.h>
                #include <sys/types.h>

                int main()
                {
           1      pid_t new_pid;
                  int i;

          63      for (i = 4; i >= 0; i--)
                    {
          31          new_pid = fork();
          62          if (new_pid == 0) {
          31            printf("In child\n");
          31          } else if (new_pid == -1) {
      ######            printf ("Fork failed\n");
      ######          } else {
          31            printf("In Parent\n");
          31          }
          62        }
          32      return 0;
          32    }

gcc:
Tue Jan 25 23:43:13 1999  J"orn Rennecke <amylaar@cygnus.co.uk>

	* calls.c (special_function_p): New argument fork_or_exec.
	(expand_call): When profile_arc_flag is set and the function
	is in the fork_or_exec group, call __bb_fork_func first.
	* libgcc2.c, _bb module (__bb_fork_func): New function.
	(__bb_exit_func): If fcntl F_SETLKW is available, use it to lock
	output file.
	* config/svr4.h (TARGET_HAS_F_SETLKW): Define.
	* tree.h (special_function_p): Update prototype.

gcc/cp:
Tue Jan 25 23:43:13 1999  J"orn Rennecke <amylaar@cygnus.co.uk>
	* cp/optimize.c (calls_setjmp_r): Supply new argument
	to special_function_p.

Index: calls.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/calls.c,v
retrieving revision 1.78
diff -p -r1.78 calls.c
*** calls.c	2000/01/18 22:45:16	1.78
--- calls.c	2000/01/25 23:40:37
*************** emit_call_1 (funexp, fndecl, funtype, st
*** 536,551 ****
     space from the stack such as alloca.  */
  
  void
! special_function_p (fndecl, returns_twice, is_longjmp,
  		    is_malloc, may_be_alloca)
       tree fndecl;
       int *returns_twice;
       int *is_longjmp;
       int *is_malloc;
       int *may_be_alloca;
  {
    *returns_twice = 0;
    *is_longjmp = 0;
    *may_be_alloca = 0;
  
    /* The function decl may have the `malloc' attribute.  */
--- 536,553 ----
     space from the stack such as alloca.  */
  
  void
! special_function_p (fndecl, returns_twice, is_longjmp, fork_or_exec,
  		    is_malloc, may_be_alloca)
       tree fndecl;
       int *returns_twice;
       int *is_longjmp;
+      int *fork_or_exec;
       int *is_malloc;
       int *may_be_alloca;
  {
    *returns_twice = 0;
    *is_longjmp = 0;
+   *fork_or_exec = 0;
    *may_be_alloca = 0;
  
    /* The function decl may have the `malloc' attribute.  */
*************** special_function_p (fndecl, returns_twic
*** 607,612 ****
--- 609,629 ----
        else if (tname[0] == 'l' && tname[1] == 'o'
  	       && ! strcmp (tname, "longjmp"))
  	*is_longjmp = 1;
+ 
+       else if ((tname[0] == 'f' && tname[1] == 'o'
+ 		&& ! strcmp (tname, "fork"))
+ 	       /* Linux specific: __clone.  check NAME to insist on the
+ 		  leading underscores, to avoid polluting the ISO / POSIX
+ 		  namespace.  */
+ 	       || (name[0] == '_' && name[1] == '_'
+ 		   && ! strcmp (tname, "clone"))
+ 	       || (tname[0] == 'e' && tname[1] == 'x' && tname[2] == 'e'
+ 		   && tname[3] == 'c' && (tname[4] == 'l' || tname[4] == 'v')
+ 		   && (tname[5] == '\0'
+ 		       || ((tname[5] == 'p' || tname[5] == 'e')
+ 			   && tname[6] == '\0'))))
+ 	*fork_or_exec = 1;
+ 
        /* Do not add any more malloc-like functions to this list,
           instead mark them as malloc functions using the malloc attribute.
           Note, realloc is not suitable for attribute malloc since
*************** expand_call (exp, target, ignore)
*** 1646,1651 ****
--- 1663,1671 ----
    int returns_twice;
    /* Nonzero if this is a call to `longjmp'.  */
    int is_longjmp;
+   /* Nonzero if this is a syscall that makes a new process in the image of
+      the current one.  */
+   int fork_or_exec;
    /* Nonzero if this is a call to an inline function.  */
    int is_integrable = 0;
    /* Nonzero if this is a call to a `const' function.
*************** expand_call (exp, target, ignore)
*** 1903,1909 ****
  
    /* See if this is a call to a function that can return more than once
       or a call to longjmp or malloc.  */
!   special_function_p (fndecl, &returns_twice, &is_longjmp,
  		      &is_malloc, &may_be_alloca);
  
    if (may_be_alloca)
--- 1923,1929 ----
  
    /* See if this is a call to a function that can return more than once
       or a call to longjmp or malloc.  */
!   special_function_p (fndecl, &returns_twice, &is_longjmp, &fork_or_exec,
  		      &is_malloc, &may_be_alloca);
  
    if (may_be_alloca)
*************** expand_call (exp, target, ignore)
*** 1927,1932 ****
--- 1947,1964 ----
    if (pending_stack_adjust >= 32
        || (pending_stack_adjust > 0 && may_be_alloca))
      do_pending_stack_adjust ();
+ 
+   if (profile_arc_flag && fork_or_exec)
+     {
+ 	/* A fork duplicates the profile information, and an exec discards
+ 	   it.  We can't rely on fork/exec to be paired.  So write out the
+ 	   profile information we have gathered so far, and clear it.  */
+       emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__bb_fork_func"), 0,
+ 			 VOIDmode, 0);
+ 
+       /* ??? When __clone is called with CLONE_VM set, profiling is
+          subject to race conditions, just as with multithreaded programs.  */
+     }
  
    /* Push the temporary stack slot level so that we can free any temporaries
       we make.  */
Index: libgcc2.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/libgcc2.c,v
retrieving revision 1.83
diff -p -r1.83 libgcc2.c
*** libgcc2.c	2000/01/23 22:33:30	1.83
--- libgcc2.c	2000/01/25 23:40:40
***************
*** 1,6 ****
  /* More subroutines needed by GCC output code on some machines.  */
  /* Compile this one with gcc.  */
! /* Copyright (C) 1989, 92-98, 1999 Free Software Foundation, Inc.
  
  This file is part of GNU CC.
  
--- 1,7 ----
  /* More subroutines needed by GCC output code on some machines.  */
  /* Compile this one with gcc.  */
! /* Copyright (C) 1989, 92, 93, 94, 95, 96, 97, 98, 1999, 2000
!    Free Software Foundation, Inc.
  
  This file is part of GNU CC.
  
*************** char *ctime ();
*** 1563,1568 ****
--- 1564,1573 ----
  #include "gbl-ctors.h"
  #include "gcov-io.h"
  #include <string.h>
+ #ifdef TARGET_HAS_F_SETLKW
+ #include <fcntl.h>
+ #include <errno.h>
+ #endif
  
  static struct bb *bb_head;
  
*************** __bb_exit_func (void)
*** 1606,1618 ****
  
        for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next)
  	{
! 	  /* If the file exists, and the number of counts in it is the same,
! 	     then merge them in.  */
! 	     
! 	  if ((da_file = fopen (ptr->filename, "rb")) != 0)
  	    {
  	      long n_counts = 0;
  	      
  	      if (__read_long (&n_counts, da_file, 8) != 0)
  		{
  		  fprintf (stderr, "arc profiling: Can't read output file %s.\n",
--- 1611,1674 ----
  
        for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next)
  	{
! 	  int firstchar;
! 
! 	  /* Make sure the output file exists -
! 	     but don't clobber exiting data.  */
! 	  if ((da_file = fopen (ptr->filename, "a")) != 0)
! 	    fclose (da_file);
! 
! 	  /* Need to re-open in order to be able to write from the start.  */
! 	  da_file = fopen (ptr->filename, "r+b");
! 	  /* Some old systems might not allow the 'b' mode modifier.
! 	     Therefore, try to open without it.  This can lead to a race
! 	     condition so that when you delete and re-create the file, the
! 	     file might be opened in text mode, but then, you shouldn't
! 	     delete the file in the first place.  */
! 	  if (da_file == 0)
! 	    da_file = fopen (ptr->filename, "r+");
! 	  if (da_file == 0)
! 	    {
! 	      fprintf (stderr, "arc profiling: Can't open output file %s.\n",
! 		       ptr->filename);
! 	      continue;
! 	    }
! 
! 	  /* After a fork, another process might try to read and/or write
! 	     the same file simultanously.  So if we can, lock the file to
! 	     avoid race conditions.  */
! #if defined (TARGET_HAS_F_SETLKW)
! 	  {
! 	    struct flock s_flock;
! 
! 	    s_flock.l_type = F_WRLCK;
! 	    s_flock.l_whence = SEEK_SET;
! 	    s_flock.l_start = 0;
! 	    s_flock.l_len = 1;
! 	    s_flock.l_pid = getpid ();
! 
! 	    while (fcntl (fileno (da_file), F_SETLKW, &s_flock)
! 		   && errno == EINTR);
! 	  }
! #endif
! 
! 	  /* If the file is not empty, and the number of counts in it is the
! 	     same, then merge them in.  */
! 	  firstchar = fgetc (da_file);
! 	  if (firstchar == EOF)
  	    {
+ 	      if (ferror (da_file))
+ 		{
+ 		  fprintf (stderr, "arc profiling: Can't read output file ");
+ 		  perror (ptr->filename);
+ 		}
+ 	    }
+ 	  else
+ 	    {
  	      long n_counts = 0;
  	      
+ 	      if (ungetc (firstchar, da_file) == EOF)
+ 		rewind (da_file);
  	      if (__read_long (&n_counts, da_file, 8) != 0)
  		{
  		  fprintf (stderr, "arc profiling: Can't read output file %s.\n",
*************** __bb_exit_func (void)
*** 1638,1653 ****
  		    }
  		}
  
- 	      if (fclose (da_file) == EOF)
- 		fprintf (stderr, "arc profiling: Error closing output file %s.\n",
- 			 ptr->filename);
  	    }
! 	  if ((da_file = fopen (ptr->filename, "wb")) == 0)
! 	    {
! 	      fprintf (stderr, "arc profiling: Can't open output file %s.\n",
! 		       ptr->filename);
! 	      continue;
! 	    }
  
  	  /* ??? Should first write a header to the file.  Preferably, a 4 byte
  	     magic number, 4 bytes containing the time the program was
--- 1694,1702 ----
  		    }
  		}
  
  	    }
! 
! 	  rewind (da_file);
  
  	  /* ??? Should first write a header to the file.  Preferably, a 4 byte
  	     magic number, 4 bytes containing the time the program was
*************** __bb_init_func (struct bb *blocks)
*** 1824,1829 ****
--- 1873,1895 ----
    blocks->zero_word = 1;
    blocks->next = bb_head;
    bb_head = blocks;
+ }
+ 
+ /* Called before fork or exec - write out profile information gathered so
+    far and reset it to zero.  This avoids duplication or loss of the
+    profile information gathered so far.  */
+ void
+ __bb_fork_func (void)
+ {
+   struct bb *ptr;
+ 
+   __bb_exit_func ();
+   for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next)
+     {
+       long i;
+       for (i = ptr->ncounts - 1; i >= 0; i--)
+ 	ptr->counts[i] = 0;
+     }
  }
  
  #ifndef MACHINE_STATE_SAVE
Index: tm.texi
===================================================================
RCS file: /cvs/gcc/egcs/gcc/tm.texi,v
retrieving revision 1.106
diff -p -r1.106 tm.texi
*** tm.texi	2000/01/18 00:30:17	1.106
--- tm.texi	2000/01/25 23:40:47
*************** specifies where the linker should look f
*** 7815,7818 ****
--- 7815,7826 ----
  
  You need only define this macro if the default of @samp{"LIBRARY_PATH"}
  is wrong.
+ 
+ @findex TARGET_HAS_F_SETLKW
+ @item TARGET_HAS_F_SETLKW
+ Define this macro iff the target supports file locking with fcntl / F_SETLKW.
+ Note that this functionality is part of POSIX.
+ Defining @code{TARGET_HAS_F_SETLKW} will enable the test coverage code
+ to use file locking when exiting a program, which avoids race conditions
+ if the program has forked.
  @end table
Index: tree.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/tree.h,v
retrieving revision 1.118
diff -p -r1.118 tree.h
*** tree.h	2000/01/24 20:10:02	1.118
--- tree.h	2000/01/25 23:40:49
*************** extern struct rtx_def *emit_line_note_fo
*** 2431,2437 ****
  
  /* In calls.c */
  extern void special_function_p	        PARAMS ((tree, int *, int *,
! 						int *, int *));
  
  /* In c-typeck.c */
  extern int mark_addressable		PARAMS ((tree));
--- 2431,2437 ----
  
  /* In calls.c */
  extern void special_function_p	        PARAMS ((tree, int *, int *,
! 						int *, int *, int *));
  
  /* In c-typeck.c */
  extern int mark_addressable		PARAMS ((tree));
Index: cp/optimize.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cp/optimize.c,v
retrieving revision 1.12
diff -p -r1.12 optimize.c
*** optimize.c	2000/01/15 04:36:04	1.12
--- optimize.c	2000/01/25 23:41:02
*************** calls_setjmp_r (tp, walk_subtrees, data)
*** 732,737 ****
--- 732,738 ----
  {
    int setjmp_p;
    int longjmp_p;
+   int fork_or_exec_p;
    int malloc_p;
    int alloca_p;
  
*************** calls_setjmp_r (tp, walk_subtrees, data)
*** 739,745 ****
    if (TREE_CODE (*tp) != FUNCTION_DECL)
      return NULL_TREE;
  
!   special_function_p (*tp, &setjmp_p, &longjmp_p, &malloc_p, &alloca_p);
  
    return setjmp_p ? *tp : NULL_TREE;
  }
--- 740,747 ----
    if (TREE_CODE (*tp) != FUNCTION_DECL)
      return NULL_TREE;
  
!   special_function_p (*tp, &setjmp_p, &longjmp_p, &fork_or_exec_p, &malloc_p,
! 		      &alloca_p);
  
    return setjmp_p ? *tp : NULL_TREE;
  }

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