Commit 8b219a76 by Nathan Sidwell Committed by Nathan Sidwell

gcov.c: Tidy.

	* gcov.c: Tidy.
	(struct line_info, struct coverage): New structures.
	(gcov_file_name, gcov_file): Remove globals.
	(output_data): Take source file parameter. Fix memory leak. Break
	up into ...
	(init_line_info, output_line_info, make_gcov_file_name,
	accumulate_branch_counts): ... here.
	(calculate_branch_probs, function_summary): Adjust.
	(main): Adjust.
	(function_*): Remove global variables.

From-SVN: r56080
parent 317e98c0
2002-08-06 Nathan Sidwell <nathan@codesourcery.com>
* gcov.c: Tidy.
(struct line_info, struct coverage): New structures.
(gcov_file_name, gcov_file): Remove globals.
(output_data): Take source file parameter. Fix memory leak. Break
up into ...
(init_line_info, output_line_info, make_gcov_file_name,
accumulate_branch_counts): ... here.
(calculate_branch_probs, function_summary): Adjust.
(main): Adjust.
(function_*): Remove global variables.
2002-08-06 Neil Booth <neil@daikokuya.co.uk> 2002-08-06 Neil Booth <neil@daikokuya.co.uk>
* dwarf2out.c: Remove unused macros. * dwarf2out.c: Remove unused macros.
......
...@@ -103,7 +103,8 @@ struct sourcefile *sources; ...@@ -103,7 +103,8 @@ struct sourcefile *sources;
/* One of these is dynamically created whenever we identify an arc in the /* One of these is dynamically created whenever we identify an arc in the
function. */ function. */
struct adj_list { struct adj_list
{
int source; int source;
int target; int target;
gcov_type arc_count; gcov_type arc_count;
...@@ -122,7 +123,8 @@ struct adj_list { ...@@ -122,7 +123,8 @@ struct adj_list {
/* Count the number of basic blocks, and create an array of these structures, /* Count the number of basic blocks, and create an array of these structures,
one for each bb in the function. */ one for each bb in the function. */
struct bb_info { struct bb_info
{
struct adj_list *succ; struct adj_list *succ;
struct adj_list *pred; struct adj_list *pred;
gcov_type succ_count; gcov_type succ_count;
...@@ -149,13 +151,37 @@ struct arcdata ...@@ -149,13 +151,37 @@ struct arcdata
/* Used to save the list of bb_graphs, one per function. */ /* Used to save the list of bb_graphs, one per function. */
struct bb_info_list { struct bb_info_list
{
/* Indexed by block number, holds the basic block graph for one function. */ /* Indexed by block number, holds the basic block graph for one function. */
struct bb_info *bb_graph; struct bb_info *bb_graph;
int num_blocks; int num_blocks;
struct bb_info_list *next; struct bb_info_list *next;
}; };
/* Used to hold information about each line. */
struct line_info
{
gcov_type count; /* execution count */
struct arcdata *branches; /* list of branch probabilities for line. */
unsigned exists : 1; /* has code associated with it. */
};
struct coverage
{
int lines;
int lines_executed;
int branches;
int branches_executed;
int branches_taken;
int calls;
int calls_executed;
char *name;
};
/* Holds a list of function basic block graphs. */ /* Holds a list of function basic block graphs. */
static struct bb_info_list *bb_graph_list = 0; static struct bb_info_list *bb_graph_list = 0;
...@@ -187,11 +213,6 @@ static char *bb_data; ...@@ -187,11 +213,6 @@ static char *bb_data;
static long bb_data_size; static long bb_data_size;
/* Name and file pointer of the output file. */
static char *gcov_file_name;
static FILE *gcov_file;
/* Name of the file mentioned on the command line. */ /* Name of the file mentioned on the command line. */
static char *input_file_name = 0; static char *input_file_name = 0;
...@@ -235,7 +256,7 @@ static void process_args PARAMS ((int, char **)); ...@@ -235,7 +256,7 @@ static void process_args PARAMS ((int, char **));
static void open_files PARAMS ((void)); static void open_files PARAMS ((void));
static void read_files PARAMS ((void)); static void read_files PARAMS ((void));
static void scan_for_source_files PARAMS ((void)); static void scan_for_source_files PARAMS ((void));
static void output_data PARAMS ((void)); static void output_data PARAMS ((struct sourcefile *));
static void print_usage PARAMS ((int)) ATTRIBUTE_NORETURN; static void print_usage PARAMS ((int)) ATTRIBUTE_NORETURN;
static void print_version PARAMS ((void)) ATTRIBUTE_NORETURN; static void print_version PARAMS ((void)) ATTRIBUTE_NORETURN;
static void init_arc PARAMS ((struct adj_list *, int, int, struct bb_info *)); static void init_arc PARAMS ((struct adj_list *, int, int, struct bb_info *));
...@@ -243,10 +264,19 @@ static struct adj_list *reverse_arcs PARAMS ((struct adj_list *)); ...@@ -243,10 +264,19 @@ static struct adj_list *reverse_arcs PARAMS ((struct adj_list *));
static gcov_type *read_profile PARAMS ((char *, long, int)); static gcov_type *read_profile PARAMS ((char *, long, int));
static void create_program_flow_graph PARAMS ((struct bb_info_list *)); static void create_program_flow_graph PARAMS ((struct bb_info_list *));
static void solve_program_flow_graph PARAMS ((struct bb_info_list *)); static void solve_program_flow_graph PARAMS ((struct bb_info_list *));
static void calculate_branch_probs PARAMS ((struct bb_info_list *, int, static void accumulate_branch_counts PARAMS ((struct coverage *,
struct arcdata **, int)); struct arcdata *));
static void function_summary PARAMS ((void)); static void calculate_branch_probs PARAMS ((struct bb_info *,
static const char *format_hwint PARAMS ((HOST_WIDEST_INT, HOST_WIDEST_INT, int)); 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 **)); extern int main PARAMS ((int, char **));
...@@ -255,6 +285,8 @@ main (argc, argv) ...@@ -255,6 +285,8 @@ main (argc, argv)
int argc; int argc;
char **argv; char **argv;
{ {
struct sourcefile *s_ptr;
gcc_init_libintl (); gcc_init_libintl ();
process_args (argc, argv); process_args (argc, argv);
...@@ -265,7 +297,8 @@ main (argc, argv) ...@@ -265,7 +297,8 @@ main (argc, argv)
scan_for_source_files (); scan_for_source_files ();
output_data (); for (s_ptr = sources; s_ptr; s_ptr = s_ptr->next)
output_data (s_ptr);
return 0; return 0;
} }
...@@ -530,7 +563,6 @@ init_arc (arcptr, source, target, bb_graph) ...@@ -530,7 +563,6 @@ init_arc (arcptr, source, target, bb_graph)
bb_graph[target].pred_count++; bb_graph[target].pred_count++;
} }
/* Reverse the arcs on an arc list. */ /* Reverse the arcs on an arc list. */
static struct adj_list * static struct adj_list *
...@@ -1037,77 +1069,61 @@ scan_for_source_files () ...@@ -1037,77 +1069,61 @@ scan_for_source_files ()
} }
} }
/* For calculating coverage at the function level. */
static int function_source_lines; /* Increment totals in FUNCTION according to arc A_PTR. */
static int function_source_lines_executed;
static int function_branches; static void
static int function_branches_executed; accumulate_branch_counts (function, a_ptr)
static int function_branches_taken; struct coverage *function;
static int function_calls; struct arcdata *a_ptr;
static int function_calls_executed; {
static char *function_name; 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++;
}
}
/* Calculate the branch taken probabilities for all arcs branches at the /* Calculate the branch taken probabilities for all arcs branches at the
end of this block. */ end of this block. */
static void static void
calculate_branch_probs (current_graph, block_num, branch_probs, last_line_num) calculate_branch_probs (block_ptr, line_info, function)
struct bb_info_list *current_graph; struct bb_info *block_ptr;
int block_num; struct line_info *line_info;
struct arcdata **branch_probs; struct coverage *function;
int last_line_num;
{ {
gcov_type total; gcov_type total;
struct adj_list *arcptr; struct adj_list *arcptr;
struct arcdata *end_ptr, *a_ptr;
total = current_graph->bb_graph[block_num].exec_count; total = block_ptr->exec_count;
for (arcptr = current_graph->bb_graph[block_num].succ; arcptr; for (arcptr = block_ptr->succ; arcptr; arcptr = arcptr->succ_next)
arcptr = arcptr->succ_next)
{ {
/* Ignore fall through arcs as they aren't really branches. */ struct arcdata *a_ptr;
/* Ignore fall through arcs as they aren't really branches. */
if (arcptr->fall_through) if (arcptr->fall_through)
continue; continue;
a_ptr = (struct arcdata *) xmalloc (sizeof (struct arcdata)); a_ptr = (struct arcdata *) xmalloc (sizeof (struct arcdata));
a_ptr->total = total; a_ptr->total = total;
if (total == 0) a_ptr->hits = total ? arcptr->arc_count : 0;
a_ptr->hits = 0;
else
a_ptr->hits = arcptr->arc_count;
a_ptr->call_insn = arcptr->fake; a_ptr->call_insn = arcptr->fake;
if (output_function_summary) if (function)
{ accumulate_branch_counts (function, a_ptr);
if (a_ptr->call_insn) /* Prepend the new branch to the list. */
{ a_ptr->next = line_info->branches;
function_calls++; line_info->branches = a_ptr;
if (a_ptr->total != 0)
function_calls_executed++;
}
else
{
function_branches++;
if (a_ptr->total != 0)
function_branches_executed++;
if (a_ptr->hits > 0)
function_branches_taken++;
}
}
/* Append the new branch to the end of the list. */
a_ptr->next = 0;
if (! branch_probs[last_line_num])
branch_probs[last_line_num] = a_ptr;
else
{
end_ptr = branch_probs[last_line_num];
while (end_ptr->next != 0)
end_ptr = end_ptr->next;
end_ptr->next = a_ptr;
}
} }
} }
...@@ -1162,137 +1178,162 @@ format_hwint (top, bottom, dp) ...@@ -1162,137 +1178,162 @@ format_hwint (top, bottom, dp)
/* Output summary info for a function. */ /* Output summary info for a function. */
static void static void
function_summary () function_summary (function, title)
struct coverage *function;
const char *title;
{ {
if (function_source_lines) if (function->lines)
fnotice (stdout, "%s of %d source lines executed in function %s\n", fnotice (stdout, "%s of %d lines executed in %s %s\n",
format_hwint (function_source_lines_executed, format_hwint (function->lines_executed,
function_source_lines, 2), function->lines, 2),
function_source_lines, function_name); function->lines, title, function->name);
else else
fnotice (stdout, "No executable source lines in function %s\n", fnotice (stdout, "No executable lines in %s %s\n",
function_name); title, function->name);
if (output_branch_probs) if (output_branch_probs)
{ {
if (function_branches) if (function->branches)
{ {
fnotice (stdout, "%s of %d branches executed in function %s\n", fnotice (stdout, "%s of %d branches executed in %s %s\n",
format_hwint (function_branches_executed, format_hwint (function->branches_executed,
function_branches, 2), function->branches, 2),
function_branches, function_name); function->branches, title, function->name);
fnotice (stdout, fnotice (stdout,
"%s of %d branches taken at least once in function %s\n", "%s of %d branches taken at least once in %s %s\n",
format_hwint (function_branches_taken, format_hwint (function->branches_taken,
function_branches, 2), function->branches, 2),
function_branches, function_name); function->branches, title, function->name);
} }
else else
fnotice (stdout, "No branches in function %s\n", function_name); fnotice (stdout, "No branches in %s %s\n", title, function->name);
if (function_calls) if (function->calls)
fnotice (stdout, "%s of %d calls executed in function %s\n", fnotice (stdout, "%s of %d calls executed in %s %s\n",
format_hwint (function_calls_executed, format_hwint (function->calls_executed,
function_calls, 2), function->calls, 2),
function_calls, function_name); function->calls, title, function->name);
else else
fnotice (stdout, "No calls in function %s\n", function_name); fnotice (stdout, "No calls in %s %s\n", title, function->name);
} }
} }
/* Calculate line execution counts, and output the data to a .tcov file. */ /* Generate an output file name. LONG_OUTPUT_NAMES and PRESERVE_PATHS
affect name generation. With preserve_paths we create a filename
static void from all path components of the source file, replacing '/' with
output_data () '#', without it we simply take the basename component. With
long_output_names we prepend the processed name of the input file
to each output name (except when the current source file is the
input file, so you don't get a double concatenation). The two
components are separated by '##'. Also '.' filename components are
removed and '..' components are renamed to '^'. */
static char *
make_gcov_file_name (src_name)
char *src_name;
{ {
/* When scanning data, this is true only if the data applies to the
current source file. */
int this_file;
/* An array indexed by line number which indicates how many times that line
was executed. */
gcov_type *line_counts;
/* An array indexed by line number which indicates whether the line was
present in the bb file (i.e. whether it had code associate with it).
Lines never executed are those which both exist, and have zero execution
counts. */
char *line_exists;
/* An array indexed by line number, which contains a list of branch
probabilities, one for each branch on that line. */
struct arcdata **branch_probs = NULL;
struct sourcefile *s_ptr;
char *source_file_name;
FILE *source_file;
struct bb_info_list *current_graph;
long count;
char *cptr; char *cptr;
long block_num; char *name = xmalloc (strlen (src_name) + strlen (input_file_name) + 10);
long line_num;
long last_line_num = 0;
int i;
struct arcdata *a_ptr;
/* Buffer used for reading in lines from the source file. */
char string[STRING_SIZE];
/* For calculating coverage at the file level. */
int total_source_lines;
int total_source_lines_executed;
int total_branches;
int total_branches_executed;
int total_branches_taken;
int total_calls;
int total_calls_executed;
/* Now, for each source file, allocate an array big enough to hold a count
for each line. Scan through the bb_data, and when the file name matches
the current file name, then for each following line number, increment
the line number execution count indicated by the execution count of
the appropriate basic block. */
for (s_ptr = sources; s_ptr; s_ptr = s_ptr->next) name[0] = 0;
if (output_long_names && strcmp (src_name, input_file_name))
{
/* Generate the input filename part. */
cptr = preserve_paths ? NULL : strrchr (input_file_name, '/');
cptr = cptr ? cptr + 1 : input_file_name;
strcat (name, cptr);
strcat (name, "##");
}
/* Generate the source filename part. */
cptr = preserve_paths ? NULL : strrchr (src_name, '/');
cptr = cptr ? cptr + 1 : src_name;
strcat (name, cptr);
if (preserve_paths)
{ {
source_file_name = s_ptr->name; /* Convert '/' to '#', remove '/./', convert '/../' to '/^/' */
char *prev;
line_counts = (gcov_type *) xcalloc (sizeof (gcov_type), s_ptr->maxlineno); for (cptr = name; (cptr = strchr ((prev = cptr), '/'));)
line_exists = xcalloc (1, s_ptr->maxlineno); {
if (output_branch_probs) unsigned shift = 0;
branch_probs = (struct arcdata **)
xcalloc (sizeof (struct arcdata *), s_ptr->maxlineno);
/* There will be a zero at the beginning of the bb info, before the if (prev + 1 == cptr && prev[0] == '.')
first list of line numbers, so must initialize block_num to 0. */
block_num = 0;
this_file = 0;
current_graph = 0;
{ {
/* Pointer into the bb_data, incremented while scanning the data. */ /* Remove '.' */
shift = 2;
}
else if (prev + 2 == cptr && prev[0] == '.' && prev[1] == '.')
{
/* Convert '..' */
shift = 1;
prev[1] = '^';
}
else
*cptr++ = '#';
if (shift)
{
cptr = prev;
do
prev[0] = prev[shift];
while (*prev++);
}
}
}
/* Don't strip off the ending for compatibility with tcov, since
this results in confusion if there is more than one file with the
same basename, e.g. tmp.c and tmp.h. */
strcat (name, ".gcov");
return name;
}
/* Scan through the bb_data, and when the file name matches the
source file name, then for each following line number, increment
the line number execution count indicated by the execution count of
the appropriate basic block. */
static void
init_line_info (line_info, total, maxlineno)
struct line_info *line_info;
struct coverage *total;
long maxlineno;
{
long block_num = 0; /* current block number */
struct bb_info *block_ptr = NULL; /* current block ptr */
struct coverage function;
struct coverage *func_ptr = NULL;
struct bb_info_list *current_graph = NULL; /* Graph for current function. */
int is_this_file = 0; /* We're scanning a block from the desired file. */
char *ptr = bb_data; char *ptr = bb_data;
long count;
long line_num;
struct line_info *line_ptr; /* line info ptr. */
memset (&function, 0, sizeof (function));
if (output_function_summary)
func_ptr = &function;
for (count = 0; count < bb_data_size; count++) for (count = 0; count < bb_data_size; count++)
{ {
long delim;
__fetch_long (&line_num, ptr, 4); __fetch_long (&line_num, ptr, 4);
ptr += 4; ptr += 4;
if (line_num == -1) if (line_num < 0)
{ {
/* Marks the beginning of a file name. Check to see whether long delim;
this is the filename we are currently collecting data for. */
if (strcmp (s_ptr->name, ptr))
this_file = 0;
else
this_file = 1;
/* Scan past the file name. */ if (line_num == -1)
do { {
count++; /* Marks the beginning of a file name. Check to see
__fetch_long (&delim, ptr, 4); whether this is the filename we are currently
ptr += 4; collecting data for. */
} while (delim != line_num); is_this_file = !strcmp (total->name, ptr);
} }
else if (line_num == -2) else if (line_num == -2)
{ {
/* Marks the start of a new function. Advance to the next /* Marks the start of a new function. Advance to the
program flow graph. */ next program flow graph. */
if (!current_graph)
if (! current_graph)
current_graph = bb_graph_list; current_graph = bb_graph_list;
else else
{ {
...@@ -1301,256 +1342,134 @@ output_data () ...@@ -1301,256 +1342,134 @@ output_data ()
; ;
else if (block_num == current_graph->num_blocks - 2) else if (block_num == current_graph->num_blocks - 2)
{ {
if (output_branch_probs && this_file) if (output_branch_probs && is_this_file)
calculate_branch_probs (current_graph, block_num, calculate_branch_probs (block_ptr, line_ptr, func_ptr);
branch_probs, last_line_num);
} }
else else
{ {
fnotice (stderr, fnotice (stderr,
"didn't use all bb entries of graph, function %s\n", "didn't use all bb entries of graph, function %s\n",
function_name); function.name);
fnotice (stderr, "block_num = %ld, num_blocks = %d\n", fnotice (stderr, "block_num = %ld, num_blocks = %d\n",
block_num, current_graph->num_blocks); block_num, current_graph->num_blocks);
} }
if (func_ptr && is_this_file)
function_summary (func_ptr, "function");
current_graph = current_graph->next; current_graph = current_graph->next;
}
block_num = 0; block_num = 0;
block_ptr = current_graph->bb_graph;
if (output_function_summary && this_file) memset (&function, 0, sizeof (function));
function_summary (); function.name = ptr;
} }
else
if (output_function_summary)
{ {
function_source_lines = 0; fnotice (stderr, "ERROR: unexpected line number %ld\n", line_num);
function_source_lines_executed = 0; abort ();
function_branches = 0;
function_branches_executed = 0;
function_branches_taken = 0;
function_calls = 0;
function_calls_executed = 0;
} }
/* Save the function name for later use. */ /* Scan past the string. */
function_name = ptr; for (delim = 0; delim != line_num; count++)
{
/* Scan past the file name. */
do {
count++;
__fetch_long (&delim, ptr, 4); __fetch_long (&delim, ptr, 4);
ptr += 4; ptr += 4;
} while (delim != line_num);
} }
else if (line_num == 0) }
else if (!line_num)
{ {
/* Marks the end of a block. */ /* Marks the end of a block. */
if (block_num >= current_graph->num_blocks) if (block_num >= current_graph->num_blocks)
{ {
fnotice (stderr, "ERROR: too many basic blocks in .bb file %s\n", fnotice (stderr, "ERROR: too many basic blocks in function %s\n",
function_name); function.name);
abort (); abort ();
} }
if (output_branch_probs && this_file) if (output_branch_probs && is_this_file)
calculate_branch_probs (current_graph, block_num, calculate_branch_probs (block_ptr, line_ptr, func_ptr);
branch_probs, last_line_num);
block_num++; block_num++;
block_ptr++;
} }
else if (this_file) else if (is_this_file)
{ {
if (output_function_summary) if (line_num >= maxlineno)
{ {
if (line_exists[line_num] == 0) fnotice (stderr, "ERROR: out of range line number in function %s\n",
function_source_lines++; function.name);
if (line_counts[line_num] == 0 abort ();
&& current_graph->bb_graph[block_num].exec_count != 0)
function_source_lines_executed++;
}
/* Accumulate execution data for this line number. */
line_counts[line_num]
+= current_graph->bb_graph[block_num].exec_count;
line_exists[line_num] = 1;
last_line_num = line_num;
}
}
} }
if (output_function_summary && this_file) line_ptr = &line_info[line_num];
function_summary (); if (func_ptr)
/* Calculate summary test coverage statistics. */
total_source_lines = 0;
total_source_lines_executed = 0;
total_branches = 0;
total_branches_executed = 0;
total_branches_taken = 0;
total_calls = 0;
total_calls_executed = 0;
for (count = 1; count < s_ptr->maxlineno; count++)
{
if (line_exists[count])
{
total_source_lines++;
if (line_counts[count])
total_source_lines_executed++;
}
if (output_branch_probs)
{
for (a_ptr = branch_probs[count]; a_ptr; a_ptr = a_ptr->next)
{
if (a_ptr->call_insn)
{ {
total_calls++; if (!line_ptr->exists)
if (a_ptr->total != 0) func_ptr->lines++;
total_calls_executed++; if (!line_ptr->count && block_ptr->exec_count)
} func_ptr->lines_executed++;
else
{
total_branches++;
if (a_ptr->total != 0)
total_branches_executed++;
if (a_ptr->hits > 0)
total_branches_taken++;
}
} }
/* Accumulate execution data for this line number. */
line_ptr->count += block_ptr->exec_count;
line_ptr->exists = 1;
} }
} }
if (total_source_lines) if (func_ptr && is_this_file)
fnotice (stdout, function_summary (func_ptr, "function");
"%s of %d source lines executed in file %s\n",
format_hwint (total_source_lines_executed,
total_source_lines, 2),
total_source_lines, source_file_name);
else
fnotice (stdout, "No executable source lines in file %s\n",
source_file_name);
if (output_branch_probs) /* Calculate summary test coverage statistics. */
{ for (line_num = 1, line_ptr = &line_info[line_num];
if (total_branches) line_num < maxlineno; line_num++, line_ptr++)
{ {
fnotice (stdout, "%s of %d branches executed in file %s\n", struct arcdata *a_ptr, *prev, *next;
format_hwint (total_branches_executed,
total_branches, 2),
total_branches, source_file_name);
fnotice (stdout,
"%s of %d branches taken at least once in file %s\n",
format_hwint (total_branches_taken,
total_branches, 2),
total_branches, source_file_name);
}
else
fnotice (stdout, "No branches in file %s\n", source_file_name);
if (total_calls)
fnotice (stdout, "%s of %d calls executed in file %s\n",
format_hwint (total_calls_executed, total_calls, 2),
total_calls, source_file_name);
else
fnotice (stdout, "No calls in file %s\n", source_file_name);
}
if (output_gcov_file) if (line_ptr->exists)
{
/* Now the statistics are ready. Read in the source file one line
at a time, and output that line to the gcov file preceded by
its execution count if non zero. */
char const *retval;
/* Generate an output file name. LONG_OUTPUT_NAMES and
PRESERVE_PATHS affect name generation. With
preserve_paths we create a filename from all path
components of the source file, replacing '/' with '#',
without it we simply take the basename component. With
long_output_names we prepend the processed name of the
input file to each output name (except when the current
source file is the input file, so you don't get a double
concatenation). The two components are separated by
'##'. Also '.' filename components are removed and '..'
components are renamed to '^'. */
gcov_file_name = xmalloc (strlen (source_file_name)
+ strlen (input_file_name) + 10);
gcov_file_name[0] = 0;
if (output_long_names && strcmp (source_file_name, input_file_name))
{ {
/* Generate the input filename part. */ total->lines++;
cptr = preserve_paths ? NULL : strrchr (input_file_name, '/'); if (line_ptr->count)
cptr = cptr ? cptr + 1 : input_file_name; total->lines_executed++;
strcat (gcov_file_name, cptr);
strcat (gcov_file_name, "##");
} }
/* Generate the source filename part. */
cptr = preserve_paths ? NULL : strrchr (source_file_name, '/');
cptr = cptr ? cptr + 1 : source_file_name;
strcat (gcov_file_name, cptr);
if (preserve_paths) /* Total and reverse the branch information. */
for (a_ptr = line_ptr->branches, prev = NULL; a_ptr; a_ptr = next)
{ {
/* Convert '/' to '#', remove '/./', convert '/../' to next = a_ptr->next;
'/^/' */ a_ptr->next = prev;
char *prev; prev = a_ptr;
for (cptr = gcov_file_name;
(cptr = strchr ((prev = cptr), '/'));)
{
unsigned shift = 0;
if (prev + 1 == cptr && prev[0] == '.') accumulate_branch_counts (total, a_ptr);
{
/* Remove '.' */
shift = 2;
} }
else if (prev + 2 == cptr line_ptr->branches = prev;
&& prev[0] == '.' && prev[1] == '.')
{
/* Convert '..' */
shift = 1;
prev[1] = '^';
} }
else }
*cptr++ = '#';
if (shift)
{
cptr = prev;
do
prev[0] = prev[shift];
while (*prev++);
}
}
}
/* Don't strip off the ending for compatibility with tcov, since
this results in confusion if there is more than one file with
the same basename, e.g. tmp.c and tmp.h. */
strcat (gcov_file_name, ".gcov");
gcov_file = fopen (gcov_file_name, "w"); /* 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. */
if (gcov_file == NULL) static void
{ output_line_info (gcov_file, line_info, total, maxlineno)
fnotice (stderr, "Could not open output file %s.\n", FILE *gcov_file;
gcov_file_name); const struct line_info *line_info;
free (line_counts); const struct coverage *total;
free (line_exists); long maxlineno;
continue; {
} FILE *source_file;
long line_num; /* current line number */
fnotice (stdout, "Creating %s.\n", gcov_file_name); const struct line_info *line_ptr; /* current line info ptr. */
char string[STRING_SIZE]; /* line buffer. */
char const *retval = ""; /* status of source file reading. */
fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, source_file_name); fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, total->name);
fprintf (gcov_file, "%9s:%5d:Object:%s\n", "-", 0, bb_file_name); fprintf (gcov_file, "%9s:%5d:Object:%s\n", "-", 0, bb_file_name);
source_file = fopen (source_file_name, "r"); source_file = fopen (total->name, "r");
if (source_file == NULL) if (!source_file)
fnotice (stderr, "Could not open source file %s.\n", {
source_file_name); fnotice (stderr, "Could not open source file %s.\n", total->name);
retval = NULL;
}
else else
{ {
struct stat status; struct stat status;
...@@ -1559,29 +1478,29 @@ output_data () ...@@ -1559,29 +1478,29 @@ output_data ()
&& status.st_mtime > bb_file_time) && status.st_mtime > bb_file_time)
{ {
fnotice (stderr, "Warning: source file %s is newer than %s\n", fnotice (stderr, "Warning: source file %s is newer than %s\n",
source_file_name, bb_file_name); total->name, bb_file_name);
fprintf (gcov_file, "%9s:%5d:Source is newer than compiler output\n", "-", 0); fprintf (gcov_file, "%9s:%5d:Source is newer than compiler output\n",
"-", 0);
} }
} }
for (retval = source_file ? "" : NULL, count = 1; for (line_num = 1, line_ptr = &line_info[line_num];
count < s_ptr->maxlineno; count++) line_num < maxlineno; line_num++, line_ptr++)
{ {
/* For lines which don't exist in the .bb file, print /* For lines which don't exist in the .bb file, print '-' before
'-' before the source line. For lines which exist the source line. For lines which exist but were never
but were never executed, print '#####' before the source executed, print '#####' before the source line. Otherwise,
line. Otherwise, print the execution count before print the execution count before the source line. There are
the source line. */ 16 spaces of indentation added before the source line so that
tabs won't be messed up. */
/* There are 16 spaces of indentation added before the source
line so that tabs won't be messed up. */
fprintf (gcov_file, "%9s:%5ld:", fprintf (gcov_file, "%9s:%5ld:",
!line_exists[count] ? "-" !line_ptr->exists ? "-"
: !line_counts[count] ? "#####" : !line_ptr->count ? "#####"
: format_hwint (line_counts[count], 0, -1), count); : format_hwint (line_ptr->count, 0, -1), line_num);
if (retval) if (retval)
{ {
/* Copy source line. */
do do
{ {
retval = fgets (string, STRING_SIZE, source_file); retval = fgets (string, STRING_SIZE, source_file);
...@@ -1589,7 +1508,7 @@ output_data () ...@@ -1589,7 +1508,7 @@ output_data ()
{ {
fnotice (stderr, fnotice (stderr,
"Unexpected EOF while reading source file %s.\n", "Unexpected EOF while reading source file %s.\n",
source_file_name); total->name);
break; break;
} }
fputs (retval, gcov_file); fputs (retval, gcov_file);
...@@ -1601,7 +1520,10 @@ output_data () ...@@ -1601,7 +1520,10 @@ output_data ()
if (output_branch_probs) if (output_branch_probs)
{ {
for (i = 0, a_ptr = branch_probs[count]; a_ptr; int i;
struct arcdata *a_ptr;
for (i = 0, a_ptr = line_ptr->branches; a_ptr;
a_ptr = a_ptr->next, i++) a_ptr = a_ptr->next, i++)
{ {
if (a_ptr->call_insn) if (a_ptr->call_insn)
...@@ -1618,8 +1540,7 @@ output_data () ...@@ -1618,8 +1540,7 @@ output_data ()
else else
{ {
if (a_ptr->total == 0) if (a_ptr->total == 0)
fnotice (gcov_file, "branch %2d never executed\n", fnotice (gcov_file, "branch %2d never executed\n", i);
i);
else else
fnotice fnotice
(gcov_file, "branch %2d taken %s\n", i, (gcov_file, "branch %2d taken %s\n", i,
...@@ -1630,14 +1551,13 @@ output_data () ...@@ -1630,14 +1551,13 @@ output_data ()
} }
} }
/* Handle all remaining source lines. There may be lines /* Handle all remaining source lines. There may be lines after the
after the last line of code. */ last line of code. */
if (retval) if (retval)
{ {
for (; (retval = fgets (string, STRING_SIZE, source_file)); for (; (retval = fgets (string, STRING_SIZE, source_file)); line_num++)
count++)
{ {
fprintf (gcov_file, "%9s:%5ld:%s", "-", count, retval); fprintf (gcov_file, "%9s:%5ld:%s", "-", line_num, retval);
while (!retval[0] || retval[strlen (retval) - 1] != '\n') while (!retval[0] || retval[strlen (retval) - 1] != '\n')
{ {
...@@ -1651,13 +1571,64 @@ output_data () ...@@ -1651,13 +1571,64 @@ output_data ()
if (source_file) if (source_file)
fclose (source_file); fclose (source_file);
}
/* Calculate line execution counts, and output a .gcov file for source
file S_PTR. Allocate an array big enough to hold a count for each
line. Scan through the bb_data, and when the file name matches the
current file name, then for each following line number, increment
the line number execution count indicated by the execution count of
the appropriate basic block. */
static void
output_data (s_ptr)
struct sourcefile *s_ptr;
{
struct line_info *line_info /* line info data */
= (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)
{
/* Now the statistics are ready. Read in the source file one
line at a time, and output that line to the gcov file
preceded by its execution information. */
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)) if (ferror (gcov_file))
fnotice (stderr, "Error writing output file %s.\n", fnotice (stderr, "Error writing output file %s.\n",
gcov_file_name); gcov_file_name);
fclose (gcov_file); fclose (gcov_file);
} }
else
fnotice (stderr, "Could not open output file %s.\n", gcov_file_name);
free (gcov_file_name);
}
free (line_counts); /* Free data. */
free (line_exists); 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);
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment