Commit 5366b186 by Nathan Sidwell Committed by Nathan Sidwell

libgcov.c (struct gcov_fn_buffer): New struct.

	libgcc/
	* libgcov.c (struct gcov_fn_buffer): New struct.
	(buffer_fn_data): New helper.
	(gcov_exit): Rework for new gcov data structures.

	gcc/
	* gcov.c (object_summary): Replace with ...
	(object_runs): ... this.
	(process_file): Remove functions with no data.
	(generate_results): Ignore files with no lines.
	(release_function): New helper, broken out of ...
	(release_structures): ... here.  Use it.
	(read_count_file): Adjust for new data file format.
	(output_lines): Use object_runs.
	* gcov-io.h (GCOV_TAG_OBJECT_SUMMARY): Obsolete.
	(struct gcov_ctr_info): Move definition.
	(struct gcov_fn_info): Add key field, use gcov_ctr_info for
	trailing array.
	(struct gcov_info): Add merge function array, remove mask and
	counts.  Trailing array is array of pointers to function info.
	* coverage.c (struct function_list): Replace counter numbers with
	counter arrays.  Add fndecl field.  GTYify.
	(counts_entry): Remove chain workspace.
	(functions_head): GTYify.
	(prg_n_ctrs): Remove.
	(fn_v_ctrs): New.
	(tree_ctr_tables): Remove.
	(read_counts_file): Cope with blank entries and expect program
	summaries before functions.  Don't warn on missing entries.
	(coverage_counter_alloc): Allocate individual function arrays.
	(tree_coverage_counter_ref, tree_coverage_counter_addr): Adjust
	for individual function arrays.
	(coverage_end_function): GTYify function list object. Finalize
	function's counter arrays.
	(build_var): New.  Create a counter-related variable with
	appropriate linkage.
	(build_fn_info_type): Adjust for new runtime structure.
	(build_fn_info_value): Rename to ...
	(build_fn_info): ... here.  Build new format data.
	(build_ctr_info_type, build_ctr_info_value): Remove.
	(build_info_type): New. Build new format data structure.
	(build_info): Adjust for new format data.
	(create_coverage): Likewise.
	* gcov-dump.c (tag_function): Recognize placeholders.

	gcc/testsuite/
	* gcc.dg/profile-dir-1.c: Adjust final scan.
	* gcc.dg/profile-dir-2.c: Adjust final scan.
	* gcc.dg/profile-dir-3.c: Adjust final scan.
	* gcc.misc-tests/gcov.exp: Adjust regexp.
	* gcc.misc-tests/gcov-12.c: New.
	* gcc.misc-tests/gcov-13.c: New.
	* gcc.misc-tests/gcovpart-13b.c: New.
	* gcc.misc-tests/gcov-14.c: New.

From-SVN: r181105
parent 74155a6f
2011-11-07 Nathan Sidwell <nathan@acm.org>
* gcov.c (object_summary): Replace with ...
(object_runs): ... this.
(process_file): Remove functions with no data.
(generate_results): Ignore files with no lines.
(release_function): New helper, broken out of ...
(release_structures): ... here. Use it.
(read_count_file): Adjust for new data file format.
(output_lines): Use object_runs.
* gcov-io.h (GCOV_TAG_OBJECT_SUMMARY): Obsolete.
(struct gcov_ctr_info): Move definition.
(struct gcov_fn_info): Add key field, use gcov_ctr_info for
trailing array.
(struct gcov_info): Add merge function array, remove mask and
counts. Trailing array is array of pointers to function info.
* coverage.c (struct function_list): Replace counter numbers with
counter arrays. Add fndecl field. GTYify.
(counts_entry): Remove chain workspace.
(functions_head): GTYify.
(prg_n_ctrs): Remove.
(fn_v_ctrs): New.
(tree_ctr_tables): Remove.
(read_counts_file): Cope with blank entries and expect program
summaries before functions. Don't warn on missing entries.
(coverage_counter_alloc): Allocate individual function arrays.
(tree_coverage_counter_ref, tree_coverage_counter_addr): Adjust
for individual function arrays.
(coverage_end_function): GTYify function list object. Finalize
function's counter arrays.
(build_var): New. Create a counter-related variable with
appropriate linkage.
(build_fn_info_type): Adjust for new runtime structure.
(build_fn_info_value): Rename to ...
(build_fn_info): ... here. Build new format data.
(build_ctr_info_type, build_ctr_info_value): Remove.
(build_info_type): New. Build new format data structure.
(build_info): Adjust for new format data.
(create_coverage): Likewise.
* gcov-dump.c (tag_function): Recognize placeholders.
2011-11-07 Georg-Johann Lay <avr@gjlay.de> 2011-11-07 Georg-Johann Lay <avr@gjlay.de>
* config/avr/constraints.md (Cm2): New constraint for int -2. * config/avr/constraints.md (Cm2): New constraint for int -2.
/* Read and write coverage files, and associated functionality. /* Read and write coverage files, and associated functionality.
Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999, Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999,
2000, 2001, 2003, 2004, 2005, 2007, 2008, 2009, 2010 2000, 2001, 2003, 2004, 2005, 2007, 2008, 2009, 2010, 2011
Free Software Foundation, Inc. Free Software Foundation, Inc.
Contributed by James E. Wilson, UC Berkeley/Cygnus Support; Contributed by James E. Wilson, UC Berkeley/Cygnus Support;
based on some ideas from Dain Samples of UC Berkeley. based on some ideas from Dain Samples of UC Berkeley.
...@@ -54,13 +54,14 @@ along with GCC; see the file COPYING3. If not see ...@@ -54,13 +54,14 @@ along with GCC; see the file COPYING3. If not see
#include "gcov-io.h" #include "gcov-io.h"
#include "gcov-io.c" #include "gcov-io.c"
struct function_list struct GTY((chain_next ("%h.next"))) function_list
{ {
struct function_list *next; /* next function */ struct function_list *next; /* next function */
unsigned ident; /* function ident */ unsigned ident; /* function ident */
unsigned lineno_checksum; /* function lineno checksum */ unsigned lineno_checksum; /* function lineno checksum */
unsigned cfg_checksum; /* function cfg checksum */ unsigned cfg_checksum; /* function cfg checksum */
unsigned n_ctrs[GCOV_COUNTERS];/* number of counters. */ tree fn_decl; /* the function decl */
tree ctr_vars[GCOV_COUNTERS]; /* counter variables. */
}; };
/* Counts information for a function. */ /* Counts information for a function. */
...@@ -75,22 +76,18 @@ typedef struct counts_entry ...@@ -75,22 +76,18 @@ typedef struct counts_entry
unsigned cfg_checksum; unsigned cfg_checksum;
gcov_type *counts; gcov_type *counts;
struct gcov_ctr_summary summary; struct gcov_ctr_summary summary;
/* Workspace */
struct counts_entry *chain;
} counts_entry_t; } counts_entry_t;
static struct function_list *functions_head = 0; static GTY(()) struct function_list *functions_head = 0;
static struct function_list **functions_tail = &functions_head; static struct function_list **functions_tail = &functions_head;
static unsigned no_coverage = 0; static unsigned no_coverage = 0;
/* Cumulative counter information for whole program. */ /* Cumulative counter information for whole program. */
static unsigned prg_ctr_mask; /* Mask of counter types generated. */ static unsigned prg_ctr_mask; /* Mask of counter types generated. */
static unsigned prg_n_ctrs[GCOV_COUNTERS]; /* Total counters allocated. */
/* Counter information for current function. */ /* Counter information for current function. */
static unsigned fn_ctr_mask; /* Mask of counters used. */ static unsigned fn_ctr_mask; /* Mask of counters used. */
static GTY(()) tree fn_v_ctrs[GCOV_COUNTERS]; /* counter variables. */
static unsigned fn_n_ctrs[GCOV_COUNTERS]; /* Counters allocated. */ static unsigned fn_n_ctrs[GCOV_COUNTERS]; /* Counters allocated. */
static unsigned fn_b_ctrs[GCOV_COUNTERS]; /* Allocation base. */ static unsigned fn_b_ctrs[GCOV_COUNTERS]; /* Allocation base. */
...@@ -105,9 +102,6 @@ static char *da_file_name; ...@@ -105,9 +102,6 @@ static char *da_file_name;
/* Hash table of count data. */ /* Hash table of count data. */
static htab_t counts_hash = NULL; static htab_t counts_hash = NULL;
/* Trees representing the counter table arrays. */
static GTY(()) tree tree_ctr_tables[GCOV_COUNTERS];
/* The names of merge functions for counters. */ /* The names of merge functions for counters. */
static const char *const ctr_merge_functions[GCOV_COUNTERS] = GCOV_MERGE_FUNCTIONS; static const char *const ctr_merge_functions[GCOV_COUNTERS] = GCOV_MERGE_FUNCTIONS;
static const char *const ctr_names[GCOV_COUNTERS] = GCOV_COUNTER_NAMES; static const char *const ctr_names[GCOV_COUNTERS] = GCOV_COUNTER_NAMES;
...@@ -117,11 +111,11 @@ static hashval_t htab_counts_entry_hash (const void *); ...@@ -117,11 +111,11 @@ static hashval_t htab_counts_entry_hash (const void *);
static int htab_counts_entry_eq (const void *, const void *); static int htab_counts_entry_eq (const void *, const void *);
static void htab_counts_entry_del (void *); static void htab_counts_entry_del (void *);
static void read_counts_file (void); static void read_counts_file (void);
static tree build_fn_info_type (unsigned); static tree build_var (tree, tree, int);
static tree build_fn_info_value (const struct function_list *, tree); static void build_fn_info_type (tree, unsigned, tree);
static tree build_ctr_info_type (void); static tree build_fn_info (const struct function_list *, tree, tree);
static tree build_ctr_info_value (unsigned, tree); static void build_info_type (tree, unsigned, tree);
static tree build_gcov_info (void); static tree build_info (tree, tree, tree, unsigned);
static void create_coverage (void); static void create_coverage (void);
/* Return the type node for gcov_type. */ /* Return the type node for gcov_type. */
...@@ -172,8 +166,8 @@ static void ...@@ -172,8 +166,8 @@ static void
read_counts_file (void) read_counts_file (void)
{ {
gcov_unsigned_t fn_ident = 0; gcov_unsigned_t fn_ident = 0;
counts_entry_t *summaried = NULL; struct gcov_summary summary;
unsigned seen_summary = 0; unsigned new_summary = 1;
gcov_unsigned_t tag; gcov_unsigned_t tag;
int is_error = 0; int is_error = 0;
unsigned lineno_checksum = 0; unsigned lineno_checksum = 0;
...@@ -216,42 +210,34 @@ read_counts_file (void) ...@@ -216,42 +210,34 @@ read_counts_file (void)
offset = gcov_position (); offset = gcov_position ();
if (tag == GCOV_TAG_FUNCTION) if (tag == GCOV_TAG_FUNCTION)
{ {
fn_ident = gcov_read_unsigned (); if (length)
lineno_checksum = gcov_read_unsigned ();
cfg_checksum = gcov_read_unsigned ();
if (seen_summary)
{ {
/* We have already seen a summary, this means that this fn_ident = gcov_read_unsigned ();
new function begins a new set of program runs. We lineno_checksum = gcov_read_unsigned ();
must unlink the summaried chain. */ cfg_checksum = gcov_read_unsigned ();
counts_entry_t *entry, *chain;
for (entry = summaried; entry; entry = chain)
{
chain = entry->chain;
entry->chain = NULL;
}
summaried = NULL;
seen_summary = 0;
} }
else
fn_ident = lineno_checksum = cfg_checksum = 0;
new_summary = 1;
} }
else if (tag == GCOV_TAG_PROGRAM_SUMMARY) else if (tag == GCOV_TAG_PROGRAM_SUMMARY)
{ {
counts_entry_t *entry; struct gcov_summary sum;
struct gcov_summary summary; unsigned ix;
gcov_read_summary (&summary); if (new_summary)
seen_summary = 1; memset (&summary, 0, sizeof (summary));
for (entry = summaried; entry; entry = entry->chain)
{
struct gcov_ctr_summary *csum = &summary.ctrs[entry->ctr];
entry->summary.runs += csum->runs; gcov_read_summary (&sum);
entry->summary.sum_all += csum->sum_all; for (ix = 0; ix != GCOV_COUNTERS_SUMMABLE; ix++)
if (entry->summary.run_max < csum->run_max) {
entry->summary.run_max = csum->run_max; summary.ctrs[ix].runs += sum.ctrs[ix].runs;
entry->summary.sum_max += csum->sum_max; summary.ctrs[ix].sum_all += sum.ctrs[ix].sum_all;
if (summary.ctrs[ix].run_max < sum.ctrs[ix].run_max)
summary.ctrs[ix].run_max = sum.ctrs[ix].run_max;
summary.ctrs[ix].sum_max += sum.ctrs[ix].sum_max;
} }
new_summary = 0;
} }
else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident) else if (GCOV_TAG_IS_COUNTER (tag) && fn_ident)
{ {
...@@ -272,6 +258,7 @@ read_counts_file (void) ...@@ -272,6 +258,7 @@ read_counts_file (void)
entry->ctr = elt.ctr; entry->ctr = elt.ctr;
entry->lineno_checksum = lineno_checksum; entry->lineno_checksum = lineno_checksum;
entry->cfg_checksum = cfg_checksum; entry->cfg_checksum = cfg_checksum;
entry->summary = summary.ctrs[elt.ctr];
entry->summary.num = n_counts; entry->summary.num = n_counts;
entry->counts = XCNEWVEC (gcov_type, n_counts); entry->counts = XCNEWVEC (gcov_type, n_counts);
} }
...@@ -298,15 +285,13 @@ read_counts_file (void) ...@@ -298,15 +285,13 @@ read_counts_file (void)
ctr_names[elt.ctr], fn_ident); ctr_names[elt.ctr], fn_ident);
goto skip_merge; goto skip_merge;
} }
else
if (elt.ctr < GCOV_COUNTERS_SUMMABLE
/* This should always be true for a just allocated entry,
and always false for an existing one. Check this way, in
case the gcov file is corrupt. */
&& (!entry->chain || summaried != entry))
{ {
entry->chain = summaried; entry->summary.runs += summary.ctrs[elt.ctr].runs;
summaried = entry; entry->summary.sum_all += summary.ctrs[elt.ctr].sum_all;
if (entry->summary.run_max < summary.ctrs[elt.ctr].run_max)
entry->summary.run_max = summary.ctrs[elt.ctr].run_max;
entry->summary.sum_max += summary.ctrs[elt.ctr].sum_max;
} }
for (ix = 0; ix != n_counts; ix++) for (ix = 0; ix != n_counts; ix++)
entry->counts[ix] += gcov_read_counter (); entry->counts[ix] += gcov_read_counter ();
...@@ -350,13 +335,12 @@ get_coverage_counts (unsigned counter, unsigned expected, ...@@ -350,13 +335,12 @@ get_coverage_counts (unsigned counter, unsigned expected,
elt.ident = current_function_funcdef_no + 1; elt.ident = current_function_funcdef_no + 1;
elt.ctr = counter; elt.ctr = counter;
entry = (counts_entry_t *) htab_find (counts_hash, &elt); entry = (counts_entry_t *) htab_find (counts_hash, &elt);
if (!entry) if (!entry || !entry->summary.num)
{ /* The function was not emitted, or is weak and not chosen in the
warning (0, "no coverage for function %qE found", final executable. Silently fail, because there's nothing we
DECL_ASSEMBLER_NAME (current_function_decl)); can do about it. */
return NULL; return NULL;
}
if (entry->cfg_checksum != cfg_checksum if (entry->cfg_checksum != cfg_checksum
|| entry->summary.num != expected) || entry->summary.num != expected)
{ {
...@@ -366,11 +350,11 @@ get_coverage_counts (unsigned counter, unsigned expected, ...@@ -366,11 +350,11 @@ get_coverage_counts (unsigned counter, unsigned expected,
warning_printed = warning_printed =
warning_at (input_location, OPT_Wcoverage_mismatch, warning_at (input_location, OPT_Wcoverage_mismatch,
"The control flow of function %qE does not match " "the control flow of function %qE does not match "
"its profile data (counter %qs)", id, ctr_names[counter]); "its profile data (counter %qs)", id, ctr_names[counter]);
if (warning_printed) if (warning_printed)
{ {
inform (input_location, "Use -Wno-error=coverage-mismatch to tolerate " inform (input_location, "use -Wno-error=coverage-mismatch to tolerate "
"the mismatch but performance may drop if the function is hot"); "the mismatch but performance may drop if the function is hot");
if (!seen_error () if (!seen_error ()
...@@ -388,12 +372,12 @@ get_coverage_counts (unsigned counter, unsigned expected, ...@@ -388,12 +372,12 @@ get_coverage_counts (unsigned counter, unsigned expected,
return NULL; return NULL;
} }
else if (entry->lineno_checksum != lineno_checksum) else if (entry->lineno_checksum != lineno_checksum)
{ {
warning (0, "Source location for function %qE have changed," warning (0, "source location for function %qE have changed,"
" the profile data may be out of date", " the profile data may be out of date",
DECL_ASSEMBLER_NAME (current_function_decl)); DECL_ASSEMBLER_NAME (current_function_decl));
} }
if (summary) if (summary)
*summary = &entry->summary; *summary = &entry->summary;
...@@ -413,28 +397,17 @@ coverage_counter_alloc (unsigned counter, unsigned num) ...@@ -413,28 +397,17 @@ coverage_counter_alloc (unsigned counter, unsigned num)
if (!num) if (!num)
return 1; return 1;
if (!tree_ctr_tables[counter]) if (!fn_v_ctrs[counter])
{ {
/* Generate and save a copy of this so it can be shared. Leave tree array_type = build_array_type (get_gcov_type (), NULL_TREE);
the index type unspecified for now; it will be set after all
functions have been compiled. */ fn_v_ctrs[counter]
char buf[20]; = build_var (current_function_decl, array_type, counter);
tree gcov_type_node = get_gcov_type ();
tree gcov_type_array_type
= build_array_type (gcov_type_node, NULL_TREE);
tree_ctr_tables[counter]
= build_decl (BUILTINS_LOCATION,
VAR_DECL, NULL_TREE, gcov_type_array_type);
TREE_STATIC (tree_ctr_tables[counter]) = 1;
ASM_GENERATE_INTERNAL_LABEL (buf, "LPBX", counter + 1);
DECL_NAME (tree_ctr_tables[counter]) = get_identifier (buf);
DECL_ALIGN (tree_ctr_tables[counter]) = TYPE_ALIGN (gcov_type_node);
if (dump_file)
fprintf (dump_file, "Using data file %s\n", da_file_name);
} }
fn_b_ctrs[counter] = fn_n_ctrs[counter]; fn_b_ctrs[counter] = fn_n_ctrs[counter];
fn_n_ctrs[counter] += num; fn_n_ctrs[counter] += num;
fn_ctr_mask |= 1 << counter; fn_ctr_mask |= 1 << counter;
return 1; return 1;
} }
...@@ -447,10 +420,11 @@ tree_coverage_counter_ref (unsigned counter, unsigned no) ...@@ -447,10 +420,11 @@ tree_coverage_counter_ref (unsigned counter, unsigned no)
tree gcov_type_node = get_gcov_type (); tree gcov_type_node = get_gcov_type ();
gcc_assert (no < fn_n_ctrs[counter] - fn_b_ctrs[counter]); gcc_assert (no < fn_n_ctrs[counter] - fn_b_ctrs[counter]);
no += prg_n_ctrs[counter] + fn_b_ctrs[counter];
no += fn_b_ctrs[counter];
/* "no" here is an array index, scaled to bytes later. */ /* "no" here is an array index, scaled to bytes later. */
return build4 (ARRAY_REF, gcov_type_node, tree_ctr_tables[counter], return build4 (ARRAY_REF, gcov_type_node, fn_v_ctrs[counter],
build_int_cst (integer_type_node, no), NULL, NULL); build_int_cst (integer_type_node, no), NULL, NULL);
} }
...@@ -462,13 +436,11 @@ tree_coverage_counter_addr (unsigned counter, unsigned no) ...@@ -462,13 +436,11 @@ tree_coverage_counter_addr (unsigned counter, unsigned no)
tree gcov_type_node = get_gcov_type (); tree gcov_type_node = get_gcov_type ();
gcc_assert (no < fn_n_ctrs[counter] - fn_b_ctrs[counter]); gcc_assert (no < fn_n_ctrs[counter] - fn_b_ctrs[counter]);
no += prg_n_ctrs[counter] + fn_b_ctrs[counter]; no += fn_b_ctrs[counter];
TREE_ADDRESSABLE (tree_ctr_tables[counter]) = 1;
/* "no" here is an array index, scaled to bytes later. */ /* "no" here is an array index, scaled to bytes later. */
return build_fold_addr_expr (build4 (ARRAY_REF, gcov_type_node, return build_fold_addr_expr (build4 (ARRAY_REF, gcov_type_node,
tree_ctr_tables[counter], fn_v_ctrs[counter],
build_int_cst (integer_type_node, no), build_int_cst (integer_type_node, no),
NULL, NULL)); NULL, NULL));
} }
...@@ -647,78 +619,159 @@ coverage_end_function (unsigned lineno_checksum, unsigned cfg_checksum) ...@@ -647,78 +619,159 @@ coverage_end_function (unsigned lineno_checksum, unsigned cfg_checksum)
{ {
struct function_list *item; struct function_list *item;
item = XCNEW (struct function_list); item = ggc_alloc_function_list ();
*functions_tail = item;
functions_tail = &item->next;
item->next = 0; item->next = 0;
item->ident = current_function_funcdef_no + 1; item->ident = current_function_funcdef_no + 1;
item->lineno_checksum = lineno_checksum; item->lineno_checksum = lineno_checksum;
item->cfg_checksum = cfg_checksum; item->cfg_checksum = cfg_checksum;
item->fn_decl = current_function_decl;
for (i = 0; i != GCOV_COUNTERS; i++) for (i = 0; i != GCOV_COUNTERS; i++)
{ {
item->n_ctrs[i] = fn_n_ctrs[i]; tree var = fn_v_ctrs[i];
prg_n_ctrs[i] += fn_n_ctrs[i];
fn_n_ctrs[i] = fn_b_ctrs[i] = 0; item->ctr_vars[i] = var;
if (var)
{
tree array_type = build_index_type (size_int (fn_n_ctrs[i] - 1));
array_type = build_array_type (get_gcov_type (), array_type);
TREE_TYPE (var) = array_type;
DECL_SIZE (var) = TYPE_SIZE (array_type);
DECL_SIZE_UNIT (var) = TYPE_SIZE_UNIT (array_type);
varpool_finalize_decl (var);
}
fn_b_ctrs[i] = fn_n_ctrs[i] = 0;
fn_v_ctrs[i] = NULL_TREE;
} }
prg_ctr_mask |= fn_ctr_mask; prg_ctr_mask |= fn_ctr_mask;
fn_ctr_mask = 0; fn_ctr_mask = 0;
/* If the function is extern (i.e. extern inline), then we won't
be outputting it, so don't chain it onto the function list. */
if (!DECL_EXTERNAL (item->fn_decl))
{
*functions_tail = item;
functions_tail = &item->next;
}
} }
bbg_function_announced = 0; bbg_function_announced = 0;
} }
/* Creates the gcov_fn_info RECORD_TYPE. */ /* Build a coverage variable of TYPE for function FN_DECL. If COUNTER
>= 0 it is a counter array, and thus local. Otherwise it is the
function structure and needs to be globalized. All cases must be
in the same comdat group as FN_DECL. */
static tree static tree
build_fn_info_type (unsigned int counters) build_var (tree fn_decl, tree type, int counter)
{
tree var = build_decl (BUILTINS_LOCATION, VAR_DECL, NULL_TREE, type);
tree fn_name = DECL_ASSEMBLER_NAME (fn_decl);
char *buf = (char *)alloca (IDENTIFIER_LENGTH (fn_name) + 10);
if (counter >= 0)
TREE_STATIC (var) = 1;
else
{
TREE_PUBLIC (var) = TREE_PUBLIC (fn_decl);
TREE_STATIC (var) = TREE_STATIC (fn_decl);
}
TREE_ADDRESSABLE (var) = 1;
DECL_ALIGN (var) = TYPE_ALIGN (type);
if (counter < 0)
sprintf (buf, "__gcov__%s", IDENTIFIER_POINTER (fn_name));
else
sprintf (buf, "__gcov%u_%s", counter, IDENTIFIER_POINTER (fn_name));
DECL_NAME (var) = get_identifier (buf);
/* Initialize assembler name so we can stream out. */
if (TREE_PUBLIC (var))
DECL_ASSEMBLER_NAME (var);
DECL_WEAK (var) = TREE_PUBLIC (var) && DECL_WEAK (fn_decl);
DECL_COMDAT (var) = DECL_COMDAT (fn_decl);
DECL_COMDAT_GROUP (var) = DECL_COMDAT_GROUP (fn_decl);
return var;
}
/* Creates the gcov_fn_info RECORD_TYPE. */
static void
build_fn_info_type (tree type, unsigned counters, tree gcov_info_type)
{ {
tree type = lang_hooks.types.make_type (RECORD_TYPE); tree ctr_info = lang_hooks.types.make_type (RECORD_TYPE);
tree field, fields; tree field, fields;
tree array_type; tree array_type;
gcc_assert (counters);
/* ctr_info::num */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
get_gcov_unsigned_t ());
fields = field;
/* ctr_info::values */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
build_pointer_type (get_gcov_type ()));
DECL_CHAIN (field) = fields;
fields = field;
finish_builtin_struct (ctr_info, "__gcov_ctr_info", fields, NULL_TREE);
/* key */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
build_pointer_type (build_qualified_type
(gcov_info_type, TYPE_QUAL_CONST)));
fields = field;
/* ident */ /* ident */
fields = build_decl (BUILTINS_LOCATION, field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ()); get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields;
fields = field;
/* lineno_checksum */ /* lineno_checksum */
field = build_decl (BUILTINS_LOCATION, field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ()); get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields; DECL_CHAIN (field) = fields;
fields = field; fields = field;
/* cfg checksum */ /* cfg checksum */
field = build_decl (BUILTINS_LOCATION, field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ()); get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields; DECL_CHAIN (field) = fields;
fields = field; fields = field;
array_type = build_index_type (size_int (counters - 1)); array_type = build_index_type (size_int (counters - 1));
array_type = build_array_type (get_gcov_unsigned_t (), array_type); array_type = build_array_type (ctr_info, array_type);
/* counters */ /* counters */
field = build_decl (BUILTINS_LOCATION, field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE, array_type);
FIELD_DECL, NULL_TREE, array_type);
DECL_CHAIN (field) = fields; DECL_CHAIN (field) = fields;
fields = field; fields = field;
finish_builtin_struct (type, "__gcov_fn_info", fields, NULL_TREE); finish_builtin_struct (type, "__gcov_fn_info", fields, NULL_TREE);
return type;
} }
/* Creates a CONSTRUCTOR for a gcov_fn_info. FUNCTION is /* Creates a CONSTRUCTOR for a gcov_fn_info. FUNCTION is
the function being processed and TYPE is the gcov_fn_info the function being processed and TYPE is the gcov_fn_info
RECORD_TYPE. */ RECORD_TYPE. KEY is the object file key. */
static tree static tree
build_fn_info_value (const struct function_list *function, tree type) build_fn_info (const struct function_list *function, tree type, tree key)
{ {
tree fields = TYPE_FIELDS (type); tree fields = TYPE_FIELDS (type);
tree ctr_type;
unsigned ix; unsigned ix;
VEC(constructor_elt,gc) *v1 = NULL; VEC(constructor_elt,gc) *v1 = NULL;
VEC(constructor_elt,gc) *v2 = NULL; VEC(constructor_elt,gc) *v2 = NULL;
/* key */
CONSTRUCTOR_APPEND_ELT (v1, fields,
build1 (ADDR_EXPR, TREE_TYPE (fields), key));
fields = DECL_CHAIN (fields);
/* ident */ /* ident */
CONSTRUCTOR_APPEND_ELT (v1, fields, CONSTRUCTOR_APPEND_ELT (v1, fields,
build_int_cstu (get_gcov_unsigned_t (), build_int_cstu (get_gcov_unsigned_t (),
...@@ -738,240 +791,194 @@ build_fn_info_value (const struct function_list *function, tree type) ...@@ -738,240 +791,194 @@ build_fn_info_value (const struct function_list *function, tree type)
fields = DECL_CHAIN (fields); fields = DECL_CHAIN (fields);
/* counters */ /* counters */
ctr_type = TREE_TYPE (TREE_TYPE (fields));
for (ix = 0; ix != GCOV_COUNTERS; ix++) for (ix = 0; ix != GCOV_COUNTERS; ix++)
if (prg_ctr_mask & (1 << ix)) if (prg_ctr_mask & (1 << ix))
CONSTRUCTOR_APPEND_ELT (v2, NULL, {
build_int_cstu (get_gcov_unsigned_t (), VEC(constructor_elt,gc) *ctr = NULL;
function->n_ctrs[ix])); tree var = function->ctr_vars[ix];
unsigned count = 0;
if (var)
count
= tree_low_cst (TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (var))), 0)
+ 1;
CONSTRUCTOR_APPEND_ELT (ctr, TYPE_FIELDS (ctr_type),
build_int_cstu (get_gcov_unsigned_t (),
count));
if (var)
CONSTRUCTOR_APPEND_ELT (ctr, DECL_CHAIN (TYPE_FIELDS (ctr_type)),
build_fold_addr_expr (var));
CONSTRUCTOR_APPEND_ELT (v2, NULL, build_constructor (ctr_type, ctr));
}
CONSTRUCTOR_APPEND_ELT (v1, fields, CONSTRUCTOR_APPEND_ELT (v1, fields,
build_constructor (TREE_TYPE (fields), v2)); build_constructor (TREE_TYPE (fields), v2));
return build_constructor (type, v1); return build_constructor (type, v1);
} }
/* Creates the gcov_ctr_info RECORD_TYPE. */ /* Creaste gcov_info_struct. N_FUNCS is the number of functions in
the trailing array. */
static tree static void
build_ctr_info_type (void) build_info_type (tree type, unsigned n_funcs, tree fn_info_type)
{ {
tree type = lang_hooks.types.make_type (RECORD_TYPE);
tree field, fields = NULL_TREE; tree field, fields = NULL_TREE;
tree gcov_ptr_type = build_pointer_type (get_gcov_type ()); tree merge_fn_type, fn_info_array;
tree gcov_merge_fn_type;
/* counters */ gcc_assert (n_funcs);
field = build_decl (BUILTINS_LOCATION,
FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ()); /* Version ident */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
get_gcov_unsigned_t ());
DECL_CHAIN (field) = fields; DECL_CHAIN (field) = fields;
fields = field; fields = field;
/* values */ /* next pointer */
field = build_decl (BUILTINS_LOCATION, field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
FIELD_DECL, NULL_TREE, gcov_ptr_type); build_pointer_type (build_qualified_type
(type, TYPE_QUAL_CONST)));
DECL_CHAIN (field) = fields; DECL_CHAIN (field) = fields;
fields = field; fields = field;
/* merge */ /* stamp */
gcov_merge_fn_type = field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
build_function_type_list (void_type_node, get_gcov_unsigned_t ());
gcov_ptr_type, get_gcov_unsigned_t (),
NULL_TREE);
field = build_decl (BUILTINS_LOCATION,
FIELD_DECL, NULL_TREE,
build_pointer_type (gcov_merge_fn_type));
DECL_CHAIN (field) = fields; DECL_CHAIN (field) = fields;
fields = field; fields = field;
finish_builtin_struct (type, "__gcov_ctr_info", fields, NULL_TREE); /* Filename */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
return type; build_pointer_type (build_qualified_type
} (char_type_node, TYPE_QUAL_CONST)));
DECL_CHAIN (field) = fields;
/* Creates a CONSTRUCTOR for a gcov_ctr_info. COUNTER is fields = field;
the counter being processed and TYPE is the gcov_ctr_info
RECORD_TYPE. */
static tree
build_ctr_info_value (unsigned int counter, tree type)
{
tree fields = TYPE_FIELDS (type);
tree fn;
VEC(constructor_elt,gc) *v = NULL;
/* counters */
CONSTRUCTOR_APPEND_ELT (v, fields,
build_int_cstu (get_gcov_unsigned_t (),
prg_n_ctrs[counter]));
fields = DECL_CHAIN (fields);
if (prg_n_ctrs[counter]) /* merge fn array */
{ merge_fn_type
tree array_type; = build_function_type_list (void_type_node,
build_pointer_type (get_gcov_type ()),
array_type = build_int_cstu (get_gcov_unsigned_t (), get_gcov_unsigned_t (), NULL_TREE);
prg_n_ctrs[counter] - 1); merge_fn_type
array_type = build_index_type (array_type); = build_array_type (build_pointer_type (merge_fn_type),
array_type = build_array_type (TREE_TYPE (TREE_TYPE (fields)), build_index_type (size_int (GCOV_COUNTERS - 1)));
array_type); field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
merge_fn_type);
TREE_TYPE (tree_ctr_tables[counter]) = array_type; DECL_CHAIN (field) = fields;
DECL_SIZE (tree_ctr_tables[counter]) = TYPE_SIZE (array_type); fields = field;
DECL_SIZE_UNIT (tree_ctr_tables[counter]) = TYPE_SIZE_UNIT (array_type);
varpool_finalize_decl (tree_ctr_tables[counter]); /* n_functions */
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
CONSTRUCTOR_APPEND_ELT (v, fields, get_gcov_unsigned_t ());
build1 (ADDR_EXPR, TREE_TYPE (fields), DECL_CHAIN (field) = fields;
tree_ctr_tables[counter])); fields = field;
}
else /* function_info pointer array */
CONSTRUCTOR_APPEND_ELT (v, fields, null_pointer_node); fn_info_type = build_pointer_type
fields = DECL_CHAIN (fields); (build_qualified_type (fn_info_type, TYPE_QUAL_CONST));
fn_info_array = build_index_type (size_int (n_funcs));
fn_info_array = build_array_type (fn_info_type, fn_info_array);
field = build_decl (BUILTINS_LOCATION, FIELD_DECL, NULL_TREE,
fn_info_array);
DECL_CHAIN (field) = fields;
fields = field;
fn = build_decl (BUILTINS_LOCATION, finish_builtin_struct (type, "__gcov_info", fields, NULL_TREE);
FUNCTION_DECL,
get_identifier (ctr_merge_functions[counter]),
TREE_TYPE (TREE_TYPE (fields)));
DECL_EXTERNAL (fn) = 1;
TREE_PUBLIC (fn) = 1;
DECL_ARTIFICIAL (fn) = 1;
TREE_NOTHROW (fn) = 1;
DECL_ASSEMBLER_NAME (fn); /* Initialize assembler name so we can stream out. */
CONSTRUCTOR_APPEND_ELT (v, fields, build1 (ADDR_EXPR, TREE_TYPE (fields), fn));
return build_constructor (type, v);
} }
/* Creates the gcov_info RECORD_TYPE and initializer for it. Returns a /* Creates the gcov_info initializer. Returns a CONSTRUCTOR. */
CONSTRUCTOR. */
static tree static tree
build_gcov_info (void) build_info (tree info_type, tree fn_type, tree key_var, unsigned n_funcs)
{ {
unsigned n_ctr_types, ix; tree info_fields = TYPE_FIELDS (info_type);
tree type, const_type; tree merge_fn_type, fn_info_ptr_type;
tree fn_info_type, fn_info_value = NULL_TREE; unsigned ix;
tree fn_info_ptr_type;
tree ctr_info_type, ctr_info_ary_type, ctr_info_value = NULL_TREE;
tree field, fields = NULL_TREE;
tree filename_string; tree filename_string;
int da_file_name_len; int da_file_name_len;
unsigned n_fns;
const struct function_list *fn; const struct function_list *fn;
tree string_type;
VEC(constructor_elt,gc) *v1 = NULL; VEC(constructor_elt,gc) *v1 = NULL;
VEC(constructor_elt,gc) *v2 = NULL; VEC(constructor_elt,gc) *v2 = NULL;
VEC(constructor_elt,gc) *v3 = NULL;
/* Count the number of active counters. */
for (n_ctr_types = 0, ix = 0; ix != GCOV_COUNTERS; ix++)
if (prg_ctr_mask & (1 << ix))
n_ctr_types++;
type = lang_hooks.types.make_type (RECORD_TYPE);
const_type = build_qualified_type (type, TYPE_QUAL_CONST);
/* Version ident */ /* Version ident */
field = build_decl (BUILTINS_LOCATION, CONSTRUCTOR_APPEND_ELT (v1, info_fields,
FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ()); build_int_cstu (TREE_TYPE (info_fields),
DECL_CHAIN (field) = fields; GCOV_VERSION));
fields = field; info_fields = DECL_CHAIN (info_fields);
CONSTRUCTOR_APPEND_ELT (v1, field,
build_int_cstu (TREE_TYPE (field), GCOV_VERSION));
/* next -- NULL */ /* next -- NULL */
field = build_decl (BUILTINS_LOCATION, CONSTRUCTOR_APPEND_ELT (v1, info_fields, null_pointer_node);
FIELD_DECL, NULL_TREE, build_pointer_type (const_type)); info_fields = DECL_CHAIN (info_fields);
DECL_CHAIN (field) = fields;
fields = field;
CONSTRUCTOR_APPEND_ELT (v1, field, null_pointer_node);
/* stamp */ /* stamp */
field = build_decl (BUILTINS_LOCATION, CONSTRUCTOR_APPEND_ELT (v1, info_fields,
FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ()); build_int_cstu (TREE_TYPE (info_fields),
DECL_CHAIN (field) = fields; local_tick));
fields = field; info_fields = DECL_CHAIN (info_fields);
CONSTRUCTOR_APPEND_ELT (v1, field,
build_int_cstu (TREE_TYPE (field), local_tick));
/* Filename */ /* Filename */
string_type = build_pointer_type (build_qualified_type (char_type_node,
TYPE_QUAL_CONST));
field = build_decl (BUILTINS_LOCATION,
FIELD_DECL, NULL_TREE, string_type);
DECL_CHAIN (field) = fields;
fields = field;
da_file_name_len = strlen (da_file_name); da_file_name_len = strlen (da_file_name);
filename_string = build_string (da_file_name_len + 1, da_file_name); filename_string = build_string (da_file_name_len + 1, da_file_name);
TREE_TYPE (filename_string) = build_array_type TREE_TYPE (filename_string) = build_array_type
(char_type_node, build_index_type (size_int (da_file_name_len))); (char_type_node, build_index_type (size_int (da_file_name_len)));
CONSTRUCTOR_APPEND_ELT (v1, field, CONSTRUCTOR_APPEND_ELT (v1, info_fields,
build1 (ADDR_EXPR, string_type, filename_string)); build1 (ADDR_EXPR, TREE_TYPE (info_fields),
filename_string));
info_fields = DECL_CHAIN (info_fields);
/* Build the fn_info type and initializer. */ /* merge fn array -- NULL slots indicate unmeasured counters */
fn_info_type = build_fn_info_type (n_ctr_types); merge_fn_type = TREE_TYPE (TREE_TYPE (info_fields));
fn_info_ptr_type = build_pointer_type (build_qualified_type for (ix = 0; ix != GCOV_COUNTERS; ix++)
(fn_info_type, TYPE_QUAL_CONST));
for (fn = functions_head, n_fns = 0; fn; fn = fn->next, n_fns++)
CONSTRUCTOR_APPEND_ELT (v2, NULL_TREE,
build_fn_info_value (fn, fn_info_type));
if (n_fns)
{ {
tree array_type; tree ptr = null_pointer_node;
array_type = build_index_type (size_int (n_fns - 1));
array_type = build_array_type (fn_info_type, array_type);
fn_info_value = build_constructor (array_type, v2); if ((1u << ix) & prg_ctr_mask)
fn_info_value = build1 (ADDR_EXPR, fn_info_ptr_type, fn_info_value); {
tree merge_fn = build_decl (BUILTINS_LOCATION,
FUNCTION_DECL,
get_identifier (ctr_merge_functions[ix]),
TREE_TYPE (merge_fn_type));
DECL_EXTERNAL (merge_fn) = 1;
TREE_PUBLIC (merge_fn) = 1;
DECL_ARTIFICIAL (merge_fn) = 1;
TREE_NOTHROW (merge_fn) = 1;
/* Initialize assembler name so we can stream out. */
DECL_ASSEMBLER_NAME (merge_fn);
ptr = build1 (ADDR_EXPR, merge_fn_type, merge_fn);
}
CONSTRUCTOR_APPEND_ELT (v2, NULL, ptr);
} }
else CONSTRUCTOR_APPEND_ELT (v1, info_fields,
fn_info_value = null_pointer_node; build_constructor (TREE_TYPE (info_fields), v2));
info_fields = DECL_CHAIN (info_fields);
/* number of functions */
field = build_decl (BUILTINS_LOCATION, /* n_functions */
FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ()); CONSTRUCTOR_APPEND_ELT (v1, info_fields,
DECL_CHAIN (field) = fields; build_int_cstu (TREE_TYPE (info_fields), n_funcs));
fields = field; info_fields = DECL_CHAIN (info_fields);
CONSTRUCTOR_APPEND_ELT (v1, field,
build_int_cstu (get_gcov_unsigned_t (), n_fns)); /* Build the fn_info type and initializer. */
fn_info_ptr_type = TREE_TYPE (TREE_TYPE (info_fields));
/* fn_info table */
field = build_decl (BUILTINS_LOCATION, for (fn = functions_head; fn; fn = fn->next)
FIELD_DECL, NULL_TREE, fn_info_ptr_type); {
DECL_CHAIN (field) = fields; tree init = build_fn_info (fn, fn_type, key_var);
fields = field; tree var = build_var (fn->fn_decl, fn_type, -1);
CONSTRUCTOR_APPEND_ELT (v1, field, fn_info_value);
DECL_INITIAL (var) = init;
/* counter_mask */ varpool_finalize_decl (var);
field = build_decl (BUILTINS_LOCATION,
FIELD_DECL, NULL_TREE, get_gcov_unsigned_t ()); CONSTRUCTOR_APPEND_ELT (v3, NULL,
DECL_CHAIN (field) = fields; build1 (ADDR_EXPR, fn_info_ptr_type, var));
fields = field; }
CONSTRUCTOR_APPEND_ELT (v1, field, CONSTRUCTOR_APPEND_ELT (v1, info_fields,
build_int_cstu (get_gcov_unsigned_t (), build_constructor (TREE_TYPE (info_fields), v3));
prg_ctr_mask)); return build_constructor (info_type, v1);
/* counters */
ctr_info_type = build_ctr_info_type ();
ctr_info_ary_type = build_index_type (size_int (n_ctr_types));
ctr_info_ary_type = build_array_type (ctr_info_type, ctr_info_ary_type);
v2 = NULL;
for (ix = 0; ix != GCOV_COUNTERS; ix++)
if (prg_ctr_mask & (1 << ix))
CONSTRUCTOR_APPEND_ELT (v2, NULL_TREE,
build_ctr_info_value (ix, ctr_info_type));
ctr_info_value = build_constructor (ctr_info_ary_type, v2);
field = build_decl (BUILTINS_LOCATION,
FIELD_DECL, NULL_TREE, ctr_info_ary_type);
DECL_CHAIN (field) = fields;
fields = field;
CONSTRUCTOR_APPEND_ELT (v1, field, ctr_info_value);
finish_builtin_struct (type, "__gcov_info", fields, NULL_TREE);
return build_constructor (type, v1);
} }
/* Write out the structure which libgcov uses to locate all the /* Write out the structure which libgcov uses to locate all the
...@@ -982,6 +989,11 @@ static void ...@@ -982,6 +989,11 @@ static void
create_coverage (void) create_coverage (void)
{ {
tree gcov_info, gcov_init, body, t; tree gcov_info, gcov_init, body, t;
tree gcov_info_type, gcov_fn_type;
unsigned n_counters = 0, n_functions = 0;
struct function_list *fn;
struct function_list **fn_prev;
unsigned ix;
char name_buf[32]; char name_buf[32];
no_coverage = 1; /* Disable any further coverage. */ no_coverage = 1; /* Disable any further coverage. */
...@@ -989,14 +1001,37 @@ create_coverage (void) ...@@ -989,14 +1001,37 @@ create_coverage (void)
if (!prg_ctr_mask) if (!prg_ctr_mask)
return; return;
t = build_gcov_info (); if (cgraph_dump_file)
fprintf (cgraph_dump_file, "Using data file %s\n", da_file_name);
for (ix = 0; ix != GCOV_COUNTERS; ix++)
if ((1u << ix) & prg_ctr_mask)
n_counters++;
for (fn_prev = &functions_head; (fn = *fn_prev);)
if (DECL_STRUCT_FUNCTION (fn->fn_decl))
{
n_functions++;
fn_prev = &fn->next;
}
else
/* The function is not being emitted, remove from list. */
*fn_prev = fn->next;
/* Build the info and fn_info types. These are mutually recursive. */
gcov_info_type = lang_hooks.types.make_type (RECORD_TYPE);
gcov_fn_type = lang_hooks.types.make_type (RECORD_TYPE);
build_fn_info_type (gcov_fn_type, n_counters, gcov_info_type);
build_info_type (gcov_info_type, n_functions, gcov_fn_type);
/* Build the gcov info var, this is referred to in its own
initializer. */
gcov_info = build_decl (BUILTINS_LOCATION, gcov_info = build_decl (BUILTINS_LOCATION,
VAR_DECL, NULL_TREE, TREE_TYPE (t)); VAR_DECL, NULL_TREE, gcov_info_type);
TREE_STATIC (gcov_info) = 1; TREE_STATIC (gcov_info) = 1;
ASM_GENERATE_INTERNAL_LABEL (name_buf, "LPBX", 0); ASM_GENERATE_INTERNAL_LABEL (name_buf, "LPBX", 0);
DECL_NAME (gcov_info) = get_identifier (name_buf); DECL_NAME (gcov_info) = get_identifier (name_buf);
DECL_INITIAL (gcov_info) = t; DECL_INITIAL (gcov_info) = build_info (gcov_info_type, gcov_fn_type,
gcov_info, n_functions);
/* Build structure. */ /* Build structure. */
varpool_finalize_decl (gcov_info); varpool_finalize_decl (gcov_info);
......
...@@ -276,23 +276,28 @@ dump_file (const char *filename) ...@@ -276,23 +276,28 @@ dump_file (const char *filename)
static void static void
tag_function (const char *filename ATTRIBUTE_UNUSED, tag_function (const char *filename ATTRIBUTE_UNUSED,
unsigned tag ATTRIBUTE_UNUSED, unsigned length ATTRIBUTE_UNUSED) unsigned tag ATTRIBUTE_UNUSED, unsigned length)
{ {
unsigned long pos = gcov_position (); unsigned long pos = gcov_position ();
printf (" ident=%u", gcov_read_unsigned ()); if (!length)
printf (", lineno_checksum=0x%08x", gcov_read_unsigned ()); printf (" placeholder");
printf (", cfg_checksum_checksum=0x%08x", gcov_read_unsigned ()); else
if (gcov_position () - pos < length)
{ {
const char *name; printf (" ident=%u", gcov_read_unsigned ());
printf (", lineno_checksum=0x%08x", gcov_read_unsigned ());
printf (", cfg_checksum_checksum=0x%08x", gcov_read_unsigned ());
name = gcov_read_string (); if (gcov_position () - pos < length)
printf (", `%s'", name ? name : "NULL"); {
name = gcov_read_string (); const char *name;
printf (" %s", name ? name : "NULL");
printf (":%u", gcov_read_unsigned ()); name = gcov_read_string ();
printf (", `%s'", name ? name : "NULL");
name = gcov_read_string ();
printf (" %s", name ? name : "NULL");
printf (":%u", gcov_read_unsigned ());
}
} }
} }
......
...@@ -130,26 +130,26 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see ...@@ -130,26 +130,26 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
blocks they are for. blocks they are for.
The data file contains the following records. The data file contains the following records.
data: {unit function-data* summary:object summary:program*}* data: {unit summary:object summary:program* function-data*}*
unit: header int32:checksum unit: header int32:checksum
function-data: announce_function arc_counts function-data: announce_function present counts
announce_function: header int32:ident announce_function: header int32:ident
int32:lineno_checksum int32:cfg_checksum int32:lineno_checksum int32:cfg_checksum
arc_counts: header int64:count* present: header int32:present
summary: int32:checksum {count-summary}GCOV_COUNTERS counts: header int64:count*
summary: int32:checksum {count-summary}GCOV_COUNTERS_SUMMABLE
count-summary: int32:num int32:runs int64:sum count-summary: int32:num int32:runs int64:sum
int64:max int64:sum_max int64:max int64:sum_max
The ANNOUNCE_FUNCTION record is the same as that in the note file, The ANNOUNCE_FUNCTION record is the same as that in the note file,
but without the source location. The ARC_COUNTS gives the counter but without the source location. The COUNTS gives the
values for those arcs that are instrumented. The SUMMARY records counter values for instrumented features. The about the whole
give information about the whole object file and about the whole
program. The checksum is used for whole program summaries, and program. The checksum is used for whole program summaries, and
disambiguates different programs which include the same disambiguates different programs which include the same
instrumented object file. There may be several program summaries, instrumented object file. There may be several program summaries,
each with a unique checksum. The object summary's checksum is zero. each with a unique checksum. The object summary's checksum is
Note that the data file might contain information from several runs zero. Note that the data file might contain information from
concatenated, or the data might be merged. several runs concatenated, or the data might be merged.
This file is included by both the compiler, gcov tools and the This file is included by both the compiler, gcov tools and the
runtime support library libgcov. IN_LIBGCOV and IN_GCOV are used to runtime support library libgcov. IN_LIBGCOV and IN_GCOV are used to
...@@ -307,7 +307,7 @@ typedef HOST_WIDEST_INT gcov_type; ...@@ -307,7 +307,7 @@ typedef HOST_WIDEST_INT gcov_type;
#define GCOV_TAG_COUNTER_BASE ((gcov_unsigned_t)0x01a10000) #define GCOV_TAG_COUNTER_BASE ((gcov_unsigned_t)0x01a10000)
#define GCOV_TAG_COUNTER_LENGTH(NUM) ((NUM) * 2) #define GCOV_TAG_COUNTER_LENGTH(NUM) ((NUM) * 2)
#define GCOV_TAG_COUNTER_NUM(LENGTH) ((LENGTH) / 2) #define GCOV_TAG_COUNTER_NUM(LENGTH) ((LENGTH) / 2)
#define GCOV_TAG_OBJECT_SUMMARY ((gcov_unsigned_t)0xa1000000) #define GCOV_TAG_OBJECT_SUMMARY ((gcov_unsigned_t)0xa1000000) /* Obsolete */
#define GCOV_TAG_PROGRAM_SUMMARY ((gcov_unsigned_t)0xa3000000) #define GCOV_TAG_PROGRAM_SUMMARY ((gcov_unsigned_t)0xa3000000)
#define GCOV_TAG_SUMMARY_LENGTH \ #define GCOV_TAG_SUMMARY_LENGTH \
(1 + GCOV_COUNTERS_SUMMABLE * (2 + 3 * 2)) (1 + GCOV_COUNTERS_SUMMABLE * (2 + 3 * 2))
...@@ -343,7 +343,7 @@ typedef HOST_WIDEST_INT gcov_type; ...@@ -343,7 +343,7 @@ typedef HOST_WIDEST_INT gcov_type;
/* A list of human readable names of the counters */ /* A list of human readable names of the counters */
#define GCOV_COUNTER_NAMES {"arcs", "interval", "pow2", "single", \ #define GCOV_COUNTER_NAMES {"arcs", "interval", "pow2", "single", \
"delta","indirect_call", "average", "ior"} "delta", "indirect_call", "average", "ior"}
/* Names of merge functions for counters. */ /* Names of merge functions for counters. */
#define GCOV_MERGE_FUNCTIONS {"__gcov_merge_add", \ #define GCOV_MERGE_FUNCTIONS {"__gcov_merge_add", \
...@@ -410,30 +410,31 @@ struct gcov_summary ...@@ -410,30 +410,31 @@ struct gcov_summary
by write_profile must match these. */ by write_profile must match these. */
#if IN_LIBGCOV #if IN_LIBGCOV
/* Information about counters for a single function. */
struct gcov_ctr_info
{
gcov_unsigned_t num; /* number of counters. */
gcov_type *values; /* their values. */
};
/* Information about a single function. This uses the trailing array /* Information about a single function. This uses the trailing array
idiom. The number of counters is determined from the counter_mask idiom. The number of counters is determined from the merge pointer
in gcov_info. We hold an array of function info, so have to array in gcov_info. The key is used to detect which of a set of
explicitly calculate the correct array stride. */ comdat functions was selected -- it points to the gcov_info object
of the object file containing the selected comdat function. */
struct gcov_fn_info struct gcov_fn_info
{ {
gcov_unsigned_t ident; /* unique ident of function */ const struct gcov_info *key; /* comdat key */
gcov_unsigned_t ident; /* unique ident of function */
gcov_unsigned_t lineno_checksum; /* function lineo_checksum */ gcov_unsigned_t lineno_checksum; /* function lineo_checksum */
gcov_unsigned_t cfg_checksum; /* function cfg checksum */ gcov_unsigned_t cfg_checksum; /* function cfg checksum */
unsigned n_ctrs[0]; /* instrumented counters */ struct gcov_ctr_info ctrs[0]; /* instrumented counters */
}; };
/* Type of function used to merge counters. */ /* Type of function used to merge counters. */
typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t); typedef void (*gcov_merge_fn) (gcov_type *, gcov_unsigned_t);
/* Information about counters. */
struct gcov_ctr_info
{
gcov_unsigned_t num; /* number of counters. */
gcov_type *values; /* their values. */
gcov_merge_fn merge; /* The function used to merge them. */
};
/* Information about a single object file. */ /* Information about a single object file. */
struct gcov_info struct gcov_info
{ {
...@@ -443,14 +444,12 @@ struct gcov_info ...@@ -443,14 +444,12 @@ struct gcov_info
gcov_unsigned_t stamp; /* uniquifying time stamp */ gcov_unsigned_t stamp; /* uniquifying time stamp */
const char *filename; /* output file name */ const char *filename; /* output file name */
gcov_merge_fn merge[GCOV_COUNTERS]; /* merge functions (null for
unused) */
unsigned n_functions; /* number of functions */ unsigned n_functions; /* number of functions */
const struct gcov_fn_info *functions; /* table of functions */ const struct gcov_fn_info *functions[0]; /* pointers to function
information */
unsigned ctr_mask; /* mask of counters instrumented. */
struct gcov_ctr_info counts[0]; /* count data. The number of bits
set in the ctr_mask field
determines how big this array
is. */
}; };
/* Register a new object file module. */ /* Register a new object file module. */
......
...@@ -265,7 +265,7 @@ static unsigned source_index; ...@@ -265,7 +265,7 @@ static unsigned source_index;
/* This holds data summary information. */ /* This holds data summary information. */
static struct gcov_summary object_summary; static unsigned object_runs;
static unsigned program_count; static unsigned program_count;
/* Modification time of graph file. */ /* Modification time of graph file. */
...@@ -362,6 +362,7 @@ static int output_branch_count (FILE *, int, const arc_t *); ...@@ -362,6 +362,7 @@ static int output_branch_count (FILE *, int, const arc_t *);
static void output_lines (FILE *, const source_t *); static void output_lines (FILE *, const source_t *);
static char *make_gcov_file_name (const char *, const char *); static char *make_gcov_file_name (const char *, const char *);
static void release_structures (void); static void release_structures (void);
static void release_function (function_t *);
extern int main (int, char **); extern int main (int, char **);
int int
...@@ -537,7 +538,7 @@ static void ...@@ -537,7 +538,7 @@ static void
process_file (const char *file_name) process_file (const char *file_name)
{ {
function_t *fn; function_t *fn;
function_t *fn_p; function_t **fn_p;
function_t *old_functions; function_t *old_functions;
/* Save and clear the list of current functions. They will be appended /* Save and clear the list of current functions. They will be appended
...@@ -558,11 +559,25 @@ process_file (const char *file_name) ...@@ -558,11 +559,25 @@ process_file (const char *file_name)
if (read_count_file ()) if (read_count_file ())
return; return;
for (fn_p = NULL, fn = functions; fn; fn_p = fn, fn = fn->next) fn_p = &functions;
solve_flow_graph (fn); while ((fn = *fn_p) != NULL)
{
if (fn->counts)
{
solve_flow_graph (fn);
fn_p = &fn->next;
}
else
{
/* The function was not in the executable -- some other
instance must have been selected. */
function_t *next = fn->next;
release_function (fn);
*fn_p = next;
}
}
if (fn_p) *fn_p = old_functions;
fn_p->next = old_functions;
} }
static void static void
...@@ -591,7 +606,7 @@ generate_results (const char *file_name) ...@@ -591,7 +606,7 @@ generate_results (const char *file_name)
{ {
accumulate_line_counts (src); accumulate_line_counts (src);
function_summary (&src->coverage, "File"); function_summary (&src->coverage, "File");
if (flag_gcov_file) if (flag_gcov_file && src->coverage.lines)
{ {
char *gcov_file_name = make_gcov_file_name (file_name, src->name); char *gcov_file_name = make_gcov_file_name (file_name, src->name);
FILE *gcov_file = fopen (gcov_file_name, "w"); FILE *gcov_file = fopen (gcov_file_name, "w");
...@@ -615,6 +630,28 @@ generate_results (const char *file_name) ...@@ -615,6 +630,28 @@ generate_results (const char *file_name)
} }
} }
/* Release a function structure */
static void
release_function (function_t *fn)
{
unsigned ix;
block_t *block;
for (ix = fn->num_blocks, block = fn->blocks; ix--; block++)
{
arc_t *arc, *arc_n;
for (arc = block->succ; arc; arc = arc_n)
{
arc_n = arc->succ_next;
free (arc);
}
}
free (fn->blocks);
free (fn->counts);
}
/* Release all memory used. */ /* Release all memory used. */
static void static void
...@@ -633,22 +670,8 @@ release_structures (void) ...@@ -633,22 +670,8 @@ release_structures (void)
while ((fn = functions)) while ((fn = functions))
{ {
unsigned ix;
block_t *block;
functions = fn->next; functions = fn->next;
for (ix = fn->num_blocks, block = fn->blocks; ix--; block++) release_function (fn);
{
arc_t *arc, *arc_n;
for (arc = block->succ; arc; arc = arc_n)
{
arc_n = arc->succ_next;
free (arc);
}
}
free (fn->blocks);
free (fn->counts);
} }
} }
...@@ -1085,35 +1108,39 @@ read_count_file (void) ...@@ -1085,35 +1108,39 @@ read_count_file (void)
unsigned length = gcov_read_unsigned (); unsigned length = gcov_read_unsigned ();
unsigned long base = gcov_position (); unsigned long base = gcov_position ();
if (tag == GCOV_TAG_OBJECT_SUMMARY) if (tag == GCOV_TAG_PROGRAM_SUMMARY)
gcov_read_summary (&object_summary);
else if (tag == GCOV_TAG_PROGRAM_SUMMARY)
program_count++;
else if (tag == GCOV_TAG_FUNCTION)
{ {
{ struct gcov_summary summary;
unsigned ident = gcov_read_unsigned (); gcov_read_summary (&summary);
struct function_info *fn_n = functions; object_runs += summary.ctrs[GCOV_COUNTER_ARCS].runs;
program_count++;
}
else if (tag == GCOV_TAG_FUNCTION && !length)
; /* placeholder */
else if (tag == GCOV_TAG_FUNCTION && length == GCOV_TAG_FUNCTION_LENGTH)
{
unsigned ident;
struct function_info *fn_n;
/* Try to find the function in the list. /* Try to find the function in the list. To speed up the
To speed up the search, first start from the last function search, first start from the last function found. */
found. */ ident = gcov_read_unsigned ();
for (fn = fn ? fn->next : NULL; ; fn = fn->next) fn_n = functions;
{ for (fn = fn ? fn->next : NULL; ; fn = fn->next)
if (fn) {
; if (fn)
else if ((fn = fn_n)) ;
fn_n = NULL; else if ((fn = fn_n))
else fn_n = NULL;
{ else
fnotice (stderr, "%s:unknown function '%u'\n", {
da_file_name, ident); fnotice (stderr, "%s:unknown function '%u'\n",
break; da_file_name, ident);
}
if (fn->ident == ident)
break; break;
} }
} if (fn->ident == ident)
break;
}
if (!fn) if (!fn)
; ;
...@@ -1908,8 +1935,7 @@ output_lines (FILE *gcov_file, const source_t *src) ...@@ -1908,8 +1935,7 @@ output_lines (FILE *gcov_file, const source_t *src)
fprintf (gcov_file, "%9s:%5d:Graph:%s\n", "-", 0, bbg_file_name); fprintf (gcov_file, "%9s:%5d:Graph:%s\n", "-", 0, bbg_file_name);
fprintf (gcov_file, "%9s:%5d:Data:%s\n", "-", 0, fprintf (gcov_file, "%9s:%5d:Data:%s\n", "-", 0,
no_data_file ? "-" : da_file_name); no_data_file ? "-" : da_file_name);
fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0, fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0, object_runs);
object_summary.ctrs[GCOV_COUNTER_ARCS].runs);
} }
fprintf (gcov_file, "%9s:%5d:Programs:%u\n", "-", 0, program_count); fprintf (gcov_file, "%9s:%5d:Programs:%u\n", "-", 0, program_count);
......
2011-11-07 Nathan Sidwell <nathan@acm.org>
* gcc.dg/profile-dir-1.c: Adjust final scan.
* gcc.dg/profile-dir-2.c: Adjust final scan.
* gcc.dg/profile-dir-3.c: Adjust final scan.
* gcc.misc-tests/gcov.exp: Adjust regexp.
* gcc.misc-tests/gcov-12.c: New.
* gcc.misc-tests/gcov-13.c: New.
* gcc.misc-tests/gcovpart-13b.c: New.
* gcc.misc-tests/gcov-14.c: New.
2011-11-07 Jason Merrill <jason@redhat.com> 2011-11-07 Jason Merrill <jason@redhat.com>
PR c++/35688 PR c++/35688
......
/* { dg-do compile } */ /* { dg-do compile } */
/* { dg-options "-O -fprofile-generate=. -fdump-ipa-profile" } */ /* { dg-options "-O -fprofile-generate=. -fdump-ipa-cgraph" } */
/* { dg-final { scan-ipa-dump " ./profile-dir-1.gcda" "profile" } } */ /* { dg-final { scan-ipa-dump " ./profile-dir-1.gcda" "cgraph" } } */
int int
main(void) main(void)
...@@ -8,4 +8,4 @@ main(void) ...@@ -8,4 +8,4 @@ main(void)
return 0; return 0;
} }
/* { dg-final { cleanup-ipa-dump "profile" } } */ /* { dg-final { cleanup-ipa-dump "cgraph" } } */
/* { dg-do compile } */ /* { dg-do compile } */
/* { dg-options "-O -fprofile-generate -fdump-ipa-profile" } */ /* { dg-options "-O -fprofile-generate -fdump-ipa-cgraph" } */
/* { dg-final { scan-ipa-dump "/profile-dir-2.gcda" "profile" } } */ /* { dg-final { scan-ipa-dump "/profile-dir-2.gcda" "cgraph" } } */
int int
main(void) main(void)
...@@ -8,4 +8,4 @@ main(void) ...@@ -8,4 +8,4 @@ main(void)
return 0; return 0;
} }
/* { dg-final { cleanup-ipa-dump "profile" } } */ /* { dg-final { cleanup-ipa-dump "cgraph" } } */
/* { dg-do compile } */ /* { dg-do compile } */
/* { dg-options "-O -fprofile-generate -fprofile-dir=. -fdump-ipa-profile" } */ /* { dg-options "-O -fprofile-generate -fprofile-dir=. -fdump-ipa-cgraph" } */
/* { dg-final { scan-ipa-dump " ./profile-dir-3.gcda" "profile" } } */ /* { dg-final { scan-ipa-dump " ./profile-dir-3.gcda" "cgraph" } } */
int int
main(void) main(void)
...@@ -8,4 +8,4 @@ main(void) ...@@ -8,4 +8,4 @@ main(void)
return 0; return 0;
} }
/* { dg-final { cleanup-ipa-dump "profile" } } */ /* { dg-final { cleanup-ipa-dump "cgraph" } } */
/* Test gcov weak ellision. */
/* { dg-options "-fprofile-arcs -ftest-coverage" } */
/* { dg-require-weak "" } */
/* { dg-do run { target native } } */
int __attribute__ ((weak)) weak ()
{
return 0; /* count(1) */
}
int main ()
{
return weak (); /* count(1) */
}
/* { dg-final { run-gcov { -a gcov-12.c } } } */
/* Test gcov weak ellision. */
/* { dg-options "-fprofile-arcs -ftest-coverage" } */
/* { dg-require-weak "" } */
/* { dg-do run { target native } } */
/* { dg-additional-sources "gcovpart-13b.c" } */
int __attribute__ ((weak)) weak ()
{
return 1; /* count(-) */
}
int main ()
{
return weak (); /* count(1) */
}
/* { dg-final { run-gcov { -a gcov-13.c } } } */
/* Test gcov extern inline. */
/* { dg-options "-O2 -fprofile-arcs -ftest-coverage" } */
/* { dg-require-weak "" } */
/* { dg-do run { target native } } */
extern int __attribute__ ((weak)) Foo ();
extern __inline int Foo ()
{
return 0; /* count(-) */
}
int (* __attribute__ ((noinline)) Bar ()) ()
{
return Foo; /* count(1) */
}
int main ()
{
return Bar () != 0; /* count(1) */
}
/* { dg-final { run-gcov { -a gcov-14.c } } } */
...@@ -33,7 +33,7 @@ if { ![is_remote host] && [string match "*/*" [lindex $GCC_UNDER_TEST 0]] } { ...@@ -33,7 +33,7 @@ if { ![is_remote host] && [string match "*/*" [lindex $GCC_UNDER_TEST 0]] } {
dg-init dg-init
# Delete old .gcda files. # Delete old .gcda files.
set files [glob -nocomplain gcov-*.gcda] set files [glob -nocomplain gcov*.gcda]
if { $files != "" } { if { $files != "" } {
eval "remote_file build delete $files" eval "remote_file build delete $files"
} }
......
int weak ()
{
return 0; /* count(1) */
}
2011-11-07 Nathan Sidwell <nathan@acm.org>
* libgcov.c (struct gcov_fn_buffer): New struct.
(buffer_fn_data): New helper.
(gcov_exit): Rework for new gcov data structures.
2011-11-07 Georg-Johann Lay <avr@gjlay.de> 2011-11-07 Georg-Johann Lay <avr@gjlay.de>
PR target/49313 PR target/49313
......
...@@ -78,6 +78,14 @@ void __gcov_merge_delta (gcov_type *counters __attribute__ ((unused)), ...@@ -78,6 +78,14 @@ void __gcov_merge_delta (gcov_type *counters __attribute__ ((unused)),
#ifdef L_gcov #ifdef L_gcov
#include "gcov-io.c" #include "gcov-io.c"
struct gcov_fn_buffer
{
struct gcov_fn_buffer *next;
unsigned fn_ix;
struct gcov_fn_info info;
/* note gcov_fn_info ends in a trailing array. */
};
/* Chain of per-object gcov structures. */ /* Chain of per-object gcov structures. */
static struct gcov_info *gcov_list; static struct gcov_info *gcov_list;
...@@ -135,6 +143,64 @@ create_file_directory (char *filename) ...@@ -135,6 +143,64 @@ create_file_directory (char *filename)
#endif #endif
} }
static struct gcov_fn_buffer **
buffer_fn_data (struct gcov_info *gi_ptr, struct gcov_fn_buffer **end_ptr,
unsigned fn_ix)
{
unsigned n_ctrs = 0, ix;
struct gcov_fn_buffer *fn_buffer;
for (ix = GCOV_COUNTERS; ix--;)
if (gi_ptr->merge[ix])
n_ctrs++;
fn_buffer = (struct gcov_fn_buffer *)malloc
(sizeof (*fn_buffer) + sizeof (fn_buffer->info.ctrs[0]) * n_ctrs);
if (!fn_buffer)
return 0; /* We'll horribly fail. */
fn_buffer->next = 0;
fn_buffer->fn_ix = fn_ix;
fn_buffer->info.ident = gcov_read_unsigned ();
fn_buffer->info.lineno_checksum = gcov_read_unsigned ();
fn_buffer->info.cfg_checksum = gcov_read_unsigned ();
for (n_ctrs = ix = 0; ix != GCOV_COUNTERS; ix++)
{
gcov_unsigned_t length;
gcov_type *values;
if (!gi_ptr->merge[ix])
continue;
if (gcov_read_unsigned () != GCOV_TAG_FOR_COUNTER (ix))
goto fail;
length = GCOV_TAG_COUNTER_NUM (gcov_read_unsigned ());
values = (gcov_type *)malloc (length * sizeof (gcov_type));
if (!values)
{
while (n_ctrs--)
free (fn_buffer->info.ctrs[n_ctrs].values);
goto fail;
}
fn_buffer->info.ctrs[n_ctrs].num = length;
fn_buffer->info.ctrs[n_ctrs].values = values;
while (length--)
*values++ = gcov_read_counter ();
n_ctrs++;
}
*end_ptr = fn_buffer;
return &fn_buffer->next;
fail:
free (fn_buffer);
return 0;
}
/* Check if VERSION of the info block PTR matches libgcov one. /* Check if VERSION of the info block PTR matches libgcov one.
Return 1 on success, or zero in case of versions mismatch. Return 1 on success, or zero in case of versions mismatch.
If FILENAME is not NULL, its value used for reporting purposes If FILENAME is not NULL, its value used for reporting purposes
...@@ -170,39 +236,46 @@ static void ...@@ -170,39 +236,46 @@ static void
gcov_exit (void) gcov_exit (void)
{ {
struct gcov_info *gi_ptr; struct gcov_info *gi_ptr;
struct gcov_summary this_program; const struct gcov_fn_info *gfi_ptr;
struct gcov_summary all; struct gcov_summary this_prg; /* summary for program. */
struct gcov_summary all_prg; /* summary for all instances of program. */
struct gcov_ctr_summary *cs_ptr; struct gcov_ctr_summary *cs_ptr;
const struct gcov_ctr_info *ci_ptr; const struct gcov_ctr_info *ci_ptr;
unsigned t_ix; unsigned t_ix, f_ix;
gcov_unsigned_t c_num; gcov_unsigned_t c_num;
const char *gcov_prefix; const char *gcov_prefix;
int gcov_prefix_strip = 0; int gcov_prefix_strip = 0;
size_t prefix_length; size_t prefix_length;
char *gi_filename, *gi_filename_up; char *gi_filename, *gi_filename_up;
memset (&all, 0, sizeof (all)); memset (&all_prg, 0, sizeof (all_prg));
/* Find the totals for this execution. */ /* Find the totals for this execution. */
memset (&this_program, 0, sizeof (this_program)); memset (&this_prg, 0, sizeof (this_prg));
for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next) for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
{ for (f_ix = 0; f_ix != gi_ptr->n_functions; f_ix++)
ci_ptr = gi_ptr->counts; {
for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++) gfi_ptr = gi_ptr->functions[f_ix];
{
if (!((1 << t_ix) & gi_ptr->ctr_mask)) if (!gfi_ptr || gfi_ptr->key != gi_ptr)
continue; continue;
ci_ptr = gfi_ptr->ctrs;
for (t_ix = 0; t_ix != GCOV_COUNTERS_SUMMABLE; t_ix++)
{
if (!gi_ptr->merge[t_ix])
continue;
cs_ptr = &this_program.ctrs[t_ix]; cs_ptr = &this_prg.ctrs[t_ix];
cs_ptr->num += ci_ptr->num; cs_ptr->num += ci_ptr->num;
for (c_num = 0; c_num < ci_ptr->num; c_num++) for (c_num = 0; c_num < ci_ptr->num; c_num++)
{ {
cs_ptr->sum_all += ci_ptr->values[c_num]; cs_ptr->sum_all += ci_ptr->values[c_num];
if (cs_ptr->run_max < ci_ptr->values[c_num]) if (cs_ptr->run_max < ci_ptr->values[c_num])
cs_ptr->run_max = ci_ptr->values[c_num]; cs_ptr->run_max = ci_ptr->values[c_num];
} }
ci_ptr++; ci_ptr++;
} }
} }
{ {
/* Check if the level of dirs to strip off specified. */ /* Check if the level of dirs to strip off specified. */
...@@ -215,6 +288,7 @@ gcov_exit (void) ...@@ -215,6 +288,7 @@ gcov_exit (void)
gcov_prefix_strip = 0; gcov_prefix_strip = 0;
} }
} }
/* Get file name relocation prefix. Non-absolute values are ignored. */ /* Get file name relocation prefix. Non-absolute values are ignored. */
gcov_prefix = getenv("GCOV_PREFIX"); gcov_prefix = getenv("GCOV_PREFIX");
if (gcov_prefix) if (gcov_prefix)
...@@ -244,24 +318,20 @@ gcov_exit (void) ...@@ -244,24 +318,20 @@ gcov_exit (void)
/* Now merge each file. */ /* Now merge each file. */
for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next) for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
{ {
struct gcov_summary this_object; unsigned n_counts;
struct gcov_summary object, program; struct gcov_summary prg; /* summary for this object over all
gcov_type *values[GCOV_COUNTERS]; program. */
const struct gcov_fn_info *fi_ptr; struct gcov_ctr_summary *cs_prg, *cs_tprg, *cs_all;
unsigned fi_stride;
unsigned c_ix, f_ix, n_counts;
struct gcov_ctr_summary *cs_obj, *cs_tobj, *cs_prg, *cs_tprg, *cs_all;
int error = 0; int error = 0;
gcov_unsigned_t tag, length; gcov_unsigned_t tag, length;
gcov_position_t summary_pos = 0; gcov_position_t summary_pos = 0;
gcov_position_t eof_pos = 0; gcov_position_t eof_pos = 0;
const char *fname, *s; const char *fname, *s;
struct gcov_fn_buffer *fn_buffer = 0;
struct gcov_fn_buffer **fn_tail = &fn_buffer;
fname = gi_ptr->filename; fname = gi_ptr->filename;
memset (&this_object, 0, sizeof (this_object));
memset (&object, 0, sizeof (object));
/* Avoid to add multiple drive letters into combined path. */ /* Avoid to add multiple drive letters into combined path. */
if (prefix_length != 0 && HAS_DRIVE_SPEC(fname)) if (prefix_length != 0 && HAS_DRIVE_SPEC(fname))
fname += 2; fname += 2;
...@@ -283,6 +353,7 @@ gcov_exit (void) ...@@ -283,6 +353,7 @@ gcov_exit (void)
level++; level++;
} }
} }
/* Update complete filename with stripped original. */ /* Update complete filename with stripped original. */
if (prefix_length != 0 && !IS_DIR_SEPARATOR (*fname)) if (prefix_length != 0 && !IS_DIR_SEPARATOR (*fname))
{ {
...@@ -293,42 +364,6 @@ gcov_exit (void) ...@@ -293,42 +364,6 @@ gcov_exit (void)
else else
strcpy (gi_filename_up, fname); strcpy (gi_filename_up, fname);
/* Totals for this object file. */
ci_ptr = gi_ptr->counts;
for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
{
if (!((1 << t_ix) & gi_ptr->ctr_mask))
continue;
cs_ptr = &this_object.ctrs[t_ix];
cs_ptr->num += ci_ptr->num;
for (c_num = 0; c_num < ci_ptr->num; c_num++)
{
cs_ptr->sum_all += ci_ptr->values[c_num];
if (cs_ptr->run_max < ci_ptr->values[c_num])
cs_ptr->run_max = ci_ptr->values[c_num];
}
ci_ptr++;
}
c_ix = 0;
for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
if ((1 << t_ix) & gi_ptr->ctr_mask)
{
values[c_ix] = gi_ptr->counts[c_ix].values;
c_ix++;
}
/* Calculate the function_info stride. This depends on the
number of counter types being measured. */
fi_stride = sizeof (struct gcov_fn_info) + c_ix * sizeof (unsigned);
if (__alignof__ (struct gcov_fn_info) > sizeof (unsigned))
{
fi_stride += __alignof__ (struct gcov_fn_info) - 1;
fi_stride &= ~(__alignof__ (struct gcov_fn_info) - 1);
}
if (!gcov_open (gi_filename)) if (!gcov_open (gi_filename))
{ {
/* Open failed likely due to missed directory. /* Open failed likely due to missed directory.
...@@ -364,83 +399,98 @@ gcov_exit (void) ...@@ -364,83 +399,98 @@ gcov_exit (void)
/* Read from a different compilation. Overwrite the file. */ /* Read from a different compilation. Overwrite the file. */
goto rewrite; goto rewrite;
/* Merge execution counts for each function. */ /* Look for program summary. */
for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) for (f_ix = ~0u;;)
{ {
fi_ptr = (const struct gcov_fn_info *) struct gcov_summary tmp;
((const char *) gi_ptr->functions + f_ix * fi_stride);
eof_pos = gcov_position ();
tag = gcov_read_unsigned (); tag = gcov_read_unsigned ();
if (tag != GCOV_TAG_PROGRAM_SUMMARY)
break;
length = gcov_read_unsigned ();
if (length != GCOV_TAG_SUMMARY_LENGTH)
goto read_mismatch;
gcov_read_summary (&tmp);
if ((error = gcov_is_error ()))
goto read_error;
if (!summary_pos && tmp.checksum == gcov_crc32)
{
prg = tmp;
summary_pos = eof_pos;
}
}
/* Merge execution counts for each function. */
for (f_ix = 0; f_ix != gi_ptr->n_functions;
f_ix++, tag = gcov_read_unsigned ())
{
gfi_ptr = gi_ptr->functions[f_ix];
if (tag != GCOV_TAG_FUNCTION)
goto read_mismatch;
length = gcov_read_unsigned (); length = gcov_read_unsigned ();
/* Check function. */ if (!length)
if (tag != GCOV_TAG_FUNCTION /* This function did not appear in the other program.
|| length != GCOV_TAG_FUNCTION_LENGTH We have nothing to merge. */
|| gcov_read_unsigned () != fi_ptr->ident continue;
|| gcov_read_unsigned () != fi_ptr->lineno_checksum
|| gcov_read_unsigned () != fi_ptr->cfg_checksum) if (length != GCOV_TAG_FUNCTION_LENGTH)
goto read_mismatch;
if (!gfi_ptr || gfi_ptr->key != gi_ptr)
{ {
read_mismatch:; /* This function appears in the other program. We
fprintf (stderr, "profiling:%s:Merge mismatch for %s\n", need to buffer the information in order to write
gi_filename, it back out -- we'll be inserting data before
f_ix + 1 ? "function" : "summaries"); this point, so cannot simply keep the data in the
goto read_fatal; file. */
fn_tail = buffer_fn_data (gi_ptr, fn_tail, f_ix);
if (!fn_tail)
goto read_mismatch;
continue;
} }
c_ix = 0; if (gcov_read_unsigned () != gfi_ptr->ident
|| gcov_read_unsigned () != gfi_ptr->lineno_checksum
|| gcov_read_unsigned () != gfi_ptr->cfg_checksum)
goto read_mismatch;
ci_ptr = gfi_ptr->ctrs;
for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++) for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
{ {
gcov_merge_fn merge; gcov_merge_fn merge = gi_ptr->merge[t_ix];
if (!((1 << t_ix) & gi_ptr->ctr_mask)) if (!merge)
continue; continue;
n_counts = fi_ptr->n_ctrs[c_ix];
merge = gi_ptr->counts[c_ix].merge;
tag = gcov_read_unsigned (); tag = gcov_read_unsigned ();
length = gcov_read_unsigned (); length = gcov_read_unsigned ();
if (tag != GCOV_TAG_FOR_COUNTER (t_ix) if (tag != GCOV_TAG_FOR_COUNTER (t_ix)
|| length != GCOV_TAG_COUNTER_LENGTH (n_counts)) || length != GCOV_TAG_COUNTER_LENGTH (ci_ptr->num))
goto read_mismatch; goto read_mismatch;
(*merge) (values[c_ix], n_counts); (*merge) (ci_ptr->values, ci_ptr->num);
values[c_ix] += n_counts; ci_ptr++;
c_ix++;
} }
if ((error = gcov_is_error ())) if ((error = gcov_is_error ()))
goto read_error; goto read_error;
} }
f_ix = ~0u; if (tag)
/* Check program & object summary */
while (1)
{ {
int is_program; read_mismatch:;
fprintf (stderr, "profiling:%s:Merge mismatch for %s\n",
eof_pos = gcov_position (); gi_filename, f_ix + 1 ? "function" : "summaries");
tag = gcov_read_unsigned (); goto read_fatal;
if (!tag)
break;
length = gcov_read_unsigned ();
is_program = tag == GCOV_TAG_PROGRAM_SUMMARY;
if (length != GCOV_TAG_SUMMARY_LENGTH
|| (!is_program && tag != GCOV_TAG_OBJECT_SUMMARY))
goto read_mismatch;
gcov_read_summary (is_program ? &program : &object);
if ((error = gcov_is_error ()))
goto read_error;
if (is_program && program.checksum == gcov_crc32)
{
summary_pos = eof_pos;
goto rewrite;
}
} }
} }
goto rewrite; goto rewrite;
read_error:; read_error:;
fprintf (stderr, error < 0 ? "profiling:%s:Overflow merging\n" fprintf (stderr, "profiling:%s:%s merging\n", gi_filename,
: "profiling:%s:Error merging\n", gi_filename); error < 0 ? "Overflow": "Error");
read_fatal:; read_fatal:;
gcov_close (); gcov_close ();
...@@ -449,29 +499,20 @@ gcov_exit (void) ...@@ -449,29 +499,20 @@ gcov_exit (void)
rewrite:; rewrite:;
gcov_rewrite (); gcov_rewrite ();
if (!summary_pos) if (!summary_pos)
memset (&program, 0, sizeof (program)); {
memset (&prg, 0, sizeof (prg));
summary_pos = eof_pos;
}
/* Merge the summaries. */ /* Merge the summaries. */
f_ix = ~0u;
for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++) for (t_ix = 0; t_ix < GCOV_COUNTERS_SUMMABLE; t_ix++)
{ {
cs_obj = &object.ctrs[t_ix]; cs_prg = &prg.ctrs[t_ix];
cs_tobj = &this_object.ctrs[t_ix]; cs_tprg = &this_prg.ctrs[t_ix];
cs_prg = &program.ctrs[t_ix]; cs_all = &all_prg.ctrs[t_ix];
cs_tprg = &this_program.ctrs[t_ix];
cs_all = &all.ctrs[t_ix];
if ((1 << t_ix) & gi_ptr->ctr_mask) if (gi_ptr->merge[t_ix])
{ {
if (!cs_obj->runs++)
cs_obj->num = cs_tobj->num;
else if (cs_obj->num != cs_tobj->num)
goto read_mismatch;
cs_obj->sum_all += cs_tobj->sum_all;
if (cs_obj->run_max < cs_tobj->run_max)
cs_obj->run_max = cs_tobj->run_max;
cs_obj->sum_max += cs_tobj->run_max;
if (!cs_prg->runs++) if (!cs_prg->runs++)
cs_prg->num = cs_tprg->num; cs_prg->num = cs_tprg->num;
else if (cs_prg->num != cs_tprg->num) else if (cs_prg->num != cs_tprg->num)
...@@ -481,78 +522,94 @@ gcov_exit (void) ...@@ -481,78 +522,94 @@ gcov_exit (void)
cs_prg->run_max = cs_tprg->run_max; cs_prg->run_max = cs_tprg->run_max;
cs_prg->sum_max += cs_tprg->run_max; cs_prg->sum_max += cs_tprg->run_max;
} }
else if (cs_obj->num || cs_prg->num) else if (cs_prg->runs)
goto read_mismatch; goto read_mismatch;
if (!cs_all->runs && cs_prg->runs) if (!cs_all->runs && cs_prg->runs)
memcpy (cs_all, cs_prg, sizeof (*cs_all)); memcpy (cs_all, cs_prg, sizeof (*cs_all));
else if (!all.checksum else if (!all_prg.checksum
&& (!GCOV_LOCKED || cs_all->runs == cs_prg->runs) && (!GCOV_LOCKED || cs_all->runs == cs_prg->runs)
&& memcmp (cs_all, cs_prg, sizeof (*cs_all))) && memcmp (cs_all, cs_prg, sizeof (*cs_all)))
{ {
fprintf (stderr, "profiling:%s:Invocation mismatch - some data files may have been removed%s", fprintf (stderr, "profiling:%s:Invocation mismatch - some data files may have been removed%s\n",
gi_filename, GCOV_LOCKED gi_filename, GCOV_LOCKED
? "" : " or concurrent update without locking support"); ? "" : " or concurrently updated without locking support");
all.checksum = ~0u; all_prg.checksum = ~0u;
} }
} }
c_ix = 0; prg.checksum = gcov_crc32;
for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
if ((1 << t_ix) & gi_ptr->ctr_mask)
{
values[c_ix] = gi_ptr->counts[c_ix].values;
c_ix++;
}
program.checksum = gcov_crc32;
/* Write out the data. */ /* Write out the data. */
gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION); if (!eof_pos)
gcov_write_unsigned (gi_ptr->stamp); {
gcov_write_tag_length (GCOV_DATA_MAGIC, GCOV_VERSION);
gcov_write_unsigned (gi_ptr->stamp);
}
if (summary_pos)
gcov_seek (summary_pos);
/* Generate whole program statistics. */
gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, &prg);
if (summary_pos < eof_pos)
gcov_seek (eof_pos);
/* Write execution counts for each function. */ /* Write execution counts for each function. */
for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++) for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
{ {
fi_ptr = (const struct gcov_fn_info *) unsigned buffered = 0;
((const char *) gi_ptr->functions + f_ix * fi_stride);
/* Announce function. */ if (fn_buffer && fn_buffer->fn_ix == f_ix)
gcov_write_tag_length (GCOV_TAG_FUNCTION, GCOV_TAG_FUNCTION_LENGTH); {
gcov_write_unsigned (fi_ptr->ident); /* Buffered data from another program. */
gcov_write_unsigned (fi_ptr->lineno_checksum); buffered = 1;
gcov_write_unsigned (fi_ptr->cfg_checksum); gfi_ptr = &fn_buffer->info;
length = GCOV_TAG_FUNCTION_LENGTH;
}
else
{
gfi_ptr = gi_ptr->functions[f_ix];
if (gfi_ptr && gfi_ptr->key == gi_ptr)
length = GCOV_TAG_FUNCTION_LENGTH;
else
length = 0;
}
gcov_write_tag_length (GCOV_TAG_FUNCTION, length);
if (!length)
continue;
gcov_write_unsigned (gfi_ptr->ident);
gcov_write_unsigned (gfi_ptr->lineno_checksum);
gcov_write_unsigned (gfi_ptr->cfg_checksum);
c_ix = 0; ci_ptr = gfi_ptr->ctrs;
for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++) for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
{ {
gcov_type *c_ptr; if (!gi_ptr->merge[t_ix])
if (!((1 << t_ix) & gi_ptr->ctr_mask))
continue; continue;
n_counts = fi_ptr->n_ctrs[c_ix]; n_counts = ci_ptr->num;
gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix), gcov_write_tag_length (GCOV_TAG_FOR_COUNTER (t_ix),
GCOV_TAG_COUNTER_LENGTH (n_counts)); GCOV_TAG_COUNTER_LENGTH (n_counts));
c_ptr = values[c_ix]; gcov_type *c_ptr = ci_ptr->values;
while (n_counts--) while (n_counts--)
gcov_write_counter (*c_ptr++); gcov_write_counter (*c_ptr++);
if (buffered)
values[c_ix] = c_ptr; free (ci_ptr->values);
c_ix++; ci_ptr++;
}
if (buffered)
{
struct gcov_fn_buffer *tmp = fn_buffer;
fn_buffer = fn_buffer->next;
free (tmp);
} }
} }
/* Object file summary. */ gcov_write_unsigned (0);
gcov_write_summary (GCOV_TAG_OBJECT_SUMMARY, &object);
/* Generate whole program statistics. */
if (eof_pos)
gcov_seek (eof_pos);
gcov_write_summary (GCOV_TAG_PROGRAM_SUMMARY, &program);
if (!summary_pos)
gcov_write_unsigned (0);
if ((error = gcov_close ())) if ((error = gcov_close ()))
fprintf (stderr, error < 0 ? fprintf (stderr, error < 0 ?
"profiling:%s:Overflow writing\n" : "profiling:%s:Overflow writing\n" :
...@@ -618,15 +675,25 @@ __gcov_flush (void) ...@@ -618,15 +675,25 @@ __gcov_flush (void)
gcov_exit (); gcov_exit ();
for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next) for (gi_ptr = gcov_list; gi_ptr; gi_ptr = gi_ptr->next)
{ {
unsigned t_ix; unsigned f_ix;
const struct gcov_ctr_info *ci_ptr;
for (t_ix = 0, ci_ptr = gi_ptr->counts; t_ix != GCOV_COUNTERS; t_ix++) for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
if ((1 << t_ix) & gi_ptr->ctr_mask) {
{ unsigned t_ix;
memset (ci_ptr->values, 0, sizeof (gcov_type) * ci_ptr->num); const struct gcov_fn_info *gfi_ptr = gi_ptr->functions[f_ix];
ci_ptr++;
} if (!gfi_ptr || gfi_ptr->key != gi_ptr)
continue;
const struct gcov_ctr_info *ci_ptr = gfi_ptr->ctrs;
for (t_ix = 0; t_ix != GCOV_COUNTERS; t_ix++)
{
if (!gi_ptr->merge[t_ix])
continue;
memset (ci_ptr->values, 0, sizeof (gcov_type) * ci_ptr->num);
ci_ptr++;
}
}
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment