#include "config.h"
#include "system.h"
#include "intl.h"
#include "version.h"
#undef abort
#include <getopt.h>
typedef HOST_WIDEST_INT gcov_type;
#include "gcov-io.h"
#define STRING_SIZE 200
struct sourcefile
{
char *name;
int maxlineno;
struct sourcefile *next;
};
struct sourcefile *sources;
struct adj_list
{
int source;
int target;
gcov_type arc_count;
unsigned int count_valid : 1;
unsigned int on_tree : 1;
unsigned int fake : 1;
unsigned int fall_through : 1;
#if 0
rtx branch_insn;
#endif
struct adj_list *pred_next;
struct adj_list *succ_next;
};
struct bb_info
{
struct adj_list *succ;
struct adj_list *pred;
gcov_type succ_count;
gcov_type pred_count;
gcov_type exec_count;
unsigned int count_valid : 1;
unsigned int on_tree : 1;
#if 0
rtx first_insn;
#endif
};
struct arcdata
{
gcov_type hits;
gcov_type total;
int call_insn;
struct arcdata *next;
};
struct bb_info_list
{
struct bb_info *bb_graph;
int num_blocks;
struct bb_info_list *next;
};
struct line_info
{
gcov_type count;
struct arcdata *branches;
unsigned exists : 1;
};
struct coverage
{
int lines;
int lines_executed;
int branches;
int branches_executed;
int branches_taken;
int calls;
int calls_executed;
char *name;
};
static struct bb_info_list *bb_graph_list = 0;
static time_t bb_file_time;
static char *bbg_file_name;
static FILE *bbg_file;
static char *da_file_name;
static FILE *da_file;
static char *bb_file_name;
static FILE *bb_file;
static char *bb_data;
static long bb_data_size;
static char *input_file_name = 0;
static int output_branch_probs = 0;
static int output_gcov_file = 1;
static int output_long_names = 0;
static int output_function_summary = 0;
static char *object_directory = 0;
static int preserve_paths = 0;
static int output_branch_counts = 0;
static void process_args PARAMS ((int, char **));
static void open_files PARAMS ((void));
static void read_files PARAMS ((void));
static void scan_for_source_files PARAMS ((void));
static void output_data PARAMS ((struct sourcefile *));
static void print_usage PARAMS ((int)) ATTRIBUTE_NORETURN;
static void print_version PARAMS ((void)) ATTRIBUTE_NORETURN;
static void init_arc PARAMS ((struct adj_list *, int, int, struct bb_info *));
static struct adj_list *reverse_arcs PARAMS ((struct adj_list *));
static gcov_type *read_profile PARAMS ((char *, long, int));
static void create_program_flow_graph PARAMS ((struct bb_info_list *));
static void solve_program_flow_graph PARAMS ((struct bb_info_list *));
static void accumulate_branch_counts PARAMS ((struct coverage *,
struct arcdata *));
static void calculate_branch_probs PARAMS ((struct bb_info *,
struct line_info *,
struct coverage *));
static void function_summary PARAMS ((struct coverage *, const char *));
static void init_line_info PARAMS ((struct line_info *,
struct coverage *, long));
static void output_line_info PARAMS ((FILE *, const struct line_info *,
const struct coverage *, long));
static char *make_gcov_file_name PARAMS ((char *));
static const char *format_hwint PARAMS ((HOST_WIDEST_INT, HOST_WIDEST_INT,
int));
extern int main PARAMS ((int, char **));
int
main (argc, argv)
int argc;
char **argv;
{
struct sourcefile *s_ptr;
gcc_init_libintl ();
process_args (argc, argv);
open_files ();
read_files ();
scan_for_source_files ();
for (s_ptr = sources; s_ptr; s_ptr = s_ptr->next)
output_data (s_ptr);
return 0;
}
static void fnotice PARAMS ((FILE *, const char *, ...)) ATTRIBUTE_PRINTF_2;
static void
fnotice VPARAMS ((FILE *file, const char *msgid, ...))
{
VA_OPEN (ap, msgid);
VA_FIXEDARG (ap, FILE *, file);
VA_FIXEDARG (ap, const char *, msgid);
vfprintf (file, _(msgid), ap);
VA_CLOSE (ap);
}
extern void fancy_abort PARAMS ((void)) ATTRIBUTE_NORETURN;
void
fancy_abort ()
{
fnotice (stderr, "Internal gcov abort.\n");
exit (FATAL_EXIT_CODE);
}
static void
print_usage (error_p)
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, " -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, "\nFor bug reporting instructions, please see:\n%s.\n",
bug_report_url);
exit (status);
}
static void
print_version ()
{
fnotice (stdout, "gcov (GCC) %s\n", version_string);
fnotice (stdout, "Copyright (C) 2001 Free Software Foundation, Inc.\n");
fnotice (stdout,
"This is free software; see the source for copying conditions. There is NO\n\
warranty; not even for MERCHANTABILITY or 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' },
{ "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' },
};
static void
process_args (argc, argv)
int argc;
char **argv;
{
int opt;
while ((opt = getopt_long (argc, argv, "hvbclnfo:p", options, NULL)) != -1)
{
switch (opt)
{
case 'h':
print_usage (false);
case 'v':
print_version ();
case 'b':
output_branch_probs = 1;
break;
case 'c':
output_branch_counts = 1;
break;
case 'n':
output_gcov_file = 0;
break;
case 'l':
output_long_names = 1;
break;
case 'f':
output_function_summary = 1;
break;
case 'o':
object_directory = optarg;
break;
case 'p':
preserve_paths = 1;
break;
default:
print_usage (true);
}
}
if (optind != argc - 1)
print_usage (true);
input_file_name = argv[optind];
}
static void
open_files ()
{
char *cptr;
char *name;
int length = strlen (input_file_name);
int base;
if (object_directory && object_directory[0])
{
struct stat status;
length += strlen (object_directory) + 2;
name = xmalloc (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 = xmalloc (length + 1);
name[0] = 0;
base = 1;
}
if (base)
{
cptr = strrchr (input_file_name, '/');
cptr = cptr ? cptr + 1 : input_file_name;
strcat (name, cptr);
}
cptr = strrchr (name, '.');
if (cptr)
*cptr = 0;
length = strlen (name);
da_file_name = xmalloc (length + 4);
bb_file_name = xmalloc (length + 4);
bbg_file_name = xmalloc (length + 5);
strcpy (da_file_name, name);
strcpy (bb_file_name, name);
strcpy (bbg_file_name, name);
strcpy (da_file_name + length, ".da");
strcpy (bb_file_name + length, ".bb");
strcpy (bbg_file_name + length, ".bbg");
bb_file = fopen (bb_file_name, "rb");
if (bb_file == NULL)
{
fnotice (stderr, "Could not open basic block file %s.\n", bb_file_name);
exit (FATAL_EXIT_CODE);
}
bbg_file = fopen (bbg_file_name, "rb");
if (bbg_file == NULL)
{
fnotice (stderr, "Could not open program flow graph file %s.\n",
bbg_file_name);
exit (FATAL_EXIT_CODE);
}
{
struct stat status;
if (!fstat (fileno (bb_file), &status))
bb_file_time = status.st_mtime;
}
da_file = fopen (da_file_name, "rb");
if (da_file == NULL)
{
fnotice (stderr, "Could not open data file %s.\n", da_file_name);
fnotice (stderr, "Assuming that all execution counts are zero.\n");
}
ungetc (getc (bbg_file), bbg_file);
if (feof (bbg_file))
{
fnotice (stderr, "No executable code associated with file %s.\n",
input_file_name);
exit (FATAL_EXIT_CODE);
}
}
static void
init_arc (arcptr, source, target, bb_graph)
struct adj_list *arcptr;
int source, target;
struct bb_info *bb_graph;
{
arcptr->target = target;
arcptr->source = source;
arcptr->arc_count = 0;
arcptr->count_valid = 0;
arcptr->on_tree = 0;
arcptr->fake = 0;
arcptr->fall_through = 0;
arcptr->succ_next = bb_graph[source].succ;
bb_graph[source].succ = arcptr;
bb_graph[source].succ_count++;
arcptr->pred_next = bb_graph[target].pred;
bb_graph[target].pred = arcptr;
bb_graph[target].pred_count++;
}
static struct adj_list *
reverse_arcs (arcptr)
struct adj_list *arcptr;
{
struct adj_list *prev = 0;
struct adj_list *next;
for ( ; arcptr; arcptr = next)
{
next = arcptr->succ_next;
arcptr->succ_next = prev;
prev = arcptr;
}
return prev;
}
static gcov_type *
read_profile (function_name, cfg_checksum, instr_arcs)
char *function_name;
long cfg_checksum;
int instr_arcs;
{
int i;
int okay = 1;
gcov_type *profile;
char *function_name_buffer;
int function_name_buffer_len;
profile = xmalloc (sizeof (gcov_type) * instr_arcs);
function_name_buffer_len = strlen (function_name) + 1;
function_name_buffer = xmalloc (function_name_buffer_len + 1);
for (i = 0; i < instr_arcs; i++)
profile[i] = 0;
if (!da_file)
return profile;
rewind (da_file);
while (1)
{
long magic, extra_bytes;
long func_count;
int i;
if (__read_long (&magic, da_file, 4) != 0)
break;
if (magic != -123)
{
okay = 0;
break;
}
if (__read_long (&func_count, da_file, 4) != 0)
{
okay = 0;
break;
}
if (__read_long (&extra_bytes, da_file, 4) != 0)
{
okay = 0;
break;
}
fseek (da_file, extra_bytes, SEEK_CUR);
for (i = 0; i < func_count; i++)
{
long arc_count;
long chksum;
int j;
if (__read_gcov_string
(function_name_buffer, function_name_buffer_len, da_file,
-1) != 0)
{
okay = 0;
break;
}
if (__read_long (&chksum, da_file, 4) != 0)
{
okay = 0;
break;
}
if (__read_long (&arc_count, da_file, 4) != 0)
{
okay = 0;
break;
}
if (strcmp (function_name_buffer, function_name) != 0
|| arc_count != instr_arcs || chksum != cfg_checksum)
{
if (fseek (da_file, arc_count * 8, SEEK_CUR) < 0)
{
okay = 0;
break;
}
}
else
{
gcov_type tmp;
for (j = 0; j < arc_count; j++)
if (__read_gcov_type (&tmp, da_file, 8) != 0)
{
okay = 0;
break;
}
else
{
profile[j] += tmp;
}
}
}
if (!okay)
break;
}
free (function_name_buffer);
if (!okay)
{
fprintf (stderr, ".da file corrupted!\n");
free (profile);
abort ();
}
return profile;
}
static void
create_program_flow_graph (bptr)
struct bb_info_list *bptr;
{
long num_blocks, number_arcs, src, dest, flag_bits, num_arcs_per_block;
int i;
struct adj_list *arcptr;
struct bb_info *bb_graph;
long cfg_checksum;
long instr_arcs = 0;
gcov_type *profile;
int profile_pos = 0;
char *function_name;
long function_name_len, tmp;
__read_long (&tmp, bbg_file, 4);
__read_long (&function_name_len, bbg_file, 4);
function_name = xmalloc (function_name_len + 1);
fread (function_name, 1, function_name_len + 1, bbg_file);
tmp = (function_name_len + 1) % 4;
if (tmp)
fseek (bbg_file, 4 - tmp, SEEK_CUR);
__read_long (&tmp, bbg_file, 4);
__read_long (&cfg_checksum, bbg_file, 4);
__read_long (&num_blocks, bbg_file, 4);
bb_graph = (struct bb_info *) xcalloc (num_blocks, sizeof (struct bb_info));
bptr->bb_graph = bb_graph;
bptr->num_blocks = num_blocks;
__read_long (&number_arcs, bbg_file, 4);
for (i = 0; i < num_blocks; i++)
{
int j;
__read_long (&num_arcs_per_block, bbg_file, 4);
for (j = 0; j < num_arcs_per_block; j++)
{
if (number_arcs-- < 0)
abort ();
src = i;
__read_long (&dest, bbg_file, 4);
arcptr = (struct adj_list *) xmalloc (sizeof (struct adj_list));
init_arc (arcptr, src, dest, bb_graph);
__read_long (&flag_bits, bbg_file, 4);
if (flag_bits & 0x1)
arcptr->on_tree++;
else
instr_arcs++;
arcptr->fake = !! (flag_bits & 0x2);
arcptr->fall_through = !! (flag_bits & 0x4);
}
}
if (number_arcs)
abort ();
__read_long (&src, bbg_file, 4);
if (src != -1)
abort ();
for (i = 0; i < num_blocks; i++)
if (bb_graph[i].succ)
bb_graph[i].succ = reverse_arcs (bb_graph[i].succ);
profile = read_profile (function_name, cfg_checksum, instr_arcs);
for (i = 0; i < num_blocks; i++)
for (arcptr = bb_graph[i].succ; arcptr; arcptr = arcptr->succ_next)
if (! arcptr->on_tree)
{
arcptr->arc_count = profile[profile_pos++];
arcptr->count_valid = 1;
bb_graph[i].succ_count--;
bb_graph[arcptr->target].pred_count--;
}
free (profile);
free (function_name);
}
static void
solve_program_flow_graph (bptr)
struct bb_info_list *bptr;
{
int passes, changes;
gcov_type total;
int i;
struct adj_list *arcptr;
struct bb_info *bb_graph;
int num_blocks;
num_blocks = bptr->num_blocks;
bb_graph = bptr->bb_graph;
changes = 1;
passes = 0;
while (changes)
{
passes++;
changes = 0;
for (i = num_blocks - 1; i >= 0; i--)
{
if (! bb_graph[i].count_valid)
{
if (bb_graph[i].succ_count == 0)
{
total = 0;
for (arcptr = bb_graph[i].succ; arcptr;
arcptr = arcptr->succ_next)
total += arcptr->arc_count;
bb_graph[i].exec_count = total;
bb_graph[i].count_valid = 1;
changes = 1;
}
else if (bb_graph[i].pred_count == 0)
{
total = 0;
for (arcptr = bb_graph[i].pred; arcptr;
arcptr = arcptr->pred_next)
total += arcptr->arc_count;
bb_graph[i].exec_count = total;
bb_graph[i].count_valid = 1;
changes = 1;
}
}
if (bb_graph[i].count_valid)
{
if (bb_graph[i].succ_count == 1)
{
total = 0;
for (arcptr = bb_graph[i].succ; arcptr;
arcptr = arcptr->succ_next)
total += arcptr->arc_count;
total = bb_graph[i].exec_count - total;
for (arcptr = bb_graph[i].succ; arcptr;
arcptr = arcptr->succ_next)
if (! arcptr->count_valid)
break;
if (! arcptr)
abort ();
arcptr->count_valid = 1;
arcptr->arc_count = total;
bb_graph[i].succ_count--;
bb_graph[arcptr->target].pred_count--;
changes = 1;
}
if (bb_graph[i].pred_count == 1)
{
total = 0;
for (arcptr = bb_graph[i].pred; arcptr;
arcptr = arcptr->pred_next)
total += arcptr->arc_count;
total = bb_graph[i].exec_count - total;
for (arcptr = bb_graph[i].pred; arcptr;
arcptr = arcptr->pred_next)
if (! arcptr->count_valid)
break;
if (! arcptr)
abort ();
arcptr->count_valid = 1;
arcptr->arc_count = total;
bb_graph[i].pred_count--;
bb_graph[arcptr->source].succ_count--;
changes = 1;
}
}
}
}
for (i = 0; i < num_blocks; i++)
if (bb_graph[i].succ_count || bb_graph[i].pred_count)
abort ();
}
static void
read_files ()
{
struct stat buf;
struct bb_info_list *list_end = 0;
struct bb_info_list *b_ptr;
while (! feof (bbg_file))
{
b_ptr = (struct bb_info_list *) xmalloc (sizeof (struct bb_info_list));
b_ptr->next = 0;
if (list_end)
list_end->next = b_ptr;
else
bb_graph_list = b_ptr;
list_end = b_ptr;
create_program_flow_graph (b_ptr);
ungetc (getc (bbg_file), bbg_file);
}
for (b_ptr = bb_graph_list; b_ptr; b_ptr = b_ptr->next)
solve_program_flow_graph (b_ptr);
stat (bb_file_name, &buf);
bb_data_size = buf.st_size / 4;
bb_data = (char *) xmalloc ((unsigned) buf.st_size);
fread (bb_data, sizeof (char), buf.st_size, bb_file);
fclose (bb_file);
if (da_file)
fclose (da_file);
fclose (bbg_file);
}
static void
scan_for_source_files ()
{
struct sourcefile *s_ptr = NULL;
char *ptr;
long count;
long line_num;
ptr = bb_data;
sources = 0;
for (count = 0; count < bb_data_size; count++)
{
__fetch_long (&line_num, ptr, 4);
ptr += 4;
if (line_num == -1)
{
s_ptr = sources;
while (s_ptr && strcmp (s_ptr->name, ptr))
s_ptr = s_ptr->next;
if (s_ptr == 0)
{
s_ptr = (struct sourcefile *) xmalloc (sizeof(struct sourcefile));
s_ptr->name = xstrdup (ptr);
s_ptr->maxlineno = 0;
s_ptr->next = sources;
sources = s_ptr;
}
{
long delim;
do {
count++;
__fetch_long (&delim, ptr, 4);
ptr += 4;
} while (delim != line_num);
}
}
else if (line_num == -2)
{
long delim;
do {
count++;
__fetch_long (&delim, ptr, 4);
ptr += 4;
} while (delim != line_num);
}
else if (line_num > 0)
{
if (s_ptr->maxlineno <= line_num)
s_ptr->maxlineno = line_num + 1;
}
else if (line_num < 0)
{
abort ();
}
}
}
static void
accumulate_branch_counts (function, a_ptr)
struct coverage *function;
struct arcdata *a_ptr;
{
if (a_ptr->call_insn)
{
function->calls++;
if (a_ptr->total)
function->calls_executed++;
}
else
{
function->branches++;
if (a_ptr->total)
function->branches_executed++;
if (a_ptr->hits)
function->branches_taken++;
}
}
static void
calculate_branch_probs (block_ptr, line_info, function)
struct bb_info *block_ptr;
struct line_info *line_info;
struct coverage *function;
{
gcov_type total;
struct adj_list *arcptr;
total = block_ptr->exec_count;
for (arcptr = block_ptr->succ; arcptr; arcptr = arcptr->succ_next)
{
struct arcdata *a_ptr;
if (arcptr->fall_through)
continue;
a_ptr = (struct arcdata *) xmalloc (sizeof (struct arcdata));
a_ptr->total = total;
a_ptr->hits = total ? arcptr->arc_count : 0;
a_ptr->call_insn = arcptr->fake;
if (function)
accumulate_branch_counts (function, a_ptr);
a_ptr->next = line_info->branches;
line_info->branches = a_ptr;
}
}
static char const *
format_hwint (top, bottom, dp)
HOST_WIDEST_INT top, 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, top);
return buffer;
}
static void
function_summary (function, title)
struct coverage *function;
const char *title;
{
if (function->lines)
fnotice (stdout, "%s of %d lines executed in %s %s\n",
format_hwint (function->lines_executed,
function->lines, 2),
function->lines, title, function->name);
else
fnotice (stdout, "No executable lines in %s %s\n",
title, function->name);
if (output_branch_probs)
{
if (function->branches)
{
fnotice (stdout, "%s of %d branches executed in %s %s\n",
format_hwint (function->branches_executed,
function->branches, 2),
function->branches, title, function->name);
fnotice (stdout,
"%s of %d branches taken at least once in %s %s\n",
format_hwint (function->branches_taken,
function->branches, 2),
function->branches, title, function->name);
}
else
fnotice (stdout, "No branches in %s %s\n", title, function->name);
if (function->calls)
fnotice (stdout, "%s of %d calls executed in %s %s\n",
format_hwint (function->calls_executed,
function->calls, 2),
function->calls, title, function->name);
else
fnotice (stdout, "No calls in %s %s\n", title, function->name);
}
}
static char *
make_gcov_file_name (src_name)
char *src_name;
{
char *cptr;
char *name = xmalloc (strlen (src_name) + strlen (input_file_name) + 10);
name[0] = 0;
if (output_long_names && strcmp (src_name, input_file_name))
{
cptr = preserve_paths ? NULL : strrchr (input_file_name, '/');
cptr = cptr ? cptr + 1 : input_file_name;
strcat (name, cptr);
strcat (name, "##");
}
cptr = preserve_paths ? NULL : strrchr (src_name, '/');
cptr = cptr ? cptr + 1 : src_name;
strcat (name, cptr);
if (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
init_line_info (line_info, total, maxlineno)
struct line_info *line_info;
struct coverage *total;
long maxlineno;
{
long block_num = 0;
struct bb_info *block_ptr = NULL;
struct coverage function;
struct coverage *func_ptr = NULL;
struct bb_info_list *current_graph = NULL;
int is_this_file = 0;
char *ptr = bb_data;
long count;
long line_num;
struct line_info *line_ptr = 0;
memset (&function, 0, sizeof (function));
if (output_function_summary)
func_ptr = &function;
for (count = 0; count < bb_data_size; count++)
{
__fetch_long (&line_num, ptr, 4);
ptr += 4;
if (line_num < 0)
{
long delim;
if (line_num == -1)
{
is_this_file = !strcmp (total->name, ptr);
}
else if (line_num == -2)
{
if (!current_graph)
current_graph = bb_graph_list;
else
{
if (block_num == current_graph->num_blocks - 1)
;
else if (block_num == current_graph->num_blocks - 2)
{
if (output_branch_probs && is_this_file)
calculate_branch_probs (block_ptr, line_ptr, func_ptr);
}
else
{
fnotice (stderr,
"didn't use all bb entries of graph, function %s\n",
function.name);
fnotice (stderr, "block_num = %ld, num_blocks = %d\n",
block_num, current_graph->num_blocks);
}
if (func_ptr && is_this_file)
function_summary (func_ptr, "function");
current_graph = current_graph->next;
}
block_num = 0;
block_ptr = current_graph->bb_graph;
memset (&function, 0, sizeof (function));
function.name = ptr;
}
else
{
fnotice (stderr, "ERROR: unexpected line number %ld\n", line_num);
abort ();
}
for (delim = 0; delim != line_num; count++)
{
__fetch_long (&delim, ptr, 4);
ptr += 4;
}
}
else if (!line_num)
{
if (block_num >= current_graph->num_blocks)
{
fnotice (stderr, "ERROR: too many basic blocks in function %s\n",
function.name);
abort ();
}
if (output_branch_probs && is_this_file)
calculate_branch_probs (block_ptr, line_ptr, func_ptr);
block_num++;
block_ptr++;
}
else if (is_this_file)
{
if (line_num >= maxlineno)
{
fnotice (stderr, "ERROR: out of range line number in function %s\n",
function.name);
abort ();
}
line_ptr = &line_info[line_num];
if (func_ptr)
{
if (!line_ptr->exists)
func_ptr->lines++;
if (!line_ptr->count && block_ptr->exec_count)
func_ptr->lines_executed++;
}
line_ptr->count += block_ptr->exec_count;
line_ptr->exists = 1;
}
}
if (func_ptr && is_this_file)
function_summary (func_ptr, "function");
for (line_num = 1, line_ptr = &line_info[line_num];
line_num < maxlineno; line_num++, line_ptr++)
{
struct arcdata *a_ptr, *prev, *next;
if (line_ptr->exists)
{
total->lines++;
if (line_ptr->count)
total->lines_executed++;
}
for (a_ptr = line_ptr->branches, prev = NULL; a_ptr; a_ptr = next)
{
next = a_ptr->next;
a_ptr->next = prev;
prev = a_ptr;
accumulate_branch_counts (total, a_ptr);
}
line_ptr->branches = prev;
}
}
static void
output_line_info (gcov_file, line_info, total, maxlineno)
FILE *gcov_file;
const struct line_info *line_info;
const struct coverage *total;
long maxlineno;
{
FILE *source_file;
long line_num;
const struct line_info *line_ptr;
char string[STRING_SIZE];
char const *retval = "";
fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, total->name);
fprintf (gcov_file, "%9s:%5d:Object:%s\n", "-", 0, bb_file_name);
source_file = fopen (total->name, "r");
if (!source_file)
{
fnotice (stderr, "Could not open source file %s.\n", total->name);
retval = NULL;
}
else
{
struct stat status;
if (!fstat (fileno (source_file), &status)
&& status.st_mtime > bb_file_time)
{
fnotice (stderr, "Warning: source file %s is newer than %s\n",
total->name, bb_file_name);
fprintf (gcov_file, "%9s:%5d:Source is newer than compiler output\n",
"-", 0);
}
}
for (line_num = 1, line_ptr = &line_info[line_num];
line_num < maxlineno; line_num++, line_ptr++)
{
fprintf (gcov_file, "%9s:%5ld:",
!line_ptr->exists ? "-"
: !line_ptr->count ? "#####"
: format_hwint (line_ptr->count, 0, -1), line_num);
if (retval)
{
do
{
retval = fgets (string, STRING_SIZE, source_file);
if (!retval)
{
fnotice (stderr,
"Unexpected EOF while reading source file %s.\n",
total->name);
break;
}
fputs (retval, gcov_file);
}
while (!retval[0] || retval[strlen (retval) - 1] != '\n');
}
if (!retval)
fputs ("??\n", gcov_file);
if (output_branch_probs)
{
int i;
struct arcdata *a_ptr;
for (i = 0, a_ptr = line_ptr->branches; a_ptr;
a_ptr = a_ptr->next, i++)
{
if (a_ptr->call_insn)
{
if (a_ptr->total == 0)
fnotice (gcov_file, "call %2d never executed\n", i);
else
fnotice
(gcov_file, "call %2d returns %s\n", i,
format_hwint (a_ptr->total - a_ptr->hits,
a_ptr->total,
-output_branch_counts));
}
else
{
if (a_ptr->total == 0)
fnotice (gcov_file, "branch %2d never executed\n", i);
else
fnotice
(gcov_file, "branch %2d taken %s\n", i,
format_hwint (a_ptr->hits, a_ptr->total,
-output_branch_counts));
}
}
}
}
if (retval)
{
for (; (retval = fgets (string, STRING_SIZE, source_file)); line_num++)
{
fprintf (gcov_file, "%9s:%5ld:%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);
}
static void
output_data (s_ptr)
struct sourcefile *s_ptr;
{
struct line_info *line_info
= (struct line_info *) xcalloc (s_ptr->maxlineno,
sizeof (struct line_info));
long line_num;
struct coverage total;
memset (&total, 0, sizeof (total));
total.name = s_ptr->name;
init_line_info (line_info, &total, s_ptr->maxlineno);
function_summary (&total, "file");
if (output_gcov_file)
{
char *gcov_file_name = make_gcov_file_name (total.name);
FILE *gcov_file = fopen (gcov_file_name, "w");
if (gcov_file)
{
fnotice (stdout, "Creating %s.\n", gcov_file_name);
output_line_info (gcov_file, line_info, &total, s_ptr->maxlineno);
if (ferror (gcov_file))
fnotice (stderr, "Error writing output file %s.\n",
gcov_file_name);
fclose (gcov_file);
}
else
fnotice (stderr, "Could not open output file %s.\n", gcov_file_name);
free (gcov_file_name);
}
for (line_num = 1; line_num != s_ptr->maxlineno; line_num++)
{
struct arcdata *branch, *next;
for (branch = line_info[line_num].branches; branch; branch = next)
{
next = branch->next;
free (branch);
}
}
free (line_info);
}