aboutsummaryrefslogtreecommitdiff
path: root/gcc/gcov.c
diff options
context:
space:
mode:
authorNathan Sidwell <nathan@codesourcery.com>2003-03-31 15:18:24 +0000
committerNathan Sidwell <nathan@gcc.gnu.org>2003-03-31 15:18:24 +0000
commit27283c7340100bf70de6af824143c052cc9d0c76 (patch)
tree79429246167a06afe199b37908d8c7b8ada6a867 /gcc/gcov.c
parent212d93131f96661d194fe74bd5be893cac093664 (diff)
downloadgcc-27283c7340100bf70de6af824143c052cc9d0c76.zip
gcc-27283c7340100bf70de6af824143c052cc9d0c76.tar.gz
gcc-27283c7340100bf70de6af824143c052cc9d0c76.tar.bz2
gcov.c: Add -a & -u options.
* gcov.c: Add -a & -u options. (struct arc_info): Add local_span, is_call_non_return, is_nonlocal_return, is_unconditional flags, remove is_call flag. (struct block_info): Add flags, is_call_site, is_nonlocal_return members. Make encodings a union with span member. (struct function_info): Add blocks_executed, line, src, line_next members. (struct coverage_info): Make branches a union with blocks member. (struct source_info): Add functions member. (object_summary, program_count): New global variables. (flag_all_blocks, flag_unconditional): New flags. (find_source, output_branch_count): New functions. (print_usage): Adjust. (options): Adjust. (process_args): Adjust. (read_graph_file) <GCOV_TAG_FUNCTION>: Adjust. <GCOV_TAG_BLOCKS>: Read flags. <GCOV_TAG_LINES>: Adjust. (read_count_file): Process SUMMARY tags. (solve_flow_graph): Set is_unconditional and clear is_call_site appropriately. (add_branch_counts): Adjust. Don't count unconditional branches. (add_line_counts): Deal with all-blocks mode, accumulate block coverage. (accumulate_line_counts): Adjust, generate local spanning tree for all-blocks mode. (output_lines): Adjust. * profile.c (branch_prob): Alter GCOV_FUNCTION_TAG record. * doc/gcov.texi: Document. testsuite: * lib/gcov.exp: Adjust call return testing strings. * g77.dg/gcov/gcov-1.f: Don't expect unconditional branches. From-SVN: r65090
Diffstat (limited to 'gcc/gcov.c')
-rw-r--r--gcc/gcov.c568
1 files changed, 459 insertions, 109 deletions
diff --git a/gcc/gcov.c b/gcc/gcov.c
index dd6dfc9..964e5e8 100644
--- a/gcc/gcov.c
+++ b/gcc/gcov.c
@@ -69,6 +69,7 @@ typedef HOST_WIDEST_INT gcov_type;
struct function_info;
struct block_info;
+struct source_info;
/* Describes an arc between two basic blocks. */
@@ -86,8 +87,17 @@ typedef struct arc_info
unsigned int fake : 1;
unsigned int fall_through : 1;
- /* Arc to a call. */
- unsigned int is_call : 1;
+ /* Arc is for a function that abnormally returns. */
+ unsigned int is_call_non_return : 1;
+
+ /* Arc is for catch/setjump. */
+ unsigned int is_nonlocal_return : 1;
+
+ /* Is an unconditional branch. */
+ unsigned int is_unconditional : 1;
+
+ /* Arc on the local block spanning tree. */
+ unsigned int local_span : 1;
/* Next branch on line. */
struct arc_info *line_next;
@@ -112,17 +122,39 @@ typedef struct block_info
/* Block execution count. */
gcov_type count;
+ unsigned flags : 13;
unsigned count_valid : 1;
unsigned valid_chain : 1;
unsigned invalid_chain : 1;
- /* Array of line numbers and source files. source files are
- introduced by a linenumber of zero, the next 'line number' is the
- number of the source file. Always starts with a source file. */
- unsigned *encoding;
- unsigned num_encodings;
+ /* Block is a call instrumenting site. */
+ unsigned is_call_site : 1;
- /* Temporary chain for solving graph. */
+ /* Block is a landing pad for longjmp or throw. */
+ unsigned is_nonlocal_return : 1;
+
+ union
+ {
+ struct
+ {
+ /* Array of line numbers and source files. source files are
+ introduced by a linenumber of zero, the next 'line number' is
+ the number of the source file. Always starts with a source
+ file. */
+ unsigned *encoding;
+ unsigned num;
+ } line; /* Valid until blocks are linked onto lines */
+ struct
+ {
+ /* Single line spanning tree workspace. Used for all-blocks mode. */
+ struct block_info *root;
+ unsigned siblings;
+ } span; /* Used in all-blocks mode, after blocks are linked onto
+ lines. */
+ } u;
+
+ /* Temporary chain for solving graph, and for chaining blocks on one
+ line. */
struct block_info *chain;
} block_t;
@@ -138,10 +170,18 @@ typedef struct function_info
/* Array of basic blocks. */
block_t *blocks;
unsigned num_blocks;
+ unsigned blocks_executed;
/* Raw arc coverage counts. */
gcov_type *counts;
unsigned num_counts;
+
+ /* First line number. */
+ unsigned line;
+ struct source_info *src;
+
+ /* Next function in same source file. */
+ struct function_info *line_next;
/* Next function. */
struct function_info *next;
@@ -170,8 +210,14 @@ typedef struct coverage_info
typedef struct line_info
{
gcov_type count; /* execution count */
- arc_t *branches; /* branches from blocks that end on this
- line. */
+ union
+ {
+ arc_t *branches; /* branches from blocks that end on this
+ line. Used for branch-counts when not
+ all-blocks mode. */
+ block_t *blocks; /* blocks which start on this line. Used
+ in all-blocks mode. */
+ } u;
unsigned exists : 1;
} line_t;
@@ -189,6 +235,10 @@ typedef struct source_info
unsigned num_lines;
coverage_t coverage;
+
+ /* Functions in this source file. These are in ascending line
+ number order. */
+ function_t *functions;
/* Next source file. */
struct source_info *next;
@@ -202,6 +252,11 @@ static function_t *functions;
static source_t *sources;
+/* This holds data summary information. */
+
+static struct gcov_summary object_summary;
+static unsigned program_count;
+
/* Modification time of graph file. */
static time_t bbg_file_time;
@@ -218,6 +273,9 @@ static char *da_file_name;
static int flag_branches = 0;
+/* Show unconditional branches too. */
+static int flag_unconditional = 0;
+
/* Output a gcov file if this is true. This is on by default, and can
be turned off by the -n option. */
@@ -229,6 +287,11 @@ static int flag_gcov_file = 1;
static int flag_long_names = 0;
+/* Output count information for every basic block, not merely those
+ that contain line number information. */
+
+static int flag_all_blocks = 0;
+
/* Output summary info for each function. */
static int flag_function_summary = 0;
@@ -256,14 +319,16 @@ static void print_usage PARAMS ((int)) ATTRIBUTE_NORETURN;
static void print_version PARAMS ((void)) ATTRIBUTE_NORETURN;
static void process_file PARAMS ((const char *));
static void create_file_names PARAMS ((const char *));
+static source_t *find_source PARAMS ((char *));
static int read_graph_file PARAMS ((void));
static int read_count_file PARAMS ((void));
static void solve_flow_graph PARAMS ((function_t *));
static void add_branch_counts PARAMS ((coverage_t *, const arc_t *));
-static void add_line_counts PARAMS ((coverage_t *, const function_t *));
+static void add_line_counts PARAMS ((coverage_t *, function_t *));
static void function_summary PARAMS ((const coverage_t *, const char *));
static const char *format_gcov PARAMS ((gcov_type, gcov_type, int));
static void accumulate_line_counts PARAMS ((source_t *));
+static int output_branch_count PARAMS ((FILE *, int, const arc_t *));
static void output_lines PARAMS ((FILE *, const source_t *));
static char *make_gcov_file_name PARAMS ((const char *, const char *));
static void release_structures PARAMS ((void));
@@ -328,6 +393,7 @@ print_usage (error_p)
fnotice (file, "Print code coverage information.\n\n");
fnotice (file, " -h, --help Print this help, then exit\n");
fnotice (file, " -v, --version Print version number, then exit\n");
+ fnotice (file, " -a, --all-blocks Show information for every basic block\n");
fnotice (file, " -b, --branch-probabilities Include branch probabilities in output\n");
fnotice (file, " -c, --branch-counts Given counts of branches taken\n\
rather than percentages\n");
@@ -337,6 +403,7 @@ print_usage (error_p)
fnotice (file, " -f, --function-summaries Output summaries for each function\n");
fnotice (file, " -o, --object-directory DIR|FILE Search for object files in DIR or called FILE\n");
fnotice (file, " -p, --preserve-paths Preserve all pathname components\n");
+ fnotice (file, " -u, --unconditional-branches Show unconditional branch counts too\n");
fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
bug_report_url);
exit (status);
@@ -365,6 +432,7 @@ static const struct option options[] =
{
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'v' },
+ { "all-blocks", no_argument, NULL, 'a' },
{ "branch-probabilities", no_argument, NULL, 'b' },
{ "branch-counts", no_argument, NULL, 'c' },
{ "no-output", no_argument, NULL, 'n' },
@@ -373,6 +441,7 @@ static const struct option options[] =
{ "preserve-paths", no_argument, NULL, 'p' },
{ "object-directory", required_argument, NULL, 'o' },
{ "object-file", required_argument, NULL, 'o' },
+ { "unconditional-branches", no_argument, NULL, 'u' },
};
/* Process args, return index to first non-arg. */
@@ -384,30 +453,30 @@ process_args (argc, argv)
{
int opt;
- while ((opt = getopt_long (argc, argv, "hvbclnfo:p", options, NULL)) != -1)
+ while ((opt = getopt_long (argc, argv, "abcfhlno:puv", options, NULL)) != -1)
{
switch (opt)
{
- case 'h':
- print_usage (false);
- /* print_usage will exit. */
- case 'v':
- print_version ();
- /* print_version will exit. */
+ case 'a':
+ flag_all_blocks = 1;
+ break;
case 'b':
flag_branches = 1;
break;
case 'c':
flag_counts = 1;
break;
- case 'n':
- flag_gcov_file = 0;
+ case 'f':
+ flag_function_summary = 1;
break;
+ case 'h':
+ print_usage (false);
+ /* print_usage will exit. */
case 'l':
flag_long_names = 1;
break;
- case 'f':
- flag_function_summary = 1;
+ case 'n':
+ flag_gcov_file = 0;
break;
case 'o':
object_directory = optarg;
@@ -415,6 +484,12 @@ process_args (argc, argv)
case 'p':
flag_preserve_paths = 1;
break;
+ case 'u':
+ flag_unconditional = 1;
+ break;
+ case 'v':
+ print_version ();
+ /* print_version will exit. */
default:
print_usage (true);
/* print_usage will exit. */
@@ -528,7 +603,6 @@ release_structures ()
arc_n = arc->succ_next;
free (arc);
}
- free (block->encoding);
}
free (fn->blocks);
free (fn->counts);
@@ -597,6 +671,34 @@ create_file_names (file_name)
return;
}
+/* Find or create a source file structure for FILE_NAME. Free
+ FILE_NAME appropriately */
+
+static source_t *
+find_source (file_name)
+ char *file_name;
+{
+
+ source_t *src;
+
+ for (src = sources; src; src = src->next)
+ if (!strcmp (file_name, src->name))
+ {
+ free (file_name);
+ break;
+ }
+ if (!src)
+ {
+ src = (source_t *)xcalloc (1, sizeof (source_t));
+ src->name = file_name;
+ src->coverage.name = file_name;
+ src->index = sources ? sources->index + 1 : 1;
+ src->next = sources;
+ sources = src;
+ }
+ return src;
+}
+
/* Read the graph file. Return nonzero on fatal error. */
static int
@@ -650,22 +752,45 @@ read_graph_file ()
goto corrupt;
base = gcov_save_position (file);
-
+
if (tag == GCOV_TAG_FUNCTION)
{
char *function_name = NULL;
- unsigned checksum;
+ char *function_file = NULL;
+ unsigned checksum, lineno;
+ source_t *src;
+ function_t *probe, *prev;
if (gcov_read_string (file, &function_name, NULL)
- || gcov_read_unsigned (file, &checksum))
+ || gcov_read_unsigned (file, &checksum)
+ || gcov_read_string (file, &function_file, NULL)
+ || gcov_read_unsigned (file, &lineno))
goto corrupt;
+ src = find_source (function_file);
fn = (function_t *)xcalloc (1, sizeof (function_t));
fn->name = function_name;
fn->checksum = checksum;
+ fn->src = src;
+ fn->line = lineno;
fn->next = functions;
functions = fn;
current_tag = tag;
+
+ if (lineno >= src->num_lines)
+ src->num_lines = lineno + 1;
+ /* Now insert it into the source file's list of
+ functions. Normally functions will be encountered in
+ ascending order, so a simple scan is quick. */
+ for (probe = src->functions, prev = NULL;
+ probe && probe->line > lineno;
+ prev = probe, probe = probe->line_next)
+ continue;
+ fn->line_next = probe;
+ if (prev)
+ prev->line_next = fn;
+ else
+ src->functions = fn;
}
else if (fn && tag == GCOV_TAG_BLOCKS)
{
@@ -674,9 +799,19 @@ read_graph_file ()
bbg_file_name, fn->name);
else
{
- fn->num_blocks = length / 4;
+ unsigned ix, num_blocks = length / 4;
+ fn->num_blocks = num_blocks;
+
fn->blocks
= (block_t *)xcalloc (fn->num_blocks, sizeof (block_t));
+ for (ix = 0; ix != num_blocks; ix++)
+ {
+ unsigned flags;
+
+ if (gcov_read_unsigned (file, &flags))
+ goto corrupt;
+ fn->blocks[ix].flags = flags;
+ }
}
}
else if (fn && tag == GCOV_TAG_ARCS)
@@ -717,7 +852,24 @@ read_graph_file ()
fn->blocks[dest].pred = arc;
fn->blocks[dest].num_pred++;
- arc->is_call = arc->fake;
+ if (arc->fake)
+ {
+ if (src)
+ {
+ /* Exceptional exit from this function, the
+ source block must be a call. */
+ fn->blocks[src].is_call_site = 1;
+ arc->is_call_non_return = 1;
+ }
+ else
+ {
+ /* Non-local return from a callee of this
+ function. The destination block is a catch or
+ setjmp. */
+ arc->is_nonlocal_return = 1;
+ fn->blocks[dest].is_nonlocal_return = 1;
+ }
+ }
if (!arc->on_tree)
fn->num_counts++;
@@ -731,7 +883,7 @@ read_graph_file ()
if (gcov_read_unsigned (file, &blockno)
|| blockno >= fn->num_blocks
- || fn->blocks[blockno].encoding)
+ || fn->blocks[blockno].u.line.encoding)
goto corrupt;
for (ix = 0; ; )
@@ -759,28 +911,15 @@ read_graph_file ()
goto corrupt;
if (!file_name)
break;
- for (src = sources; src; src = src->next)
- if (!strcmp (file_name, src->name))
- {
- free (file_name);
- break;
- }
- if (!src)
- {
- src = (source_t *)xcalloc (1, sizeof (source_t));
- src->name = file_name;
- src->coverage.name = file_name;
- src->index = sources ? sources->index + 1 : 1;
- src->next = sources;
- sources = src;
- }
+ src = find_source (file_name);
+
line_nos[ix++] = 0;
line_nos[ix++] = src->index;
}
}
- fn->blocks[blockno].encoding = line_nos;
- fn->blocks[blockno].num_encodings = ix;
+ fn->blocks[blockno].u.line.encoding = line_nos;
+ fn->blocks[blockno].u.line.num = ix;
}
else if (current_tag && !GCOV_TAG_IS_SUBTAG (current_tag, tag))
{
@@ -906,7 +1045,15 @@ read_count_file ()
goto cleanup;
}
base = gcov_save_position (file);
- if (tag == GCOV_TAG_FUNCTION)
+ if (tag == GCOV_TAG_OBJECT_SUMMARY)
+ {
+ if (gcov_read_summary (file, &object_summary))
+ goto corrupt;
+ }
+ else if (tag == GCOV_TAG_PROGRAM_SUMMARY
+ || tag == GCOV_TAG_INCORRECT_SUMMARY)
+ program_count++;
+ else if (tag == GCOV_TAG_FUNCTION)
{
unsigned checksum;
struct function_info *fn_n = functions;
@@ -977,6 +1124,7 @@ solve_flow_graph (fn)
unsigned ix;
arc_t *arc;
gcov_type *count_ptr = fn->counts;
+ block_t *blk;
block_t *valid_blocks = NULL; /* valid, but unpropagated blocks. */
block_t *invalid_blocks = NULL; /* invalid, but inferable blocks. */
@@ -1004,25 +1152,50 @@ solve_flow_graph (fn)
/* Propagate the measured counts, this must be done in the same
order as the code in profile.c */
- for (ix = 0; ix != fn->num_blocks; ix++)
+ for (ix = 0, blk = fn->blocks; ix != fn->num_blocks; ix++, blk++)
{
block_t const *prev_dst = NULL;
int out_of_order = 0;
+ int non_fake_succ = 0;
- for (arc = fn->blocks[ix].succ; arc; arc = arc->succ_next)
+ for (arc = blk->succ; arc; arc = arc->succ_next)
{
+ if (!arc->fake)
+ non_fake_succ++;
+
if (!arc->on_tree)
{
if (count_ptr)
arc->count = *count_ptr++;
arc->count_valid = 1;
- fn->blocks[ix].num_succ--;
+ blk->num_succ--;
arc->dst->num_pred--;
}
if (prev_dst && prev_dst > arc->dst)
out_of_order = 1;
prev_dst = arc->dst;
}
+ if (non_fake_succ == 1)
+ {
+ /* If there is only one non-fake exit, it is an
+ unconditional branch. */
+ for (arc = blk->succ; arc; arc = arc->succ_next)
+ if (!arc->fake)
+ {
+ arc->is_unconditional = 1;
+ /* If this block is instrumenting a call, it might be
+ an artifical block. It is not artificial if it has
+ a non-fallthrough exit, or the destination of the
+ exit has more than one entry. */
+ if (!arc->fall_through
+ || arc->dst->pred != arc || arc->pred_next)
+ blk->is_call_site = 0;
+ }
+ }
+ else
+ /* If there is more than one exit, it cannot be an artificial
+ call instrumenting site. */
+ blk->is_call_site = 0;
/* Sort the successor arcs into ascending dst order. profile.c
normally produces arcs in the right order, but sometimes with
@@ -1030,7 +1203,7 @@ solve_flow_graph (fn)
smart sort. */
if (out_of_order)
{
- arc_t *start = fn->blocks[ix].succ;
+ arc_t *start = blk->succ;
unsigned changes = 1;
while (changes)
@@ -1058,20 +1231,18 @@ solve_flow_graph (fn)
}
}
}
- fn->blocks[ix].succ = start;
+ blk->succ = start;
}
/* Place it on the invalid chain, it will be ignored if that's
wrong. */
- fn->blocks[ix].invalid_chain = 1;
- fn->blocks[ix].chain = invalid_blocks;
- invalid_blocks = &fn->blocks[ix];
+ blk->invalid_chain = 1;
+ blk->chain = invalid_blocks;
+ invalid_blocks = blk;
}
while (invalid_blocks || valid_blocks)
{
- block_t *blk;
-
while ((blk = invalid_blocks))
{
gcov_type total = 0;
@@ -1196,13 +1367,13 @@ add_branch_counts (coverage, arc)
coverage_t *coverage;
const arc_t *arc;
{
- if (arc->is_call)
+ if (arc->is_call_non_return)
{
coverage->calls++;
if (arc->src->count)
coverage->calls_executed++;
}
- else
+ else if (!arc->is_unconditional)
{
coverage->branches++;
if (arc->src->count)
@@ -1374,7 +1545,7 @@ make_gcov_file_name (input_name, src_name)
static void
add_line_counts (coverage, fn)
coverage_t *coverage;
- const function_t *fn;
+ function_t *fn;
{
unsigned ix;
line_t *line = NULL; /* this is propagated from one iteration to the
@@ -1383,13 +1554,16 @@ add_line_counts (coverage, fn)
/* Scan each basic block. */
for (ix = 0; ix != fn->num_blocks; ix++)
{
- const block_t *block = &fn->blocks[ix];
+ block_t *block = &fn->blocks[ix];
unsigned *encoding;
const source_t *src = NULL;
unsigned jx;
+ line_t *first_line = NULL;
- for (jx = 0, encoding = block->encoding;
- jx != block->num_encodings; jx++, encoding++)
+ if (block->count && ix && ix + 1 != fn->num_blocks)
+ fn->blocks_executed++;
+ for (jx = 0, encoding = block->u.line.encoding;
+ jx != block->u.line.num; jx++, encoding++)
if (!*encoding)
{
unsigned src_n = *++encoding;
@@ -1406,26 +1580,38 @@ add_line_counts (coverage, fn)
{
if (!line->exists)
coverage->lines++;
- if (!line->count && block->count)
+ if (!line->count && block->count)
coverage->lines_executed++;
}
line->exists = 1;
line->count += block->count;
+ if (!first_line)
+ first_line = line;
}
-
- if (line && flag_branches)
+ free (block->u.line.encoding);
+ block->u.span.root = NULL;
+ if (!first_line)
+ first_line = line;
+
+ if (!ix || ix + 1 == fn->num_blocks)
+ /* Entry or exit block */;
+ else if (flag_all_blocks)
{
- arc_t *arc;
+ if (!first_line)
+ first_line = &fn->src->lines[fn->line];
+ block->chain = first_line->u.blocks;
+ first_line->u.blocks = block;
+ }
+ else if (flag_branches)
+ {
+ arc_t *arc;
+
for (arc = block->succ; arc; arc = arc->succ_next)
{
- /* Ignore fall through arcs as they aren't really branches. */
- if (arc->fall_through)
- continue;
-
- arc->line_next = line->branches;
- line->branches = arc;
- if (coverage)
+ arc->line_next = line->u.branches;
+ line->u.branches = arc;
+ if (coverage && !arc->is_unconditional)
add_branch_counts (coverage, arc);
}
}
@@ -1441,22 +1627,123 @@ accumulate_line_counts (src)
source_t *src;
{
line_t *line;
+ function_t *fn, *fn_p, *fn_n;
unsigned ix;
+
+ /* Reverse the function order. */
+ for (fn = src->functions, fn_p = NULL; fn;
+ fn_p = fn, fn = fn_n)
+ {
+ fn_n = fn->line_next;
+ fn->line_next = fn_p;
+ }
+ src->functions = fn_p;
for (ix = src->num_lines, line = src->lines; ix--; line++)
{
- arc_t *arc, *arc_p, *arc_n;
-
- /* Total and reverse the branch information. */
- for (arc = line->branches, arc_p = NULL; arc; arc_p = arc, arc = arc_n)
+ if (!flag_all_blocks)
{
- arc_n = arc->line_next;
- arc->line_next = arc_p;
-
- add_branch_counts (&src->coverage, arc);
+ arc_t *arc, *arc_p, *arc_n;
+
+ /* Total and reverse the branch information. */
+ for (arc = line->u.branches, arc_p = NULL; arc;
+ arc_p = arc, arc = arc_n)
+ {
+ arc_n = arc->line_next;
+ arc->line_next = arc_p;
+
+ add_branch_counts (&src->coverage, arc);
+ }
+ line->u.branches = arc_p;
}
- line->branches = arc_p;
+ else if (line->u.blocks)
+ {
+ /* The user expects the line count to be the number of times
+ a line has been executed. Simply summing the block count
+ will give an artificially high number. The Right Thing
+ is to generate the spanning tree of the blocks on this
+ line, and the sum the entry arcs to that tree. */
+ block_t *block, *block_p, *block_n;
+ int changes = 1;
+ gcov_type count = 0;
+
+ /* Reverse the block information */
+ for (block = line->u.blocks, block_p = NULL; block;
+ block_p = block, block = block_n)
+ {
+ block_n = block->chain;
+ block->chain = block_p;
+ /* Each block is it's own spanning tree, with no siblings */
+ block->u.span.root = block;
+ block->u.span.siblings = 0;
+ }
+ line->u.blocks = block_p;
+ while (changes)
+ {
+ changes = 0;
+
+ for (block = line->u.blocks; block; block = block->chain)
+ {
+ arc_t *arc;
+
+ for (arc = block->succ; arc; arc = arc->succ_next)
+ {
+ block_t *dst = arc->dst;
+
+ if (!dst->u.span.root)
+ /* Not on this line. */;
+ else if (dst->u.span.root == block->u.span.root)
+ /* Same spanning tree. */;
+ else
+ {
+ block_t *root = block->u.span.root;
+ block_t *dst_root = dst->u.span.root;
+
+ /* Join spanning trees */
+ if (root->u.span.siblings && !dst_root->u.span.root)
+ {
+ root = dst->u.span.root;
+ dst_root = block->u.span.root;
+ }
+
+ dst->u.span.root = root;
+ root->u.span.siblings += 1 + dst->u.span.siblings;
+ if (dst->u.span.siblings)
+ {
+ block_t *dst_sib;
+
+ dst->u.span.siblings = 0;
+ for (dst_sib = line->u.blocks; dst_sib;
+ dst_sib = dst_sib->chain)
+ if (dst_sib->u.span.root == dst_root)
+ dst_sib->u.span.root = root;
+ }
+ arc->local_span = 1;
+ changes = 1;
+ }
+ }
+ }
+ }
+
+ /* Now sum the entry counts */
+ for (block = line->u.blocks; block; block = block->chain)
+ {
+ arc_t *arc;
+
+ for (arc = block->succ; arc; arc = arc->succ_next)
+ {
+ if (!arc->local_span)
+ count += arc->count;
+ if (flag_branches)
+ add_branch_counts (&src->coverage, arc);
+ }
+ block->u.span.root = NULL;
+ }
+
+ line->count = count;
+ }
+
if (line->exists)
{
src->coverage.lines++;
@@ -1466,6 +1753,50 @@ accumulate_line_counts (src)
}
}
+/* Ouput information about ARC number IX. Returns non-zero if
+ anything is output. */
+
+static int
+output_branch_count (gcov_file, ix, arc)
+ FILE *gcov_file;
+ int ix;
+ const arc_t *arc;
+{
+
+ if (arc->is_call_non_return)
+ {
+ if (arc->src->count)
+ {
+ fnotice (gcov_file, "call %2d returned %s\n", ix,
+ format_gcov (arc->src->count - arc->count,
+ arc->src->count, -flag_counts));
+ }
+ else
+ fnotice (gcov_file, "call %2d never executed\n", ix);
+ }
+ else if (!arc->is_unconditional)
+ {
+ if (arc->src->count)
+ fnotice (gcov_file, "branch %2d taken %s%s\n", ix,
+ format_gcov (arc->count, arc->src->count, -flag_counts),
+ arc->fall_through ? " (fallthrough)" : "");
+ else
+ fnotice (gcov_file, "branch %2d never executed\n", ix);
+ }
+ else if (flag_unconditional && !arc->src->is_call_site)
+ {
+ if (arc->src->count)
+ fnotice (gcov_file, "unconditional %2d taken %s\n", ix,
+ format_gcov (arc->count, arc->src->count, -flag_counts));
+ else
+ fnotice (gcov_file, "unconditional %2d never executed\n", ix);
+ }
+ else
+ return 0;
+ return 1;
+
+}
+
/* Read in the source file one line at a time, and output that line to
the gcov file preceded by its execution count and other
information. */
@@ -1480,10 +1811,13 @@ output_lines (gcov_file, src)
const line_t *line; /* current line info ptr. */
char string[STRING_SIZE]; /* line buffer. */
char const *retval = ""; /* status of source file reading. */
+ function_t *fn = src->functions;
fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, src->name);
fprintf (gcov_file, "%9s:%5d:Graph:%s\n", "-", 0, bbg_file_name);
fprintf (gcov_file, "%9s:%5d:Data:%s\n", "-", 0, da_file_name);
+ fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0, object_summary.runs);
+ fprintf (gcov_file, "%9s:%5d:Programs:%u\n", "-", 0, program_count);
source_file = fopen (src->name, "r");
if (!source_file)
@@ -1508,6 +1842,25 @@ output_lines (gcov_file, src)
for (line_num = 1, line = &src->lines[line_num];
line_num < src->num_lines; line_num++, line++)
{
+ for (; fn && fn->line == line_num; fn = fn->line_next)
+ {
+ arc_t *arc = fn->blocks[fn->num_blocks - 1].pred;
+ gcov_type return_count = fn->blocks[fn->num_blocks - 1].count;
+
+ for (; arc; arc = arc->pred_next)
+ if (arc->fake)
+ return_count -= arc->count;
+
+ fprintf (gcov_file, "function %s", fn->name);
+ fprintf (gcov_file, " called %s",
+ format_gcov (fn->blocks[0].count, 0, -1));
+ fprintf (gcov_file, " returned %s",
+ format_gcov (return_count, fn->blocks[0].count, 0));
+ fprintf (gcov_file, " blocks executed %s",
+ format_gcov (fn->blocks_executed, fn->num_blocks - 2, 0));
+ fprintf (gcov_file, "\n");
+ }
+
/* For lines which don't exist in the .bb file, print '-' before
the source line. For lines which exist but were never
executed, print '#####' before the source line. Otherwise,
@@ -1535,37 +1888,34 @@ output_lines (gcov_file, src)
}
if (!retval)
fputs ("??\n", gcov_file);
-
- if (flag_branches)
+
+ if (flag_all_blocks)
{
- int ix;
- arc_t *arc;
+ block_t *block;
+ int ix, jx;
- for (ix = 0, arc = line->branches; arc; arc = arc->line_next, ix++)
+ for (ix = jx = 0, block = line->u.blocks; block;
+ block = block->chain)
{
- if (arc->is_call)
- {
- if (arc->src->count)
- fnotice
- (gcov_file, "call %2d returns %s\n", ix,
- format_gcov (arc->src->count - arc->count,
- arc->src->count,
- -flag_counts));
- else
- fnotice (gcov_file, "call %2d never executed\n", ix);
- }
- else
- {
- if (arc->src->count)
- fnotice
- (gcov_file, "branch %2d taken %s\n", ix,
- format_gcov (arc->count, arc->src->count,
- -flag_counts));
- else
- fnotice (gcov_file, "branch %2d never executed\n", ix);
- }
+ arc_t *arc;
+
+ if (!block->is_call_site)
+ fprintf (gcov_file, "%9s:%5u-block %2d\n",
+ !line->exists ? "-" : !block->count ? "$$$$$"
+ : format_gcov (block->count, 0, -1), line_num, ix++);
+ if (flag_branches)
+ for (arc = block->succ; arc; arc = arc->succ_next)
+ jx += output_branch_count (gcov_file, jx, arc);
}
}
+ else if (flag_branches)
+ {
+ int ix;
+ arc_t *arc;
+
+ for (ix = 0, arc = line->u.branches; arc; arc = arc->line_next)
+ ix += output_branch_count (gcov_file, ix, arc);
+ }
}
/* Handle all remaining source lines. There may be lines after the