This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
fork-proofing -ftest-coverage / gcov
- To: meissner at cygnus dot com, jason at cygnus dot com
- Subject: fork-proofing -ftest-coverage / gcov
- From: Joern Rennecke <amylaar at cygnus dot co dot uk>
- Date: Tue, 25 Jan 2000 23:57:45 +0000 (GMT)
- CC: amylaar at pasanda dot cygnus dot co dot uk, gcc-patches at gcc dot gnu dot org
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;
}