1376 lines
37 KiB
C
1376 lines
37 KiB
C
|
/* Gcov.c: prepend line execution counts and branch probabilities to a
|
|||
|
source file.
|
|||
|
Copyright (C) 1990, 91, 92, 93, 94, 96, 1997 Free Software Foundation, Inc.
|
|||
|
Contributed by James E. Wilson of Cygnus Support.
|
|||
|
Mangled by Bob Manson of Cygnus Support.
|
|||
|
|
|||
|
Gcov is free software; you can redistribute it and/or modify
|
|||
|
it under the terms of the GNU General Public License as published by
|
|||
|
the Free Software Foundation; either version 2, or (at your option)
|
|||
|
any later version.
|
|||
|
|
|||
|
Gcov is distributed in the hope that it will be useful,
|
|||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||
|
GNU General Public License for more details.
|
|||
|
|
|||
|
You should have received a copy of the GNU General Public License
|
|||
|
along with Gcov; see the file COPYING. If not, write to
|
|||
|
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|||
|
|
|||
|
/* ??? The code in final.c that produces the struct bb assumes that there is
|
|||
|
no padding between the fields. This is not necessary true. The current
|
|||
|
code can only be trusted if longs and pointers are the same size. */
|
|||
|
|
|||
|
/* ??? No need to print an execution count on every line, could just print
|
|||
|
it on the first line of each block, and only print it on a subsequent
|
|||
|
line in the same block if the count changes. */
|
|||
|
|
|||
|
/* ??? Print a list of the ten blocks with the highest execution counts,
|
|||
|
and list the line numbers corresponding to those blocks. Also, perhaps
|
|||
|
list the line numbers with the highest execution counts, only printing
|
|||
|
the first if there are several which are all listed in the same block. */
|
|||
|
|
|||
|
/* ??? Should have an option to print the number of basic blocks, and the
|
|||
|
percent of them that are covered. */
|
|||
|
|
|||
|
/* ??? Does not correctly handle the case where two .bb files refer to the
|
|||
|
same included source file. For example, if one has a short file containing
|
|||
|
only inline functions, which is then included in two other files, then
|
|||
|
there will be two .bb files which refer to the include file, but there
|
|||
|
is no way to get the total execution counts for the included file, can
|
|||
|
only get execution counts for one or the other of the including files. */
|
|||
|
|
|||
|
#include "config.h"
|
|||
|
#include "system.h"
|
|||
|
#include <sys/stat.h>
|
|||
|
#include "gansidecl.h"
|
|||
|
|
|||
|
#include "gcov-io.h"
|
|||
|
|
|||
|
/* The .bb file format consists of several lists of 4-byte integers
|
|||
|
which are the line numbers of each basic block in the file. Each
|
|||
|
list is terminated by a zero. These lists correspond to the basic
|
|||
|
blocks in the reconstructed program flow graph.
|
|||
|
|
|||
|
A line number of -1 indicates that a source file name (padded to a
|
|||
|
long boundary) follows. The padded file name is followed by
|
|||
|
another -1 to make it easy to scan past file names. A -2 indicates
|
|||
|
that a function name (padded to a long boundary) follows; the name
|
|||
|
is followed by another -2 to make it easy to scan past the function
|
|||
|
name.
|
|||
|
|
|||
|
The .bbg file contains enough info to enable gcov to reconstruct the
|
|||
|
program flow graph. The first word is the number of basic blocks,
|
|||
|
the second word is the number of arcs, followed by the list of arcs
|
|||
|
(source bb, dest bb pairs), then a -1, then the number of instrumented
|
|||
|
arcs followed by the instrumented arcs, followed by another -1. This
|
|||
|
is repeated for each function.
|
|||
|
|
|||
|
The .da file contains the execution count for each instrumented branch.
|
|||
|
|
|||
|
The .bb and .bbg files are created by giving GCC the -ftest-coverage option,
|
|||
|
and the .da files are created when an executable compiled with
|
|||
|
-fprofile-arcs is run. */
|
|||
|
|
|||
|
/* The functions in this file for creating and solution program flow graphs
|
|||
|
are very similar to functions in the gcc source file profile.c. */
|
|||
|
|
|||
|
char gcov_version_string[] = "GNU gcov version 1.5\n";
|
|||
|
|
|||
|
/* This is the size of the buffer used to read in source file lines. */
|
|||
|
|
|||
|
#define STRING_SIZE 200
|
|||
|
|
|||
|
/* One copy of this structure is created for each source file mentioned in the
|
|||
|
.bb file. */
|
|||
|
|
|||
|
struct sourcefile
|
|||
|
{
|
|||
|
char *name;
|
|||
|
int maxlineno;
|
|||
|
struct sourcefile *next;
|
|||
|
};
|
|||
|
|
|||
|
/* This points to the head of the sourcefile structure list. */
|
|||
|
|
|||
|
struct sourcefile *sources;
|
|||
|
|
|||
|
/* One of these is dynamically created whenever we identify an arc in the
|
|||
|
function. */
|
|||
|
|
|||
|
struct adj_list {
|
|||
|
int source;
|
|||
|
int target;
|
|||
|
int arc_count;
|
|||
|
unsigned int count_valid : 1;
|
|||
|
unsigned int on_tree : 1;
|
|||
|
unsigned int fake : 1;
|
|||
|
unsigned int fall_through : 1;
|
|||
|
#if 0
|
|||
|
/* Not needed for gcov, but defined in profile.c. */
|
|||
|
rtx branch_insn;
|
|||
|
#endif
|
|||
|
struct adj_list *pred_next;
|
|||
|
struct adj_list *succ_next;
|
|||
|
};
|
|||
|
|
|||
|
/* Count the number of basic blocks, and create an array of these structures,
|
|||
|
one for each bb in the function. */
|
|||
|
|
|||
|
struct bb_info {
|
|||
|
struct adj_list *succ;
|
|||
|
struct adj_list *pred;
|
|||
|
int succ_count;
|
|||
|
int pred_count;
|
|||
|
int exec_count;
|
|||
|
unsigned int count_valid : 1;
|
|||
|
unsigned int on_tree : 1;
|
|||
|
#if 0
|
|||
|
/* Not needed for gcov, but defined in profile.c. */
|
|||
|
rtx first_insn;
|
|||
|
#endif
|
|||
|
};
|
|||
|
|
|||
|
/* When outputting branch probabilities, one of these structures is created
|
|||
|
for each branch/call. */
|
|||
|
|
|||
|
struct arcdata
|
|||
|
{
|
|||
|
int prob;
|
|||
|
int call_insn;
|
|||
|
struct arcdata *next;
|
|||
|
};
|
|||
|
|
|||
|
/* Used to save the list of bb_graphs, one per function. */
|
|||
|
|
|||
|
struct bb_info_list {
|
|||
|
/* Indexed by block number, holds the basic block graph for one function. */
|
|||
|
struct bb_info *bb_graph;
|
|||
|
int num_blocks;
|
|||
|
struct bb_info_list *next;
|
|||
|
};
|
|||
|
|
|||
|
/* Holds a list of function basic block graphs. */
|
|||
|
|
|||
|
static struct bb_info_list *bb_graph_list = 0;
|
|||
|
|
|||
|
/* Name and file pointer of the input file for the basic block graph. */
|
|||
|
|
|||
|
static char *bbg_file_name;
|
|||
|
static FILE *bbg_file;
|
|||
|
|
|||
|
/* Name and file pointer of the input file for the arc count data. */
|
|||
|
|
|||
|
static char *da_file_name;
|
|||
|
static FILE *da_file;
|
|||
|
|
|||
|
/* Name and file pointer of the input file for the basic block line counts. */
|
|||
|
|
|||
|
static char *bb_file_name;
|
|||
|
static FILE *bb_file;
|
|||
|
|
|||
|
/* Holds the entire contents of the bb_file read into memory. */
|
|||
|
|
|||
|
static char *bb_data;
|
|||
|
|
|||
|
/* Size of bb_data array in longs. */
|
|||
|
|
|||
|
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. */
|
|||
|
|
|||
|
static char *input_file_name = 0;
|
|||
|
|
|||
|
/* Output branch probabilities if true. */
|
|||
|
|
|||
|
static int output_branch_probs = 0;
|
|||
|
|
|||
|
/* Output a gcov file if this is true. This is on by default, and can
|
|||
|
be turned off by the -n option. */
|
|||
|
|
|||
|
static int output_gcov_file = 1;
|
|||
|
|
|||
|
/* For included files, make the gcov output file name include the name of
|
|||
|
the input source file. For example, if x.h is included in a.c, then the
|
|||
|
output file name is a.c.x.h.gcov instead of x.h.gcov. This works only
|
|||
|
when a single source file is specified. */
|
|||
|
|
|||
|
static int output_long_names = 0;
|
|||
|
|
|||
|
/* Output summary info for each function. */
|
|||
|
|
|||
|
static int output_function_summary = 0;
|
|||
|
|
|||
|
/* Object directory file prefix. This is the directory where .bb and .bbg
|
|||
|
files are looked for, if non-zero. */
|
|||
|
|
|||
|
static char *object_directory = 0;
|
|||
|
|
|||
|
/* Forward declarations. */
|
|||
|
static void process_args PROTO ((int, char **));
|
|||
|
static void open_files PROTO ((void));
|
|||
|
static void read_files PROTO ((void));
|
|||
|
static void scan_for_source_files PROTO ((void));
|
|||
|
static void output_data PROTO ((void));
|
|||
|
static void print_usage PROTO ((void));
|
|||
|
char * xmalloc ();
|
|||
|
|
|||
|
int
|
|||
|
main (argc, argv)
|
|||
|
int argc;
|
|||
|
char **argv;
|
|||
|
{
|
|||
|
process_args (argc, argv);
|
|||
|
|
|||
|
open_files ();
|
|||
|
|
|||
|
read_files ();
|
|||
|
|
|||
|
scan_for_source_files ();
|
|||
|
|
|||
|
output_data ();
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
char *
|
|||
|
xmalloc (size)
|
|||
|
unsigned size;
|
|||
|
{
|
|||
|
register char *value = (char *) malloc (size);
|
|||
|
if (value == 0)
|
|||
|
{
|
|||
|
fprintf (stderr, "error: virtual memory exhausted");
|
|||
|
exit (FATAL_EXIT_CODE);
|
|||
|
}
|
|||
|
return value;
|
|||
|
}
|
|||
|
|
|||
|
/* More 'friendly' abort that prints the line and file.
|
|||
|
config.h can #define abort fancy_abort if you like that sort of thing. */
|
|||
|
|
|||
|
void
|
|||
|
fancy_abort ()
|
|||
|
{
|
|||
|
fprintf (stderr, "Internal gcc abort.\n");
|
|||
|
exit (FATAL_EXIT_CODE);
|
|||
|
}
|
|||
|
|
|||
|
/* Print a usage message and exit. */
|
|||
|
|
|||
|
static void
|
|||
|
print_usage ()
|
|||
|
{
|
|||
|
fprintf (stderr, "gcov [-b] [-v] [-n] [-l] [-f] [-o OBJDIR] file\n");
|
|||
|
exit (FATAL_EXIT_CODE);
|
|||
|
}
|
|||
|
|
|||
|
/* Parse the command line. */
|
|||
|
|
|||
|
static void
|
|||
|
process_args (argc, argv)
|
|||
|
int argc;
|
|||
|
char **argv;
|
|||
|
{
|
|||
|
int i;
|
|||
|
|
|||
|
for (i = 1; i < argc; i++)
|
|||
|
{
|
|||
|
if (argv[i][0] == '-')
|
|||
|
{
|
|||
|
if (argv[i][1] == 'b')
|
|||
|
output_branch_probs = 1;
|
|||
|
else if (argv[i][1] == 'v')
|
|||
|
fputs (gcov_version_string, stderr);
|
|||
|
else if (argv[i][1] == 'n')
|
|||
|
output_gcov_file = 0;
|
|||
|
else if (argv[i][1] == 'l')
|
|||
|
output_long_names = 1;
|
|||
|
else if (argv[i][1] == 'f')
|
|||
|
output_function_summary = 1;
|
|||
|
else if (argv[i][1] == 'o' && argv[i][2] == '\0')
|
|||
|
object_directory = argv[++i];
|
|||
|
else
|
|||
|
print_usage ();
|
|||
|
}
|
|||
|
else if (! input_file_name)
|
|||
|
input_file_name = argv[i];
|
|||
|
else
|
|||
|
print_usage ();
|
|||
|
}
|
|||
|
|
|||
|
if (! input_file_name)
|
|||
|
print_usage ();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Find and open the .bb, .da, and .bbg files. */
|
|||
|
|
|||
|
static void
|
|||
|
open_files ()
|
|||
|
{
|
|||
|
int count, objdir_count;
|
|||
|
char *cptr;
|
|||
|
|
|||
|
/* Determine the names of the .bb, .bbg, and .da files. Strip off the
|
|||
|
extension, if any, and append the new extensions. */
|
|||
|
count = strlen (input_file_name);
|
|||
|
if (object_directory)
|
|||
|
objdir_count = strlen (object_directory);
|
|||
|
else
|
|||
|
objdir_count = 0;
|
|||
|
|
|||
|
da_file_name = xmalloc (count + objdir_count + 4);
|
|||
|
bb_file_name = xmalloc (count + objdir_count + 4);
|
|||
|
bbg_file_name = xmalloc (count + objdir_count + 5);
|
|||
|
|
|||
|
if (object_directory)
|
|||
|
{
|
|||
|
strcpy (da_file_name, object_directory);
|
|||
|
strcpy (bb_file_name, object_directory);
|
|||
|
strcpy (bbg_file_name, object_directory);
|
|||
|
|
|||
|
if (object_directory[objdir_count - 1] != '/')
|
|||
|
{
|
|||
|
strcat (da_file_name, "/");
|
|||
|
strcat (bb_file_name, "/");
|
|||
|
strcat (bbg_file_name, "/");
|
|||
|
}
|
|||
|
|
|||
|
cptr = rindex (input_file_name, '/');
|
|||
|
if (cptr)
|
|||
|
{
|
|||
|
strcat (da_file_name, cptr + 1);
|
|||
|
strcat (bb_file_name, cptr + 1);
|
|||
|
strcat (bbg_file_name, cptr + 1);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
strcat (da_file_name, input_file_name);
|
|||
|
strcat (bb_file_name, input_file_name);
|
|||
|
strcat (bbg_file_name, input_file_name);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
strcpy (da_file_name, input_file_name);
|
|||
|
strcpy (bb_file_name, input_file_name);
|
|||
|
strcpy (bbg_file_name, input_file_name);
|
|||
|
}
|
|||
|
|
|||
|
cptr = rindex (bb_file_name, '.');
|
|||
|
if (cptr)
|
|||
|
strcpy (cptr, ".bb");
|
|||
|
else
|
|||
|
strcat (bb_file_name, ".bb");
|
|||
|
|
|||
|
cptr = rindex (da_file_name, '.');
|
|||
|
if (cptr)
|
|||
|
strcpy (cptr, ".da");
|
|||
|
else
|
|||
|
strcat (da_file_name, ".da");
|
|||
|
|
|||
|
cptr = rindex (bbg_file_name, '.');
|
|||
|
if (cptr)
|
|||
|
strcpy (cptr, ".bbg");
|
|||
|
else
|
|||
|
strcat (bbg_file_name, ".bbg");
|
|||
|
|
|||
|
bb_file = fopen (bb_file_name, "r");
|
|||
|
if (bb_file == NULL)
|
|||
|
{
|
|||
|
fprintf (stderr, "Could not open basic block file %s.\n", bb_file_name);
|
|||
|
exit (FATAL_EXIT_CODE);
|
|||
|
}
|
|||
|
|
|||
|
/* If none of the functions in the file were executed, then there won't
|
|||
|
be a .da file. Just assume that all counts are zero in this case. */
|
|||
|
da_file = fopen (da_file_name, "r");
|
|||
|
if (da_file == NULL)
|
|||
|
{
|
|||
|
fprintf (stderr, "Could not open data file %s.\n", da_file_name);
|
|||
|
fprintf (stderr, "Assuming that all execution counts are zero.\n");
|
|||
|
}
|
|||
|
|
|||
|
bbg_file = fopen (bbg_file_name, "r");
|
|||
|
if (bbg_file == NULL)
|
|||
|
{
|
|||
|
fprintf (stderr, "Could not open program flow graph file %s.\n",
|
|||
|
bbg_file_name);
|
|||
|
exit (FATAL_EXIT_CODE);
|
|||
|
}
|
|||
|
|
|||
|
/* Check for empty .bbg file. This indicates that there is no executable
|
|||
|
code in this source file. */
|
|||
|
/* Set the EOF condition if at the end of file. */
|
|||
|
ungetc (getc (bbg_file), bbg_file);
|
|||
|
if (feof (bbg_file))
|
|||
|
{
|
|||
|
fprintf (stderr, "No executable code associated with file %s.\n",
|
|||
|
input_file_name);
|
|||
|
exit (FATAL_EXIT_CODE);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Initialize a new arc. */
|
|||
|
|
|||
|
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++;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Reverse the arcs on a arc list. */
|
|||
|
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Construct the program flow graph from the .bbg file, and read in the data
|
|||
|
in the .da file. */
|
|||
|
|
|||
|
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;
|
|||
|
|
|||
|
/* Read the number of blocks. */
|
|||
|
__read_long (&num_blocks, bbg_file, 4);
|
|||
|
|
|||
|
/* Create an array of size bb number of bb_info structs. Bzero it. */
|
|||
|
bb_graph = (struct bb_info *) xmalloc (num_blocks
|
|||
|
* sizeof (struct bb_info));
|
|||
|
bzero ((char *) bb_graph, sizeof (struct bb_info) * num_blocks);
|
|||
|
|
|||
|
bptr->bb_graph = bb_graph;
|
|||
|
bptr->num_blocks = num_blocks;
|
|||
|
|
|||
|
/* Read and create each arc from the .bbg file. */
|
|||
|
__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);
|
|||
|
arcptr->on_tree = flag_bits & 0x1;
|
|||
|
arcptr->fake = !! (flag_bits & 0x2);
|
|||
|
arcptr->fall_through = !! (flag_bits & 0x4);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (number_arcs)
|
|||
|
abort ();
|
|||
|
|
|||
|
/* Read and ignore the -1 separating the arc list from the arc list of the
|
|||
|
next function. */
|
|||
|
__read_long (&src, bbg_file, 4);
|
|||
|
if (src != -1)
|
|||
|
abort ();
|
|||
|
|
|||
|
/* Must reverse the order of all succ arcs, to ensure that they match
|
|||
|
the order of the data in the .da file. */
|
|||
|
|
|||
|
for (i = 0; i < num_blocks; i++)
|
|||
|
if (bb_graph[i].succ)
|
|||
|
bb_graph[i].succ = reverse_arcs (bb_graph[i].succ);
|
|||
|
|
|||
|
/* For each arc not on the spanning tree, set its execution count from
|
|||
|
the .da file. */
|
|||
|
|
|||
|
/* The first count in the .da file is the number of times that the function
|
|||
|
was entered. This is the exec_count for block zero. */
|
|||
|
|
|||
|
/* This duplicates code in branch_prob in profile.c. */
|
|||
|
|
|||
|
for (i = 0; i < num_blocks; i++)
|
|||
|
for (arcptr = bb_graph[i].succ; arcptr; arcptr = arcptr->succ_next)
|
|||
|
if (! arcptr->on_tree)
|
|||
|
{
|
|||
|
long tmp_count = 0;;
|
|||
|
if (da_file && __read_long (&tmp_count, da_file, 8))
|
|||
|
abort();
|
|||
|
|
|||
|
arcptr->arc_count = tmp_count;
|
|||
|
arcptr->count_valid = 1;
|
|||
|
bb_graph[i].succ_count--;
|
|||
|
bb_graph[arcptr->target].pred_count--;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void
|
|||
|
solve_program_flow_graph (bptr)
|
|||
|
struct bb_info_list *bptr;
|
|||
|
{
|
|||
|
int passes, changes, 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;
|
|||
|
|
|||
|
/* For every block in the file,
|
|||
|
- if every exit/entrance arc has a known count, then set the block count
|
|||
|
- if the block count is known, and every exit/entrance arc but one has
|
|||
|
a known execution count, then set the count of the remaining arc
|
|||
|
|
|||
|
As arc counts are set, decrement the succ/pred count, but don't delete
|
|||
|
the arc, that way we can easily tell when all arcs are known, or only
|
|||
|
one arc is unknown. */
|
|||
|
|
|||
|
/* The order that the basic blocks are iterated through is important.
|
|||
|
Since the code that finds spanning trees starts with block 0, low numbered
|
|||
|
arcs are put on the spanning tree in preference to high numbered arcs.
|
|||
|
Hence, most instrumented arcs are at the end. Graph solving works much
|
|||
|
faster if we propagate numbers from the end to the start.
|
|||
|
|
|||
|
This takes an average of slightly more than 3 passes. */
|
|||
|
|
|||
|
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;
|
|||
|
/* One of the counts will be invalid, but it is zero,
|
|||
|
so adding it in also doesn't hurt. */
|
|||
|
for (arcptr = bb_graph[i].succ; arcptr;
|
|||
|
arcptr = arcptr->succ_next)
|
|||
|
total += arcptr->arc_count;
|
|||
|
/* Calculate count for remaining arc by conservation. */
|
|||
|
total = bb_graph[i].exec_count - total;
|
|||
|
/* Search for the invalid arc, and set its count. */
|
|||
|
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;
|
|||
|
/* One of the counts will be invalid, but it is zero,
|
|||
|
so adding it in also doesn't hurt. */
|
|||
|
for (arcptr = bb_graph[i].pred; arcptr;
|
|||
|
arcptr = arcptr->pred_next)
|
|||
|
total += arcptr->arc_count;
|
|||
|
/* Calculate count for remaining arc by conservation. */
|
|||
|
total = bb_graph[i].exec_count - total;
|
|||
|
/* Search for the invalid arc, and set its count. */
|
|||
|
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;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* If the graph has been correctly solved, every block will have a
|
|||
|
succ and pred count of zero. */
|
|||
|
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;
|
|||
|
long total;
|
|||
|
|
|||
|
/* Read and ignore the first word of the .da file, which is the count of
|
|||
|
how many numbers follow. */
|
|||
|
if (da_file && __read_long (&total, da_file, 8))
|
|||
|
abort();
|
|||
|
|
|||
|
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;
|
|||
|
|
|||
|
/* Read in the data in the .bbg file and reconstruct the program flow
|
|||
|
graph for one function. */
|
|||
|
create_program_flow_graph (b_ptr);
|
|||
|
|
|||
|
/* Set the EOF condition if at the end of file. */
|
|||
|
ungetc (getc (bbg_file), bbg_file);
|
|||
|
}
|
|||
|
|
|||
|
/* Check to make sure the .da file data is valid. */
|
|||
|
|
|||
|
if (da_file)
|
|||
|
{
|
|||
|
if (feof (da_file))
|
|||
|
fprintf (stderr, ".da file contents exhausted too early\n");
|
|||
|
/* Should be at end of file now. */
|
|||
|
if (__read_long (&total, da_file, 8) == 0)
|
|||
|
fprintf (stderr, ".da file contents not exhausted\n");
|
|||
|
}
|
|||
|
|
|||
|
/* Calculate all of the basic block execution counts and branch
|
|||
|
taken probabilities. */
|
|||
|
|
|||
|
for (b_ptr = bb_graph_list; b_ptr; b_ptr = b_ptr->next)
|
|||
|
solve_program_flow_graph (b_ptr);
|
|||
|
|
|||
|
/* Read in all of the data from the .bb file. This info will be accessed
|
|||
|
sequentially twice. */
|
|||
|
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);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Scan the data in the .bb file to find all source files referenced,
|
|||
|
and the largest line number mentioned in each one. */
|
|||
|
|
|||
|
static void
|
|||
|
scan_for_source_files ()
|
|||
|
{
|
|||
|
struct sourcefile *s_ptr = NULL;
|
|||
|
char *ptr;
|
|||
|
int count;
|
|||
|
long line_num;
|
|||
|
|
|||
|
/* Search the bb_data to find:
|
|||
|
1) The number of sources files contained herein, and
|
|||
|
2) The largest line number for each source file. */
|
|||
|
|
|||
|
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)
|
|||
|
{
|
|||
|
/* A source file name follows. Check to see if we already have
|
|||
|
a sourcefile structure for this file. */
|
|||
|
s_ptr = sources;
|
|||
|
while (s_ptr && strcmp (s_ptr->name, ptr))
|
|||
|
s_ptr = s_ptr->next;
|
|||
|
|
|||
|
if (s_ptr == 0)
|
|||
|
{
|
|||
|
/* No sourcefile structure for this file name exists, create
|
|||
|
a new one, and append it to the front of the sources list. */
|
|||
|
s_ptr = (struct sourcefile *) xmalloc (sizeof(struct sourcefile));
|
|||
|
s_ptr->name = xmalloc (strlen ((char *) ptr) + 1);
|
|||
|
strcpy (s_ptr->name, (char *) ptr);
|
|||
|
s_ptr->maxlineno = 0;
|
|||
|
s_ptr->next = sources;
|
|||
|
sources = s_ptr;
|
|||
|
}
|
|||
|
|
|||
|
/* Scan past the file name. */
|
|||
|
{
|
|||
|
long delim;
|
|||
|
do {
|
|||
|
count++;
|
|||
|
__fetch_long (&delim, ptr, 4);
|
|||
|
ptr += 4;
|
|||
|
} while (delim != line_num);
|
|||
|
}
|
|||
|
}
|
|||
|
else if (line_num == -2)
|
|||
|
{
|
|||
|
long delim;
|
|||
|
|
|||
|
/* A function name follows. Ignore it. */
|
|||
|
do {
|
|||
|
count++;
|
|||
|
__fetch_long (&delim, ptr, 4);
|
|||
|
ptr += 4;
|
|||
|
} while (delim != line_num);
|
|||
|
}
|
|||
|
/* There will be a zero before the first file name, in which case s_ptr
|
|||
|
will still be uninitialized. So, only try to set the maxlineno
|
|||
|
field if line_num is non-zero. */
|
|||
|
else if (line_num > 0)
|
|||
|
{
|
|||
|
if (s_ptr->maxlineno <= line_num)
|
|||
|
s_ptr->maxlineno = line_num + 1;
|
|||
|
}
|
|||
|
else if (line_num < 0)
|
|||
|
{
|
|||
|
/* Don't know what this is, but it's garbage. */
|
|||
|
abort();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* For calculating coverage at the function level. */
|
|||
|
|
|||
|
static int function_source_lines;
|
|||
|
static int function_source_lines_executed;
|
|||
|
static int function_branches;
|
|||
|
static int function_branches_executed;
|
|||
|
static int function_branches_taken;
|
|||
|
static int function_calls;
|
|||
|
static int function_calls_executed;
|
|||
|
static char *function_name;
|
|||
|
|
|||
|
/* Calculate the branch taken probabilities for all arcs branches at the
|
|||
|
end of this block. */
|
|||
|
|
|||
|
static void
|
|||
|
calculate_branch_probs (current_graph, block_num, branch_probs, last_line_num)
|
|||
|
struct bb_info_list *current_graph;
|
|||
|
int block_num;
|
|||
|
struct arcdata **branch_probs;
|
|||
|
int last_line_num;
|
|||
|
{
|
|||
|
int total;
|
|||
|
struct adj_list *arcptr;
|
|||
|
struct arcdata *end_ptr, *a_ptr;
|
|||
|
|
|||
|
total = current_graph->bb_graph[block_num].exec_count;
|
|||
|
for (arcptr = current_graph->bb_graph[block_num].succ; arcptr;
|
|||
|
arcptr = arcptr->succ_next)
|
|||
|
{
|
|||
|
/* Ignore fall through arcs as they aren't really branches. */
|
|||
|
|
|||
|
if (arcptr->fall_through)
|
|||
|
continue;
|
|||
|
|
|||
|
a_ptr = (struct arcdata *) xmalloc (sizeof (struct arcdata));
|
|||
|
if (total == 0)
|
|||
|
a_ptr->prob = -1;
|
|||
|
else
|
|||
|
a_ptr->prob = ((arcptr->arc_count * 100) + (total >> 1)) / total;
|
|||
|
a_ptr->call_insn = arcptr->fake;
|
|||
|
|
|||
|
if (output_function_summary)
|
|||
|
{
|
|||
|
if (a_ptr->call_insn)
|
|||
|
{
|
|||
|
function_calls++;
|
|||
|
if (a_ptr->prob != -1)
|
|||
|
function_calls_executed++;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
function_branches++;
|
|||
|
if (a_ptr->prob != -1)
|
|||
|
function_branches_executed++;
|
|||
|
if (a_ptr->prob > 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;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Output summary info for a function. */
|
|||
|
|
|||
|
static void
|
|||
|
function_summary ()
|
|||
|
{
|
|||
|
if (function_source_lines)
|
|||
|
fprintf (stdout, "%6.2f%% of %d source lines executed in function %s\n",
|
|||
|
(((double) function_source_lines_executed / function_source_lines)
|
|||
|
* 100), function_source_lines, function_name);
|
|||
|
else
|
|||
|
fprintf (stdout, "No executable source lines in function %s\n",
|
|||
|
function_name);
|
|||
|
|
|||
|
if (output_branch_probs)
|
|||
|
{
|
|||
|
if (function_branches)
|
|||
|
{
|
|||
|
fprintf (stdout, "%6.2f%% of %d branches executed in function %s\n",
|
|||
|
(((double) function_branches_executed / function_branches)
|
|||
|
* 100), function_branches, function_name);
|
|||
|
fprintf (stdout,
|
|||
|
"%6.2f%% of %d branches taken at least once in function %s\n",
|
|||
|
(((double) function_branches_taken / function_branches)
|
|||
|
* 100), function_branches, function_name);
|
|||
|
}
|
|||
|
else
|
|||
|
fprintf (stdout, "No branches in function %s\n", function_name);
|
|||
|
if (function_calls)
|
|||
|
fprintf (stdout, "%6.2f%% of %d calls executed in function %s\n",
|
|||
|
(((double) function_calls_executed / function_calls)
|
|||
|
* 100), function_calls, function_name);
|
|||
|
else
|
|||
|
fprintf (stdout, "No calls in function %s\n", function_name);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Calculate line execution counts, and output the data to a .tcov file. */
|
|||
|
|
|||
|
static void
|
|||
|
output_data ()
|
|||
|
{
|
|||
|
/* 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. */
|
|||
|
long *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;
|
|||
|
int count;
|
|||
|
char *cptr;
|
|||
|
long block_num;
|
|||
|
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)
|
|||
|
{
|
|||
|
/* If this is a relative file name, and an object directory has been
|
|||
|
specified, then make it relative to the object directory name. */
|
|||
|
if (*s_ptr->name != '/' && object_directory != 0
|
|||
|
&& *object_directory != '\0')
|
|||
|
{
|
|||
|
int objdir_count = strlen (object_directory);
|
|||
|
source_file_name = xmalloc (objdir_count + strlen (s_ptr->name) + 2);
|
|||
|
strcpy (source_file_name, object_directory);
|
|||
|
if (object_directory[objdir_count - 1] != '/')
|
|||
|
source_file_name[objdir_count++] = '/';
|
|||
|
strcpy (source_file_name + objdir_count, s_ptr->name);
|
|||
|
}
|
|||
|
else
|
|||
|
source_file_name = s_ptr->name;
|
|||
|
|
|||
|
line_counts = (long *) xmalloc (sizeof (long) * s_ptr->maxlineno);
|
|||
|
bzero ((char *) line_counts, sizeof (long) * s_ptr->maxlineno);
|
|||
|
line_exists = xmalloc (s_ptr->maxlineno);
|
|||
|
bzero (line_exists, s_ptr->maxlineno);
|
|||
|
if (output_branch_probs)
|
|||
|
{
|
|||
|
branch_probs = (struct arcdata **) xmalloc (sizeof (struct arcdata **)
|
|||
|
* s_ptr->maxlineno);
|
|||
|
bzero ((char *) branch_probs,
|
|||
|
sizeof (struct arcdata **) * s_ptr->maxlineno);
|
|||
|
}
|
|||
|
|
|||
|
/* There will be a zero at the beginning of the bb info, before the
|
|||
|
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. */
|
|||
|
char *ptr = bb_data;
|
|||
|
for (count = 0; count < bb_data_size; count++)
|
|||
|
{
|
|||
|
long delim;
|
|||
|
|
|||
|
__fetch_long (&line_num, ptr, 4);
|
|||
|
ptr += 4;
|
|||
|
if (line_num == -1)
|
|||
|
{
|
|||
|
/* Marks the beginning of a file name. Check to see whether
|
|||
|
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. */
|
|||
|
do {
|
|||
|
count++;
|
|||
|
__fetch_long (&delim, ptr, 4);
|
|||
|
ptr += 4;
|
|||
|
} while (delim != line_num);
|
|||
|
}
|
|||
|
else if (line_num == -2)
|
|||
|
{
|
|||
|
/* Marks the start of a new function. Advance to the next
|
|||
|
program flow graph. */
|
|||
|
|
|||
|
if (! current_graph)
|
|||
|
current_graph = bb_graph_list;
|
|||
|
else
|
|||
|
{
|
|||
|
if (block_num == current_graph->num_blocks - 1)
|
|||
|
/* Last block falls through to exit. */
|
|||
|
;
|
|||
|
else if (block_num == current_graph->num_blocks - 2)
|
|||
|
{
|
|||
|
if (output_branch_probs && this_file)
|
|||
|
calculate_branch_probs (current_graph, block_num,
|
|||
|
branch_probs, last_line_num);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
fprintf (stderr,
|
|||
|
"didn't use all bb entries of graph, function %s\n",
|
|||
|
function_name);
|
|||
|
fprintf (stderr, "block_num = %ld, num_blocks = %d\n",
|
|||
|
block_num, current_graph->num_blocks);
|
|||
|
}
|
|||
|
|
|||
|
current_graph = current_graph->next;
|
|||
|
block_num = 0;
|
|||
|
|
|||
|
if (output_function_summary && this_file)
|
|||
|
function_summary ();
|
|||
|
}
|
|||
|
|
|||
|
if (output_function_summary)
|
|||
|
{
|
|||
|
function_source_lines = 0;
|
|||
|
function_source_lines_executed = 0;
|
|||
|
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. */
|
|||
|
function_name = ptr;
|
|||
|
|
|||
|
/* Scan past the file name. */
|
|||
|
do {
|
|||
|
count++;
|
|||
|
__fetch_long (&delim, ptr, 4);
|
|||
|
ptr += 4;
|
|||
|
} while (delim != line_num);
|
|||
|
}
|
|||
|
else if (line_num == 0)
|
|||
|
{
|
|||
|
/* Marks the end of a block. */
|
|||
|
|
|||
|
if (block_num >= current_graph->num_blocks)
|
|||
|
{
|
|||
|
fprintf (stderr, "ERROR: too many basic blocks in .bb file %s\n",
|
|||
|
function_name);
|
|||
|
abort ();
|
|||
|
}
|
|||
|
|
|||
|
if (output_branch_probs && this_file)
|
|||
|
calculate_branch_probs (current_graph, block_num,
|
|||
|
branch_probs, last_line_num);
|
|||
|
|
|||
|
block_num++;
|
|||
|
}
|
|||
|
else if (this_file)
|
|||
|
{
|
|||
|
if (output_function_summary)
|
|||
|
{
|
|||
|
if (line_exists[line_num] == 0)
|
|||
|
function_source_lines++;
|
|||
|
if (line_counts[line_num] == 0
|
|||
|
&& 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)
|
|||
|
function_summary ();
|
|||
|
|
|||
|
/* 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 (a_ptr->prob != -1)
|
|||
|
total_calls_executed++;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
total_branches++;
|
|||
|
if (a_ptr->prob != -1)
|
|||
|
total_branches_executed++;
|
|||
|
if (a_ptr->prob > 0)
|
|||
|
total_branches_taken++;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (total_source_lines)
|
|||
|
fprintf (stdout,
|
|||
|
"%6.2f%% of %d source lines executed in file %s\n",
|
|||
|
(((double) total_source_lines_executed / total_source_lines)
|
|||
|
* 100), total_source_lines, source_file_name);
|
|||
|
else
|
|||
|
fprintf (stdout, "No executable source lines in file %s\n",
|
|||
|
source_file_name);
|
|||
|
|
|||
|
if (output_branch_probs)
|
|||
|
{
|
|||
|
if (total_branches)
|
|||
|
{
|
|||
|
fprintf (stdout, "%6.2f%% of %d branches executed in file %s\n",
|
|||
|
(((double) total_branches_executed / total_branches)
|
|||
|
* 100), total_branches, source_file_name);
|
|||
|
fprintf (stdout,
|
|||
|
"%6.2f%% of %d branches taken at least once in file %s\n",
|
|||
|
(((double) total_branches_taken / total_branches)
|
|||
|
* 100), total_branches, source_file_name);
|
|||
|
}
|
|||
|
else
|
|||
|
fprintf (stdout, "No branches in file %s\n", source_file_name);
|
|||
|
if (total_calls)
|
|||
|
fprintf (stdout, "%6.2f%% of %d calls executed in file %s\n",
|
|||
|
(((double) total_calls_executed / total_calls)
|
|||
|
* 100), total_calls, source_file_name);
|
|||
|
else
|
|||
|
fprintf (stdout, "No calls in file %s\n", source_file_name);
|
|||
|
}
|
|||
|
|
|||
|
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 count if non zero. */
|
|||
|
|
|||
|
source_file = fopen (source_file_name, "r");
|
|||
|
if (source_file == NULL)
|
|||
|
{
|
|||
|
fprintf (stderr, "Could not open source file %s.\n",
|
|||
|
source_file_name);
|
|||
|
free (line_counts);
|
|||
|
free (line_exists);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
count = strlen (source_file_name);
|
|||
|
cptr = rindex (s_ptr->name, '/');
|
|||
|
if (cptr)
|
|||
|
cptr = cptr + 1;
|
|||
|
else
|
|||
|
cptr = s_ptr->name;
|
|||
|
if (output_long_names && strcmp (cptr, input_file_name))
|
|||
|
{
|
|||
|
gcov_file_name = xmalloc (count + 7 + strlen (input_file_name));
|
|||
|
|
|||
|
cptr = rindex (input_file_name, '/');
|
|||
|
if (cptr)
|
|||
|
strcpy (gcov_file_name, cptr + 1);
|
|||
|
else
|
|||
|
strcpy (gcov_file_name, input_file_name);
|
|||
|
|
|||
|
strcat (gcov_file_name, ".");
|
|||
|
|
|||
|
cptr = rindex (source_file_name, '/');
|
|||
|
if (cptr)
|
|||
|
strcat (gcov_file_name, cptr + 1);
|
|||
|
else
|
|||
|
strcat (gcov_file_name, source_file_name);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
gcov_file_name = xmalloc (count + 6);
|
|||
|
cptr = rindex (source_file_name, '/');
|
|||
|
if (cptr)
|
|||
|
strcpy (gcov_file_name, cptr + 1);
|
|||
|
else
|
|||
|
strcpy (gcov_file_name, source_file_name);
|
|||
|
}
|
|||
|
|
|||
|
/* 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");
|
|||
|
|
|||
|
if (gcov_file == NULL)
|
|||
|
{
|
|||
|
fprintf (stderr, "Could not open output file %s.\n",
|
|||
|
gcov_file_name);
|
|||
|
fclose (source_file);
|
|||
|
free (line_counts);
|
|||
|
free (line_exists);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
fprintf (stdout, "Creating %s.\n", gcov_file_name);
|
|||
|
|
|||
|
for (count = 1; count < s_ptr->maxlineno; count++)
|
|||
|
{
|
|||
|
char *retval;
|
|||
|
int len;
|
|||
|
|
|||
|
retval = fgets (string, STRING_SIZE, source_file);
|
|||
|
|
|||
|
/* For lines which don't exist in the .bb file, print nothing
|
|||
|
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. */
|
|||
|
if (line_exists[count])
|
|||
|
{
|
|||
|
if (line_counts[count])
|
|||
|
fprintf (gcov_file, "%12ld %s", line_counts[count],
|
|||
|
string);
|
|||
|
else
|
|||
|
fprintf (gcov_file, " ###### %s", string);
|
|||
|
}
|
|||
|
else
|
|||
|
fprintf (gcov_file, "\t\t%s", string);
|
|||
|
|
|||
|
/* In case the source file line is larger than our buffer, keep
|
|||
|
reading and outputting lines until we get a newline. */
|
|||
|
len = strlen (string);
|
|||
|
while ((len == 0 || string[strlen (string) - 1] != '\n')
|
|||
|
&& retval != NULL)
|
|||
|
{
|
|||
|
retval = fgets (string, STRING_SIZE, source_file);
|
|||
|
fputs (string, gcov_file);
|
|||
|
}
|
|||
|
|
|||
|
if (output_branch_probs)
|
|||
|
{
|
|||
|
for (i = 0, a_ptr = branch_probs[count]; a_ptr;
|
|||
|
a_ptr = a_ptr->next, i++)
|
|||
|
{
|
|||
|
if (a_ptr->call_insn)
|
|||
|
{
|
|||
|
if (a_ptr->prob == -1)
|
|||
|
fprintf (gcov_file, "call %d never executed\n", i);
|
|||
|
else
|
|||
|
fprintf (gcov_file,
|
|||
|
"call %d returns = %d%%\n",
|
|||
|
i, 100 - a_ptr->prob);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (a_ptr->prob == -1)
|
|||
|
fprintf (gcov_file, "branch %d never executed\n",
|
|||
|
i);
|
|||
|
else
|
|||
|
fprintf (gcov_file, "branch %d taken = %d%%\n", i,
|
|||
|
a_ptr->prob);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Gracefully handle errors while reading the source file. */
|
|||
|
if (retval == NULL)
|
|||
|
{
|
|||
|
fprintf (stderr,
|
|||
|
"Unexpected EOF while reading source file %s.\n",
|
|||
|
source_file_name);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Handle all remaining source lines. There may be lines
|
|||
|
after the last line of code. */
|
|||
|
|
|||
|
{
|
|||
|
char *retval = fgets (string, STRING_SIZE, source_file);
|
|||
|
while (retval != NULL)
|
|||
|
{
|
|||
|
int len;
|
|||
|
|
|||
|
fprintf (gcov_file, "\t\t%s", string);
|
|||
|
|
|||
|
/* In case the source file line is larger than our buffer, keep
|
|||
|
reading and outputting lines until we get a newline. */
|
|||
|
len = strlen (string);
|
|||
|
while ((len == 0 || string[strlen (string) - 1] != '\n')
|
|||
|
&& retval != NULL)
|
|||
|
{
|
|||
|
retval = fgets (string, STRING_SIZE, source_file);
|
|||
|
fputs (string, gcov_file);
|
|||
|
}
|
|||
|
|
|||
|
retval = fgets (string, STRING_SIZE, source_file);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
fclose (source_file);
|
|||
|
fclose (gcov_file);
|
|||
|
}
|
|||
|
|
|||
|
free (line_counts);
|
|||
|
free (line_exists);
|
|||
|
}
|
|||
|
}
|