Commit b7c9bf28 by Jan Hubicka Committed by Jan Hubicka

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
parent 786de7eb
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> 2002-05-09 Kazu Hirata <kazu@cs.umass.edu>
* sbitmap.c: Fix formatting. * sbitmap.c: Fix formatting.
......
...@@ -1484,7 +1484,7 @@ conflict.o : conflict.c $(CONFIG_H) $(SYSTEM_H) $(OBSTACK_H) $(HASHTAB_H) \ ...@@ -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 \ 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 \ 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) \ 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) \ 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) \ insn-config.h $(REGS_H) hard-reg-set.h $(RECOG_H) $(EXPR_H) \
real.h $(PREDICT_H) $(BASIC_BLOCK_H) function.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 \ ...@@ -1568,7 +1568,7 @@ sched-vis.o : sched-vis.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) sched-int.h \
$(TARGET_H) real.h $(TARGET_H) real.h
final.o : final.c $(CONFIG_H) $(SYSTEM_H) $(RTL_H) $(TREE_H) flags.h intl.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 \ $(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) 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) \ 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 \ $(REGS_H) $(RECOG_H) $(EXPR_H) hard-reg-set.h flags.h insn-config.h \
......
...@@ -363,6 +363,8 @@ program flow. ...@@ -363,6 +363,8 @@ program flow.
In the @file{.bbg} file, the format is: In the @file{.bbg} file, the format is:
@smallexample @smallexample
name of function #0
checksum of function #0
number of basic blocks for function #0 (4-byte number) number of basic blocks for function #0 (4-byte number)
total number of arcs 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) 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 ...@@ -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 list of basic blocks, and to verify that the file has been read
correctly. 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 The @file{.da} file is generated when a program containing object files
built with the GCC @option{-fprofile-arcs} option is executed. A built with the GCC @option{-fprofile-arcs} option is executed. A
separate @file{.da} file is created for each source file compiled with 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 ...@@ -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 absolute pathname in the resulting object file. This path name is
derived from the source file name by substituting a @file{.da} suffix. 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 The @file{.da} consists of several blocks (one for each run) with the following structure:
number is the number of counts in the file, followed by the counts @smallexample
(stored as 8-byte numbers). Each count corresponds to the number of "magic" number @minus{}123 (4-byte number)
times each arc in the program is executed. The counts are cumulative; number of functions (4-byte number)
each time the program is executed, it attempts to combine the existing length of the "extension block" in bytes
@file{.da} files with the new counts for this invocation of the extension block (variable length)
program. It ignores the contents of any @file{.da} files whose number of name of function #0 (the same format as in .bbg file)
arcs doesn't correspond to the current program, and merely overwrites checksum of function #0
them instead. 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 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 integers; the functions in this header provide a machine-independent
......
...@@ -68,6 +68,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA ...@@ -68,6 +68,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
#include "target.h" #include "target.h"
#include "debug.h" #include "debug.h"
#include "expr.h" #include "expr.h"
#include "profile.h"
#ifdef XCOFF_DEBUGGING_INFO #ifdef XCOFF_DEBUGGING_INFO
#include "xcoffout.h" /* Needed for external data #include "xcoffout.h" /* Needed for external data
...@@ -114,9 +115,6 @@ static int high_function_linenum; ...@@ -114,9 +115,6 @@ static int high_function_linenum;
/* Filename of last NOTE. */ /* Filename of last NOTE. */
static const char *last_filename; 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. */ extern int length_unit_log; /* This is defined in insn-attrtab.c. */
/* Nonzero while outputting an `asm' with operands. /* Nonzero while outputting an `asm' with operands.
...@@ -198,6 +196,17 @@ static char *line_note_exists; ...@@ -198,6 +196,17 @@ static char *line_note_exists;
rtx current_insn_predicate; rtx current_insn_predicate;
#endif #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 #ifdef HAVE_ATTR_length
static int asm_insn_count PARAMS ((rtx)); static int asm_insn_count PARAMS ((rtx));
#endif #endif
...@@ -237,7 +246,7 @@ init_final (filename) ...@@ -237,7 +246,7 @@ init_final (filename)
} }
/* Called at end of source file, /* 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 void
end_final (filename) end_final (filename)
...@@ -246,127 +255,267 @@ end_final (filename) ...@@ -246,127 +255,267 @@ end_final (filename)
if (profile_arc_flag) if (profile_arc_flag)
{ {
char name[20]; char name[20];
int align = exact_log2 (BIGGEST_ALIGNMENT / BITS_PER_UNIT); tree string_type, string_cst;
int size, rounded; tree structure_decl, structure_value, structure_pointer_type;
int long_bytes = LONG_TYPE_SIZE / BITS_PER_UNIT; tree field_decl, decl_chain, value_chain;
int gcov_type_bytes = GCOV_TYPE_SIZE / BITS_PER_UNIT; tree nwords_field_value, domain_type;
int pointer_bytes = POINTER_SIZE / BITS_PER_UNIT;
unsigned int align2 = LONG_TYPE_SIZE;
size = gcov_type_bytes * count_instrumented_edges;
rounded = size;
rounded += (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1;
rounded = (rounded / (BIGGEST_ALIGNMENT / BITS_PER_UNIT)
* (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
/* ??? 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 (); /* Build types. */
string_type = build_pointer_type (char_type_node);
/* Libgcc2 bb structure. */
structure_decl = make_node (RECORD_TYPE);
TYPE_PACKED (structure_decl) = flag_pack_struct;
structure_pointer_type = build_pointer_type (structure_decl);
/* Output the main header, of 11 words: /* Output the main header, of 7 words:
0: 1 if this file is initialized, else 0. 0: 1 if this file is initialized, else 0.
1: address of file name (LPBX1). 1: address of file name (LPBX1).
2: address of table of counts (LPBX2). 2: address of table of counts (LPBX2).
3: number of counts in the table. 3: number of counts in the table.
4: always 0, for compatibility with Sun. 4: always 0, libgcc2 uses this as a pointer to next ``struct bb''
The following are GNU extensions: The following are GNU extensions:
5: address of table of start addrs of basic blocks (LPBX3). 5: Number of bytes in this header.
6: Number of bytes in this header. 6: address of table of function checksums (LPBX7). */
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. */
ASM_OUTPUT_ALIGN (asm_out_file, align);
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 0); /* The zero word. */
decl_chain =
/* Zero word. */ build_decl (FIELD_DECL, get_identifier ("zero_word"),
assemble_integer (const0_rtx, long_bytes, align2, 1); long_integer_type_node);
value_chain = build_tree_list (decl_chain, integer_zero_node);
/* Address of filename. */ /* 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 (); char *cwd = getpwd ();
int len = strlen (filename) + strlen (cwd) + 1; int da_filename_len = strlen (filename) + strlen (cwd) + 4 + 1;
char *data_file = (char *) alloca (len + 4); char *da_filename = (char *) alloca (da_filename_len);
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;
}
/* 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);
strcpy (data_file, cwd); field_decl =
strcat (data_file, "/"); build_decl (FIELD_DECL, get_identifier ("counts"),
strcat (data_file, filename); gcov_type_pointer_type);
strip_off_ending (data_file, len); value_chain = tree_cons (field_decl,
strcat (data_file, ".da"); build1 (ADDR_EXPR,
assemble_string (data_file, strlen (data_file) + 1); gcov_type_array_pointer_type,
counts_table), value_chain);
TREE_CHAIN (field_decl) = decl_chain;
decl_chain = field_decl;
} }
/* Make space for the table of counts. */ /* Count of the # of instrumented arcs. */
if (size == 0) field_decl =
{ build_decl (FIELD_DECL, get_identifier ("ncounts"),
/* Realign data section. */ long_integer_type_node);
ASM_OUTPUT_ALIGN (asm_out_file, align); value_chain = tree_cons (field_decl,
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 2); convert (long_integer_type_node,
if (size != 0) build_int_2 (profile_info.
assemble_zeros (size); count_instrumented_edges,
} 0)), value_chain);
else TREE_CHAIN (field_decl) = decl_chain;
{ decl_chain = field_decl;
ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 2);
#ifdef ASM_OUTPUT_SHARED_LOCAL /* Pointer to the next bb. */
if (flag_shared_data) field_decl =
ASM_OUTPUT_SHARED_LOCAL (asm_out_file, name, size, rounded); build_decl (FIELD_DECL, get_identifier ("next"),
else structure_pointer_type);
#endif value_chain = tree_cons (field_decl, null_pointer_node, value_chain);
#ifdef ASM_OUTPUT_ALIGNED_DECL_LOCAL TREE_CHAIN (field_decl) = decl_chain;
ASM_OUTPUT_ALIGNED_DECL_LOCAL (asm_out_file, NULL_TREE, name, decl_chain = field_decl;
size, BIGGEST_ALIGNMENT);
#else /* Number of words. We'll set this after entire structure is laid out. */
#ifdef ASM_OUTPUT_ALIGNED_LOCAL field_decl =
ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size, build_decl (FIELD_DECL, get_identifier ("nwords"),
BIGGEST_ALIGNMENT); long_integer_type_node);
#else value_chain = nwords_field_value =
ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded); tree_cons (field_decl, NULL, value_chain);
#endif TREE_CHAIN (field_decl) = decl_chain;
#endif 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) ...@@ -1781,6 +1930,24 @@ final (first, file, optimize, prescan)
insn = final_scan_insn (insn, file, optimize, prescan, 0); 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); free (line_note_exists);
line_note_exists = NULL; line_note_exists = NULL;
} }
......
...@@ -6318,6 +6318,8 @@ prepare_function_start () ...@@ -6318,6 +6318,8 @@ prepare_function_start ()
current_function_outgoing_args_size = 0; current_function_outgoing_args_size = 0;
cfun->arc_profile = profile_arc_flag || flag_test_coverage;
(*lang_hooks.function.init) (cfun); (*lang_hooks.function.init) (cfun);
if (init_machine_status) if (init_machine_status)
(*init_machine_status) (cfun); (*init_machine_status) (cfun);
......
...@@ -437,6 +437,9 @@ struct function ...@@ -437,6 +437,9 @@ struct function
generated. */ generated. */
unsigned int instrument_entry_exit : 1; 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. */ /* Nonzero if profiling code should be generated. */
unsigned int profile : 1; unsigned int profile : 1;
......
...@@ -24,13 +24,24 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA ...@@ -24,13 +24,24 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
#include <stdio.h> #include <stdio.h>
#include <sys/types.h> #include <sys/types.h>
static int __fetch_long PARAMS ((long *, char *, size_t)) ATTRIBUTE_UNUSED; static int __fetch_long PARAMS ((long *, char *, size_t))
static int __read_long PARAMS ((long *, FILE *, size_t)) ATTRIBUTE_UNUSED; ATTRIBUTE_UNUSED;
static int __write_long PARAMS ((long, FILE *, size_t)) ATTRIBUTE_UNUSED; static int __read_long PARAMS ((long *, FILE *, size_t))
static int __fetch_gcov_type PARAMS ((gcov_type *, char *, size_t)) ATTRIBUTE_UNUSED; ATTRIBUTE_UNUSED;
static int __store_gcov_type PARAMS ((gcov_type, char *, size_t)) ATTRIBUTE_UNUSED; static int __write_long PARAMS ((long, FILE *, size_t))
static int __read_gcov_type PARAMS ((gcov_type *, FILE *, size_t)) ATTRIBUTE_UNUSED; ATTRIBUTE_UNUSED;
static int __write_gcov_type PARAMS ((gcov_type, 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. */ /* These routines only work for signed values. */
...@@ -193,4 +204,94 @@ __read_long (dest, file, bytes) ...@@ -193,4 +204,94 @@ __read_long (dest, file, bytes)
return __fetch_long (dest, c, 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 */ #endif /* ! GCC_GCOV_IO_H */
...@@ -232,6 +232,7 @@ static void print_usage PARAMS ((int)) ATTRIBUTE_NORETURN; ...@@ -232,6 +232,7 @@ static void print_usage PARAMS ((int)) ATTRIBUTE_NORETURN;
static void print_version PARAMS ((void)) ATTRIBUTE_NORETURN; static void print_version PARAMS ((void)) ATTRIBUTE_NORETURN;
static void init_arc PARAMS ((struct adj_list *, int, int, struct bb_info *)); static void init_arc PARAMS ((struct adj_list *, int, int, struct bb_info *));
static struct adj_list *reverse_arcs PARAMS ((struct adj_list *)); 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 create_program_flow_graph PARAMS ((struct bb_info_list *));
static void solve_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, static void calculate_branch_probs PARAMS ((struct bb_info_list *, int,
...@@ -538,6 +539,130 @@ reverse_arcs (arcptr) ...@@ -538,6 +539,130 @@ reverse_arcs (arcptr)
return prev; 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 /* Construct the program flow graph from the .bbg file, and read in the data
in the .da file. */ in the .da file. */
...@@ -550,6 +675,29 @@ create_program_flow_graph (bptr) ...@@ -550,6 +675,29 @@ create_program_flow_graph (bptr)
int i; int i;
struct adj_list *arcptr; struct adj_list *arcptr;
struct bb_info *bb_graph; 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 the number of blocks. */
__read_long (&num_blocks, bbg_file, 4); __read_long (&num_blocks, bbg_file, 4);
...@@ -579,7 +727,10 @@ create_program_flow_graph (bptr) ...@@ -579,7 +727,10 @@ create_program_flow_graph (bptr)
init_arc (arcptr, src, dest, bb_graph); init_arc (arcptr, src, dest, bb_graph);
__read_long (&flag_bits, bbg_file, 4); __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->fake = !! (flag_bits & 0x2);
arcptr->fall_through = !! (flag_bits & 0x4); arcptr->fall_through = !! (flag_bits & 0x4);
} }
...@@ -601,6 +752,10 @@ create_program_flow_graph (bptr) ...@@ -601,6 +752,10 @@ create_program_flow_graph (bptr)
if (bb_graph[i].succ) if (bb_graph[i].succ)
bb_graph[i].succ = reverse_arcs (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 /* For each arc not on the spanning tree, set its execution count from
the .da file. */ the .da file. */
...@@ -613,15 +768,13 @@ create_program_flow_graph (bptr) ...@@ -613,15 +768,13 @@ create_program_flow_graph (bptr)
for (arcptr = bb_graph[i].succ; arcptr; arcptr = arcptr->succ_next) for (arcptr = bb_graph[i].succ; arcptr; arcptr = arcptr->succ_next)
if (! arcptr->on_tree) if (! arcptr->on_tree)
{ {
gcov_type tmp_count = 0; arcptr->arc_count = profile[profile_pos++];
if (da_file && __read_gcov_type (&tmp_count, da_file, 8))
abort ();
arcptr->arc_count = tmp_count;
arcptr->count_valid = 1; arcptr->count_valid = 1;
bb_graph[i].succ_count--; bb_graph[i].succ_count--;
bb_graph[arcptr->target].pred_count--; bb_graph[arcptr->target].pred_count--;
} }
free (profile);
free (function_name);
} }
static void static void
...@@ -755,12 +908,6 @@ read_files () ...@@ -755,12 +908,6 @@ read_files ()
struct stat buf; struct stat buf;
struct bb_info_list *list_end = 0; struct bb_info_list *list_end = 0;
struct bb_info_list *b_ptr; 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)) while (! feof (bbg_file))
{ {
...@@ -781,17 +928,6 @@ read_files () ...@@ -781,17 +928,6 @@ read_files ()
ungetc (getc (bbg_file), bbg_file); 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 /* Calculate all of the basic block execution counts and branch
taken probabilities. */ taken probabilities. */
......
...@@ -1238,12 +1238,11 @@ __eprintf (const char *string, const char *expression, ...@@ -1238,12 +1238,11 @@ __eprintf (const char *string, const char *expression,
#ifdef L_bb #ifdef L_bb
#if LONG_TYPE_SIZE == GCOV_TYPE_SIZE struct bb_function_info {
typedef long gcov_type; long checksum;
#else int arc_count;
typedef long long gcov_type; const char *name;
#endif };
/* Structure emitted by -a */ /* Structure emitted by -a */
struct bb struct bb
...@@ -1253,14 +1252,10 @@ struct bb ...@@ -1253,14 +1252,10 @@ struct bb
gcov_type *counts; gcov_type *counts;
long ncounts; long ncounts;
struct bb *next; struct bb *next;
const unsigned long *addresses;
/* Older GCC's did not emit these fields. */ /* Older GCC's did not emit these fields. */
long nwords; long nwords;
const char **functions; struct bb_function_info *function_infos;
const long *line_nums;
const char **filenames;
char *flags;
}; };
#ifdef BLOCK_PROFILER_CODE #ifdef BLOCK_PROFILER_CODE
...@@ -1283,39 +1278,66 @@ BLOCK_PROFILER_CODE ...@@ -1283,39 +1278,66 @@ BLOCK_PROFILER_CODE
#include <errno.h> #include <errno.h>
#endif #endif
#include <gthr.h>
static struct bb *bb_head; static struct bb *bb_head;
int __global_counters = 0, __gthreads_active = 0;
void void
__bb_exit_func (void) __bb_exit_func (void)
{ {
FILE *da_file; FILE *da_file;
int i;
struct bb *ptr; struct bb *ptr;
long n_counters_p = 0;
gcov_type max_counter_p = 0;
gcov_type sum_counters_p = 0;
if (bb_head == 0) if (bb_head == 0)
return; return;
i = strlen (bb_head->filename) - 3; /* Calculate overall "statistics". */
for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next)
{
int i;
n_counters_p += ptr->ncounts;
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) for (ptr = bb_head; ptr != (struct bb *) 0; ptr = ptr->next)
{ {
int firstchar; gcov_type max_counter_o = 0;
gcov_type sum_counters_o = 0;
int i;
/* Make sure the output file exists - /* Calculate the per-object statistics. */
but don't clobber exiting data. */
if ((da_file = fopen (ptr->filename, "a")) != 0)
fclose (da_file);
/* Need to re-open in order to be able to write from the start. */ for (i = 0; i < ptr->ncounts; i++)
da_file = fopen (ptr->filename, "r+b"); {
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. /* Some old systems might not allow the 'b' mode modifier.
Therefore, try to open without it. This can lead to a race Therefore, try to open without it. This can lead to a race
condition so that when you delete and re-create the file, the condition so that when you delete and re-create the file, the
file might be opened in text mode, but then, you shouldn't file might be opened in text mode, but then, you shouldn't
delete the file in the first place. */ delete the file in the first place. */
if (da_file == 0) if (da_file == 0)
da_file = fopen (ptr->filename, "r+"); da_file = fopen (ptr->filename, "a");
if (da_file == 0) if (da_file == 0)
{ {
fprintf (stderr, "arc profiling: Can't open output file %s.\n", fprintf (stderr, "arc profiling: Can't open output file %s.\n",
...@@ -1341,92 +1363,96 @@ __bb_exit_func (void) ...@@ -1341,92 +1363,96 @@ __bb_exit_func (void)
} }
#endif #endif
/* If the file is not empty, and the number of counts in it is the if (__write_long (-123, da_file, 4) != 0) /* magic */
same, then merge them in. */
firstchar = fgetc (da_file);
if (firstchar == EOF)
{ {
if (ferror (da_file)) fprintf (stderr, "arc profiling: Error writing output file %s.\n",
{ ptr->filename);
fprintf (stderr, "arc profiling: Can't read output file ");
perror (ptr->filename);
}
} }
else else
{ {
long n_counts = 0;
if (ungetc (firstchar, da_file) == EOF) struct bb_function_info *fn_info;
rewind (da_file); gcov_type *count_ptr = ptr->counts;
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; int i;
int count_functions = 0;
for (i = 0; i < n_counts; i++) for (fn_info = ptr->function_infos; fn_info->arc_count != -1;
{ fn_info++)
gcov_type v = 0; count_functions++;
if (__read_gcov_type (&v, da_file, 8) != 0) /* 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++)
{
/* new function. */
if (__write_gcov_string
(fn_info->name, strlen (fn_info->name), da_file, -1) != 0)
{ {
fprintf (stderr, fprintf (stderr,
"arc profiling: Can't read output file %s.\n", "arc profiling: Error writing output file %s.\n",
ptr->filename); ptr->filename);
break; break;
} }
ptr->counts[i] += v;
}
}
if (__write_long (fn_info->checksum, da_file, 4) != 0)
{
fprintf (stderr,
"arc profiling: Error writing output file %s.\n",
ptr->filename);
break;
} }
rewind (da_file); if (__write_long (fn_info->arc_count, da_file, 4) != 0)
/* ??? 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,
fprintf (stderr, "arc profiling: Error writing output file %s.\n", "arc profiling: Error writing output file %s.\n",
ptr->filename); ptr->filename);
break;
} }
else
{ for (i = fn_info->arc_count; i > 0; i--, count_ptr++)
int j;
gcov_type *count_ptr = ptr->counts;
int ret = 0;
for (j = ptr->ncounts; j > 0; j--)
{ {
if (__write_gcov_type (*count_ptr, da_file, 8) != 0) if (__write_gcov_type (*count_ptr, da_file, 8) != 0)
break;
}
if (i) /* there was an error */
{ {
ret = 1; fprintf (stderr,
"arc profiling: Error writing output file %s.\n",
ptr->filename);
break; 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", fprintf (stderr, "arc profiling: Error closing output file %s.\n",
ptr->filename); ptr->filename);
} }
return;
} }
void void
...@@ -1438,7 +1464,7 @@ __bb_init_func (struct bb *blocks) ...@@ -1438,7 +1464,7 @@ __bb_init_func (struct bb *blocks)
if (blocks->zero_word) if (blocks->zero_word)
return; return;
/* Initialize destructor. */ /* Initialize destructor and per-thread data. */
if (!bb_head) if (!bb_head)
atexit (__bb_exit_func); atexit (__bb_exit_func);
......
...@@ -31,9 +31,14 @@ struct bb; ...@@ -31,9 +31,14 @@ struct bb;
extern void __bb_exit_func (void); extern void __bb_exit_func (void);
extern void __bb_init_func (struct bb *); extern void __bb_init_func (struct bb *);
extern void __bb_fork_func (void); extern void __bb_fork_func (void);
extern void __bb_trace_func (void);
extern void __bb_trace_ret (void); #if LONG_TYPE_SIZE == GCOV_TYPE_SIZE
extern void __bb_init_trace_func (struct bb *, unsigned long); typedef long gcov_type;
#else
typedef long long gcov_type;
#endif
extern gcov_type *__bb_find_arc_counters (void);
struct exception_descriptor; struct exception_descriptor;
extern short int __get_eh_table_language (struct exception_descriptor *); extern short int __get_eh_table_language (struct exception_descriptor *);
......
...@@ -49,6 +49,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA ...@@ -49,6 +49,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
#include "basic-block.h" #include "basic-block.h"
#include "gcov-io.h" #include "gcov-io.h"
#include "target.h" #include "target.h"
#include "profile.h"
#include "libfuncs.h"
#include "langhooks.h" #include "langhooks.h"
/* Additional information about the edges we need. */ /* Additional information about the edges we need. */
...@@ -92,11 +94,6 @@ static FILE *bb_file; ...@@ -92,11 +94,6 @@ static FILE *bb_file;
static char *last_bb_file_name; 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 /* Collect statistics on the performance of this pass for the entire source
file. */ file. */
...@@ -118,6 +115,8 @@ static rtx gen_edge_profiler PARAMS ((int)); ...@@ -118,6 +115,8 @@ static rtx gen_edge_profiler PARAMS ((int));
static void instrument_edges PARAMS ((struct edge_list *)); static void instrument_edges PARAMS ((struct edge_list *));
static void output_gcov_string PARAMS ((const char *, long)); static void output_gcov_string PARAMS ((const char *, long));
static void compute_branch_probabilities PARAMS ((void)); 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 basic_block find_group PARAMS ((basic_block));
static void union_groups PARAMS ((basic_block, basic_block)); static void union_groups PARAMS ((basic_block, basic_block));
...@@ -163,8 +162,9 @@ instrument_edges (el) ...@@ -163,8 +162,9 @@ instrument_edges (el)
} }
} }
profile_info.count_edges_instrumented_now = num_instr_edges;
total_num_edges_instrumented += 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; total_num_blocks_created += num_edges;
if (rtl_dump_file) if (rtl_dump_file)
...@@ -205,6 +205,167 @@ output_gcov_string (string, delimiter) ...@@ -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. /* Compute the branch probabilities for the various branches.
Annotate them accordingly. */ Annotate them accordingly. */
...@@ -218,6 +379,8 @@ compute_branch_probabilities () ...@@ -218,6 +379,8 @@ compute_branch_probabilities ()
int hist_br_prob[20]; int hist_br_prob[20];
int num_never_executed; int num_never_executed;
int num_branches; int num_branches;
gcov_type *exec_counts = get_exec_counts ();
int exec_counts_pos = 0;
/* Attach extra info block to each bb. */ /* Attach extra info block to each bb. */
...@@ -253,14 +416,13 @@ compute_branch_probabilities () ...@@ -253,14 +416,13 @@ compute_branch_probabilities ()
if (!EDGE_INFO (e)->ignore && !EDGE_INFO (e)->on_tree) if (!EDGE_INFO (e)->ignore && !EDGE_INFO (e)->on_tree)
{ {
num_edges++; num_edges++;
if (da_file) if (exec_counts)
{ {
gcov_type value; e->count = exec_counts[exec_counts_pos++];
__read_gcov_type (&value, da_file, 8);
e->count = value;
} }
else else
e->count = 0; e->count = 0;
EDGE_INFO (e)->count_valid = 1; EDGE_INFO (e)->count_valid = 1;
BB_INFO (bb)->succ_count--; BB_INFO (bb)->succ_count--;
BB_INFO (e->dest)->pred_count--; BB_INFO (e->dest)->pred_count--;
...@@ -517,6 +679,36 @@ compute_branch_probabilities () ...@@ -517,6 +679,36 @@ compute_branch_probabilities ()
} }
free_aux_for_blocks (); 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. /* Instrument and/or analyze program behavior based on program flow graph.
...@@ -542,6 +734,12 @@ branch_prob () ...@@ -542,6 +734,12 @@ branch_prob ()
int num_edges, ignored_edges; int num_edges, ignored_edges;
struct edge_list *el; 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. */ /* Start of a function. */
if (flag_test_coverage) if (flag_test_coverage)
output_gcov_string (current_function_name, (long) -2); output_gcov_string (current_function_name, (long) -2);
...@@ -758,6 +956,12 @@ branch_prob () ...@@ -758,6 +956,12 @@ branch_prob ()
{ {
int flag_bits; 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. */ /* The plus 2 stands for entry and exit block. */
__write_long (n_basic_blocks + 2, bbg_file, 4); __write_long (n_basic_blocks + 2, bbg_file, 4);
__write_long (num_edges - ignored_edges + 1, bbg_file, 4); __write_long (num_edges - ignored_edges + 1, bbg_file, 4);
...@@ -884,14 +1088,21 @@ find_spanning_tree (el) ...@@ -884,14 +1088,21 @@ find_spanning_tree (el)
/* Add fake edge exit to entry we can't instrument. */ /* Add fake edge exit to entry we can't instrument. */
union_groups (EXIT_BLOCK_PTR, ENTRY_BLOCK_PTR); 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++) for (i = 0; i < num_edges; i++)
{ {
edge e = INDEX_EDGE (el, 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 && !EDGE_INFO (e)->ignore
&& (find_group (e->src) != find_group (e->dest))) && (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; EDGE_INFO (e)->on_tree = 1;
union_groups (e->src, e->dest); union_groups (e->src, e->dest);
} }
...@@ -905,6 +1116,9 @@ find_spanning_tree (el) ...@@ -905,6 +1116,9 @@ find_spanning_tree (el)
&& !EDGE_INFO (e)->ignore && !EDGE_INFO (e)->ignore
&& (find_group (e->src) != find_group (e->dest))) && (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; EDGE_INFO (e)->on_tree = 1;
union_groups (e->src, e->dest); union_groups (e->src, e->dest);
} }
...@@ -917,6 +1131,9 @@ find_spanning_tree (el) ...@@ -917,6 +1131,9 @@ find_spanning_tree (el)
if (find_group (e->src) != find_group (e->dest) if (find_group (e->src) != find_group (e->dest)
&& !EDGE_INFO (e)->ignore) && !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; EDGE_INFO (e)->on_tree = 1;
union_groups (e->src, e->dest); union_groups (e->src, e->dest);
} }
...@@ -975,12 +1192,6 @@ init_branch_prob (filename) ...@@ -975,12 +1192,6 @@ init_branch_prob (filename)
if ((da_file = fopen (da_file_name, "rb")) == 0) if ((da_file = fopen (da_file_name, "rb")) == 0)
warning ("file %s not found, execution counts assumed to be zero", warning ("file %s not found, execution counts assumed to be zero",
da_file_name); 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) if (profile_arc_flag)
...@@ -1011,22 +1222,8 @@ end_branch_prob () ...@@ -1011,22 +1222,8 @@ end_branch_prob ()
fclose (bbg_file); fclose (bbg_file);
} }
if (flag_branch_probabilities) if (flag_branch_probabilities && da_file)
{
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); fclose (da_file);
}
}
if (rtl_dump_file) if (rtl_dump_file)
{ {
...@@ -1097,6 +1294,8 @@ gen_edge_profiler (edgeno) ...@@ -1097,6 +1294,8 @@ gen_edge_profiler (edgeno)
tmp = expand_simple_binop (mode, PLUS, mem_ref, const1_rtx, tmp = expand_simple_binop (mode, PLUS, mem_ref, const1_rtx,
mem_ref, 0, OPTAB_WIDEN); mem_ref, 0, OPTAB_WIDEN);
set_mem_alias_set (mem_ref, new_alias_set ());
if (tmp != mem_ref) if (tmp != mem_ref)
emit_move_insn (copy_rtx (mem_ref), tmp); emit_move_insn (copy_rtx (mem_ref), tmp);
...@@ -1118,9 +1317,6 @@ output_func_start_profiler () ...@@ -1118,9 +1317,6 @@ output_func_start_profiler ()
rtx table_address; rtx table_address;
enum machine_mode mode = mode_for_size (GCOV_TYPE_SIZE, MODE_INT, 0); enum machine_mode mode = mode_for_size (GCOV_TYPE_SIZE, MODE_INT, 0);
int save_flag_inline_functions = flag_inline_functions; 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 /* It's either already been output, or we don't need it because we're
not doing profile-edges. */ not doing profile-edges. */
...@@ -1163,6 +1359,7 @@ output_func_start_profiler () ...@@ -1163,6 +1359,7 @@ output_func_start_profiler ()
init_function_start (fndecl, input_filename, lineno); init_function_start (fndecl, input_filename, lineno);
(*lang_hooks.decls.pushlevel) (0); (*lang_hooks.decls.pushlevel) (0);
expand_function_start (fndecl, 0); expand_function_start (fndecl, 0);
cfun->arc_profile = 0;
/* Actually generate the code to call __bb_init_func. */ /* Actually generate the code to call __bb_init_func. */
ASM_GENERATE_INTERNAL_LABEL (buf, "LPBX", 0); ASM_GENERATE_INTERNAL_LABEL (buf, "LPBX", 0);
...@@ -1179,20 +1376,10 @@ output_func_start_profiler () ...@@ -1179,20 +1376,10 @@ output_func_start_profiler ()
flag_inline_functions. */ flag_inline_functions. */
flag_inline_functions = 0; 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); rest_of_compilation (fndecl);
/* Reset flag_inline_functions to its original value. */ /* Reset flag_inline_functions to its original value. */
flag_inline_functions = save_flag_inline_functions; 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) if (! quiet_flag)
fflush (asm_out_file); fflush (asm_out_file);
......
...@@ -364,6 +364,11 @@ int profile_flag = 0; ...@@ -364,6 +364,11 @@ int profile_flag = 0;
int profile_arc_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. */ /* Nonzero if generating info for gcov to calculate line test coverage. */
int flag_test_coverage = 0; int flag_test_coverage = 0;
...@@ -1061,6 +1066,8 @@ static const lang_independent_options f_options[] = ...@@ -1061,6 +1066,8 @@ static const lang_independent_options f_options[] =
N_("Support synchronous non-call exceptions") }, N_("Support synchronous non-call exceptions") },
{"profile-arcs", &profile_arc_flag, 1, {"profile-arcs", &profile_arc_flag, 1,
N_("Insert arc based program profiling code") }, 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, {"test-coverage", &flag_test_coverage, 1,
N_("Create data files needed by gcov") }, N_("Create data files needed by gcov") },
{"branch-probabilities", &flag_branch_probabilities, 1, {"branch-probabilities", &flag_branch_probabilities, 1,
...@@ -2891,14 +2898,13 @@ rest_of_compilation (decl) ...@@ -2891,14 +2898,13 @@ rest_of_compilation (decl)
close_dump_file (DFI_cfg, print_rtl_with_bb, insns); close_dump_file (DFI_cfg, print_rtl_with_bb, insns);
/* Do branch profiling and static profile estimation passes. */ /* Do branch profiling and static profile estimation passes. */
if (optimize > 0 || profile_arc_flag || flag_test_coverage if (optimize > 0 || cfun->arc_profile || flag_branch_probabilities)
|| flag_branch_probabilities)
{ {
struct loops loops; struct loops loops;
timevar_push (TV_BRANCH_PROB); timevar_push (TV_BRANCH_PROB);
open_dump_file (DFI_bp, decl); 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 (); branch_prob ();
/* Discover and record the loop depth at the head of each basic /* Discover and record the loop depth at the head of each basic
......
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