PATCH: gcov: new option to merge results

Tristan Gingold gingold@adacore.com
Thu Jun 7 09:05:00 GMT 2007


Hi,

this patch adds a new option to gcov: --merge-results (-m).
When this option is set, gcov reads all input files before writing  
results instead of writing results after each input file.
Therefore, if two .gcda reference a common source file com.c, only  
one com.c.gcov file is generated with counts added rather than two
com.c.gcov, the second one overwriting the first one.

Bootstrapped and reg-tested on x86-linux.

Tristan.

2007-06-04  Tristan Gingold  <gingold@adacore.com>

	* gcov.c: Comments updated.
	(source_info): Add file_time field.
	(source_index): New variable.
	(flag_merge_results): New variable.
	(generate_results): New function extracted from process_file.
	(main): Call generate_results if merging results.
	Call to release_structures moved into process_file.
	(print_usage): Document new merge-results option.
	(options): Entry added for merge-results option.
	(process_args): Handle merge-results option.
	(process_file): Save and restore chain of functions, generate
	results and free structures only if not merging results.
	Free file names here.
	(release_structures): File names are now freed in process_file.
	(find_source): File date is now read here and modifications in
	source files is checked here.
	(read_graph_file): Only reverse order of functions for the current
	object file.
	(make_gcov_file_name): Do not generate long names if input_name is
	NULL.
	(output_lines): If merging results do not display graph, data and
	runs informations.
	Checking source file modification is done in find_source.

Index: gcc/doc/gcov.texi
===================================================================
--- gcc/doc/gcov.texi	(revision 125216)
+++ gcc/doc/gcov.texi	(working copy)
@@ -113,7 +113,7 @@
@section Invoking @command{gcov}
@smallexample
-gcov @r{[}@var{options}@r{]} @var{sourcefile}
+gcov @r{[}@var{options}@r{]} @var{sourcefiles}
@end smallexample
@command{gcov} accepts the following options:
@@ -125,6 +125,7 @@
       [@option{-b}|@option{--branch-probabilities}]
       [@option{-c}|@option{--branch-counts}]
       [@option{-n}|@option{--no-output}]
+     [@option{-m}|@option{--merge-results}]
       [@option{-l}|@option{--long-file-names}]
       [@option{-p}|@option{--preserve-paths}]
       [@option{-f}|@option{--function-summaries}]
@@ -171,6 +172,15 @@
@itemx --no-output
Do not create the @command{gcov} output file.
+@item -m
+@itemx --merge-results
+Normally @command{gcov} processes source file successively.  As a
+consequence an output file generated for a source file may be  
overwritten by
+a later source file.  This may happen if the two source files  
include a third
+file containing (generally inline) code.  When this option is set
+@command{gcov} handles all source files, merges the results and then  
generates
+the output files.
+
@item -l
@itemx --long-file-names
Create long file names for included source files.  For example, if the
Index: gcc/gcov.c
===================================================================
--- gcc/gcov.c	(revision 125216)
+++ gcc/gcov.c	(working copy)
@@ -29,15 +29,6 @@
/* ??? Should have an option to print the number of basic blocks, and  
the
     percent of them that are covered.  */
-/* ??? Does not correctly handle the case where two .bb files refer to
-   the same included source file.  For example, if one has a short
-   file containing only inline functions, which is then included in
-   two other files, then there will be two .bb files which refer to
-   the include file, but there is no way to get the total execution
-   counts for the included file, can only get execution counts for one
-   or the other of the including files. this can be fixed by --ratios
-   --long-file-names --preserve-paths and perl.  */
-
/* Need an option to show individual block counts, and show
     probabilities of fall through arcs.  */
@@ -54,7 +45,7 @@
#include "gcov-io.h"
#include "gcov-io.c"
-/* The bbg file is generated by -ftest-coverage option. The da file is
+/* The gcno file is generated by -ftest-coverage option. The gcda  
file is
     generated by a program compiled with -fprofile-arcs. Their formats
     are documented in gcov-io.h.  */
@@ -234,6 +225,7 @@
    /* Name of source file.  */
    char *name;
    unsigned index;
+  time_t file_time;
    /* Array of line information.  */
    line_t *lines;
@@ -253,10 +245,15 @@
static function_t *functions;
-/* This points to the head of the sourcefile structure list.  */
+/* This points to the head of the sourcefile structure list.  New  
elements
+   are always prepended.  */
static source_t *sources;
+/* Next index for a source file.  */
+
+static unsigned source_index;
+
/* This holds data summary information.  */
static struct gcov_summary object_summary;
@@ -324,12 +321,21 @@
static int flag_counts = 0;
+/* Compute and display results after reading all data files.  This  
way if two
+   or more gcda file refer to the same source file (eg inline  
subprograms in
+   a .h file), the counts are added.
+   If the option is not set result files are generated after each  
data file
+   (and therefore may be overwritten).  */
+
+static int flag_merge_results = 0;
+
/* Forward declarations.  */
static void fnotice (FILE *, const char *, ...) ATTRIBUTE_PRINTF_2;
static int process_args (int, char **);
static void print_usage (int) ATTRIBUTE_NORETURN;
static void print_version (void) ATTRIBUTE_NORETURN;
static void process_file (const char *);
+static void generate_results (const char *);
static void create_file_names (const char *);
static source_t *find_source (const char *);
static int read_graph_file (void);
@@ -361,11 +367,10 @@
      print_usage (true);
    for (; argno != argc; argno++)
-    {
-      release_structures ();
+    process_file (argv[argno]);
-      process_file (argv[argno]);
-    }
+  if (flag_merge_results)
+    generate_results (NULL);
    return 0;
}
@@ -389,7 +394,7 @@
    FILE *file = error_p ? stderr : stdout;
    int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
-  fnotice (file, "Usage: gcov [OPTION]... SOURCEFILE\n\n");
+  fnotice (file, "Usage: gcov [OPTION]... SOURCEFILE...\n\n");
    fnotice (file, "Print code coverage information.\n\n");
    fnotice (file, "  -h, --help                      Print this  
help, then exit\n");
    fnotice (file, "  -v, --version                   Print version  
number, then exit\n");
@@ -397,6 +402,7 @@
    fnotice (file, "  -b, --branch-probabilities      Include branch  
probabilities in output\n");
    fnotice (file, "  -c, --branch-counts             Given counts of  
branches taken\n\
                                      rather than percentages\n");
+  fnotice (file, "  -m, --merge-results             Merge results  
from all source files\n");
    fnotice (file, "  -n, --no-output                 Do not create  
an output file\n");
    fnotice (file, "  -l, --long-file-names           Use long output  
file names for included\n\
                                      source files\n");
@@ -432,6 +438,7 @@
    { "branch-probabilities", no_argument,       NULL, 'b' },
    { "branch-counts",        no_argument,       NULL, 'c' },
    { "no-output",            no_argument,       NULL, 'n' },
+  { "merge-results",        no_argument,       NULL, 'm' },
    { "long-file-names",      no_argument,       NULL, 'l' },
    { "function-summaries",   no_argument,       NULL, 'f' },
    { "preserve-paths",       no_argument,       NULL, 'p' },
@@ -448,7 +455,7 @@
{
    int opt;
-  while ((opt = getopt_long (argc, argv, "abcfhlno:puv", options,  
NULL)) != -1)
+  while ((opt = getopt_long (argc, argv, "abcfhlmno:puv", options,  
NULL)) != -1)
      {
        switch (opt)
	{
@@ -470,6 +477,9 @@
	case 'l':
	  flag_long_names = 1;
	  break;
+	case 'm':
+	  flag_merge_results = 1;
+	  break;
	case 'n':
	  flag_gcov_file = 0;
	  break;
@@ -499,9 +509,15 @@
static void
process_file (const char *file_name)
{
-  source_t *src;
    function_t *fn;
+  function_t *fn_p;
+  function_t *old_functions;
+  /* Save and clear the list of current functions.  They will be  
appended
+     later.  */
+  old_functions = functions;
+  functions = NULL;
+
    create_file_names (file_name);
    if (read_graph_file ())
      return;
@@ -515,8 +531,31 @@
    if (read_count_file ())
      return;
-  for (fn = functions; fn; fn = fn->next)
+  for (fn_p = NULL, fn = functions; fn; fn_p = fn, fn = fn->next)
      solve_flow_graph (fn);
+
+  if (fn_p)
+    fn_p->next = old_functions;
+
+  if (!flag_merge_results)
+    {
+      generate_results (file_name);
+      release_structures ();
+    }
+
+  free (bbg_file_name);
+  free (da_file_name);
+  da_file_name = bbg_file_name = NULL;
+  bbg_file_time = 0;
+  bbg_stamp = 0;
+}
+
+static void
+generate_results (const char *file_name)
+{
+  source_t *src;
+  function_t *fn;
+
    for (src = sources; src; src = src->next)
      src->lines = XCNEWVEC (line_t, src->num_lines);
    for (fn = functions; fn; fn = fn->next)
@@ -569,12 +608,6 @@
    function_t *fn;
    source_t *src;
-  free (bbg_file_name);
-  free (da_file_name);
-  da_file_name = bbg_file_name = NULL;
-  bbg_file_time = 0;
-  bbg_stamp = 0;
-
    while ((src = sources))
      {
        sources = src->next;
@@ -673,21 +706,43 @@
find_source (const char *file_name)
{
    source_t *src;
+  struct stat status;
    if (!file_name)
      file_name = "<unknown>";
    for (src = sources; src; src = src->next)
      if (!strcmp (file_name, src->name))
-      return src;
+      break;
-  src = XCNEW (source_t);
-  src->name = xstrdup (file_name);
-  src->coverage.name = src->name;
-  src->index = sources ? sources->index + 1 : 1;
-  src->next = sources;
-  sources = src;
+  if (!src)
+    {
+      src = XCNEW (source_t);
+      src->name = xstrdup (file_name);
+      src->coverage.name = src->name;
+      src->index = source_index++;
+      src->next = sources;
+      sources = src;
+
+      if (!stat (file_name, &status))
+	src->file_time = status.st_mtime;
+    }
+  if (src->file_time > bbg_file_time)
+    {
+      static int info_emitted;
+
+      fnotice (stderr, "%s:source file is newer than graph file '% 
s'\n",
+	       src->name, bbg_file_name);
+      if (!info_emitted)
+	{
+	  fnotice (stderr,
+		   "(the message is only displayed one per source file)\n");
+	  info_emitted = 1;
+	}
+      src->file_time = 0;
+    }
+
    return src;
}
@@ -699,6 +754,7 @@
    unsigned version;
    unsigned current_tag = 0;
    struct function_info *fn = NULL;
+  function_t *old_functions_head = functions;
    source_t *src = NULL;
    unsigned ix;
    unsigned tag;
@@ -920,7 +976,9 @@
    {
      function_t *fn, *fn_p, *fn_n;
-    for (fn_p = NULL, fn = functions; fn; fn_p = fn, fn = fn_n)
+    for (fn_p = old_functions_head, fn = functions;
+	 fn != old_functions_head;
+	 fn_p = fn, fn = fn_n)
        {
	unsigned ix;
@@ -1012,6 +1070,9 @@
	  unsigned ident = gcov_read_unsigned ();
	  struct function_info *fn_n = functions;
+	  /* Try to find the function in the list.
+	     To speed up the search, first start from the last function
+	     found.   */
	  for (fn = fn ? fn->next : NULL; ; fn = fn->next)
	    {
	      if (fn)
@@ -1424,16 +1485,22 @@
make_gcov_file_name (const char *input_name, const char *src_name)
{
    char *cptr;
-  char *name = XNEWVEC (char, strlen (src_name) + strlen  
(input_name) + 10);
+  char *name;
-  name[0] = 0;
-  if (flag_long_names && strcmp (src_name, input_name))
+  if (flag_long_names && input_name && strcmp (src_name, input_name))
      {
+      name = XNEWVEC (char, strlen (src_name) + strlen (input_name)  
+ 10);
+      name[0] = 0;
        /* Generate the input filename part.  */
        cptr = flag_preserve_paths ? NULL : strrchr (input_name, '/');
        strcat (name, cptr ? cptr + 1 : input_name);
        strcat (name, "##");
      }
+  else
+    {
+      name = XNEWVEC (char, strlen (src_name) + 10);
+      name[0] = 0;
+    }
    /* Generate the source filename part.  */
    cptr = flag_preserve_paths ? NULL : strrchr (src_name, '/');
@@ -1787,11 +1854,14 @@
    function_t *fn = NULL;
    fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, src->name);
-  fprintf (gcov_file, "%9s:%5d:Graph:%s\n", "-", 0, bbg_file_name);
-  fprintf (gcov_file, "%9s:%5d:Data:%s\n", "-", 0,
-	   no_data_file ? "-" : da_file_name);
-  fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0,
-	   object_summary.ctrs[GCOV_COUNTER_ARCS].runs);
+  if (!flag_merge_results)
+    {
+      fprintf (gcov_file, "%9s:%5d:Graph:%s\n", "-", 0, bbg_file_name);
+      fprintf (gcov_file, "%9s:%5d:Data:%s\n", "-", 0,
+	       no_data_file ? "-" : da_file_name);
+      fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0,
+	       object_summary.ctrs[GCOV_COUNTER_ARCS].runs);
+    }
    fprintf (gcov_file, "%9s:%5d:Programs:%u\n", "-", 0, program_count);
    source_file = fopen (src->name, "r");
@@ -1800,19 +1870,8 @@
        fnotice (stderr, "%s:cannot open source file\n", src->name);
        retval = NULL;
      }
-  else
-    {
-      struct stat status;
+  else if (src->file_time == 0)
+    fprintf (gcov_file, "%9s:%5d:Source is newer than graph\n", "-",  
0);
-      if (!fstat (fileno (source_file), &status)
-	  && status.st_mtime > bbg_file_time)
-	{
-	  fnotice (stderr, "%s:source file is newer than graph file '%s'\n",
-		   src->name, bbg_file_name);
-	  fprintf (gcov_file, "%9s:%5d:Source is newer than graph\n",
-		   "-", 0);
-	}
-    }
-
    if (flag_branches)
      fn = src->functions;



More information about the Gcc-patches mailing list