final.c (end_final): Use C trees to output data structures for profiling.

* final.c (end_final): Use C trees to output data structures for profiling.

	* Makefile.in (LIBGCC_DEPS): Added missing dependency on gcov-io.h
        (profile.o): New dependency profile.h
        (final.o): New dependency profile.h
        * profile.h: New file. New global structure profile_info.
        * final.h (count_edges_instrumented_now): Declare.
        (current_function_cfg_checksum): Declare.
        (function_list): New structure.
        (functions_head, functions_tail): New static variables.
        (end_final): Emits more data, removed some -ax stuff.
        (final): Stores function names and chcksums.
        * gcov-io.h (__write_gcov_string): New function.
        (__read_gcov_string): New function.
        * gcov.c (read_profile): New function.
        (create_program_flow_graph): Uses read_profile instead of reading
	da_file.
        (read_files): Removed da_file checking, it's done by read_profile now.
        * libgcc2.c (bb_function_info): New structure.
        (bb): New field in structure, removed some -ax stuff.
        (__bb_exit_func): Changed structure of da_file.
        * profile.c (count_edges_instrumented_now): New global variable.
        (current_function_cfg_checksum): New global variable.
        (max_counter_in_program): New global variable.
        (get_exec_counts): New function.
        (compute_checksum): New function.
        (instrument_edges): Sets count_edges_instrumented_now.
        (compute_branch_probabilities): Uses get_exec_counts instead of
	reading da_file.
        (branch_prob): Calls compute_checksum and writes extra data to bbg_file.
        (init_branch_prob): Removed da_file checking, done in get_exec_counts
	now.
        (end_branch_prob): Removed da_file checking, done in get_exec_counts
	now.
        * gcov.texi: Updated information about gcov file format.

Co-Authored-By: Pavel Nejedly <bim@atrey.karlin.mff.cuni.cz>

From-SVN: r53326
This commit is contained in:
Jan Hubicka 2002-05-09 14:54:19 +02:00 committed by Jan Hubicka
parent 786de7eb9a
commit b7c9bf289f
12 changed files with 994 additions and 300 deletions

View File

@ -1,3 +1,42 @@
Thu May 9 14:52:45 CEST 2002 Jan Hubicka <jh@suse.cz>
Pavel Nejedly <bim@atrey.karlin.mff.cuni.cz>
* final.c (end_final): Use C trees to output data structures for profiling.
* Makefile.in (LIBGCC_DEPS): Added missing dependency on gcov-io.h
(profile.o): New dependency profile.h
(final.o): New dependency profile.h
* profile.h: New file. New global structure profile_info.
* final.h (count_edges_instrumented_now): Declare.
(current_function_cfg_checksum): Declare.
(function_list): New structure.
(functions_head, functions_tail): New static variables.
(end_final): Emits more data, removed some -ax stuff.
(final): Stores function names and chcksums.
* gcov-io.h (__write_gcov_string): New function.
(__read_gcov_string): New function.
* gcov.c (read_profile): New function.
(create_program_flow_graph): Uses read_profile instead of reading
da_file.
(read_files): Removed da_file checking, it's done by read_profile now.
* libgcc2.c (bb_function_info): New structure.
(bb): New field in structure, removed some -ax stuff.
(__bb_exit_func): Changed structure of da_file.
* profile.c (count_edges_instrumented_now): New global variable.
(current_function_cfg_checksum): New global variable.
(max_counter_in_program): New global variable.
(get_exec_counts): New function.
(compute_checksum): New function.
(instrument_edges): Sets count_edges_instrumented_now.
(compute_branch_probabilities): Uses get_exec_counts instead of
reading da_file.
(branch_prob): Calls compute_checksum and writes extra data to bbg_file.
(init_branch_prob): Removed da_file checking, done in get_exec_counts
now.
(end_branch_prob): Removed da_file checking, done in get_exec_counts
now.
* gcov.texi: Updated information about gcov file format.
2002-05-09 Kazu Hirata <kazu@cs.umass.edu>
* sbitmap.c: Fix formatting.

View File

@ -1484,7 +1484,7 @@ conflict.o : conflict.c $(CONFIG_H) $(SYSTEM_H) $(OBSTACK_H) $(HASHTAB_H) \
profile.o : profile.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h \
insn-config.h output.h $(REGS_H) $(EXPR_H) function.h \
gcov-io.h toplev.h $(GGC_H) hard-reg-set.h $(BASIC_BLOCK_H) $(TARGET_H) \
langhooks.h
langhooks.h profile.h libfuncs.h
loop.o : loop.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) flags.h $(LOOP_H) \
insn-config.h $(REGS_H) hard-reg-set.h $(RECOG_H) $(EXPR_H) \
real.h $(PREDICT_H) $(BASIC_BLOCK_H) function.h \
@ -1568,7 +1568,7 @@ sched-vis.o : sched-vis.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) sched-int.h \
$(TARGET_H) real.h
final.o : final.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h intl.h \
$(REGS_H) $(RECOG_H) conditions.h insn-config.h $(INSN_ATTR_H) function.h \
real.h output.h hard-reg-set.h except.h debug.h xcoffout.h \
real.h output.h hard-reg-set.h except.h debug.h xcoffout.h profile.h \
toplev.h reload.h dwarf2out.h $(BASIC_BLOCK_H) $(TM_P_H) $(TARGET_H) $(EXPR_H)
recog.o : recog.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) function.h $(BASIC_BLOCK_H) \
$(REGS_H) $(RECOG_H) $(EXPR_H) hard-reg-set.h flags.h insn-config.h \

View File

@ -363,6 +363,8 @@ program flow.
In the @file{.bbg} file, the format is:
@smallexample
name of function #0
checksum of function #0
number of basic blocks for function #0 (4-byte number)
total number of arcs for function #0 (4-byte number)
count of arcs in basic block #0 (4-byte number)
@ -383,6 +385,9 @@ A @minus{}1 (stored as a 4-byte number) is used to separate each function's
list of basic blocks, and to verify that the file has been read
correctly.
The function name is stored as a @minus{}1 (4 bytes), the length (4 bytes),
the name itself (padded to 4-byte boundary) followed by a @minus{}1 (4 bytes).
The @file{.da} file is generated when a program containing object files
built with the GCC @option{-fprofile-arcs} option is executed. A
separate @file{.da} file is created for each source file compiled with
@ -390,15 +395,32 @@ this option, and the name of the @file{.da} file is stored as an
absolute pathname in the resulting object file. This path name is
derived from the source file name by substituting a @file{.da} suffix.
The format of the @file{.da} file is fairly simple. The first 8-byte
number is the number of counts in the file, followed by the counts
(stored as 8-byte numbers). Each count corresponds to the number of
times each arc in the program is executed. The counts are cumulative;
each time the program is executed, it attempts to combine the existing
@file{.da} files with the new counts for this invocation of the
program. It ignores the contents of any @file{.da} files whose number of
arcs doesn't correspond to the current program, and merely overwrites
them instead.
The @file{.da} consists of several blocks (one for each run) with the following structure:
@smallexample
"magic" number @minus{}123 (4-byte number)
number of functions (4-byte number)
length of the "extension block" in bytes
extension block (variable length)
name of function #0 (the same format as in .bbg file)
checksum of function #0
number of instrumented arcs (4-byte number)
count of arc #0 (8-byte number)
count of arc #1 (8-byte number)
@dots{}
count of arc #M_0 (8-byte number)
name of function #1 (the same format as in .bbg file)
checksum of function #1
@dots{}
@end smallexample
The current structure of the extension block is as follows:
@smallexample
number of instrumented arcs in whole program (4-byte number)
sum all of instrumented arcs in whole program (8-byte number)
maximal value of counter in whole program (8-byte number)
number of instrumented arcs in the object file (4-byte number)
sum all of instrumented arcs in the object file (8-byte number)
maximal value of counter in the object file (8-byte number)
@end smallexample
All three of these files use the functions in @file{gcov-io.h} to store
integers; the functions in this header provide a machine-independent

View File

@ -68,6 +68,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
#include "target.h"
#include "debug.h"
#include "expr.h"
#include "profile.h"
#ifdef XCOFF_DEBUGGING_INFO
#include "xcoffout.h" /* Needed for external data
@ -114,9 +115,6 @@ static int high_function_linenum;
/* Filename of last NOTE. */
static const char *last_filename;
/* Number of instrumented arcs when profile_arc_flag is set. */
extern int count_instrumented_edges;
extern int length_unit_log; /* This is defined in insn-attrtab.c. */
/* Nonzero while outputting an `asm' with operands.
@ -198,6 +196,17 @@ static char *line_note_exists;
rtx current_insn_predicate;
#endif
struct function_list
{
struct function_list *next; /* next function */
const char *name; /* function name */
long cfg_checksum; /* function checksum */
long count_edges; /* number of intrumented edges in this function */
};
static struct function_list *functions_head = 0;
static struct function_list **functions_tail = &functions_head;
#ifdef HAVE_ATTR_length
static int asm_insn_count PARAMS ((rtx));
#endif
@ -237,7 +246,7 @@ init_final (filename)
}
/* Called at end of source file,
to output the block-profiling table for this entire compilation. */
to output the arc-profiling table for this entire compilation. */
void
end_final (filename)
@ -246,128 +255,268 @@ end_final (filename)
if (profile_arc_flag)
{
char name[20];
int align = exact_log2 (BIGGEST_ALIGNMENT / BITS_PER_UNIT);
int size, rounded;
int long_bytes = LONG_TYPE_SIZE / BITS_PER_UNIT;
int gcov_type_bytes = GCOV_TYPE_SIZE / BITS_PER_UNIT;
int pointer_bytes = POINTER_SIZE / BITS_PER_UNIT;
unsigned int align2 = LONG_TYPE_SIZE;
tree string_type, string_cst;
tree structure_decl, structure_value, structure_pointer_type;
tree field_decl, decl_chain, value_chain;
tree nwords_field_value, domain_type;
size = gcov_type_bytes * count_instrumented_edges;
rounded = size;
/* Build types. */
string_type = build_pointer_type (char_type_node);
rounded += (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1;
rounded = (rounded / (BIGGEST_ALIGNMENT / BITS_PER_UNIT)
* (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
/* Libgcc2 bb structure. */
structure_decl = make_node (RECORD_TYPE);
TYPE_PACKED (structure_decl) = flag_pack_struct;
structure_pointer_type = build_pointer_type (structure_decl);
/* ??? This _really_ ought to be done with a structure layout
and with assemble_constructor. If long_bytes != pointer_bytes
we'll be emitting unaligned data at some point. */
if (long_bytes != pointer_bytes)
abort ();
data_section ();
/* Output the main header, of 11 words:
0: 1 if this file is initialized, else 0.
1: address of file name (LPBX1).
2: address of table of counts (LPBX2).
3: number of counts in the table.
4: always 0, for compatibility with Sun.
/* Output the main header, of 7 words:
0: 1 if this file is initialized, else 0.
1: address of file name (LPBX1).
2: address of table of counts (LPBX2).
3: number of counts in the table.
4: always 0, libgcc2 uses this as a pointer to next ``struct bb''
The following are GNU extensions:
5: address of table of start addrs of basic blocks (LPBX3).
6: Number of bytes in this header.
7: address of table of function names (LPBX4).
8: address of table of line numbers (LPBX5) or 0.
9: address of table of file names (LPBX6) or 0.
10: space reserved for basic block profiling. */
5: Number of bytes in this header.
6: address of table of function checksums (LPBX7). */
ASM_OUTPUT_ALIGN (asm_out_file, align);
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 0);
/* Zero word. */
assemble_integer (const0_rtx, long_bytes, align2, 1);
/* The zero word. */
decl_chain =
build_decl (FIELD_DECL, get_identifier ("zero_word"),
long_integer_type_node);
value_chain = build_tree_list (decl_chain, integer_zero_node);
/* Address of filename. */
ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 1);
assemble_integer (gen_rtx_SYMBOL_REF (Pmode, name), pointer_bytes,
align2, 1);
/* Address of count table. */
ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 2);
assemble_integer (gen_rtx_SYMBOL_REF (Pmode, name), pointer_bytes,
align2, 1);
/* Count of the # of instrumented arcs. */
assemble_integer (GEN_INT (count_instrumented_edges),
long_bytes, align2, 1);
/* Zero word (link field). */
assemble_integer (const0_rtx, pointer_bytes, align2, 1);
assemble_integer (const0_rtx, pointer_bytes, align2, 1);
/* Byte count for extended structure. */
assemble_integer (GEN_INT (11 * UNITS_PER_WORD), long_bytes, align2, 1);
/* Address of function name table. */
assemble_integer (const0_rtx, pointer_bytes, align2, 1);
/* Address of line number and filename tables if debugging. */
assemble_integer (const0_rtx, pointer_bytes, align2, 1);
assemble_integer (const0_rtx, pointer_bytes, align2, 1);
/* Space for extension ptr (link field). */
assemble_integer (const0_rtx, UNITS_PER_WORD, align2, 1);
/* Output the file name changing the suffix to .d for
Sun tcov compatibility. */
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 1);
{
char *cwd = getpwd ();
int len = strlen (filename) + strlen (cwd) + 1;
char *data_file = (char *) alloca (len + 4);
int da_filename_len = strlen (filename) + strlen (cwd) + 4 + 1;
char *da_filename = (char *) alloca (da_filename_len);
strcpy (data_file, cwd);
strcat (data_file, "/");
strcat (data_file, filename);
strip_off_ending (data_file, len);
strcat (data_file, ".da");
assemble_string (data_file, strlen (data_file) + 1);
strcpy (da_filename, cwd);
strcat (da_filename, "/");
strcat (da_filename, filename);
strip_off_ending (da_filename, da_filename_len - 3);
strcat (da_filename, ".da");
field_decl =
build_decl (FIELD_DECL, get_identifier ("filename"), string_type);
string_cst = build_string (strlen (da_filename) + 1, da_filename);
domain_type = build_index_type (build_int_2 (strlen (da_filename) + 1,
0));
TREE_TYPE (string_cst) =
build_array_type (char_type_node, domain_type);
value_chain = tree_cons (field_decl,
build1 (ADDR_EXPR, string_type, string_cst),
value_chain);
TREE_CHAIN (field_decl) = decl_chain;
decl_chain = field_decl;
}
/* Make space for the table of counts. */
if (size == 0)
{
/* Realign data section. */
ASM_OUTPUT_ALIGN (asm_out_file, align);
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 2);
if (size != 0)
assemble_zeros (size);
}
else
{
ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 2);
#ifdef ASM_OUTPUT_SHARED_LOCAL
if (flag_shared_data)
ASM_OUTPUT_SHARED_LOCAL (asm_out_file, name, size, rounded);
else
#endif
#ifdef ASM_OUTPUT_ALIGNED_DECL_LOCAL
ASM_OUTPUT_ALIGNED_DECL_LOCAL (asm_out_file, NULL_TREE, name,
size, BIGGEST_ALIGNMENT);
#else
#ifdef ASM_OUTPUT_ALIGNED_LOCAL
ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size,
BIGGEST_ALIGNMENT);
#else
ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded);
#endif
#endif
}
/* Table of counts. */
{
tree gcov_type_type = make_unsigned_type (GCOV_TYPE_SIZE);
tree gcov_type_pointer_type = build_pointer_type (gcov_type_type);
tree gcov_type_array_type, gcov_type_array_pointer_type;
tree domain_tree = build_index_type (build_int_2
(profile_info.
count_instrumented_edges - 1,
0));
tree counts_table;
gcov_type_array_type = build_array_type (gcov_type_type, domain_tree);
gcov_type_array_pointer_type =
build_pointer_type (gcov_type_array_type);
/* No values. */
counts_table =
build (VAR_DECL, gcov_type_array_type, NULL_TREE, NULL_TREE);
TREE_STATIC (counts_table) = 1;
ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 2);
DECL_NAME (counts_table) = get_identifier (name);
assemble_variable (counts_table, 0, 0, 0);
field_decl =
build_decl (FIELD_DECL, get_identifier ("counts"),
gcov_type_pointer_type);
value_chain = tree_cons (field_decl,
build1 (ADDR_EXPR,
gcov_type_array_pointer_type,
counts_table), value_chain);
TREE_CHAIN (field_decl) = decl_chain;
decl_chain = field_decl;
}
/* Count of the # of instrumented arcs. */
field_decl =
build_decl (FIELD_DECL, get_identifier ("ncounts"),
long_integer_type_node);
value_chain = tree_cons (field_decl,
convert (long_integer_type_node,
build_int_2 (profile_info.
count_instrumented_edges,
0)), value_chain);
TREE_CHAIN (field_decl) = decl_chain;
decl_chain = field_decl;
/* Pointer to the next bb. */
field_decl =
build_decl (FIELD_DECL, get_identifier ("next"),
structure_pointer_type);
value_chain = tree_cons (field_decl, null_pointer_node, value_chain);
TREE_CHAIN (field_decl) = decl_chain;
decl_chain = field_decl;
/* Number of words. We'll set this after entire structure is laid out. */
field_decl =
build_decl (FIELD_DECL, get_identifier ("nwords"),
long_integer_type_node);
value_chain = nwords_field_value =
tree_cons (field_decl, NULL, value_chain);
TREE_CHAIN (field_decl) = decl_chain;
decl_chain = field_decl;
/* struct bb_function []. */
{
struct function_list *item;
int num_nodes;
tree checksum_field, arc_count_field, name_field;
tree domain;
tree array_value_chain = NULL_TREE;
tree bb_fn_struct_type;
tree bb_fn_struct_array_type;
tree bb_fn_struct_array_pointer_type;
tree bb_fn_struct_pointer_type;
tree field_value, field_value_chain;
bb_fn_struct_type = make_node (RECORD_TYPE);
TYPE_PACKED (bb_fn_struct_type) = flag_pack_struct;
checksum_field = build_decl (FIELD_DECL, get_identifier ("checksum"),
long_integer_type_node);
arc_count_field =
build_decl (FIELD_DECL, get_identifier ("arc_count"),
integer_type_node);
TREE_CHAIN (checksum_field) = arc_count_field;
name_field =
build_decl (FIELD_DECL, get_identifier ("name"), string_type);
TREE_CHAIN (arc_count_field) = name_field;
TYPE_FIELDS (bb_fn_struct_type) = checksum_field;
num_nodes = 0;
for (item = functions_head; item != 0; item = item->next)
num_nodes++;
/* Note that the array contains a terminator, hence no - 1. */
domain = build_index_type (build_int_2 (num_nodes, 0));
bb_fn_struct_pointer_type = build_pointer_type (bb_fn_struct_type);
bb_fn_struct_array_type = build_array_type (bb_fn_struct_type,
domain);
bb_fn_struct_array_pointer_type =
build_pointer_type (bb_fn_struct_array_type);
layout_type (bb_fn_struct_type);
layout_type (bb_fn_struct_pointer_type);
layout_type (bb_fn_struct_array_type);
layout_type (bb_fn_struct_array_pointer_type);
for (item = functions_head; item != 0; item = item->next)
{
/* create constructor for structure. */
field_value_chain = build_tree_list (checksum_field,
convert
(long_integer_type_node,
build_int_2 (item->
cfg_checksum,
0)));
field_value_chain =
tree_cons (arc_count_field,
convert (integer_type_node,
build_int_2 (item->count_edges, 0)),
field_value_chain);
string_cst = build_string (strlen (item->name) + 1, item->name);
domain_type = build_index_type (build_int_2 (strlen (item->name) +
1, 0));
TREE_TYPE (string_cst) = build_array_type (char_type_node,
domain_type);
field_value_chain = tree_cons (name_field,
build1 (ADDR_EXPR, string_type,
string_cst),
field_value_chain);
/* Add to chain. */
array_value_chain = tree_cons (NULL_TREE,
build (CONSTRUCTOR,
bb_fn_struct_type,
NULL_TREE,
nreverse
(field_value_chain)),
array_value_chain);
}
/* Add terminator. */
field_value = build_tree_list (arc_count_field,
convert (integer_type_node,
build_int_2 (-1, 0)));
array_value_chain = tree_cons (NULL_TREE,
build (CONSTRUCTOR, bb_fn_struct_type,
NULL_TREE, field_value),
array_value_chain);
/* Create constructor for array. */
field_decl =
build_decl (FIELD_DECL, get_identifier ("function_infos"),
bb_fn_struct_pointer_type);
value_chain = tree_cons (field_decl,
build1 (ADDR_EXPR,
bb_fn_struct_array_pointer_type,
build (CONSTRUCTOR,
bb_fn_struct_array_type,
NULL_TREE,
nreverse
(array_value_chain))),
value_chain);
TREE_CHAIN (field_decl) = decl_chain;
decl_chain = field_decl;
}
/* Finish structure. */
TYPE_FIELDS (structure_decl) = nreverse (decl_chain);
layout_type (structure_decl);
structure_value =
build (VAR_DECL, structure_decl, NULL_TREE, NULL_TREE);
DECL_INITIAL (structure_value) =
build (CONSTRUCTOR, structure_decl, NULL_TREE,
nreverse (value_chain));
TREE_STATIC (structure_value) = 1;
ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 0);
DECL_NAME (structure_value) = get_identifier (name);
/* Set number of words in this structure. */
TREE_VALUE (nwords_field_value) =
build_int_2 (TREE_INT_CST_LOW (TYPE_SIZE_UNIT (structure_decl)) /
(INT_TYPE_SIZE / BITS_PER_UNIT), 0);
/* Build structure. */
assemble_variable (structure_value, 0, 0, 0);
/* Offset to table of arc counters for thread-safe profiling. */
{
tree table_offset_var = make_node (VAR_DECL);
TREE_TYPE (table_offset_var) = build_pointer_type (integer_type_node);
DECL_INITIAL (table_offset_var) = integer_zero_node;
DECL_NAME (table_offset_var) = get_identifier (".LPBF0");
TREE_STATIC (table_offset_var) = 1;
assemble_variable (table_offset_var, 0, 0, 0);
}
}
}
@ -1781,6 +1930,24 @@ final (first, file, optimize, prescan)
insn = final_scan_insn (insn, file, optimize, prescan, 0);
}
/* Store function names for edge-profiling. */
if (profile_arc_flag)
{
struct function_list *new_item = xmalloc (sizeof (struct function_list));
/* Add function to linked list. */
new_item->next = 0;
*functions_tail = new_item;
functions_tail = &new_item->next;
/* Set values. */
new_item->cfg_checksum = profile_info.current_function_cfg_checksum;
new_item->count_edges = profile_info.count_edges_instrumented_now;
new_item->name = xstrdup (current_function_name);
}
free (line_note_exists);
line_note_exists = NULL;
}

View File

@ -6318,6 +6318,8 @@ prepare_function_start ()
current_function_outgoing_args_size = 0;
cfun->arc_profile = profile_arc_flag || flag_test_coverage;
(*lang_hooks.function.init) (cfun);
if (init_machine_status)
(*init_machine_status) (cfun);

View File

@ -437,6 +437,9 @@ struct function
generated. */
unsigned int instrument_entry_exit : 1;
/* Nonzero if no profiling should be done for the function. */
unsigned int arc_profile : 1;
/* Nonzero if profiling code should be generated. */
unsigned int profile : 1;

View File

@ -24,13 +24,24 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
#include <stdio.h>
#include <sys/types.h>
static int __fetch_long PARAMS ((long *, char *, size_t)) ATTRIBUTE_UNUSED;
static int __read_long PARAMS ((long *, FILE *, size_t)) ATTRIBUTE_UNUSED;
static int __write_long PARAMS ((long, FILE *, size_t)) ATTRIBUTE_UNUSED;
static int __fetch_gcov_type PARAMS ((gcov_type *, char *, size_t)) ATTRIBUTE_UNUSED;
static int __store_gcov_type PARAMS ((gcov_type, char *, size_t)) ATTRIBUTE_UNUSED;
static int __read_gcov_type PARAMS ((gcov_type *, FILE *, size_t)) ATTRIBUTE_UNUSED;
static int __write_gcov_type PARAMS ((gcov_type, FILE *, size_t)) ATTRIBUTE_UNUSED;
static int __fetch_long PARAMS ((long *, char *, size_t))
ATTRIBUTE_UNUSED;
static int __read_long PARAMS ((long *, FILE *, size_t))
ATTRIBUTE_UNUSED;
static int __write_long PARAMS ((long, FILE *, size_t))
ATTRIBUTE_UNUSED;
static int __fetch_gcov_type PARAMS ((gcov_type *, char *, size_t))
ATTRIBUTE_UNUSED;
static int __store_gcov_type PARAMS ((gcov_type, char *, size_t))
ATTRIBUTE_UNUSED;
static int __read_gcov_type PARAMS ((gcov_type *, FILE *, size_t))
ATTRIBUTE_UNUSED;
static int __write_gcov_type PARAMS ((gcov_type, FILE *, size_t))
ATTRIBUTE_UNUSED;
static int __write_gcov_string PARAMS ((const char *, size_t, FILE*, long))
ATTRIBUTE_UNUSED;
static int __read_gcov_string PARAMS ((char *, size_t, FILE*, long))
ATTRIBUTE_UNUSED;
/* These routines only work for signed values. */
@ -193,4 +204,94 @@ __read_long (dest, file, bytes)
return __fetch_long (dest, c, bytes);
}
/* Writes string in gcov format. */
static int
__write_gcov_string (string, length, file, delim)
const char *string;
size_t length;
FILE *file;
long delim;
{
size_t temp = length + 1;
/* delimiter */
if (__write_long (delim, file, 4) != 0)
return 1;
if (__write_long (length, file, 4) != 0)
return 1;
if (fwrite (string, temp, 1, file) != 1)
return 1;
temp &= 3;
if (temp)
{
char c[4];
c[0] = c[1] = c[2] = c[3] = 0;
if (fwrite (c, sizeof (char), 4 - temp, file) != 4 - temp)
return 1;
}
if (__write_long (delim, file, 4) != 0)
return 1;
return 0;
}
/* Reads string in gcov format. */
static int
__read_gcov_string (string, max_length, file, delim)
char *string;
size_t max_length;
FILE *file;
long delim;
{
long delim_from_file;
long length;
long read_length;
long tmp;
if (__read_long (&delim_from_file, file, 4) != 0)
return 1;
if (delim_from_file != delim)
return 1;
if (__read_long (&length, file, 4) != 0)
return 1;
if (length > (long) max_length)
read_length = max_length;
else
read_length = length;
tmp = (((length + 1) - 1) / 4 + 1) * 4;
/* This is the size occupied by the string in the file */
if (fread (string, read_length, 1, file) != 1)
return 1;
string[read_length] = 0;
if (fseek (file, tmp - read_length, SEEK_CUR) < 0)
return 1;
if (__read_long (&delim_from_file, file, 4) != 0)
return 1;
if (delim_from_file != delim)
return 1;
return 0;
}
#endif /* ! GCC_GCOV_IO_H */

View File

@ -232,6 +232,7 @@ static void print_usage PARAMS ((int)) ATTRIBUTE_NORETURN;
static void print_version PARAMS ((void)) ATTRIBUTE_NORETURN;
static void init_arc PARAMS ((struct adj_list *, int, int, struct bb_info *));
static struct adj_list *reverse_arcs PARAMS ((struct adj_list *));
static gcov_type *read_profile PARAMS ((char *, long, int));
static void create_program_flow_graph PARAMS ((struct bb_info_list *));
static void solve_program_flow_graph PARAMS ((struct bb_info_list *));
static void calculate_branch_probs PARAMS ((struct bb_info_list *, int,
@ -538,6 +539,130 @@ reverse_arcs (arcptr)
return prev;
}
/* Reads profiles from the .da file and compute a hybrid profile. */
static gcov_type *
read_profile (function_name, cfg_checksum, instr_arcs)
char *function_name;
long cfg_checksum;
int instr_arcs;
{
int i;
int okay = 1;
gcov_type *profile;
char *function_name_buffer;
int function_name_buffer_len;
profile = xmalloc (sizeof (gcov_type) * instr_arcs);
rewind (da_file);
function_name_buffer_len = strlen (function_name) + 1;
function_name_buffer = xmalloc (function_name_buffer_len + 1);
for (i = 0; i < instr_arcs; i++)
profile[i] = 0;
if (!da_file)
return profile;
while (1)
{
long magic, extra_bytes;
long func_count;
int i;
if (__read_long (&magic, da_file, 4) != 0)
break;
if (magic != -123)
{
okay = 0;
break;
}
if (__read_long (&func_count, da_file, 4) != 0)
{
okay = 0;
break;
}
if (__read_long (&extra_bytes, da_file, 4) != 0)
{
okay = 0;
break;
}
/* skip extra data emited by __bb_exit_func. */
fseek (da_file, extra_bytes, SEEK_CUR);
for (i = 0; i < func_count; i++)
{
long arc_count;
long chksum;
int j;
if (__read_gcov_string
(function_name_buffer, function_name_buffer_len, da_file,
-1) != 0)
{
okay = 0;
break;
}
if (__read_long (&chksum, da_file, 4) != 0)
{
okay = 0;
break;
}
if (__read_long (&arc_count, da_file, 4) != 0)
{
okay = 0;
break;
}
if (strcmp (function_name_buffer, function_name) != 0
|| arc_count != instr_arcs || chksum != cfg_checksum)
{
/* skip */
if (fseek (da_file, arc_count * 8, SEEK_CUR) < 0)
{
okay = 0;
break;
}
}
else
{
gcov_type tmp;
for (j = 0; j < arc_count; j++)
if (__read_gcov_type (&tmp, da_file, 8) != 0)
{
okay = 0;
break;
}
else
{
profile[j] += tmp;
}
}
}
if (!okay)
break;
}
free (function_name_buffer);
if (!okay)
{
fprintf (stderr, ".da file corrupted!\n");
free (profile);
abort ();
}
return profile;
}
/* Construct the program flow graph from the .bbg file, and read in the data
in the .da file. */
@ -550,6 +675,29 @@ create_program_flow_graph (bptr)
int i;
struct adj_list *arcptr;
struct bb_info *bb_graph;
long cfg_checksum;
long instr_arcs = 0;
gcov_type *profile;
int profile_pos = 0;
char *function_name;
long function_name_len, tmp;
/* Read function name. */
__read_long (&tmp, bbg_file, 4); /* ignore -1. */
__read_long (&function_name_len, bbg_file, 4);
function_name = xmalloc (function_name_len + 1);
fread (function_name, 1, function_name_len + 1, bbg_file);
/* Skip padding. */
tmp = (function_name_len + 1) % 4;
if (tmp)
fseek (bbg_file, 4 - tmp, SEEK_CUR);
__read_long (&tmp, bbg_file, 4); /* ignore -1. */
/* Read the cfg checksum. */
__read_long (&cfg_checksum, bbg_file, 4);
/* Read the number of blocks. */
__read_long (&num_blocks, bbg_file, 4);
@ -579,7 +727,10 @@ create_program_flow_graph (bptr)
init_arc (arcptr, src, dest, bb_graph);
__read_long (&flag_bits, bbg_file, 4);
arcptr->on_tree = flag_bits & 0x1;
if (flag_bits & 0x1)
arcptr->on_tree++;
else
instr_arcs++;
arcptr->fake = !! (flag_bits & 0x2);
arcptr->fall_through = !! (flag_bits & 0x4);
}
@ -601,6 +752,10 @@ create_program_flow_graph (bptr)
if (bb_graph[i].succ)
bb_graph[i].succ = reverse_arcs (bb_graph[i].succ);
/* Read profile from the .da file. */
profile = read_profile (function_name, cfg_checksum, instr_arcs);
/* For each arc not on the spanning tree, set its execution count from
the .da file. */
@ -613,15 +768,13 @@ create_program_flow_graph (bptr)
for (arcptr = bb_graph[i].succ; arcptr; arcptr = arcptr->succ_next)
if (! arcptr->on_tree)
{
gcov_type tmp_count = 0;
if (da_file && __read_gcov_type (&tmp_count, da_file, 8))
abort ();
arcptr->arc_count = tmp_count;
arcptr->arc_count = profile[profile_pos++];
arcptr->count_valid = 1;
bb_graph[i].succ_count--;
bb_graph[arcptr->target].pred_count--;
}
free (profile);
free (function_name);
}
static void
@ -755,12 +908,6 @@ read_files ()
struct stat buf;
struct bb_info_list *list_end = 0;
struct bb_info_list *b_ptr;
long total;
/* Read and ignore the first word of the .da file, which is the count of
how many numbers follow. */
if (da_file && __read_long (&total, da_file, 8))
abort ();
while (! feof (bbg_file))
{
@ -781,17 +928,6 @@ read_files ()
ungetc (getc (bbg_file), bbg_file);
}
/* Check to make sure the .da file data is valid. */
if (da_file)
{
if (feof (da_file))
fnotice (stderr, ".da file contents exhausted too early\n");
/* Should be at end of file now. */
if (__read_long (&total, da_file, 8) == 0)
fnotice (stderr, ".da file contents not exhausted\n");
}
/* Calculate all of the basic block execution counts and branch
taken probabilities. */

View File

@ -1238,12 +1238,11 @@ __eprintf (const char *string, const char *expression,
#ifdef L_bb
#if LONG_TYPE_SIZE == GCOV_TYPE_SIZE
typedef long gcov_type;
#else
typedef long long gcov_type;
#endif
struct bb_function_info {
long checksum;
int arc_count;
const char *name;
};
/* Structure emitted by -a */
struct bb
@ -1253,14 +1252,10 @@ struct bb
gcov_type *counts;
long ncounts;
struct bb *next;
const unsigned long *addresses;
/* Older GCC's did not emit these fields. */
long nwords;
const char **functions;
const long *line_nums;
const char **filenames;
char *flags;
struct bb_function_info *function_infos;
};
#ifdef BLOCK_PROFILER_CODE
@ -1283,39 +1278,66 @@ BLOCK_PROFILER_CODE
#include <errno.h>
#endif
#include <gthr.h>
static struct bb *bb_head;
int __global_counters = 0, __gthreads_active = 0;
void
__bb_exit_func (void)
{
FILE *da_file;
int i;
struct bb *ptr;
long n_counters_p = 0;
gcov_type max_counter_p = 0;
gcov_type sum_counters_p = 0;
if (bb_head == 0)
return;
i = strlen (bb_head->filename) - 3;
/* Calculate overall "statistics". */
for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next)
{
int firstchar;
int i;
/* Make sure the output file exists -
but don't clobber exiting data. */
if ((da_file = fopen (ptr->filename, "a")) != 0)
fclose (da_file);
n_counters_p += ptr->ncounts;
/* Need to re-open in order to be able to write from the start. */
da_file = fopen (ptr->filename, "r+b");
for (i = 0; i < ptr->ncounts; i++)
{
sum_counters_p += ptr->counts[i];
if (ptr->counts[i] > max_counter_p)
max_counter_p = ptr->counts[i];
}
}
for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next)
{
gcov_type max_counter_o = 0;
gcov_type sum_counters_o = 0;
int i;
/* Calculate the per-object statistics. */
for (i = 0; i < ptr->ncounts; i++)
{
sum_counters_o += ptr->counts[i];
if (ptr->counts[i] > max_counter_o)
max_counter_o = ptr->counts[i];
}
/* open the file for appending, creating it if necessary. */
da_file = fopen (ptr->filename, "ab");
/* Some old systems might not allow the 'b' mode modifier.
Therefore, try to open without it. This can lead to a race
condition so that when you delete and re-create the file, the
file might be opened in text mode, but then, you shouldn't
delete the file in the first place. */
if (da_file == 0)
da_file = fopen (ptr->filename, "r+");
da_file = fopen (ptr->filename, "a");
if (da_file == 0)
{
fprintf (stderr, "arc profiling: Can't open output file %s.\n",
@ -1341,92 +1363,96 @@ __bb_exit_func (void)
}
#endif
/* If the file is not empty, and the number of counts in it is the
same, then merge them in. */
firstchar = fgetc (da_file);
if (firstchar == EOF)
if (__write_long (-123, da_file, 4) != 0) /* magic */
{
if (ferror (da_file))
{
fprintf (stderr, "arc profiling: Can't read output file ");
perror (ptr->filename);
}
}
else
{
long n_counts = 0;
if (ungetc (firstchar, da_file) == EOF)
rewind (da_file);
if (__read_long (&n_counts, da_file, 8) != 0)
{
fprintf (stderr, "arc profiling: Can't read output file %s.\n",
ptr->filename);
continue;
}
if (n_counts == ptr->ncounts)
{
int i;
for (i = 0; i < n_counts; i++)
{
gcov_type v = 0;
if (__read_gcov_type (&v, da_file, 8) != 0)
{
fprintf (stderr,
"arc profiling: Can't read output file %s.\n",
ptr->filename);
break;
}
ptr->counts[i] += v;
}
}
}
rewind (da_file);
/* ??? Should first write a header to the file. Preferably, a 4 byte
magic number, 4 bytes containing the time the program was
compiled, 4 bytes containing the last modification time of the
source file, and 4 bytes indicating the compiler options used.
That way we can easily verify that the proper source/executable/
data file combination is being used from gcov. */
if (__write_gcov_type (ptr->ncounts, da_file, 8) != 0)
{
fprintf (stderr, "arc profiling: Error writing output file %s.\n",
ptr->filename);
}
else
{
int j;
struct bb_function_info *fn_info;
gcov_type *count_ptr = ptr->counts;
int ret = 0;
for (j = ptr->ncounts; j > 0; j--)
int i;
int count_functions = 0;
for (fn_info = ptr->function_infos; fn_info->arc_count != -1;
fn_info++)
count_functions++;
/* number of functions in this block. */
__write_long (count_functions, da_file, 4);
/* length of extra data in bytes. */
__write_long ((4 + 8 + 8) + (4 + 8 + 8), da_file, 4);
/* overall statistics. */
/* number of counters. */
__write_long (n_counters_p, da_file, 4);
/* sum of counters. */
__write_gcov_type (sum_counters_p, da_file, 8);
/* maximal counter. */
__write_gcov_type (max_counter_p, da_file, 8);
/* per-object statistics. */
/* number of counters. */
__write_long (ptr->ncounts, da_file, 4);
/* sum of counters. */
__write_gcov_type (sum_counters_o, da_file, 8);
/* maximal counter. */
__write_gcov_type (max_counter_o, da_file, 8);
/* write execution counts for each function. */
for (fn_info = ptr->function_infos; fn_info->arc_count != -1;
fn_info++)
{
if (__write_gcov_type (*count_ptr, da_file, 8) != 0)
/* new function. */
if (__write_gcov_string
(fn_info->name, strlen (fn_info->name), da_file, -1) != 0)
{
ret = 1;
fprintf (stderr,
"arc profiling: Error writing output file %s.\n",
ptr->filename);
break;
}
if (__write_long (fn_info->checksum, da_file, 4) != 0)
{
fprintf (stderr,
"arc profiling: Error writing output file %s.\n",
ptr->filename);
break;
}
if (__write_long (fn_info->arc_count, da_file, 4) != 0)
{
fprintf (stderr,
"arc profiling: Error writing output file %s.\n",
ptr->filename);
break;
}
for (i = fn_info->arc_count; i > 0; i--, count_ptr++)
{
if (__write_gcov_type (*count_ptr, da_file, 8) != 0)
break;
}
if (i) /* there was an error */
{
fprintf (stderr,
"arc profiling: Error writing output file %s.\n",
ptr->filename);
break;
}
count_ptr++;
}
if (ret)
fprintf (stderr, "arc profiling: Error writing output file %s.\n",
ptr->filename);
}
if (fclose (da_file) == EOF)
if (fclose (da_file) != 0)
fprintf (stderr, "arc profiling: Error closing output file %s.\n",
ptr->filename);
}
return;
}
void
@ -1437,8 +1463,8 @@ __bb_init_func (struct bb *blocks)
if (blocks->zero_word)
return;
/* Initialize destructor. */
/* Initialize destructor and per-thread data. */
if (!bb_head)
atexit (__bb_exit_func);
@ -1451,7 +1477,7 @@ __bb_init_func (struct bb *blocks)
/* Called before fork or exec - write out profile information gathered so
far and reset it to zero. This avoids duplication or loss of the
profile information gathered so far. */
void
void
__bb_fork_func (void)
{
struct bb *ptr;

View File

@ -31,9 +31,14 @@ struct bb;
extern void __bb_exit_func (void);
extern void __bb_init_func (struct bb *);
extern void __bb_fork_func (void);
extern void __bb_trace_func (void);
extern void __bb_trace_ret (void);
extern void __bb_init_trace_func (struct bb *, unsigned long);
#if LONG_TYPE_SIZE == GCOV_TYPE_SIZE
typedef long gcov_type;
#else
typedef long long gcov_type;
#endif
extern gcov_type *__bb_find_arc_counters (void);
struct exception_descriptor;
extern short int __get_eh_table_language (struct exception_descriptor *);

View File

@ -49,6 +49,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
#include "basic-block.h"
#include "gcov-io.h"
#include "target.h"
#include "profile.h"
#include "libfuncs.h"
#include "langhooks.h"
/* Additional information about the edges we need. */
@ -92,11 +94,6 @@ static FILE *bb_file;
static char *last_bb_file_name;
/* Used by final, for allocating the proper amount of storage for the
instrumented arc execution counts. */
int count_instrumented_edges;
/* Collect statistics on the performance of this pass for the entire source
file. */
@ -118,6 +115,8 @@ static rtx gen_edge_profiler PARAMS ((int));
static void instrument_edges PARAMS ((struct edge_list *));
static void output_gcov_string PARAMS ((const char *, long));
static void compute_branch_probabilities PARAMS ((void));
static gcov_type * get_exec_counts PARAMS ((void));
static long compute_checksum PARAMS ((void));
static basic_block find_group PARAMS ((basic_block));
static void union_groups PARAMS ((basic_block, basic_block));
@ -163,8 +162,9 @@ instrument_edges (el)
}
}
profile_info.count_edges_instrumented_now = num_instr_edges;
total_num_edges_instrumented += num_instr_edges;
count_instrumented_edges = total_num_edges_instrumented;
profile_info.count_instrumented_edges = total_num_edges_instrumented;
total_num_blocks_created += num_edges;
if (rtl_dump_file)
@ -205,6 +205,167 @@ output_gcov_string (string, delimiter)
}
/* Computes hybrid profile for all matching entries in da_file.
Sets max_counter_in_program as a side effect. */
static gcov_type *
get_exec_counts ()
{
int num_edges = 0;
int i;
int okay = 1;
int mismatch = 0;
gcov_type *profile;
char *function_name_buffer;
int function_name_buffer_len;
gcov_type max_counter_in_run;
profile_info.max_counter_in_program = 0;
profile_info.count_profiles_merged = 0;
/* No .da file, no execution counts. */
if (!da_file)
return 0;
/* Count the edges to be (possibly) instrumented. */
for (i = 0; i < n_basic_blocks + 2; i++)
{
basic_block bb = GCOV_INDEX_TO_BB (i);
edge e;
for (e = bb->succ; e; e = e->succ_next)
if (!EDGE_INFO (e)->ignore && !EDGE_INFO (e)->on_tree)
{
num_edges++;
}
}
/* now read and combine all matching profiles. */
profile = xmalloc (sizeof (gcov_type) * num_edges);
rewind (da_file);
function_name_buffer_len = strlen (current_function_name) + 1;
function_name_buffer = xmalloc (function_name_buffer_len + 1);
for (i = 0; i < num_edges; i++)
profile[i] = 0;
while (1)
{
long magic, extra_bytes;
long func_count;
int i;
if (__read_long (&magic, da_file, 4) != 0)
break;
if (magic != -123)
{
okay = 0;
break;
}
if (__read_long (&func_count, da_file, 4) != 0)
{
okay = 0;
break;
}
if (__read_long (&extra_bytes, da_file, 4) != 0)
{
okay = 0;
break;
}
fseek (da_file, 4 + 8, SEEK_CUR);
/* read the maximal counter. */
__read_gcov_type (&max_counter_in_run, da_file, 8);
/* skip the rest of "statistics" emited by __bb_exit_func. */
fseek (da_file, extra_bytes - (4 + 8 + 8), SEEK_CUR);
for (i = 0; i < func_count; i++)
{
long arc_count;
long chksum;
int j;
if (__read_gcov_string
(function_name_buffer, function_name_buffer_len, da_file,
-1) != 0)
{
okay = 0;
break;
}
if (__read_long (&chksum, da_file, 4) != 0)
{
okay = 0;
break;
}
if (__read_long (&arc_count, da_file, 4) != 0)
{
okay = 0;
break;
}
if (strcmp (function_name_buffer, current_function_name) != 0)
{
/* skip */
if (fseek (da_file, arc_count * 8, SEEK_CUR) < 0)
{
okay = 0;
break;
}
}
else if (arc_count != num_edges
|| chksum != profile_info.current_function_cfg_checksum)
okay = 0, mismatch = 1;
else
{
gcov_type tmp;
profile_info.max_counter_in_program += max_counter_in_run;
profile_info.count_profiles_merged++;
for (j = 0; j < arc_count; j++)
if (__read_gcov_type (&tmp, da_file, 8) != 0)
{
okay = 0;
break;
}
else
{
profile[j] += tmp;
}
}
}
if (!okay)
break;
}
free (function_name_buffer);
if (!okay)
{
if (mismatch)
error
("Profile does not match flowgraph of function %s (out of date?)",
current_function_name);
else
error (".da file corrupted");
free (profile);
return 0;
}
return profile;
}
/* Compute the branch probabilities for the various branches.
Annotate them accordingly. */
@ -218,6 +379,8 @@ compute_branch_probabilities ()
int hist_br_prob[20];
int num_never_executed;
int num_branches;
gcov_type *exec_counts = get_exec_counts ();
int exec_counts_pos = 0;
/* Attach extra info block to each bb. */
@ -253,14 +416,13 @@ compute_branch_probabilities ()
if (!EDGE_INFO (e)->ignore && !EDGE_INFO (e)->on_tree)
{
num_edges++;
if (da_file)
if (exec_counts)
{
gcov_type value;
__read_gcov_type (&value, da_file, 8);
e->count = value;
e->count = exec_counts[exec_counts_pos++];
}
else
e->count = 0;
EDGE_INFO (e)->count_valid = 1;
BB_INFO (bb)->succ_count--;
BB_INFO (e->dest)->pred_count--;
@ -517,6 +679,36 @@ compute_branch_probabilities ()
}
free_aux_for_blocks ();
if (exec_counts)
free (exec_counts);
}
/* Compute checksum for the current function. */
#define CHSUM_HASH 500000003
#define CHSUM_SHIFT 2
static long
compute_checksum ()
{
long chsum = 0;
int i;
for (i = 0; i < n_basic_blocks ; i++)
{
basic_block bb = BASIC_BLOCK (i);
edge e;
for (e = bb->succ; e; e = e->succ_next)
{
chsum = ((chsum << CHSUM_SHIFT) + (BB_TO_GCOV_INDEX (e->dest) + 1)) % CHSUM_HASH;
}
chsum = (chsum << CHSUM_SHIFT) % CHSUM_HASH;
}
return chsum;
}
/* Instrument and/or analyze program behavior based on program flow graph.
@ -542,6 +734,12 @@ branch_prob ()
int num_edges, ignored_edges;
struct edge_list *el;
profile_info.current_function_cfg_checksum = compute_checksum ();
if (rtl_dump_file)
fprintf (rtl_dump_file, "CFG checksum is %ld\n",
profile_info.current_function_cfg_checksum);
/* Start of a function. */
if (flag_test_coverage)
output_gcov_string (current_function_name, (long) -2);
@ -758,6 +956,12 @@ branch_prob ()
{
int flag_bits;
__write_gcov_string (current_function_name,
strlen (current_function_name), bbg_file, -1);
/* write checksum. */
__write_long (profile_info.current_function_cfg_checksum, bbg_file, 4);
/* The plus 2 stands for entry and exit block. */
__write_long (n_basic_blocks + 2, bbg_file, 4);
__write_long (num_edges - ignored_edges + 1, bbg_file, 4);
@ -884,14 +1088,21 @@ find_spanning_tree (el)
/* Add fake edge exit to entry we can't instrument. */
union_groups (EXIT_BLOCK_PTR, ENTRY_BLOCK_PTR);
/* First add all abnormal edges to the tree unless they form an cycle. */
/* First add all abnormal edges to the tree unless they form an cycle. Also
add all edges to EXIT_BLOCK_PTR to avoid inserting profiling code behind
setting return value from function. */
for (i = 0; i < num_edges; i++)
{
edge e = INDEX_EDGE (el, i);
if ((e->flags & (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL | EDGE_FAKE))
if (((e->flags & (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL | EDGE_FAKE))
|| e->dest == EXIT_BLOCK_PTR
)
&& !EDGE_INFO (e)->ignore
&& (find_group (e->src) != find_group (e->dest)))
{
if (rtl_dump_file)
fprintf (rtl_dump_file, "Abnormal edge %d to %d put to tree\n",
e->src->index, e->dest->index);
EDGE_INFO (e)->on_tree = 1;
union_groups (e->src, e->dest);
}
@ -905,6 +1116,9 @@ find_spanning_tree (el)
&& !EDGE_INFO (e)->ignore
&& (find_group (e->src) != find_group (e->dest)))
{
if (rtl_dump_file)
fprintf (rtl_dump_file, "Critical edge %d to %d put to tree\n",
e->src->index, e->dest->index);
EDGE_INFO (e)->on_tree = 1;
union_groups (e->src, e->dest);
}
@ -917,6 +1131,9 @@ find_spanning_tree (el)
if (find_group (e->src) != find_group (e->dest)
&& !EDGE_INFO (e)->ignore)
{
if (rtl_dump_file)
fprintf (rtl_dump_file, "Normal edge %d to %d put to tree\n",
e->src->index, e->dest->index);
EDGE_INFO (e)->on_tree = 1;
union_groups (e->src, e->dest);
}
@ -975,12 +1192,6 @@ init_branch_prob (filename)
if ((da_file = fopen (da_file_name, "rb")) == 0)
warning ("file %s not found, execution counts assumed to be zero",
da_file_name);
/* The first word in the .da file gives the number of instrumented
edges, which is not needed for our purposes. */
if (da_file)
__read_long (&len, da_file, 8);
}
if (profile_arc_flag)
@ -1011,22 +1222,8 @@ end_branch_prob ()
fclose (bbg_file);
}
if (flag_branch_probabilities)
{
if (da_file)
{
long temp;
/* This seems slightly dangerous, as it presumes the EOF
flag will not be set until an attempt is made to read
past the end of the file. */
if (feof (da_file))
error (".da file contents exhausted too early");
/* Should be at end of file now. */
if (__read_long (&temp, da_file, 8) == 0)
error (".da file contents not exhausted");
fclose (da_file);
}
}
if (flag_branch_probabilities && da_file)
fclose (da_file);
if (rtl_dump_file)
{
@ -1097,6 +1294,8 @@ gen_edge_profiler (edgeno)
tmp = expand_simple_binop (mode, PLUS, mem_ref, const1_rtx,
mem_ref, 0, OPTAB_WIDEN);
set_mem_alias_set (mem_ref, new_alias_set ());
if (tmp != mem_ref)
emit_move_insn (copy_rtx (mem_ref), tmp);
@ -1118,9 +1317,6 @@ output_func_start_profiler ()
rtx table_address;
enum machine_mode mode = mode_for_size (GCOV_TYPE_SIZE, MODE_INT, 0);
int save_flag_inline_functions = flag_inline_functions;
int save_flag_test_coverage = flag_test_coverage;
int save_profile_arc_flag = profile_arc_flag;
int save_flag_branch_probabilities = flag_branch_probabilities;
/* It's either already been output, or we don't need it because we're
not doing profile-edges. */
@ -1163,6 +1359,7 @@ output_func_start_profiler ()
init_function_start (fndecl, input_filename, lineno);
(*lang_hooks.decls.pushlevel) (0);
expand_function_start (fndecl, 0);
cfun->arc_profile = 0;
/* Actually generate the code to call __bb_init_func. */
ASM_GENERATE_INTERNAL_LABEL (buf, "LPBX", 0);
@ -1179,20 +1376,10 @@ output_func_start_profiler ()
flag_inline_functions. */
flag_inline_functions = 0;
/* Don't instrument the function that turns on instrumentation. Which
is also handy since we'd get silly warnings about not consuming all
of our da_file input. */
flag_test_coverage = 0;
profile_arc_flag = 0;
flag_branch_probabilities = 0;
rest_of_compilation (fndecl);
/* Reset flag_inline_functions to its original value. */
flag_inline_functions = save_flag_inline_functions;
flag_test_coverage = save_flag_test_coverage;
profile_arc_flag = save_profile_arc_flag;
flag_branch_probabilities = save_flag_branch_probabilities;
if (! quiet_flag)
fflush (asm_out_file);

View File

@ -364,6 +364,11 @@ int profile_flag = 0;
int profile_arc_flag = 0;
/* Nonzero if we should not attempt to generate thread-safe
code to profile program flow graph arcs. */
int flag_unsafe_profile_arcs = 0;
/* Nonzero if generating info for gcov to calculate line test coverage. */
int flag_test_coverage = 0;
@ -1061,6 +1066,8 @@ static const lang_independent_options f_options[] =
N_("Support synchronous non-call exceptions") },
{"profile-arcs", &profile_arc_flag, 1,
N_("Insert arc based program profiling code") },
{"unsafe-profile-arcs", &flag_unsafe_profile_arcs, 1,
N_("Avoid thread safety profiling overhead") },
{"test-coverage", &flag_test_coverage, 1,
N_("Create data files needed by gcov") },
{"branch-probabilities", &flag_branch_probabilities, 1,
@ -2891,14 +2898,13 @@ rest_of_compilation (decl)
close_dump_file (DFI_cfg, print_rtl_with_bb, insns);
/* Do branch profiling and static profile estimation passes. */
if (optimize > 0 || profile_arc_flag || flag_test_coverage
|| flag_branch_probabilities)
if (optimize > 0 || cfun->arc_profile || flag_branch_probabilities)
{
struct loops loops;
timevar_push (TV_BRANCH_PROB);
open_dump_file (DFI_bp, decl);
if (profile_arc_flag || flag_test_coverage || flag_branch_probabilities)
if (cfun->arc_profile || flag_branch_probabilities)
branch_prob ();
/* Discover and record the loop depth at the head of each basic