#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "intl.h"
#include "version.h"
#include <getopt.h>
#define IN_GCOV 1
#include "gcov-io.h"
#include "gcov-io.c"
#define STRING_SIZE 200
struct function_info;
struct block_info;
struct source_info;
typedef struct arc_info
{
struct block_info *src;
struct block_info *dst;
gcov_type count;
gcov_type cs_count;
unsigned int count_valid : 1;
unsigned int on_tree : 1;
unsigned int fake : 1;
unsigned int fall_through : 1;
unsigned int is_call_non_return : 1;
unsigned int is_nonlocal_return : 1;
unsigned int is_unconditional : 1;
unsigned int cycle : 1;
struct arc_info *line_next;
struct arc_info *succ_next;
struct arc_info *pred_next;
} arc_t;
typedef struct block_info
{
arc_t *succ;
arc_t *pred;
gcov_type num_succ;
gcov_type num_pred;
gcov_type count;
unsigned flags : 13;
unsigned count_valid : 1;
unsigned valid_chain : 1;
unsigned invalid_chain : 1;
unsigned is_call_site : 1;
unsigned is_call_return : 1;
unsigned is_nonlocal_return : 1;
union
{
struct
{
unsigned *encoding;
unsigned num;
} line;
struct
{
arc_t *arc;
unsigned ident;
} cycle;
} u;
struct block_info *chain;
} block_t;
typedef struct function_info
{
char *name;
unsigned ident;
unsigned checksum;
block_t *blocks;
unsigned num_blocks;
unsigned blocks_executed;
gcov_type *counts;
unsigned num_counts;
unsigned line;
struct source_info *src;
struct function_info *line_next;
struct function_info *next;
} function_t;
typedef struct coverage_info
{
int lines;
int lines_executed;
int branches;
int branches_executed;
int branches_taken;
int calls;
int calls_executed;
char *name;
} coverage_t;
typedef struct line_info
{
gcov_type count;
union
{
arc_t *branches;
block_t *blocks;
} u;
unsigned exists : 1;
} line_t;
typedef struct source_info
{
char *name;
unsigned index;
line_t *lines;
unsigned num_lines;
coverage_t coverage;
function_t *functions;
struct source_info *next;
} source_t;
static function_t *functions;
static source_t *sources;
static struct gcov_summary object_summary;
static unsigned program_count;
static time_t bbg_file_time;
static char *bbg_file_name;
static unsigned bbg_stamp;
static char *da_file_name;
static int no_data_file;
static int flag_branches = 0;
static int flag_unconditional = 0;
static int flag_gcov_file = 1;
static int flag_long_names = 0;
static int flag_all_blocks = 0;
static int flag_function_summary = 0;
static char *object_directory = 0;
static int flag_preserve_paths = 0;
static int flag_counts = 0;
static void fnotice (FILE *, const char *, ...) ATTRIBUTE_PRINTF_2;
static int process_args (int, char **);
static void print_usage (int) ATTRIBUTE_NORETURN;
static void print_version (void) ATTRIBUTE_NORETURN;
static void process_file (const char *);
static void create_file_names (const char *);
static source_t *find_source (const char *);
static int read_graph_file (void);
static int read_count_file (void);
static void solve_flow_graph (function_t *);
static void add_branch_counts (coverage_t *, const arc_t *);
static void add_line_counts (coverage_t *, function_t *);
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 *);
static int output_branch_count (FILE *, int, const arc_t *);
static void output_lines (FILE *, const source_t *);
static char *make_gcov_file_name (const char *, const char *);
static void release_structures (void);
extern int main (int, char **);
int
main (int argc, char **argv)
{
int argno;
unlock_std_streams ();
gcc_init_libintl ();
argno = process_args (argc, argv);
if (optind == argc)
print_usage (true);
for (; argno != argc; argno++)
{
release_structures ();
process_file (argv[argno]);
}
return 0;
}
static void
fnotice (FILE *file, const char *cmsgid, ...)
{
va_list ap;
va_start (ap, cmsgid);
vfprintf (file, _(cmsgid), ap);
va_end (ap);
}
static void
print_usage (int error_p)
{
FILE *file = error_p ? stderr : stdout;
int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
fnotice (file, "Usage: gcov [OPTION]... SOURCEFILE\n\n");
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");
fnotice (file, " -n, --no-output Do not create an output file\n");
fnotice (file, " -l, --long-file-names Use long output file names for included\n\
source files\n");
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);
}
static void
print_version (void)
{
fnotice (stdout, "gcov (GCC) %s\n", version_string);
fprintf (stdout, "Copyright %s 2006 Free Software Foundation, Inc.\n",
_("(C)"));
fnotice (stdout,
_("This is free software; see the source for copying conditions.\n"
"There is NO warranty; not even for MERCHANTABILITY or \n"
"FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
exit (SUCCESS_EXIT_CODE);
}
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' },
{ "long-file-names", no_argument, NULL, 'l' },
{ "function-summaries", no_argument, NULL, 'f' },
{ "preserve-paths", no_argument, NULL, 'p' },
{ "object-directory", required_argument, NULL, 'o' },
{ "object-file", required_argument, NULL, 'o' },
{ "unconditional-branches", no_argument, NULL, 'u' },
{ 0, 0, 0, 0 }
};
static int
process_args (int argc, char **argv)
{
int opt;
while ((opt = getopt_long (argc, argv, "abcfhlno:puv", options, NULL)) != -1)
{
switch (opt)
{
case 'a':
flag_all_blocks = 1;
break;
case 'b':
flag_branches = 1;
break;
case 'c':
flag_counts = 1;
break;
case 'f':
flag_function_summary = 1;
break;
case 'h':
print_usage (false);
case 'l':
flag_long_names = 1;
break;
case 'n':
flag_gcov_file = 0;
break;
case 'o':
object_directory = optarg;
break;
case 'p':
flag_preserve_paths = 1;
break;
case 'u':
flag_unconditional = 1;
break;
case 'v':
print_version ();
default:
print_usage (true);
}
}
return optind;
}
static void
process_file (const char *file_name)
{
source_t *src;
function_t *fn;
create_file_names (file_name);
if (read_graph_file ())
return;
if (!functions)
{
fnotice (stderr, "%s:no functions found\n", bbg_file_name);
return;
}
if (read_count_file ())
return;
for (fn = functions; fn; fn = fn->next)
solve_flow_graph (fn);
for (src = sources; src; src = src->next)
src->lines = XCNEWVEC (line_t, src->num_lines);
for (fn = functions; fn; fn = fn->next)
{
coverage_t coverage;
memset (&coverage, 0, sizeof (coverage));
coverage.name = fn->name;
add_line_counts (flag_function_summary ? &coverage : NULL, fn);
if (flag_function_summary)
{
function_summary (&coverage, "Function");
fnotice (stdout, "\n");
}
}
for (src = sources; src; src = src->next)
{
accumulate_line_counts (src);
function_summary (&src->coverage, "File");
if (flag_gcov_file)
{
char *gcov_file_name = make_gcov_file_name (file_name, src->name);
FILE *gcov_file = fopen (gcov_file_name, "w");
if (gcov_file)
{
fnotice (stdout, "%s:creating '%s'\n",
src->name, gcov_file_name);
output_lines (gcov_file, src);
if (ferror (gcov_file))
fnotice (stderr, "%s:error writing output file '%s'\n",
src->name, gcov_file_name);
fclose (gcov_file);
}
else
fnotice (stderr, "%s:could not open output file '%s'\n",
src->name, gcov_file_name);
free (gcov_file_name);
}
fnotice (stdout, "\n");
}
}
static void
release_structures (void)
{
function_t *fn;
source_t *src;
free (bbg_file_name);
free (da_file_name);
da_file_name = bbg_file_name = NULL;
bbg_file_time = 0;
bbg_stamp = 0;
while ((src = sources))
{
sources = src->next;
free (src->name);
free (src->lines);
}
while ((fn = functions))
{
unsigned ix;
block_t *block;
functions = fn->next;
for (ix = fn->num_blocks, block = fn->blocks; ix--; block++)
{
arc_t *arc, *arc_n;
for (arc = block->succ; arc; arc = arc_n)
{
arc_n = arc->succ_next;
free (arc);
}
}
free (fn->blocks);
free (fn->counts);
}
}
static void
create_file_names (const char *file_name)
{
char *cptr;
char *name;
int length = strlen (file_name);
int base;
if (object_directory && object_directory[0])
{
struct stat status;
length += strlen (object_directory) + 2;
name = XNEWVEC (char, length);
name[0] = 0;
base = !stat (object_directory, &status) && S_ISDIR (status.st_mode);
strcat (name, object_directory);
if (base && name[strlen (name) - 1] != '/')
strcat (name, "/");
}
else
{
name = XNEWVEC (char, length + 1);
name[0] = 0;
base = 1;
}
if (base)
{
cptr = strrchr (file_name, '/');
strcat (name, cptr ? cptr + 1 : file_name);
}
cptr = strrchr (name, '.');
if (cptr)
*cptr = 0;
length = strlen (name);
bbg_file_name = XNEWVEC (char, length + strlen (GCOV_NOTE_SUFFIX) + 1);
strcpy (bbg_file_name, name);
strcpy (bbg_file_name + length, GCOV_NOTE_SUFFIX);
da_file_name = XNEWVEC (char, length + strlen (GCOV_DATA_SUFFIX) + 1);
strcpy (da_file_name, name);
strcpy (da_file_name + length, GCOV_DATA_SUFFIX);
free (name);
return;
}
static source_t *
find_source (const char *file_name)
{
source_t *src;
if (!file_name)
file_name = "<unknown>";
for (src = sources; src; src = src->next)
if (!strcmp (file_name, src->name))
return src;
src = XCNEW (source_t);
src->name = xstrdup (file_name);
src->coverage.name = src->name;
src->index = sources ? sources->index + 1 : 1;
src->next = sources;
sources = src;
return src;
}
static int
read_graph_file (void)
{
unsigned version;
unsigned current_tag = 0;
struct function_info *fn = NULL;
source_t *src = NULL;
unsigned ix;
unsigned tag;
if (!gcov_open (bbg_file_name, 1))
{
fnotice (stderr, "%s:cannot open graph file\n", bbg_file_name);
return 1;
}
bbg_file_time = gcov_time ();
if (!gcov_magic (gcov_read_unsigned (), GCOV_NOTE_MAGIC))
{
fnotice (stderr, "%s:not a gcov graph file\n", bbg_file_name);
gcov_close ();
return 1;
}
version = gcov_read_unsigned ();
if (version != GCOV_VERSION)
{
char v[4], e[4];
GCOV_UNSIGNED2STRING (v, version);
GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
fnotice (stderr, "%s:version '%.4s', prefer '%.4s'\n",
bbg_file_name, v, e);
}
bbg_stamp = gcov_read_unsigned ();
while ((tag = gcov_read_unsigned ()))
{
unsigned length = gcov_read_unsigned ();
gcov_position_t base = gcov_position ();
if (tag == GCOV_TAG_FUNCTION)
{
char *function_name;
unsigned ident, checksum, lineno;
source_t *src;
function_t *probe, *prev;
ident = gcov_read_unsigned ();
checksum = gcov_read_unsigned ();
function_name = xstrdup (gcov_read_string ());
src = find_source (gcov_read_string ());
lineno = gcov_read_unsigned ();
fn = XCNEW (function_t);
fn->name = function_name;
fn->ident = ident;
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;
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)
{
if (fn->blocks)
fnotice (stderr, "%s:already seen blocks for '%s'\n",
bbg_file_name, fn->name);
else
{
unsigned ix, num_blocks = GCOV_TAG_BLOCKS_NUM (length);
fn->num_blocks = num_blocks;
fn->blocks = XCNEWVEC (block_t, fn->num_blocks);
for (ix = 0; ix != num_blocks; ix++)
fn->blocks[ix].flags = gcov_read_unsigned ();
}
}
else if (fn && tag == GCOV_TAG_ARCS)
{
unsigned src = gcov_read_unsigned ();
unsigned num_dests = GCOV_TAG_ARCS_NUM (length);
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 ();
if (dest >= fn->num_blocks)
goto corrupt;
arc = XCNEW (arc_t);
arc->dst = &fn->blocks[dest];
arc->src = &fn->blocks[src];
arc->count = 0;
arc->count_valid = 0;
arc->on_tree = !!(flags & GCOV_ARC_ON_TREE);
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->pred_next = fn->blocks[dest].pred;
fn->blocks[dest].pred = arc;
fn->blocks[dest].num_pred++;
if (arc->fake)
{
if (src)
{
fn->blocks[src].is_call_site = 1;
arc->is_call_non_return = 1;
}
else
{
arc->is_nonlocal_return = 1;
fn->blocks[dest].is_nonlocal_return = 1;
}
}
if (!arc->on_tree)
fn->num_counts++;
}
}
else if (fn && tag == GCOV_TAG_LINES)
{
unsigned blockno = gcov_read_unsigned ();
unsigned *line_nos = XCNEWVEC (unsigned, length - 1);
if (blockno >= fn->num_blocks || fn->blocks[blockno].u.line.encoding)
goto corrupt;
for (ix = 0; ; )
{
unsigned lineno = gcov_read_unsigned ();
if (lineno)
{
if (!ix)
{
line_nos[ix++] = 0;
line_nos[ix++] = src->index;
}
line_nos[ix++] = lineno;
if (lineno >= src->num_lines)
src->num_lines = lineno + 1;
}
else
{
const char *file_name = gcov_read_string ();
if (!file_name)
break;
src = find_source (file_name);
line_nos[ix++] = 0;
line_nos[ix++] = src->index;
}
}
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))
{
fn = NULL;
current_tag = 0;
}
gcov_sync (base, length);
if (gcov_is_error ())
{
corrupt:;
fnotice (stderr, "%s:corrupted\n", bbg_file_name);
gcov_close ();
return 1;
}
}
gcov_close ();
{
source_t *src, *src_p, *src_n;
for (src_p = NULL, src = sources; src; src_p = src, src = src_n)
{
src_n = src->next;
src->next = src_p;
}
sources = src_p;
}
{
function_t *fn, *fn_p, *fn_n;
for (fn_p = NULL, fn = functions; fn; fn_p = fn, fn = fn_n)
{
unsigned ix;
fn_n = fn->next;
fn->next = fn_p;
for (ix = fn->num_blocks; ix--;)
{
arc_t *arc, *arc_p, *arc_n;
for (arc_p = NULL, arc = fn->blocks[ix].succ; arc;
arc_p = arc, arc = arc_n)
{
arc_n = arc->succ_next;
arc->succ_next = arc_p;
}
fn->blocks[ix].succ = arc_p;
for (arc_p = NULL, arc = fn->blocks[ix].pred; arc;
arc_p = arc, arc = arc_n)
{
arc_n = arc->pred_next;
arc->pred_next = arc_p;
}
fn->blocks[ix].pred = arc_p;
}
}
functions = fn_p;
}
return 0;
}
static int
read_count_file (void)
{
unsigned ix;
unsigned version;
unsigned tag;
function_t *fn = NULL;
int error = 0;
if (!gcov_open (da_file_name, 1))
{
fnotice (stderr, "%s:cannot open data file, assuming not executed\n",
da_file_name);
no_data_file = 1;
return 0;
}
if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC))
{
fnotice (stderr, "%s:not a gcov data file\n", da_file_name);
cleanup:;
gcov_close ();
return 1;
}
version = gcov_read_unsigned ();
if (version != GCOV_VERSION)
{
char v[4], e[4];
GCOV_UNSIGNED2STRING (v, version);
GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
fnotice (stderr, "%s:version '%.4s', prefer version '%.4s'\n",
da_file_name, v, e);
}
tag = gcov_read_unsigned ();
if (tag != bbg_stamp)
{
fnotice (stderr, "%s:stamp mismatch with graph file\n", da_file_name);
goto cleanup;
}
while ((tag = gcov_read_unsigned ()))
{
unsigned length = gcov_read_unsigned ();
unsigned long base = gcov_position ();
if (tag == GCOV_TAG_OBJECT_SUMMARY)
gcov_read_summary (&object_summary);
else if (tag == GCOV_TAG_PROGRAM_SUMMARY)
program_count++;
else if (tag == GCOV_TAG_FUNCTION)
{
unsigned ident = gcov_read_unsigned ();
struct function_info *fn_n = functions;
for (fn = fn ? fn->next : NULL; ; fn = fn->next)
{
if (fn)
;
else if ((fn = fn_n))
fn_n = NULL;
else
{
fnotice (stderr, "%s:unknown function '%u'\n",
da_file_name, ident);
break;
}
if (fn->ident == ident)
break;
}
if (!fn)
;
else if (gcov_read_unsigned () != fn->checksum)
{
mismatch:;
fnotice (stderr, "%s:profile mismatch for '%s'\n",
da_file_name, fn->name);
goto cleanup;
}
}
else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) && fn)
{
if (length != GCOV_TAG_COUNTER_LENGTH (fn->num_counts))
goto mismatch;
if (!fn->counts)
fn->counts = XCNEWVEC (gcov_type, fn->num_counts);
for (ix = 0; ix != fn->num_counts; ix++)
fn->counts[ix] += gcov_read_counter ();
}
gcov_sync (base, length);
if ((error = gcov_is_error ()))
{
fnotice (stderr, error < 0 ? "%s:overflowed\n" : "%s:corrupted\n",
da_file_name);
goto cleanup;
}
}
gcov_close ();
return 0;
}
static void
solve_flow_graph (function_t *fn)
{
unsigned ix;
arc_t *arc;
gcov_type *count_ptr = fn->counts;
block_t *blk;
block_t *valid_blocks = NULL;
block_t *invalid_blocks = NULL;
if (fn->num_blocks < 2)
fnotice (stderr, "%s:'%s' lacks entry and/or exit blocks\n",
bbg_file_name, fn->name);
else
{
if (fn->blocks[0].num_pred)
fnotice (stderr, "%s:'%s' has arcs to entry block\n",
bbg_file_name, fn->name);
else
fn->blocks[0].num_pred = ~(unsigned)0;
if (fn->blocks[fn->num_blocks - 1].num_succ)
fnotice (stderr, "%s:'%s' has arcs from exit block\n",
bbg_file_name, fn->name);
else
fn->blocks[fn->num_blocks - 1].num_succ = ~(unsigned)0;
}
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 = 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;
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)
{
for (arc = blk->succ; arc; arc = arc->succ_next)
if (!arc->fake)
{
arc->is_unconditional = 1;
if (blk->is_call_site && arc->fall_through
&& arc->dst->pred == arc && !arc->pred_next)
arc->dst->is_call_return = 1;
}
}
if (out_of_order)
{
arc_t *start = blk->succ;
unsigned changes = 1;
while (changes)
{
arc_t *arc, *arc_p, *arc_n;
changes = 0;
for (arc_p = NULL, arc = start; (arc_n = arc->succ_next);)
{
if (arc->dst > arc_n->dst)
{
changes = 1;
if (arc_p)
arc_p->succ_next = arc_n;
else
start = arc_n;
arc->succ_next = arc_n->succ_next;
arc_n->succ_next = arc;
arc_p = arc_n;
}
else
{
arc_p = arc;
arc = arc_n;
}
}
}
blk->succ = start;
}
blk->invalid_chain = 1;
blk->chain = invalid_blocks;
invalid_blocks = blk;
}
while (invalid_blocks || valid_blocks)
{
while ((blk = invalid_blocks))
{
gcov_type total = 0;
const arc_t *arc;
invalid_blocks = blk->chain;
blk->invalid_chain = 0;
if (!blk->num_succ)
for (arc = blk->succ; arc; arc = arc->succ_next)
total += arc->count;
else if (!blk->num_pred)
for (arc = blk->pred; arc; arc = arc->pred_next)
total += arc->count;
else
continue;
blk->count = total;
blk->count_valid = 1;
blk->chain = valid_blocks;
blk->valid_chain = 1;
valid_blocks = blk;
}
while ((blk = valid_blocks))
{
gcov_type total;
arc_t *arc, *inv_arc;
valid_blocks = blk->chain;
blk->valid_chain = 0;
if (blk->num_succ == 1)
{
block_t *dst;
total = blk->count;
inv_arc = NULL;
for (arc = blk->succ; arc; arc = arc->succ_next)
{
total -= arc->count;
if (!arc->count_valid)
inv_arc = arc;
}
dst = inv_arc->dst;
inv_arc->count_valid = 1;
inv_arc->count = total;
blk->num_succ--;
dst->num_pred--;
if (dst->count_valid)
{
if (dst->num_pred == 1 && !dst->valid_chain)
{
dst->chain = valid_blocks;
dst->valid_chain = 1;
valid_blocks = dst;
}
}
else
{
if (!dst->num_pred && !dst->invalid_chain)
{
dst->chain = invalid_blocks;
dst->invalid_chain = 1;
invalid_blocks = dst;
}
}
}
if (blk->num_pred == 1)
{
block_t *src;
total = blk->count;
inv_arc = NULL;
for (arc = blk->pred; arc; arc = arc->pred_next)
{
total -= arc->count;
if (!arc->count_valid)
inv_arc = arc;
}
src = inv_arc->src;
inv_arc->count_valid = 1;
inv_arc->count = total;
blk->num_pred--;
src->num_succ--;
if (src->count_valid)
{
if (src->num_succ == 1 && !src->valid_chain)
{
src->chain = valid_blocks;
src->valid_chain = 1;
valid_blocks = src;
}
}
else
{
if (!src->num_succ && !src->invalid_chain)
{
src->chain = invalid_blocks;
src->invalid_chain = 1;
invalid_blocks = src;
}
}
}
}
}
for (ix = 0; ix < fn->num_blocks; ix++)
if (!fn->blocks[ix].count_valid)
{
fnotice (stderr, "%s:graph is unsolvable for '%s'\n",
bbg_file_name, fn->name);
break;
}
}
static void
add_branch_counts (coverage_t *coverage, const arc_t *arc)
{
if (arc->is_call_non_return)
{
coverage->calls++;
if (arc->src->count)
coverage->calls_executed++;
}
else if (!arc->is_unconditional)
{
coverage->branches++;
if (arc->src->count)
coverage->branches_executed++;
if (arc->count)
coverage->branches_taken++;
}
}
static char const *
format_gcov (gcov_type top, gcov_type bottom, int dp)
{
static char buffer[20];
if (dp >= 0)
{
float ratio = bottom ? (float)top / bottom : 0;
int ix;
unsigned limit = 100;
unsigned percent;
for (ix = dp; ix--; )
limit *= 10;
percent = (unsigned) (ratio * limit + (float)0.5);
if (percent <= 0 && top)
percent = 1;
else if (percent >= limit && top != bottom)
percent = limit - 1;
ix = sprintf (buffer, "%.*u%%", dp + 1, percent);
if (dp)
{
dp++;
do
{
buffer[ix+1] = buffer[ix];
ix--;
}
while (dp--);
buffer[ix + 1] = '.';
}
}
else
sprintf (buffer, HOST_WIDEST_INT_PRINT_DEC, (HOST_WIDEST_INT)top);
return buffer;
}
static void
function_summary (const coverage_t *coverage, const char *title)
{
fnotice (stdout, "%s '%s'\n", title, coverage->name);
if (coverage->lines)
fnotice (stdout, "Lines executed:%s of %d\n",
format_gcov (coverage->lines_executed, coverage->lines, 2),
coverage->lines);
else
fnotice (stdout, "No executable lines\n");
if (flag_branches)
{
if (coverage->branches)
{
fnotice (stdout, "Branches executed:%s of %d\n",
format_gcov (coverage->branches_executed,
coverage->branches, 2),
coverage->branches);
fnotice (stdout, "Taken at least once:%s of %d\n",
format_gcov (coverage->branches_taken,
coverage->branches, 2),
coverage->branches);
}
else
fnotice (stdout, "No branches\n");
if (coverage->calls)
fnotice (stdout, "Calls executed:%s of %d\n",
format_gcov (coverage->calls_executed, coverage->calls, 2),
coverage->calls);
else
fnotice (stdout, "No calls\n");
}
}
static char *
make_gcov_file_name (const char *input_name, const char *src_name)
{
char *cptr;
char *name = XNEWVEC (char, strlen (src_name) + strlen (input_name) + 10);
name[0] = 0;
if (flag_long_names && strcmp (src_name, input_name))
{
cptr = flag_preserve_paths ? NULL : strrchr (input_name, '/');
strcat (name, cptr ? cptr + 1 : input_name);
strcat (name, "##");
}
cptr = flag_preserve_paths ? NULL : strrchr (src_name, '/');
strcat (name, cptr ? cptr + 1 : src_name);
if (flag_preserve_paths)
{
char *prev;
for (cptr = name; (cptr = strchr ((prev = cptr), '/'));)
{
unsigned shift = 0;
if (prev + 1 == cptr && prev[0] == '.')
{
shift = 2;
}
else if (prev + 2 == cptr && prev[0] == '.' && prev[1] == '.')
{
shift = 1;
prev[1] = '^';
}
else
*cptr++ = '#';
if (shift)
{
cptr = prev;
do
prev[0] = prev[shift];
while (*prev++);
}
}
}
strcat (name, ".gcov");
return name;
}
static void
add_line_counts (coverage_t *coverage, function_t *fn)
{
unsigned ix;
line_t *line = NULL;
for (ix = 0; ix != fn->num_blocks; ix++)
{
block_t *block = &fn->blocks[ix];
unsigned *encoding;
const source_t *src = NULL;
unsigned jx;
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;
for (src = sources; src->index != src_n; src = src->next)
continue;
jx++;
}
else
{
line = &src->lines[*encoding];
if (coverage)
{
if (!line->exists)
coverage->lines++;
if (!line->count && block->count)
coverage->lines_executed++;
}
line->exists = 1;
line->count += block->count;
}
free (block->u.line.encoding);
block->u.cycle.arc = NULL;
block->u.cycle.ident = ~0U;
if (!ix || ix + 1 == fn->num_blocks)
;
else if (flag_all_blocks)
{
line_t *block_line = line ? line : &fn->src->lines[fn->line];
block->chain = block_line->u.blocks;
block_line->u.blocks = block;
}
else if (flag_branches)
{
arc_t *arc;
for (arc = block->succ; arc; arc = arc->succ_next)
{
arc->line_next = line->u.branches;
line->u.branches = arc;
if (coverage && !arc->is_unconditional)
add_branch_counts (coverage, arc);
}
}
}
if (!line)
fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name);
}
static void
accumulate_line_counts (source_t *src)
{
line_t *line;
function_t *fn, *fn_p, *fn_n;
unsigned ix;
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++)
{
if (!flag_all_blocks)
{
arc_t *arc, *arc_p, *arc_n;
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;
}
else if (line->u.blocks)
{
block_t *block, *block_p, *block_n;
gcov_type count = 0;
for (block = line->u.blocks, block_p = NULL; block;
block_p = block, block = block_n)
{
block_n = block->chain;
block->chain = block_p;
block->u.cycle.ident = ix;
}
line->u.blocks = block_p;
for (block = line->u.blocks; block; block = block->chain)
{
arc_t *arc;
for (arc = block->pred; arc; arc = arc->pred_next)
{
if (arc->src->u.cycle.ident != ix)
count += arc->count;
if (flag_branches)
add_branch_counts (&src->coverage, arc);
}
for (arc = block->succ; arc; arc = arc->succ_next)
arc->cs_count = arc->count;
}
for (block = line->u.blocks; block; block = block->chain)
{
block_t *head = block;
arc_t *arc;
next_vertex:;
arc = head->succ;
current_vertex:;
while (arc)
{
block_t *dst = arc->dst;
if (
arc->cycle
|| dst->u.cycle.ident != ix
|| dst->u.cycle.arc)
{
arc = arc->succ_next;
continue;
}
if (dst == block)
{
gcov_type cycle_count = arc->cs_count;
arc_t *cycle_arc = arc;
arc_t *probe_arc;
for (dst = head; (probe_arc = dst->u.cycle.arc);
dst = probe_arc->src)
if (cycle_count > probe_arc->cs_count)
{
cycle_count = probe_arc->cs_count;
cycle_arc = probe_arc;
}
count += cycle_count;
cycle_arc->cycle = 1;
arc->cs_count -= cycle_count;
for (dst = head; (probe_arc = dst->u.cycle.arc);
dst = probe_arc->src)
probe_arc->cs_count -= cycle_count;
while (head != cycle_arc->src)
{
arc = head->u.cycle.arc;
head->u.cycle.arc = NULL;
head = arc->src;
}
arc = arc->succ_next;
continue;
}
dst->u.cycle.arc = arc;
head = dst;
goto next_vertex;
}
arc = head->u.cycle.arc;
if (arc)
{
head->u.cycle.arc = NULL;
head = arc->src;
arc = arc->succ_next;
goto current_vertex;
}
block->u.cycle.ident = ~0U;
}
line->count = count;
}
if (line->exists)
{
src->coverage.lines++;
if (line->count)
src->coverage.lines_executed++;
}
}
}
static int
output_branch_count (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->dst->is_call_return)
{
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;
}
static void
output_lines (FILE *gcov_file, const source_t *src)
{
FILE *source_file;
unsigned line_num;
const line_t *line;
char string[STRING_SIZE];
char const *retval = "";
function_t *fn = NULL;
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,
no_data_file ? "-" : da_file_name);
fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0,
object_summary.ctrs[GCOV_COUNTER_ARCS].runs);
fprintf (gcov_file, "%9s:%5d:Programs:%u\n", "-", 0, program_count);
source_file = fopen (src->name, "r");
if (!source_file)
{
fnotice (stderr, "%s:cannot open source file\n", src->name);
retval = NULL;
}
else
{
struct stat status;
if (!fstat (fileno (source_file), &status)
&& status.st_mtime > bbg_file_time)
{
fnotice (stderr, "%s:source file is newer than graph file '%s'\n",
src->name, bbg_file_name);
fprintf (gcov_file, "%9s:%5d:Source is newer than graph\n",
"-", 0);
}
}
if (flag_branches)
fn = src->functions;
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");
}
fprintf (gcov_file, "%9s:%5u:",
!line->exists ? "-" : !line->count ? "#####"
: format_gcov (line->count, 0, -1), line_num);
if (retval)
{
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);
if (flag_all_blocks)
{
block_t *block;
arc_t *arc;
int ix, jx;
for (ix = jx = 0, block = line->u.blocks; block;
block = block->chain)
{
if (!block->is_call_return)
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);
}
}
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);
}
}
}
if (source_file)
fclose (source_file);
}