/* Gcov.c: prepend line execution counts and branch probabilities to a
source file.
Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999,
- 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
+ 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
Free Software Foundation, Inc.
Contributed by James E. Wilson of Cygnus Support.
Mangled by Bob Manson of Cygnus Support.
/* This is the size of the buffer used to read in source file lines. */
-#define STRING_SIZE 200
-
struct function_info;
struct block_info;
struct source_info;
unsigned int fake : 1;
unsigned int fall_through : 1;
+ /* Arc to a catch handler. */
+ unsigned int is_throw : 1;
+
/* Arc is for a function that abnormally returns. */
unsigned int is_call_non_return : 1;
/* Block execution count. */
gcov_type count;
- unsigned flags : 13;
+ unsigned flags : 12;
unsigned count_valid : 1;
unsigned valid_chain : 1;
unsigned invalid_chain : 1;
+ unsigned exceptional : 1;
/* Block is a call instrumenting site. */
unsigned is_call_site : 1; /* Does the call. */
unsigned lineno_checksum;
unsigned cfg_checksum;
+ /* The graph contains at least one fake incoming edge. */
+ unsigned has_catch : 1;
+
/* Array of basic blocks. */
block_t *blocks;
unsigned num_blocks;
in all-blocks mode. */
} u;
unsigned exists : 1;
+ unsigned unexceptional : 1;
} line_t;
/* Describes a file mentioned in the block graph. Contains an array
static unsigned object_runs;
static unsigned program_count;
+static unsigned total_lines;
+static unsigned total_executed;
+
/* Modification time of graph file. */
static time_t bbg_file_time;
static function_t *read_graph_file (void);
static int read_count_file (function_t *);
static void solve_flow_graph (function_t *);
+static void find_exception_blocks (function_t *);
static void add_branch_counts (coverage_t *, const arc_t *);
static void add_line_counts (coverage_t *, function_t *);
+static void executed_summary (unsigned, unsigned);
static void function_summary (const coverage_t *, const char *);
static const char *format_gcov (gcov_type, gcov_type, int);
static void accumulate_line_counts (source_t *);
/* Handle response files. */
expandargv (&argc, &argv);
+ a_names = 10;
+ names = XNEWVEC (name_map_t, a_names);
+ a_sources = 10;
+ sources = XNEWVEC (source_t, a_sources);
+
argno = process_args (argc, argv);
if (optind == argc)
print_usage (true);
print_version (void)
{
fnotice (stdout, "gcov %s%s\n", pkgversion_string, version_string);
- fprintf (stdout, "Copyright %s 2011 Free Software Foundation, Inc.\n",
+ fprintf (stdout, "Copyright %s 2012 Free Software Foundation, Inc.\n",
_("(C)"));
fnotice (stdout,
_("This is free software; see the source for copying conditions.\n"
sources[src].num_lines = line + 1;
solve_flow_graph (fn);
+ if (fn->has_catch)
+ find_exception_blocks (fn);
*fn_end = fn;
fn_end = &fn->next;
}
#if HAVE_DOS_BASED_FILE_SYSTEM
if (first && src->coverage.name[1] == ':')
- first = src->coverage.name[2]
+ first = src->coverage.name[2];
#endif
- if (IS_DIR_SEPARATOR (first))
- continue;
+ if (IS_DIR_SEPARATOR (first))
+ continue;
}
accumulate_line_counts (src);
function_summary (&src->coverage, "File");
- if (flag_gcov_file && src->coverage.lines)
+ total_lines += src->coverage.lines;
+ total_executed += src->coverage.lines_executed;
+ if (flag_gcov_file)
{
char *gcov_file_name
= make_gcov_file_name (file_name, src->coverage.name);
- FILE *gcov_file = fopen (gcov_file_name, "w");
- if (gcov_file)
+ if (src->coverage.lines)
{
- fnotice (stdout, "Creating '%s'\n", gcov_file_name);
- output_lines (gcov_file, src);
- if (ferror (gcov_file))
+ FILE *gcov_file = fopen (gcov_file_name, "w");
+
+ if (gcov_file)
+ {
+ fnotice (stdout, "Creating '%s'\n", gcov_file_name);
+ output_lines (gcov_file, src);
+ if (ferror (gcov_file))
fnotice (stderr, "Error writing output file '%s'\n",
gcov_file_name);
- fclose (gcov_file);
+ fclose (gcov_file);
+ }
+ else
+ fnotice (stderr, "Could not open output file '%s'\n",
+ gcov_file_name);
}
else
- fnotice (stderr, "Could not open output file '%s'\n",
- gcov_file_name);
+ {
+ unlink (gcov_file_name);
+ fnotice (stdout, "Removing '%s'\n", gcov_file_name);
+ }
free (gcov_file_name);
}
fnotice (stdout, "\n");
}
+
+ if (!file_name)
+ executed_summary (total_lines, total_executed);
}
/* Release a function structure */
{
/* Extend the name map array -- we'll be inserting one or two
entries. */
- if (!a_names)
- a_names = 10;
a_names *= 2;
name_map = XNEWVEC (name_map_t, a_names);
memcpy (name_map, names, n_names * sizeof (*names));
if (n_sources == a_sources)
{
- if (!a_sources)
- a_sources = 10;
a_sources *= 2;
src = XNEWVEC (source_t, a_sources);
memcpy (src, sources, n_sources * sizeof (*sources));
{
unsigned src = gcov_read_unsigned ();
unsigned num_dests = GCOV_TAG_ARCS_NUM (length);
+ block_t *src_blk = &fn->blocks[src];
+ unsigned mark_catches = 0;
+ struct arc_info *arc;
if (src >= fn->num_blocks || fn->blocks[src].succ)
goto corrupt;
while (num_dests--)
{
- struct arc_info *arc;
unsigned dest = gcov_read_unsigned ();
unsigned flags = gcov_read_unsigned ();
arc = XCNEW (arc_t);
arc->dst = &fn->blocks[dest];
- arc->src = &fn->blocks[src];
+ arc->src = src_blk;
arc->count = 0;
arc->count_valid = 0;
arc->fake = !!(flags & GCOV_ARC_FAKE);
arc->fall_through = !!(flags & GCOV_ARC_FALLTHROUGH);
- arc->succ_next = fn->blocks[src].succ;
- fn->blocks[src].succ = arc;
- fn->blocks[src].num_succ++;
+ arc->succ_next = src_blk->succ;
+ src_blk->succ = arc;
+ src_blk->num_succ++;
arc->pred_next = fn->blocks[dest].pred;
fn->blocks[dest].pred = arc;
source block must be a call. */
fn->blocks[src].is_call_site = 1;
arc->is_call_non_return = 1;
+ mark_catches = 1;
}
else
{
/* Non-local return from a callee of this
- function. The destination block is a catch or
- setjmp. */
+ function. The destination block is a setjmp. */
arc->is_nonlocal_return = 1;
fn->blocks[dest].is_nonlocal_return = 1;
}
if (!arc->on_tree)
fn->num_counts++;
}
+
+ if (mark_catches)
+ {
+ /* We have a fake exit from this block. The other
+ non-fall through exits must be to catch handlers.
+ Mark them as catch arcs. */
+
+ for (arc = src_blk->succ; arc; arc = arc->succ_next)
+ if (!arc->fake && !arc->fall_through)
+ {
+ arc->is_throw = 1;
+ fn->has_catch = 1;
+ }
+ }
}
else if (fn && tag == GCOV_TAG_LINES)
{
}
}
+/* Mark all the blocks only reachable via an incoming catch. */
+
+static void
+find_exception_blocks (function_t *fn)
+{
+ unsigned ix;
+ block_t **queue = XALLOCAVEC (block_t *, fn->num_blocks);
+
+ /* First mark all blocks as exceptional. */
+ for (ix = fn->num_blocks; ix--;)
+ fn->blocks[ix].exceptional = 1;
+
+ /* Now mark all the blocks reachable via non-fake edges */
+ queue[0] = fn->blocks;
+ queue[0]->exceptional = 0;
+ for (ix = 1; ix;)
+ {
+ block_t *block = queue[--ix];
+ const arc_t *arc;
+
+ for (arc = block->succ; arc; arc = arc->succ_next)
+ if (!arc->fake && !arc->is_throw && arc->dst->exceptional)
+ {
+ arc->dst->exceptional = 0;
+ queue[ix++] = arc->dst;
+ }
+ }
+}
\f
/* Increment totals in COVERAGE according to arc ARC. */
return buffer;
}
-
-/* Output summary info for a function. */
+/* Summary of execution */
static void
-function_summary (const coverage_t *coverage, const char *title)
+executed_summary (unsigned lines, unsigned executed)
{
- fnotice (stdout, "%s '%s'\n", title, coverage->name);
-
- if (coverage->lines)
+ if (lines)
fnotice (stdout, "Lines executed:%s of %d\n",
- format_gcov (coverage->lines_executed, coverage->lines, 2),
- coverage->lines);
+ format_gcov (executed, lines, 2), lines);
else
fnotice (stdout, "No executable lines\n");
+}
+
+/* Output summary info for a function or file. */
+
+static void
+function_summary (const coverage_t *coverage, const char *title)
+{
+ fnotice (stdout, "%s '%s'\n", title, coverage->name);
+ executed_summary (coverage->lines, coverage->lines_executed);
if (flag_branches)
{
{
/* '..', we can only elide it and the previous directory, if
we're not a symlink. */
- struct stat buf;
-
+ struct stat ATTRIBUTE_UNUSED buf;
+
*ptr = 0;
- if (dd_base == ptr || stat (result, &buf) || S_ISLNK (buf.st_mode))
+ if (dd_base == ptr
+#if defined (S_ISLNK)
+ /* S_ISLNK is not POSIX.1-1996. */
+ || stat (result, &buf) || S_ISLNK (buf.st_mode)
+#endif
+ )
{
/* Cannot elide, or unreadable or a symlink. */
dd_base = ptr + 2 + slash;
coverage->lines_executed++;
}
line->exists = 1;
+ if (!block->exceptional)
+ line->unexceptional = 1;
line->count += block->count;
}
free (block->u.line.encoding);
static int
output_branch_count (FILE *gcov_file, int ix, const arc_t *arc)
{
-
if (arc->is_call_non_return)
{
if (arc->src->count)
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)" : "");
+ arc->fall_through ? " (fallthrough)"
+ : arc->is_throw ? " (throw)" : "");
else
fnotice (gcov_file, "branch %2d never executed\n", ix);
}
}
+static const char *
+read_line (FILE *file)
+{
+ static char *string;
+ static size_t string_len;
+ size_t pos = 0;
+ char *ptr;
+
+ if (!string_len)
+ {
+ string_len = 200;
+ string = XNEWVEC (char, string_len);
+ }
+
+ while ((ptr = fgets (string + pos, string_len - pos, file)))
+ {
+ size_t len = strlen (string + pos);
+
+ if (string[pos + len - 1] == '\n')
+ {
+ string[pos + len - 1] = 0;
+ return string;
+ }
+ pos += len;
+ ptr = XNEWVEC (char, string_len * 2);
+ if (ptr)
+ {
+ memcpy (ptr, string, pos);
+ string = ptr;
+ string_len += 2;
+ }
+ else
+ pos = 0;
+ }
+
+ return pos ? string : NULL;
+}
+
/* 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. */
FILE *source_file;
unsigned line_num; /* current line number. */
const line_t *line; /* current line info ptr. */
- char string[STRING_SIZE]; /* line buffer. */
- char const *retval = ""; /* status of source file reading. */
+ const char *retval = ""; /* status of source file reading. */
function_t *fn = NULL;
fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, src->coverage.name);
fprintf (gcov_file, "\n");
}
+ if (retval)
+ retval = read_line (source_file);
+
/* 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,
- print the execution count before the source line. There are
- 16 spaces of indentation added before the source line so that
- tabs won't be messed up. */
- fprintf (gcov_file, "%9s:%5u:",
- !line->exists ? "-" : !line->count ? "#####"
- : format_gcov (line->count, 0, -1), line_num);
-
- if (retval)
- {
- /* Copy source line. */
- do
- {
- retval = fgets (string, STRING_SIZE, source_file);
- if (!retval)
- break;
- fputs (retval, gcov_file);
- }
- while (!retval[0] || retval[strlen (retval) - 1] != '\n');
- }
- if (!retval)
- fputs ("/*EOF*/\n", gcov_file);
+ executed, print '#####' or '=====' before the source line.
+ Otherwise, print the execution count before the source line.
+ There are 16 spaces of indentation added before the source
+ line so that tabs won't be messed up. */
+ fprintf (gcov_file, "%9s:%5u:%s\n",
+ !line->exists ? "-" : line->count
+ ? format_gcov (line->count, 0, -1)
+ : line->unexceptional ? "#####" : "=====", line_num,
+ retval ? retval : "/*EOF*/");
if (flag_all_blocks)
{
{
if (!block->is_call_return)
fprintf (gcov_file, "%9s:%5u-block %2d\n",
- !line->exists ? "-" : !block->count ? "$$$$$"
- : format_gcov (block->count, 0, -1),
+ !line->exists ? "-" : block->count
+ ? format_gcov (block->count, 0, -1)
+ : block->exceptional ? "%%%%%" : "$$$$$",
line_num, ix++);
if (flag_branches)
for (arc = block->succ; arc; arc = arc->succ_next)
last line of code. */
if (retval)
{
- for (; (retval = fgets (string, STRING_SIZE, source_file)); line_num++)
- {
- fprintf (gcov_file, "%9s:%5u:%s", "-", line_num, retval);
-
- while (!retval[0] || retval[strlen (retval) - 1] != '\n')
- {
- retval = fgets (string, STRING_SIZE, source_file);
- if (!retval)
- break;
- fputs (retval, gcov_file);
- }
- }
+ for (; (retval = read_line (source_file)); line_num++)
+ fprintf (gcov_file, "%9s:%5u:%s\n", "-", line_num, retval);
}
if (source_file)