aboutsummaryrefslogtreecommitdiff
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
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
-rw-r--r--gcc/ChangeLog32
-rw-r--r--gcc/doc/gcov.texi88
-rw-r--r--gcc/gcov-io.h6
-rw-r--r--gcc/gcov.c568
-rw-r--r--gcc/profile.c6
-rw-r--r--gcc/testsuite/ChangeLog5
-rw-r--r--gcc/testsuite/g77.dg/gcov/gcov-1.f8
-rw-r--r--gcc/testsuite/lib/gcov.exp6
8 files changed, 584 insertions, 135 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index b779ea3..820deea 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,35 @@
+2003-03-31 Nathan Sidwell <nathan@codesourcery.com>
+
+ * 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.
+
2003-03-31 Kazu Hirata <kazu@cs.umass.edu>
* config/h8300/h8300.md: Organize peephole2's that trasform
diff --git a/gcc/doc/gcov.texi b/gcc/doc/gcov.texi
index b7cfee2..c1eae2c 100644
--- a/gcc/doc/gcov.texi
+++ b/gcc/doc/gcov.texi
@@ -120,6 +120,7 @@ gcov @r{[}@var{options}@r{]} @var{sourcefile}
@ignore
@c man begin SYNOPSIS
gcov [@option{-v}|@option{--version}] [@option{-h}|@option{--help}]
+ [@option{-a}|@option{--all-blocks}]
[@option{-b}|@option{--branch-probabilities}]
[@option{-c}|@option{--branch-counts}]
[@option{-n}|@option{--no-output}]
@@ -127,6 +128,7 @@ gcov [@option{-v}|@option{--version}] [@option{-h}|@option{--help}]
[@option{-p}|@option{--preserve-paths}]
[@option{-f}|@option{--function-summaries}]
[@option{-o}|@option{--object-directory} @var{directory|file}] @var{sourcefile}
+ [@option{-u}|@option{--unconditional-branches}]
@c man end
@c man begin SEEALSO
gpl(7), gfdl(7), fsf-funding(7), gcc(1) and the Info entry for @file{gcc}.
@@ -145,11 +147,23 @@ exit without doing any further processing.
Display the @command{gcov} version number (on the standard output),
and exit without doing any further processing.
+@item -a
+@itemx --all-blocks
+Write individual execution counts for every basic block. Normally gcov
+outputs execution counts only for the main blocks of a line. With this
+option you can determine if blocks within a single line are not being
+executed. In this mode each block is shown, and contributes to the
+occupancy and execution count of, the first line of source that it
+contains. A multi-line block will only contribute to that first line,
+and other lines will not be show to contain code, unless a subsequent
+block begins on those lines.
+
@item -b
@itemx --branch-probabilities
Write branch frequencies to the output file, and write branch summary
info to the standard output. This option allows you to see how often
-each branch in your program was taken.
+each branch in your program was taken. Unconditional branches will not
+be shown, unless the @option{-u} option is given.
@item -c
@itemx --branch-counts
@@ -192,6 +206,11 @@ source file name, without its extension. If a file is specified here,
the data files are named after that file, without its extension. If this
option is not supplied, it defaults to the current directory.
+@item -u
+@itemx --unconditional-branches
+When branch counts are given, include those of unconditional branches.
+Unconditional branches are normally not interesting.
+
@end table
Gcov should be run with the current directory the same as that when you
@@ -248,26 +267,68 @@ Here is a sample:
@smallexample
-: 0:Source:tmp.c
- -: 0:Object:tmp.bb
+ -: 0:Graph:tmp.bbg
+ -: 0:Data:tmp.da
+ -: 0:Runs:1
+ -: 0:Programs:1
+ -: 1:#include <stdio.h>
+ -: 2:
+ -: 3:int main (void)
+function main called 1 returned 1 blocks executed 75%
+ 1: 4:@{
+ 1: 5: int i, total;
+ -: 6:
+ 1: 7: total = 0;
+ -: 8:
+ 11: 9: for (i = 0; i < 10; i++)
+ 10: 10: total += i;
+ -: 11:
+ 1: 12: if (total != 45)
+ #####: 13: printf ("Failure\n");
+ -: 14: else
+ 1: 15: printf ("Success\n");
+ 1: 16: return 0;
+ -: 17:@}
+@end smallexample
+
+When you use the @option{-a} option, you will get individual block
+counts, and the output looks like this:
+
+@smallexample
+ -: 0:Source:tmp.c
+ -: 0:Graph:tmp.bbg
+ -: 0:Data:tmp.da
+ -: 0:Runs:1
+ -: 0:Programs:1
-: 1:#include <stdio.h>
-: 2:
-: 3:int main (void)
+function main called 1 returned 1 blocks executed 75%
1: 4:@{
+ 1: 4-block 0
1: 5: int i, total;
-: 6:
1: 7: total = 0;
-: 8:
11: 9: for (i = 0; i < 10; i++)
+ 11: 9-block 0
10: 10: total += i;
+ 10: 10-block 0
-: 11:
1: 12: if (total != 45)
+ 1: 12-block 0
#####: 13: printf ("Failure\n");
+ $$$$$: 13-block 0
-: 14: else
1: 15: printf ("Success\n");
+ 1: 15-block 0
1: 16: return 0;
- 1: 17:@}
+ 1: 16-block 0
+ -: 17:@}
@end smallexample
+As you can see, line 13 contains a basic block that was not executed.
+
@need 450
When you use the @option{-b} option, your output looks like this:
@@ -284,31 +345,34 @@ Here is a sample of a resulting @file{tmp.c.gcov} file:
@smallexample
-: 0:Source:tmp.c
- -: 0:Object:tmp.bb
+ -: 0:Graph:tmp.bbg
+ -: 0:Data:tmp.da
+ -: 0:Runs:1
+ -: 0:Programs:1
-: 1:#include <stdio.h>
-: 2:
-: 3:int main (void)
+function main called 1 returned 1 blocks executed 75%
1: 4:@{
1: 5: int i, total;
-: 6:
1: 7: total = 0;
-: 8:
11: 9: for (i = 0; i < 10; i++)
-branch 0: taken 90%
-branch 1: taken 100%
-branch 2: taken 100%
+branch 0 taken 91% (fallthrough)
+branch 1 taken 9%
10: 10: total += i;
-: 11:
1: 12: if (total != 45)
-branch 0: taken 100%
+branch 0 taken 0% (fallthrough)
+branch 1 taken 100%
#####: 13: printf ("Failure\n");
-call 0: never executed
-branch 1: never executed
+call 0 never executed
-: 14: else
1: 15: printf ("Success\n");
-call 0: returns 100%
+call 0 called 1 returned 100%
1: 16: return 0;
- 1: 17:@}
+ -: 17:@}
@end smallexample
For each basic block, a line is printed after the last line of the basic
diff --git a/gcc/gcov-io.h b/gcc/gcov-io.h
index 4fcd7de..14dc584 100644
--- a/gcc/gcov-io.h
+++ b/gcc/gcov-io.h
@@ -90,6 +90,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
bbg: function-graph*
function-graph: announce_function basic_blocks {arcs | lines}*
announce_function: header string:name int32:checksum
+ string:source int32:lineno
basic_block: header int32:flags*
arcs: header int32:block_no arc*
arc: int32:dest_block int32:flags
@@ -121,7 +122,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
summary: in32:checksum int32:runs int32:arcs int64:sum int64:max \
int64:max_sum int64:sum_max
- The ANNOUNCE_FUNCTION record is the same as that in the BBG file.
+ The ANNOUNCE_FUNCTION record is the same as that in the BBG file,
+ but without the source location.
The ARC_COUNTS gives the counter values for those arcs that are
instrumented. The SUMMARY records give information about the whole
object file and about the whole program. The checksum is used for
@@ -195,7 +197,7 @@ typedef long long gcov_type;
(GCOV_TAG_MASK (TAG) > GCOV_TAG_MASK (SUB))
/* Basic block flags. */
-#define GCOV_BLOCK_UNEXPECTED (1 << 0)
+#define GCOV_BLOCK_UNEXPECTED (1 << 1)
/* Arc flags. */
#define GCOV_ARC_ON_TREE (1 << 0)
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
diff --git a/gcc/profile.c b/gcc/profile.c
index 6631b5e..68435df 100644
--- a/gcc/profile.c
+++ b/gcc/profile.c
@@ -926,7 +926,7 @@ branch_prob ()
unsigned num_edges, ignored_edges;
struct edge_list *el;
const char *name = IDENTIFIER_POINTER
- (DECL_ASSEMBLER_NAME (current_function_decl));
+ (DECL_ASSEMBLER_NAME (current_function_decl));
profile_info.current_function_cfg_checksum = compute_checksum ();
for (i = 0; i < profile_info.n_sections; i++)
@@ -1086,6 +1086,8 @@ branch_prob ()
if (flag_test_coverage && bbg_file)
{
long offset;
+ const char *file = DECL_SOURCE_FILE (current_function_decl);
+ unsigned line = DECL_SOURCE_LINE (current_function_decl);
/* Announce function */
if (gcov_write_unsigned (bbg_file, GCOV_TAG_FUNCTION)
@@ -1094,6 +1096,8 @@ branch_prob ()
strlen (name))
|| gcov_write_unsigned (bbg_file,
profile_info.current_function_cfg_checksum)
+ || gcov_write_string (bbg_file, file, strlen (file))
+ || gcov_write_unsigned (bbg_file, line)
|| gcov_write_length (bbg_file, offset))
goto bbg_error;
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index fdc97e1..d70d335 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2003-03-31 Nathan Sidwell <nathan@codesourcery.com>
+
+ * lib/gcov.exp: Adjust call return testing strings.
+ * g77.dg/gcov/gcov-1.f: Don't expect unconditional branches.
+
2003-03-31 Roger Sayle <roger@eyesopen.com>
* gcc.dg/builtins-3.c: Add new tests for sin and cos.
diff --git a/gcc/testsuite/g77.dg/gcov/gcov-1.f b/gcc/testsuite/g77.dg/gcov/gcov-1.f
index c4b82ec..fa0ce55 100644
--- a/gcc/testsuite/g77.dg/gcov/gcov-1.f
+++ b/gcc/testsuite/g77.dg/gcov/gcov-1.f
@@ -118,9 +118,7 @@ C Test simple GOTO.
if (f .ne. 0) goto 100 ! count(2)
! branch(end)
gt1 = 1 ! count(1)
- ! branch(100)
goto 101 ! count(1)
- ! branch(end)
100 gt1 = 2 ! count(1)
101 continue ! count(2)
end
@@ -136,9 +134,7 @@ C Test simple GOTO again, this time out of a DO loop.
if (i .eq. f) goto 100 ! count(19)
end do
gt2 = 4 ! count(1)
- ! branch(100)
goto 101 ! count(1)
- ! branch(end)
100 gt2 = 8 ! count(1)
101 continue ! count(2)
end
@@ -149,17 +145,13 @@ C Test computed GOTO.
integer i
goto (101, 102, 103, 104), i ! count(2)
gt3 = 8 ! count(1)
- ! branch(100)
goto 105 ! count(1)
- ! branch(end)
101 gt3 = 1024
goto 105
102 gt3 = 2048
goto 105
103 gt3 = 16 ! count(1)
- ! branch(100)
goto 105 ! count(1)
- ! branch(end)
104 gt3 = 4096
goto 105
105 gt3 = gt3 * 2 ! count(2)
diff --git a/gcc/testsuite/lib/gcov.exp b/gcc/testsuite/lib/gcov.exp
index a6c70c4..3350d28 100644
--- a/gcc/testsuite/lib/gcov.exp
+++ b/gcc/testsuite/lib/gcov.exp
@@ -168,7 +168,7 @@ proc verify-calls { testcase file } {
set n 0
while { [gets $fd line] >= 0 } {
regexp "^\[^:\]+: *(\[0-9\]+):" "$line" all n
- if [regexp "returns" $line] {
+ if [regexp "return" $line] {
verbose "Processing returns line $n: $line" 3
if [regexp "returns\\((\[0-9 \]+)\\)" "$line" all new_shouldbe] {
# All percentages in the current list should have been seen.
@@ -179,12 +179,12 @@ proc verify-calls { testcase file } {
}
# Record the percentages to check for.
set shouldbe $new_shouldbe
- } elseif [regexp "call +\[0-9\]+ returns (-\[0-9\]+)%" "$line" \
+ } elseif [regexp "call +\[0-9\]+ returned (-\[0-9\]+)%" "$line" \
all returns] {
# Percentages should never be negative.
fail "$n: negative percentage: $returns"
incr failed
- } elseif [regexp "call +\[0-9\]+ returns (\[0-9\]+)%" "$line" \
+ } elseif [regexp "call +\[0-9\]+ returned (\[0-9\]+)%" "$line" \
all returns] {
# For branches we check that percentages are not greater than
# 100 but call return percentages can be, as for setjmp(), so