This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
PATCH: gcov: new option to merge results
- From: Tristan Gingold <gingold at adacore dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Thu, 7 Jun 2007 11:04:16 +0200
- Subject: PATCH: gcov: new option to merge results
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;