aboutsummaryrefslogtreecommitdiff
path: root/gcc/profile.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/profile.c')
-rw-r--r--gcc/profile.c511
1 files changed, 206 insertions, 305 deletions
diff --git a/gcc/profile.c b/gcc/profile.c
index 68435df..ec53065 100644
--- a/gcc/profile.c
+++ b/gcc/profile.c
@@ -101,6 +101,27 @@ struct function_list
/* the sections */
};
+
+/* Counts information for a function. */
+typedef struct counts_entry
+{
+ /* We hash by */
+ char *function_name;
+ unsigned section;
+
+ /* Store */
+ unsigned checksum;
+ unsigned n_counts;
+ gcov_type *counts;
+ unsigned merged;
+ gcov_type max_counter;
+ gcov_type max_counter_sum;
+
+ /* Workspace */
+ struct counts_entry *chain;
+
+} counts_entry_t;
+
static struct function_list *functions_head = 0;
static struct function_list **functions_tail = &functions_head;
@@ -119,12 +140,10 @@ struct profile_info profile_info;
/* Name and file pointer of the output file for the basic block graph. */
-static FILE *bbg_file;
static char *bbg_file_name;
/* Name and file pointer of the input file for the arc count data. */
-static FILE *da_file;
static char *da_file_name;
/* The name of the count table. Used by the edge profiling code. */
@@ -149,11 +168,10 @@ static void find_spanning_tree PARAMS ((struct edge_list *));
static rtx gen_edge_profiler PARAMS ((int));
static void instrument_edges PARAMS ((struct edge_list *));
static void compute_branch_probabilities PARAMS ((void));
-static hashval_t htab_counts_index_hash PARAMS ((const void *));
-static int htab_counts_index_eq PARAMS ((const void *, const void *));
-static void htab_counts_index_del PARAMS ((void *));
-static void cleanup_counts_index PARAMS ((int));
-static int index_counts_file PARAMS ((void));
+static hashval_t htab_counts_entry_hash PARAMS ((const void *));
+static int htab_counts_entry_eq PARAMS ((const void *, const void *));
+static void htab_counts_entry_del PARAMS ((void *));
+static void read_counts_file PARAMS ((const char *));
static gcov_type * get_exec_counts PARAMS ((void));
static unsigned compute_checksum PARAMS ((void));
static basic_block find_group PARAMS ((basic_block));
@@ -218,106 +236,61 @@ instrument_edges (el)
fprintf (rtl_dump_file, "%d edges instrumented\n", num_instr_edges);
}
-struct section_reference
-{
- long offset;
- int owns_summary;
- long *summary;
-};
-
-struct da_index_entry
-{
- /* We hash by */
- char *function_name;
- unsigned section;
- /* and store */
- unsigned checksum;
- unsigned n_offsets;
- struct section_reference *offsets;
-};
-
static hashval_t
-htab_counts_index_hash (of)
+htab_counts_entry_hash (of)
const void *of;
{
- const struct da_index_entry *entry = of;
+ const counts_entry_t *entry = of;
return htab_hash_string (entry->function_name) ^ entry->section;
}
static int
-htab_counts_index_eq (of1, of2)
+htab_counts_entry_eq (of1, of2)
const void *of1;
const void *of2;
{
- const struct da_index_entry *entry1 = of1;
- const struct da_index_entry *entry2 = of2;
+ const counts_entry_t *entry1 = of1;
+ const counts_entry_t *entry2 = of2;
return !strcmp (entry1->function_name, entry2->function_name)
- && entry1->section == entry2->section;
+ && entry1->section == entry2->section;
}
static void
-htab_counts_index_del (what)
- void *what;
+htab_counts_entry_del (of)
+ void *of;
{
- struct da_index_entry *entry = what;
- unsigned i;
+ counts_entry_t *entry = of;
- for (i = 0; i < entry->n_offsets; i++)
- {
- struct section_reference *act = entry->offsets + i;
- if (act->owns_summary)
- free (act->summary);
- }
free (entry->function_name);
- free (entry->offsets);
+ free (entry->counts);
free (entry);
}
-static char *counts_file_name;
-static htab_t counts_file_index = NULL;
+static htab_t counts_hash = NULL;
static void
-cleanup_counts_index (close_file)
- int close_file;
-{
- if (da_file && close_file)
- {
- fclose (da_file);
- da_file = NULL;
- }
- if (counts_file_name)
- free (counts_file_name);
- counts_file_name = NULL;
- if (counts_file_index)
- htab_delete (counts_file_index);
- counts_file_index = NULL;
-}
-
-static int
-index_counts_file ()
+read_counts_file (const char *name)
{
char *function_name_buffer = NULL;
unsigned magic, version, ix, checksum;
- long *summary;
-
- /* No .da file, no data. */
- if (!da_file)
- return 0;
- counts_file_index = htab_create (10, htab_counts_index_hash, htab_counts_index_eq, htab_counts_index_del);
-
- /* Now index all profile sections. */
- rewind (da_file);
-
- summary = NULL;
-
- if (gcov_read_unsigned (da_file, &magic) || magic != GCOV_DATA_MAGIC)
+ counts_entry_t *summaried = NULL;
+ unsigned seen_summary = 0;
+
+ if (!gcov_open (name, 1))
{
- warning ("`%s' is not a gcov data file", da_file_name);
- goto cleanup;
+ warning ("file %s not found, execution counts assumed to be zero", name);
+ return;
}
- if (gcov_read_unsigned (da_file, &version) || version != GCOV_VERSION)
+
+ if (gcov_read_unsigned (&magic) || magic != GCOV_DATA_MAGIC)
+ {
+ warning ("`%s' is not a gcov data file", name);
+ gcov_close ();
+ return;
+ }
+ else if (gcov_read_unsigned (&version) || version != GCOV_VERSION)
{
char v[4], e[4];
magic = GCOV_VERSION;
@@ -327,97 +300,121 @@ index_counts_file ()
v[ix] = version;
e[ix] = magic;
}
- warning ("`%s' is version `%.4s', expected version `%.4s'",
- da_file_name, v, e);
- goto cleanup;
+ warning ("`%s' is version `%.4s', expected version `%.4s'", name, v, e);
+ gcov_close ();
+ return;
}
+ counts_hash = htab_create (10,
+ htab_counts_entry_hash, htab_counts_entry_eq,
+ htab_counts_entry_del);
while (1)
{
unsigned tag, length;
long offset;
- offset = gcov_save_position (da_file);
- if (gcov_read_unsigned (da_file, &tag)
- || gcov_read_unsigned (da_file, &length))
+ offset = gcov_save_position ();
+ if (gcov_read_unsigned (&tag) || gcov_read_unsigned (&length))
{
- if (feof (da_file))
+ if (gcov_eof ())
break;
corrupt:;
- warning ("`%s' is corrupted", da_file_name);
- goto cleanup;
+ warning ("`%s' is corrupted", name);
+ cleanup:
+ htab_delete (counts_hash);
+ break;
}
if (tag == GCOV_TAG_FUNCTION)
{
- if (gcov_read_string (da_file, &function_name_buffer, NULL)
- || gcov_read_unsigned (da_file, &checksum))
+ if (gcov_read_string (&function_name_buffer)
+ || gcov_read_unsigned (&checksum))
goto corrupt;
- continue;
+ if (seen_summary)
+ {
+ /* We have already seen a summary, this means that this
+ new function begins a new set of program runs. We
+ must unlink the summaried chain. */
+ counts_entry_t *entry, *chain;
+
+ for (entry = summaried; entry; entry = chain)
+ {
+ chain = entry->chain;
+
+ entry->max_counter_sum += entry->max_counter;
+ entry->chain = NULL;
+ }
+ summaried = NULL;
+ seen_summary = 0;
+ }
}
- if (tag == GCOV_TAG_PROGRAM_SUMMARY)
+ else if (tag == GCOV_TAG_PROGRAM_SUMMARY)
{
- if (length != GCOV_SUMMARY_LENGTH)
+ counts_entry_t *entry;
+ struct gcov_summary summary;
+
+ if (length != GCOV_SUMMARY_LENGTH
+ || gcov_read_summary (&summary))
goto corrupt;
- if (summary)
- *summary = offset;
- summary = NULL;
+ seen_summary = 1;
+ for (entry = summaried; entry; entry = entry->chain)
+ {
+ entry->merged += summary.runs;
+ if (entry->max_counter < summary.arc_sum_max)
+ entry->max_counter = summary.arc_sum_max;
+ }
}
- else
+ else if (GCOV_TAG_IS_SUBTAG (GCOV_TAG_FUNCTION, tag)
+ && function_name_buffer)
{
- if (function_name_buffer)
- {
- struct da_index_entry **slot, elt;
- elt.function_name = function_name_buffer;
- elt.section = tag;
+ counts_entry_t **slot, *entry, elt;
+ unsigned n_counts = length / 8;
+ unsigned ix;
+ gcov_type count;
- slot = (struct da_index_entry **)
- htab_find_slot (counts_file_index, &elt, INSERT);
- if (*slot)
- {
- if ((*slot)->checksum != checksum)
- {
- warning ("profile mismatch for `%s'", function_name_buffer);
- goto cleanup;
- }
- (*slot)->n_offsets++;
- (*slot)->offsets = xrealloc ((*slot)->offsets,
- sizeof (struct section_reference) * (*slot)->n_offsets);
- }
- else
- {
- *slot = xmalloc (sizeof (struct da_index_entry));
- (*slot)->function_name = xstrdup (function_name_buffer);
- (*slot)->section = tag;
- (*slot)->checksum = checksum;
- (*slot)->n_offsets = 1;
- (*slot)->offsets = xmalloc (sizeof (struct section_reference));
- }
- (*slot)->offsets[(*slot)->n_offsets - 1].offset = offset;
- if (summary)
- (*slot)->offsets[(*slot)->n_offsets - 1].owns_summary = 0;
- else
- {
- summary = xmalloc (sizeof (long));
- *summary = -1;
- (*slot)->offsets[(*slot)->n_offsets - 1].owns_summary = 1;
- }
- (*slot)->offsets[(*slot)->n_offsets - 1].summary = summary;
+ elt.function_name = function_name_buffer;
+ elt.section = tag;
+
+ slot = (counts_entry_t **) htab_find_slot
+ (counts_hash, &elt, INSERT);
+ entry = *slot;
+ if (!entry)
+ {
+ *slot = entry = xmalloc (sizeof (counts_entry_t));
+ entry->function_name = xstrdup (function_name_buffer);
+ entry->section = tag;
+ entry->checksum = checksum;
+ entry->n_counts = n_counts;
+ entry->counts = xcalloc (n_counts, sizeof (gcov_type));
+ }
+ else if (entry->checksum != checksum || entry->n_counts != n_counts)
+ {
+ warning ("profile mismatch for `%s'", function_name_buffer);
+ goto cleanup;
+ }
+
+ /* 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. */
+ if (!entry->chain || summaried != entry)
+ {
+ entry->chain = summaried;
+ summaried = entry;
+ }
+ for (ix = 0; ix != n_counts; ix++)
+ {
+ if (gcov_read_counter (&count))
+ goto corrupt;
+ entry->counts[ix] += count;
}
}
- if (gcov_skip (da_file, length))
- goto corrupt;
+ else
+ if (gcov_skip (length))
+ goto corrupt;
}
free (function_name_buffer);
-
- return 1;
-
-cleanup:
- cleanup_counts_index (1);
- if (function_name_buffer)
- free (function_name_buffer);
- return 0;
+ gcov_close ();
}
/* Computes hybrid profile for all matching entries in da_file.
@@ -428,26 +425,17 @@ get_exec_counts ()
{
unsigned num_edges = 0;
basic_block bb;
- gcov_type *profile;
- gcov_type max_count;
- unsigned ix, i, tag, length, num;
const char *name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (current_function_decl));
- struct da_index_entry *entry, what;
- struct section_reference *act;
- gcov_type count;
- struct gcov_summary summ;
+ counts_entry_t *entry, elt;
profile_info.max_counter_in_program = 0;
profile_info.count_profiles_merged = 0;
- /* No .da file, no execution counts. */
- if (!da_file)
+ /* No hash table, no counts. */
+ if (!counts_hash)
return NULL;
- if (!counts_file_index)
- abort ();
/* Count the edges to be (possibly) instrumented. */
-
FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR, NULL, next_bb)
{
edge e;
@@ -456,81 +444,24 @@ get_exec_counts ()
num_edges++;
}
- /* now read and combine all matching profiles. */
-
- profile = xmalloc (sizeof (gcov_type) * num_edges);
-
- for (ix = 0; ix < num_edges; ix++)
- profile[ix] = 0;
-
- what.function_name = (char *) name;
- what.section = GCOV_TAG_ARC_COUNTS;
- entry = htab_find (counts_file_index, &what);
+ elt.function_name = (char *) name;
+ elt.section = GCOV_TAG_ARC_COUNTS;
+ entry = htab_find (counts_hash, &elt);
if (!entry)
{
warning ("No profile for function '%s' found.", name);
- goto cleanup;
+ return NULL;
}
- if (entry->checksum != profile_info.current_function_cfg_checksum)
+ if (entry->checksum != profile_info.current_function_cfg_checksum
+ || num_edges != entry->n_counts)
{
warning ("profile mismatch for `%s'", current_function_name);
- goto cleanup;
+ return NULL;
}
- for (i = 0; i < entry->n_offsets; i++)
- {
- act = entry->offsets + i;
-
- /* Read arc counters. */
- max_count = 0;
- gcov_resync (da_file, act->offset, 0);
-
- if (gcov_read_unsigned (da_file, &tag)
- || gcov_read_unsigned (da_file, &length)
- || tag != GCOV_TAG_ARC_COUNTS)
- {
- /* We have already passed through file, so any error means
- something is rotten. */
- abort ();
- }
- num = length / 8;
-
- if (num != num_edges)
- {
- warning ("profile mismatch for `%s'", current_function_name);
- goto cleanup;
- }
-
- for (ix = 0; ix != num; ix++)
- {
- if (gcov_read_counter (da_file, &count))
- abort ();
- if (count > max_count)
- max_count = count;
- profile[ix] += count;
- }
-
- /* Read program summary. */
- if (*act->summary != -1)
- {
- gcov_resync (da_file, *act->summary, 0);
- if (gcov_read_unsigned (da_file, &tag)
- || gcov_read_unsigned (da_file, &length)
- || tag != GCOV_TAG_PROGRAM_SUMMARY
- || gcov_read_summary (da_file, &summ))
- abort ();
- profile_info.count_profiles_merged += summ.runs;
- profile_info.max_counter_in_program += summ.arc_sum_max;
- }
- else
- summ.runs = 0;
- if (!summ.runs)
- {
- profile_info.count_profiles_merged++;
- profile_info.max_counter_in_program += max_count;
- }
- }
+ profile_info.count_profiles_merged = entry->merged;
+ profile_info.max_counter_in_program = entry->max_counter_sum;
if (rtl_dump_file)
{
@@ -539,12 +470,7 @@ get_exec_counts ()
(int)profile_info.max_counter_in_program);
}
- return profile;
-
-cleanup:;
- free (profile);
- cleanup_counts_index (1);
- return NULL;
+ return entry->counts;
}
@@ -858,8 +784,6 @@ compute_branch_probabilities ()
}
free_aux_for_blocks ();
- if (exec_counts)
- free (exec_counts);
find_counters_section (GCOV_TAG_ARC_COUNTS)->present = 1;
}
@@ -1083,32 +1007,30 @@ branch_prob ()
edge output the source and target basic block numbers.
NOTE: The format of this file must be compatible with gcov. */
- if (flag_test_coverage && bbg_file)
+ if (gcov_ok ())
{
long offset;
const char *file = DECL_SOURCE_FILE (current_function_decl);
unsigned line = DECL_SOURCE_LINE (current_function_decl);
/* Announce function */
- if (gcov_write_unsigned (bbg_file, GCOV_TAG_FUNCTION)
- || !(offset = gcov_reserve_length (bbg_file))
- || gcov_write_string (bbg_file, name,
- strlen (name))
- || gcov_write_unsigned (bbg_file,
- profile_info.current_function_cfg_checksum)
- || gcov_write_string (bbg_file, file, strlen (file))
- || gcov_write_unsigned (bbg_file, line)
- || gcov_write_length (bbg_file, offset))
+ if (gcov_write_unsigned (GCOV_TAG_FUNCTION)
+ || !(offset = gcov_reserve_length ())
+ || gcov_write_string (name)
+ || gcov_write_unsigned (profile_info.current_function_cfg_checksum)
+ || gcov_write_string (file)
+ || gcov_write_unsigned (line)
+ || gcov_write_length (offset))
goto bbg_error;
/* Basic block flags */
- if (gcov_write_unsigned (bbg_file, GCOV_TAG_BLOCKS)
- || !(offset = gcov_reserve_length (bbg_file)))
+ if (gcov_write_unsigned (GCOV_TAG_BLOCKS)
+ || !(offset = gcov_reserve_length ()))
goto bbg_error;
for (i = 0; i != (unsigned) (n_basic_blocks + 2); i++)
- if (gcov_write_unsigned (bbg_file, 0))
+ if (gcov_write_unsigned (0))
goto bbg_error;
- if (gcov_write_length (bbg_file, offset))
+ if (gcov_write_length (offset))
goto bbg_error;
/* Arcs */
@@ -1116,9 +1038,9 @@ branch_prob ()
{
edge e;
- if (gcov_write_unsigned (bbg_file, GCOV_TAG_ARCS)
- || !(offset = gcov_reserve_length (bbg_file))
- || gcov_write_unsigned (bbg_file, BB_TO_GCOV_INDEX (bb)))
+ if (gcov_write_unsigned (GCOV_TAG_ARCS)
+ || !(offset = gcov_reserve_length ())
+ || gcov_write_unsigned (BB_TO_GCOV_INDEX (bb)))
goto bbg_error;
for (e = bb->succ; e; e = e->succ_next)
@@ -1135,14 +1057,13 @@ branch_prob ()
if (e->flags & EDGE_FALLTHRU)
flag_bits |= GCOV_ARC_FALLTHROUGH;
- if (gcov_write_unsigned (bbg_file,
- BB_TO_GCOV_INDEX (e->dest))
- || gcov_write_unsigned (bbg_file, flag_bits))
+ if (gcov_write_unsigned (BB_TO_GCOV_INDEX (e->dest))
+ || gcov_write_unsigned (flag_bits))
goto bbg_error;
}
}
- if (gcov_write_length (bbg_file, offset))
+ if (gcov_write_length (offset))
goto bbg_error;
}
@@ -1185,10 +1106,10 @@ branch_prob ()
{
if (offset)
/*NOP*/;
- else if (gcov_write_unsigned (bbg_file, GCOV_TAG_LINES)
- || !(offset = gcov_reserve_length (bbg_file))
- || gcov_write_unsigned (bbg_file,
- BB_TO_GCOV_INDEX (bb)))
+ else if (gcov_write_unsigned (GCOV_TAG_LINES)
+ || !(offset = gcov_reserve_length ())
+ || (gcov_write_unsigned
+ (BB_TO_GCOV_INDEX (bb))))
goto bbg_error;
/* If this is a new source file, then output
the file's name to the .bb file. */
@@ -1197,12 +1118,11 @@ branch_prob ()
prev_file_name))
{
prev_file_name = NOTE_SOURCE_FILE (insn);
- if (gcov_write_unsigned (bbg_file, 0)
- || gcov_write_string (bbg_file, prev_file_name,
- strlen (prev_file_name)))
+ if (gcov_write_unsigned (0)
+ || gcov_write_string (prev_file_name))
goto bbg_error;
}
- if (gcov_write_unsigned (bbg_file, NOTE_LINE_NUMBER (insn)))
+ if (gcov_write_unsigned (NOTE_LINE_NUMBER (insn)))
goto bbg_error;
}
}
@@ -1211,14 +1131,13 @@ branch_prob ()
if (offset)
{
- if (gcov_write_unsigned (bbg_file, 0)
- || gcov_write_string (bbg_file, NULL, 0)
- || gcov_write_length (bbg_file, offset))
+ if (gcov_write_unsigned (0)
+ || gcov_write_string (NULL)
+ || gcov_write_length (offset))
{
bbg_error:;
warning ("error writing `%s'", bbg_file_name);
- fclose (bbg_file);
- bbg_file = NULL;
+ gcov_error ();
}
}
}
@@ -1395,38 +1314,27 @@ init_branch_prob (filename)
int len = strlen (filename);
int i;
+ da_file_name = (char *) xmalloc (len + strlen (GCOV_DATA_SUFFIX) + 1);
+ strcpy (da_file_name, filename);
+ strcat (da_file_name, GCOV_DATA_SUFFIX);
+
+ if (flag_branch_probabilities)
+ read_counts_file (da_file_name);
+
if (flag_test_coverage)
{
/* Open the bbg output file. */
bbg_file_name = (char *) xmalloc (len + strlen (GCOV_GRAPH_SUFFIX) + 1);
strcpy (bbg_file_name, filename);
strcat (bbg_file_name, GCOV_GRAPH_SUFFIX);
- bbg_file = fopen (bbg_file_name, "wb");
- if (!bbg_file)
- fatal_io_error ("cannot open %s", bbg_file_name);
-
- if (gcov_write_unsigned (bbg_file, GCOV_GRAPH_MAGIC)
- || gcov_write_unsigned (bbg_file, GCOV_VERSION))
+ if (!gcov_open (bbg_file_name, -1))
{
- fclose (bbg_file);
- fatal_io_error ("cannot write `%s'", bbg_file_name);
+ error ("cannot open %s", bbg_file_name);
+ gcov_error ();
}
- }
-
- da_file_name = (char *) xmalloc (len + strlen (GCOV_DATA_SUFFIX) + 1);
- strcpy (da_file_name, filename);
- strcat (da_file_name, GCOV_DATA_SUFFIX);
-
- if (flag_branch_probabilities)
- {
- da_file = fopen (da_file_name, "rb");
- if (!da_file)
- warning ("file %s not found, execution counts assumed to be zero",
- da_file_name);
- if (counts_file_index && strcmp (da_file_name, counts_file_name))
- cleanup_counts_index (0);
- if (index_counts_file ())
- counts_file_name = xstrdup (da_file_name);
+ else if (gcov_write_unsigned (GCOV_GRAPH_MAGIC)
+ || gcov_write_unsigned (GCOV_VERSION))
+ gcov_error ();
}
if (profile_arc_flag)
@@ -1459,27 +1367,20 @@ end_branch_prob ()
{
if (flag_test_coverage)
{
- if (bbg_file)
- {
-#if !SELF_COVERAGE
- /* If the compiler is instrumented, we should not remove the
- counts file, because we might be recompiling
- ourselves. The .da files are all removed during copying
- the stage1 files. */
- unlink (da_file_name);
+ int error = gcov_close ();
+
+ if (error)
+ unlink (bbg_file_name);
+#if SELF_COVERAGE
+ /* If the compiler is instrumented, we should not
+ unconditionally remove the counts file, because we might be
+ recompiling ourselves. The .da files are all removed during
+ copying the stage1 files. */
+ if (error)
#endif
- fclose (bbg_file);
- }
- else
- {
- unlink (bbg_file_name);
- unlink (da_file_name);
- }
+ unlink (da_file_name);
}
- if (da_file)
- fclose (da_file);
-
if (rtl_dump_file)
{
fprintf (rtl_dump_file, "\n");