/* Gcov.c: prepend line execution counts and branch probabilities to a
source file.
- Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999,
+ 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
Contributed by James E. Wilson of Cygnus Support.
Mangled by Bob Manson of Cygnus Support.
+ Mangled further by Nathan Sidwell <nathan@codesourcery.com>
Gcov is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
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, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, 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. */
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA. */
/* ??? Print a list of the ten blocks with the highest execution counts,
and list the line numbers corresponding to those blocks. Also, perhaps
/* ??? 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. */
+/* ??? 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. this can be fixed by --ratios
+ --long-file-names --preserve-paths and perl. */
+
+/* Need an option to show individual block counts, and show
+ probabilities of fall through arcs. */
#include "config.h"
#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
#include "intl.h"
#include "version.h"
-#undef abort
#include <getopt.h>
-typedef HOST_WIDEST_INT gcov_type;
+#define IN_GCOV 1
#include "gcov-io.h"
+#include "gcov-io.c"
-/* 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 bbg file is generated by -ftest-coverage option. The da file is
+ generated by a program compiled with -fprofile-arcs. Their formats
+ are documented in gcov-io.h. */
/* The functions in this file for creating and solution program flow graphs
- are very similar to functions in the gcc source file profile.c. */
+ are very similar to functions in the gcc source file profile.c. In
+ some places we make use of the knowledge of how profile.c works to
+ select particular algorithms here. */
/* 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;
-};
+struct function_info;
+struct block_info;
+struct source_info;
-/* This points to the head of the sourcefile structure list. */
+/* Describes an arc between two basic blocks. */
-struct sourcefile *sources;
+typedef struct arc_info
+{
+ /* source and destination blocks. */
+ struct block_info *src;
+ struct block_info *dst;
-/* One of these is dynamically created whenever we identify an arc in the
- function. */
+ /* transition counts. */
+ gcov_type count;
+ /* used in cycle search, so that we do not clobber original counts. */
+ gcov_type cs_count;
-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
- /* 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. */
+ /* Arc is for a function that abnormally returns. */
+ unsigned int is_call_non_return : 1;
-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
- /* Not needed for gcov, but defined in profile.c. */
- rtx first_insn;
-#endif
-};
+ /* Arc is for catch/setjump. */
+ unsigned int is_nonlocal_return : 1;
+
+ /* Is an unconditional branch. */
+ unsigned int is_unconditional : 1;
+
+ /* Loop making arc. */
+ unsigned int cycle : 1;
+
+ /* Next branch on line. */
+ struct arc_info *line_next;
-/* When outputting branch probabilities, one of these structures is created
- for each branch/call. */
+ /* Links to next arc on src and dst lists. */
+ struct arc_info *succ_next;
+ struct arc_info *pred_next;
+} arc_t;
-struct arcdata
+/* Describes a basic block. Contains lists of arcs to successor and
+ predecessor blocks. */
+
+typedef struct block_info
{
- gcov_type hits;
- gcov_type total;
- int call_insn;
- struct arcdata *next;
-};
+ /* Chain of exit and entry arcs. */
+ arc_t *succ;
+ arc_t *pred;
+
+ /* Number of unprocessed exit and entry arcs. */
+ gcov_type num_succ;
+ gcov_type num_pred;
+
+ /* Block execution count. */
+ gcov_type count;
+ unsigned flags : 13;
+ unsigned count_valid : 1;
+ unsigned valid_chain : 1;
+ unsigned invalid_chain : 1;
+
+ /* Block is a call instrumenting site. */
+ unsigned is_call_site : 1; /* Does the call. */
+ unsigned is_call_return : 1; /* Is the return. */
+
+ /* Block is a landing pad for longjmp or throw. */
+ unsigned is_nonlocal_return : 1;
+
+ union
+ {
+ struct
+ {
+ /* Array of line numbers and source files. source files are
+ introduced by a linenumber of zero, the next 'line number' is
+ the number of the source file. Always starts with a source
+ file. */
+ unsigned *encoding;
+ unsigned num;
+ } line; /* Valid until blocks are linked onto lines */
+ struct
+ {
+ /* Single line graph cycle workspace. Used for all-blocks
+ mode. */
+ arc_t *arc;
+ unsigned ident;
+ } cycle; /* Used in all-blocks mode, after blocks are linked onto
+ lines. */
+ } u;
-/* Used to save the list of bb_graphs, one per function. */
+ /* Temporary chain for solving graph, and for chaining blocks on one
+ line. */
+ struct block_info *chain;
-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;
-};
+} block_t;
+
+/* Describes a single function. Contains an array of basic blocks. */
+
+typedef struct function_info
+{
+ /* Name of function. */
+ char *name;
+ unsigned ident;
+ unsigned checksum;
+
+ /* Array of basic blocks. */
+ block_t *blocks;
+ unsigned num_blocks;
+ unsigned blocks_executed;
+
+ /* Raw arc coverage counts. */
+ gcov_type *counts;
+ unsigned num_counts;
+
+ /* First line number. */
+ unsigned line;
+ struct source_info *src;
+
+ /* Next function in same source file. */
+ struct function_info *line_next;
+
+ /* Next function. */
+ struct function_info *next;
+} function_t;
+
+/* Describes coverage of a file or function. */
+
+typedef struct coverage_info
+{
+ int lines;
+ int lines_executed;
+
+ int branches;
+ int branches_executed;
+ int branches_taken;
+
+ int calls;
+ int calls_executed;
+
+ char *name;
+} coverage_t;
+
+/* Describes a single line of source. Contains a chain of basic blocks
+ with code on it. */
+
+typedef struct line_info
+{
+ gcov_type count; /* execution count */
+ union
+ {
+ arc_t *branches; /* branches from blocks that end on this
+ line. Used for branch-counts when not
+ all-blocks mode. */
+ block_t *blocks; /* blocks which start on this line. Used
+ in all-blocks mode. */
+ } u;
+ unsigned exists : 1;
+} line_t;
+
+/* Describes a file mentioned in the block graph. Contains an array
+ of line info. */
+
+typedef struct source_info
+{
+ /* Name of source file. */
+ char *name;
+ unsigned index;
+
+ /* Array of line information. */
+ line_t *lines;
+ unsigned num_lines;
+
+ coverage_t coverage;
+
+ /* Functions in this source file. These are in ascending line
+ number order. */
+ function_t *functions;
+
+ /* Next source file. */
+ struct source_info *next;
+} source_t;
/* Holds a list of function basic block graphs. */
-static struct bb_info_list *bb_graph_list = 0;
+static function_t *functions;
-/* Name and file pointer of the input file for the basic block graph. */
+/* This points to the head of the sourcefile structure list. */
-static char *bbg_file_name;
-static FILE *bbg_file;
+static source_t *sources;
-/* Name and file pointer of the input file for the arc count data. */
+/* This holds data summary information. */
-static char *da_file_name;
-static FILE *da_file;
+static struct gcov_summary object_summary;
+static unsigned program_count;
-/* Name and file pointer of the input file for the basic block line counts. */
+/* Modification time of graph file. */
-static char *bb_file_name;
-static FILE *bb_file;
+static time_t bbg_file_time;
-/* Holds the entire contents of the bb_file read into memory. */
+/* Name and file pointer of the input file for the basic block graph. */
-static char *bb_data;
+static char *bbg_file_name;
-/* Size of bb_data array in longs. */
+/* Stamp of the bbg file */
+static unsigned bbg_stamp;
-static long bb_data_size;
+/* Name and file pointer of the input file for the arc count data. */
-/* Name and file pointer of the output file. */
+static char *da_file_name;
-static char *gcov_file_name;
-static FILE *gcov_file;
+/* Data file is missing. */
-/* Name of the file mentioned on the command line. */
+static int no_data_file;
-static char *input_file_name = 0;
+/* Output branch probabilities. */
-/* Output branch probabilities if true. */
+static int flag_branches = 0;
-static int output_branch_probs = 0;
+/* Show unconditional branches too. */
+static int flag_unconditional = 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;
+static int flag_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. */
-/* 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 flag_long_names = 0;
-static int output_long_names = 0;
+/* Output count information for every basic block, not merely those
+ that contain line number information. */
+
+static int flag_all_blocks = 0;
/* Output summary info for each function. */
-static int output_function_summary = 0;
+static int flag_function_summary = 0;
-/* Object directory file prefix. This is the directory where .bb and .bbg
- files are looked for, if non-zero. */
+/* Object directory file prefix. This is the directory/file where the
+ graph and data files are looked for, if nonzero. */
static char *object_directory = 0;
+/* Preserve all pathname components. Needed when object files and
+ source files are in subdirectories. '/' is mangled as '#', '.' is
+ elided and '..' mangled to '^'. */
+
+static int flag_preserve_paths = 0;
+
/* Output the number of times a branch was taken as opposed to the percentage
- of times it was taken. Turned on by the -c option */
+ of times it was taken. */
-static int output_branch_counts = 0;
+static int flag_counts = 0;
/* Forward declarations. */
-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 ((void));
-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 calculate_branch_probs PARAMS ((struct bb_info_list *, int,
- struct arcdata **, int));
-static void function_summary PARAMS ((void));
-
-extern int main PARAMS ((int, char **));
+static void fnotice (FILE *, const char *, ...) ATTRIBUTE_PRINTF_2;
+static int process_args (int, char **);
+static void print_usage (int) ATTRIBUTE_NORETURN;
+static void print_version (void) ATTRIBUTE_NORETURN;
+static void process_file (const char *);
+static void create_file_names (const char *);
+static source_t *find_source (const char *);
+static int read_graph_file (void);
+static int read_count_file (void);
+static void solve_flow_graph (function_t *);
+static void add_branch_counts (coverage_t *, const arc_t *);
+static void add_line_counts (coverage_t *, function_t *);
+static void function_summary (const coverage_t *, const char *);
+static const char *format_gcov (gcov_type, gcov_type, int);
+static void accumulate_line_counts (source_t *);
+static int output_branch_count (FILE *, int, const arc_t *);
+static void output_lines (FILE *, const source_t *);
+static char *make_gcov_file_name (const char *, const char *);
+static void release_structures (void);
+extern int main (int, char **);
int
-main (argc, argv)
- int argc;
- char **argv;
+main (int argc, char **argv)
{
- gcc_init_libintl ();
+ int argno;
- process_args (argc, argv);
+ /* Unlock the stdio streams. */
+ unlock_std_streams ();
- open_files ();
+ gcc_init_libintl ();
- read_files ();
+ argno = process_args (argc, argv);
+ if (optind == argc)
+ print_usage (true);
- scan_for_source_files ();
+ for (; argno != argc; argno++)
+ {
+ release_structures ();
- output_data ();
+ process_file (argv[argno]);
+ }
return 0;
}
-static void fnotice PARAMS ((FILE *, const char *, ...)) ATTRIBUTE_PRINTF_2;
static void
-fnotice VPARAMS ((FILE *file, const char *msgid, ...))
+fnotice (FILE *file, const char *cmsgid, ...)
{
- VA_OPEN (ap, msgid);
- VA_FIXEDARG (ap, FILE *, file);
- VA_FIXEDARG (ap, const char *, msgid);
+ va_list ap;
- vfprintf (file, _(msgid), ap);
- VA_CLOSE (ap);
-}
-
-/* More 'friendly' abort that prints the line and file.
- config.h can #define abort fancy_abort if you like that sort of thing. */
-extern void fancy_abort PARAMS ((void)) ATTRIBUTE_NORETURN;
-
-void
-fancy_abort ()
-{
- fnotice (stderr, "Internal gcov abort.\n");
- exit (FATAL_EXIT_CODE);
+ va_start (ap, cmsgid);
+ vfprintf (file, _(cmsgid), ap);
+ va_end (ap);
}
\f
/* Print a usage message and exit. If ERROR_P is nonzero, this is an error,
otherwise the output of --help. */
static void
-print_usage (error_p)
- int error_p;
+print_usage (int error_p)
{
FILE *file = error_p ? stderr : stdout;
int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
+
fnotice (file, "Usage: gcov [OPTION]... SOURCEFILE\n\n");
fnotice (file, "Print code coverage information.\n\n");
fnotice (file, " -h, --help Print this help, then exit\n");
fnotice (file, " -v, --version Print version number, then exit\n");
+ fnotice (file, " -a, --all-blocks Show information for every basic block\n");
fnotice (file, " -b, --branch-probabilities Include branch probabilities in output\n");
fnotice (file, " -c, --branch-counts Given counts of branches taken\n\
rather than percentages\n");
fnotice (file, " -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 OBJDIR Search for object files in OBJDIR\n");
+ fnotice (file, " -o, --object-directory DIR|FILE Search for object files in DIR or called FILE\n");
+ fnotice (file, " -p, --preserve-paths Preserve all pathname components\n");
+ fnotice (file, " -u, --unconditional-branches Show unconditional branch counts too\n");
fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
- GCCBUGURL);
+ bug_report_url);
exit (status);
}
/* Print version information and exit. */
static void
-print_version ()
+print_version (void)
{
fnotice (stdout, "gcov (GCC) %s\n", version_string);
- fnotice (stdout, "Copyright (C) 2001 Free Software Foundation, Inc.\n");
+ fprintf (stdout, "Copyright %s 2006 Free Software Foundation, Inc.\n",
+ _("(C)"));
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");
+ _("This is free software; see the source for copying conditions.\n"
+ "There is NO warranty; not even for MERCHANTABILITY or \n"
+ "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
exit (SUCCESS_EXIT_CODE);
}
{
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'v' },
+ { "all-blocks", no_argument, NULL, 'a' },
{ "branch-probabilities", no_argument, NULL, 'b' },
{ "branch-counts", no_argument, NULL, 'c' },
{ "no-output", no_argument, NULL, 'n' },
{ "long-file-names", no_argument, NULL, 'l' },
{ "function-summaries", no_argument, NULL, 'f' },
- { "object-directory", required_argument, NULL, 'o' }
+ { "preserve-paths", no_argument, NULL, 'p' },
+ { "object-directory", required_argument, NULL, 'o' },
+ { "object-file", required_argument, NULL, 'o' },
+ { "unconditional-branches", no_argument, NULL, 'u' },
+ { 0, 0, 0, 0 }
};
-/* Parse the command line. */
+/* Process args, return index to first non-arg. */
-static void
-process_args (argc, argv)
- int argc;
- char **argv;
+static int
+process_args (int argc, char **argv)
{
int opt;
- while ((opt = getopt_long (argc, argv, "hvbclnfo:", options, NULL)) != -1)
+ while ((opt = getopt_long (argc, argv, "abcfhlno:puv", options, NULL)) != -1)
{
switch (opt)
{
- case 'h':
- print_usage (false);
- /* print_usage will exit. */
- case 'v':
- print_version ();
- /* print_version will exit. */
+ case 'a':
+ flag_all_blocks = 1;
+ break;
case 'b':
- output_branch_probs = 1;
+ flag_branches = 1;
break;
case 'c':
- output_branch_counts = 1;
+ flag_counts = 1;
break;
- case 'n':
- output_gcov_file = 0;
+ case 'f':
+ flag_function_summary = 1;
break;
+ case 'h':
+ print_usage (false);
+ /* print_usage will exit. */
case 'l':
- output_long_names = 1;
+ flag_long_names = 1;
break;
- case 'f':
- output_function_summary = 1;
+ case 'n':
+ flag_gcov_file = 0;
break;
case 'o':
object_directory = optarg;
break;
+ case 'p':
+ flag_preserve_paths = 1;
+ break;
+ case 'u':
+ flag_unconditional = 1;
+ break;
+ case 'v':
+ print_version ();
+ /* print_version will exit. */
default:
print_usage (true);
/* print_usage will exit. */
}
}
- if (optind != argc - 1)
- print_usage (true);
-
- input_file_name = argv[optind];
+ return optind;
}
-
-/* Find and open the .bb, .da, and .bbg files. */
+/* Process a single source file. */
static void
-open_files ()
+process_file (const char *file_name)
{
- int count, objdir_count;
- char *cptr;
+ source_t *src;
+ function_t *fn;
- /* 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;
+ create_file_names (file_name);
+ if (read_graph_file ())
+ return;
+
+ if (!functions)
+ {
+ fnotice (stderr, "%s:no functions found\n", bbg_file_name);
+ return;
+ }
- 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 (read_count_file ())
+ return;
- if (object_directory)
+ for (fn = functions; fn; fn = fn->next)
+ solve_flow_graph (fn);
+ for (src = sources; src; src = src->next)
+ src->lines = XCNEWVEC (line_t, src->num_lines);
+ for (fn = functions; fn; fn = fn->next)
{
- strcpy (da_file_name, object_directory);
- strcpy (bb_file_name, object_directory);
- strcpy (bbg_file_name, object_directory);
+ coverage_t coverage;
- if (object_directory[objdir_count - 1] != '/')
+ memset (&coverage, 0, sizeof (coverage));
+ coverage.name = fn->name;
+ add_line_counts (flag_function_summary ? &coverage : NULL, fn);
+ if (flag_function_summary)
{
- strcat (da_file_name, "/");
- strcat (bb_file_name, "/");
- strcat (bbg_file_name, "/");
+ function_summary (&coverage, "Function");
+ fnotice (stdout, "\n");
}
+ }
- cptr = strrchr (input_file_name, '/');
- if (cptr)
- {
- strcat (da_file_name, cptr + 1);
- strcat (bb_file_name, cptr + 1);
- strcat (bbg_file_name, cptr + 1);
- }
- else
+ for (src = sources; src; src = src->next)
+ {
+ accumulate_line_counts (src);
+ function_summary (&src->coverage, "File");
+ if (flag_gcov_file)
{
- strcat (da_file_name, input_file_name);
- strcat (bb_file_name, input_file_name);
- strcat (bbg_file_name, input_file_name);
+ char *gcov_file_name = make_gcov_file_name (file_name, src->name);
+ FILE *gcov_file = fopen (gcov_file_name, "w");
+
+ if (gcov_file)
+ {
+ fnotice (stdout, "%s:creating '%s'\n",
+ src->name, gcov_file_name);
+ output_lines (gcov_file, src);
+ if (ferror (gcov_file))
+ fnotice (stderr, "%s:error writing output file '%s'\n",
+ src->name, gcov_file_name);
+ fclose (gcov_file);
+ }
+ else
+ fnotice (stderr, "%s:could not open output file '%s'\n",
+ src->name, gcov_file_name);
+ free (gcov_file_name);
}
+ fnotice (stdout, "\n");
}
- else
- {
- strcpy (da_file_name, input_file_name);
- strcpy (bb_file_name, input_file_name);
- strcpy (bbg_file_name, input_file_name);
- }
+}
- cptr = strrchr (bb_file_name, '.');
- if (cptr)
- strcpy (cptr, ".bb");
- else
- strcat (bb_file_name, ".bb");
+/* Release all memory used. */
- cptr = strrchr (da_file_name, '.');
- if (cptr)
- strcpy (cptr, ".da");
- else
- strcat (da_file_name, ".da");
+static void
+release_structures (void)
+{
+ function_t *fn;
+ source_t *src;
- cptr = strrchr (bbg_file_name, '.');
- if (cptr)
- strcpy (cptr, ".bbg");
- else
- strcat (bbg_file_name, ".bbg");
+ free (bbg_file_name);
+ free (da_file_name);
+ da_file_name = bbg_file_name = NULL;
+ bbg_file_time = 0;
+ bbg_stamp = 0;
- bb_file = fopen (bb_file_name, "rb");
- if (bb_file == NULL)
+ while ((src = sources))
{
- fnotice (stderr, "Could not open basic block file %s.\n", bb_file_name);
- exit (FATAL_EXIT_CODE);
- }
+ sources = src->next;
- /* 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, "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");
+ free (src->name);
+ free (src->lines);
}
- bbg_file = fopen (bbg_file_name, "rb");
- if (bbg_file == NULL)
+ while ((fn = functions))
{
- fnotice (stderr, "Could not open program flow graph file %s.\n",
- bbg_file_name);
- exit (FATAL_EXIT_CODE);
- }
+ unsigned ix;
+ block_t *block;
- /* 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))
- {
- fnotice (stderr, "No executable code associated with file %s.\n",
- input_file_name);
- exit (FATAL_EXIT_CODE);
+ functions = fn->next;
+ for (ix = fn->num_blocks, block = fn->blocks; ix--; block++)
+ {
+ arc_t *arc, *arc_n;
+
+ for (arc = block->succ; arc; arc = arc_n)
+ {
+ arc_n = arc->succ_next;
+ free (arc);
+ }
+ }
+ free (fn->blocks);
+ free (fn->counts);
}
}
-\f
-/* Initialize a new arc. */
+
+/* Generate the names of the graph and data files. If OBJECT_DIRECTORY
+ is not specified, these are looked for in the current directory,
+ and named from the basename of the FILE_NAME sans extension. If
+ OBJECT_DIRECTORY is specified and is a directory, the files are in
+ that directory, but named from the basename of the FILE_NAME, sans
+ extension. Otherwise OBJECT_DIRECTORY is taken to be the name of
+ the object *file*, and the data files are named from that. */
static void
-init_arc (arcptr, source, target, bb_graph)
- struct adj_list *arcptr;
- int source, target;
- struct bb_info *bb_graph;
+create_file_names (const char *file_name)
{
- 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++;
-}
+ char *cptr;
+ char *name;
+ int length = strlen (file_name);
+ int base;
+ if (object_directory && object_directory[0])
+ {
+ struct stat status;
-/* Reverse the arcs on an arc list. */
+ length += strlen (object_directory) + 2;
+ name = XNEWVEC (char, length);
+ name[0] = 0;
-static struct adj_list *
-reverse_arcs (arcptr)
- struct adj_list *arcptr;
-{
- struct adj_list *prev = 0;
- struct adj_list *next;
+ base = !stat (object_directory, &status) && S_ISDIR (status.st_mode);
+ strcat (name, object_directory);
+ if (base && name[strlen (name) - 1] != '/')
+ strcat (name, "/");
+ }
+ else
+ {
+ name = XNEWVEC (char, length + 1);
+ name[0] = 0;
+ base = 1;
+ }
- for ( ; arcptr; arcptr = next)
+ if (base)
{
- next = arcptr->succ_next;
- arcptr->succ_next = prev;
- prev = arcptr;
+ /* Append source file name. */
+ cptr = strrchr (file_name, '/');
+ strcat (name, cptr ? cptr + 1 : file_name);
}
- return prev;
-}
+ /* Remove the extension. */
+ cptr = strrchr (name, '.');
+ if (cptr)
+ *cptr = 0;
-/* Reads profiles from the .da file and compute a hybrid profile. */
+ length = strlen (name);
+
+ bbg_file_name = XNEWVEC (char, length + strlen (GCOV_NOTE_SUFFIX) + 1);
+ strcpy (bbg_file_name, name);
+ strcpy (bbg_file_name + length, GCOV_NOTE_SUFFIX);
-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;
+ da_file_name = XNEWVEC (char, length + strlen (GCOV_DATA_SUFFIX) + 1);
+ strcpy (da_file_name, name);
+ strcpy (da_file_name + length, GCOV_DATA_SUFFIX);
- profile = xmalloc (sizeof (gcov_type) * instr_arcs);
- rewind (da_file);
- function_name_buffer_len = strlen (function_name) + 1;
- function_name_buffer = xmalloc (function_name_buffer_len + 1);
+ free (name);
+ return;
+}
- for (i = 0; i < instr_arcs; i++)
- profile[i] = 0;
+/* Find or create a source file structure for FILE_NAME. Copies
+ FILE_NAME on creation */
- if (!da_file)
- return profile;
+static source_t *
+find_source (const char *file_name)
+{
+ source_t *src;
- while (1)
- {
- long magic, extra_bytes;
- long func_count;
- int i;
+ if (!file_name)
+ file_name = "<unknown>";
- if (__read_long (&magic, da_file, 4) != 0)
- break;
+ for (src = sources; src; src = src->next)
+ if (!strcmp (file_name, src->name))
+ return src;
- if (magic != -123)
- {
- okay = 0;
- break;
- }
+ src = XCNEW (source_t);
+ src->name = xstrdup (file_name);
+ src->coverage.name = src->name;
+ src->index = sources ? sources->index + 1 : 1;
+ src->next = sources;
+ sources = src;
- if (__read_long (&func_count, da_file, 4) != 0)
- {
- okay = 0;
- break;
- }
+ return src;
+}
- if (__read_long (&extra_bytes, da_file, 4) != 0)
- {
- okay = 0;
- break;
- }
+/* Read the graph file. Return nonzero on fatal error. */
- /* skip extra data emited by __bb_exit_func. */
- fseek (da_file, extra_bytes, SEEK_CUR);
+static int
+read_graph_file (void)
+{
+ unsigned version;
+ unsigned current_tag = 0;
+ struct function_info *fn = NULL;
+ source_t *src = NULL;
+ unsigned ix;
+ unsigned tag;
+
+ if (!gcov_open (bbg_file_name, 1))
+ {
+ fnotice (stderr, "%s:cannot open graph file\n", bbg_file_name);
+ return 1;
+ }
+ bbg_file_time = gcov_time ();
+ if (!gcov_magic (gcov_read_unsigned (), GCOV_NOTE_MAGIC))
+ {
+ fnotice (stderr, "%s:not a gcov graph file\n", bbg_file_name);
+ gcov_close ();
+ return 1;
+ }
- for (i = 0; i < func_count; i++)
- {
- long arc_count;
- long chksum;
- int j;
+ version = gcov_read_unsigned ();
+ if (version != GCOV_VERSION)
+ {
+ char v[4], e[4];
- if (__read_gcov_string
- (function_name_buffer, function_name_buffer_len, da_file,
- -1) != 0)
- {
- okay = 0;
- break;
- }
+ GCOV_UNSIGNED2STRING (v, version);
+ GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
- if (__read_long (&chksum, da_file, 4) != 0)
- {
- okay = 0;
- break;
- }
+ fnotice (stderr, "%s:version '%.4s', prefer '%.4s'\n",
+ bbg_file_name, v, e);
+ }
+ bbg_stamp = gcov_read_unsigned ();
- if (__read_long (&arc_count, da_file, 4) != 0)
- {
- okay = 0;
- break;
- }
+ while ((tag = gcov_read_unsigned ()))
+ {
+ unsigned length = gcov_read_unsigned ();
+ gcov_position_t base = gcov_position ();
- if (strcmp (function_name_buffer, function_name) != 0
- || arc_count != instr_arcs || chksum != cfg_checksum)
- {
- /* skip */
- if (fseek (da_file, arc_count * 8, SEEK_CUR) < 0)
- {
- okay = 0;
- break;
- }
- }
+ if (tag == GCOV_TAG_FUNCTION)
+ {
+ char *function_name;
+ unsigned ident, checksum, lineno;
+ source_t *src;
+ function_t *probe, *prev;
+
+ ident = gcov_read_unsigned ();
+ checksum = gcov_read_unsigned ();
+ function_name = xstrdup (gcov_read_string ());
+ src = find_source (gcov_read_string ());
+ lineno = gcov_read_unsigned ();
+
+ fn = XCNEW (function_t);
+ fn->name = function_name;
+ fn->ident = ident;
+ fn->checksum = checksum;
+ fn->src = src;
+ fn->line = lineno;
+
+ fn->next = functions;
+ functions = fn;
+ current_tag = tag;
+
+ if (lineno >= src->num_lines)
+ src->num_lines = lineno + 1;
+ /* Now insert it into the source file's list of
+ functions. Normally functions will be encountered in
+ ascending order, so a simple scan is quick. */
+ for (probe = src->functions, prev = NULL;
+ probe && probe->line > lineno;
+ prev = probe, probe = probe->line_next)
+ continue;
+ fn->line_next = probe;
+ if (prev)
+ prev->line_next = fn;
+ else
+ src->functions = fn;
+ }
+ else if (fn && tag == GCOV_TAG_BLOCKS)
+ {
+ if (fn->blocks)
+ fnotice (stderr, "%s:already seen blocks for '%s'\n",
+ bbg_file_name, fn->name);
else
{
- gcov_type tmp;
+ unsigned ix, num_blocks = GCOV_TAG_BLOCKS_NUM (length);
+ fn->num_blocks = num_blocks;
- for (j = 0; j < arc_count; j++)
- if (__read_gcov_type (&tmp, da_file, 8) != 0)
- {
- okay = 0;
- break;
- }
- else
- {
- profile[j] += tmp;
- }
+ fn->blocks = XCNEWVEC (block_t, fn->num_blocks);
+ for (ix = 0; ix != num_blocks; ix++)
+ fn->blocks[ix].flags = gcov_read_unsigned ();
}
}
+ else if (fn && tag == GCOV_TAG_ARCS)
+ {
+ unsigned src = gcov_read_unsigned ();
+ unsigned num_dests = GCOV_TAG_ARCS_NUM (length);
- if (!okay)
- break;
+ if (src >= fn->num_blocks || fn->blocks[src].succ)
+ goto corrupt;
- }
+ while (num_dests--)
+ {
+ struct arc_info *arc;
+ unsigned dest = gcov_read_unsigned ();
+ unsigned flags = gcov_read_unsigned ();
- free (function_name_buffer);
+ if (dest >= fn->num_blocks)
+ goto corrupt;
+ arc = XCNEW (arc_t);
- if (!okay)
- {
- fprintf (stderr, ".da file corrupted!\n");
- free (profile);
- abort ();
- }
+ arc->dst = &fn->blocks[dest];
+ arc->src = &fn->blocks[src];
- return profile;
-}
+ arc->count = 0;
+ arc->count_valid = 0;
+ arc->on_tree = !!(flags & GCOV_ARC_ON_TREE);
+ arc->fake = !!(flags & GCOV_ARC_FAKE);
+ arc->fall_through = !!(flags & GCOV_ARC_FALLTHROUGH);
-/* Construct the program flow graph from the .bbg file, and read in the data
- in the .da file. */
+ arc->succ_next = fn->blocks[src].succ;
+ fn->blocks[src].succ = arc;
+ fn->blocks[src].num_succ++;
-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 function name. */
- __read_long (&tmp, bbg_file, 4); /* ignore -1. */
- __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);
-
- /* Skip padding. */
- tmp = (function_name_len + 1) % 4;
+ arc->pred_next = fn->blocks[dest].pred;
+ fn->blocks[dest].pred = arc;
+ fn->blocks[dest].num_pred++;
+
+ if (arc->fake)
+ {
+ if (src)
+ {
+ /* Exceptional exit from this function, the
+ source block must be a call. */
+ fn->blocks[src].is_call_site = 1;
+ arc->is_call_non_return = 1;
+ }
+ else
+ {
+ /* Non-local return from a callee of this
+ function. The destination block is a catch or
+ setjmp. */
+ arc->is_nonlocal_return = 1;
+ fn->blocks[dest].is_nonlocal_return = 1;
+ }
+ }
- if (tmp)
- fseek (bbg_file, 4 - tmp, SEEK_CUR);
+ if (!arc->on_tree)
+ fn->num_counts++;
+ }
+ }
+ else if (fn && tag == GCOV_TAG_LINES)
+ {
+ unsigned blockno = gcov_read_unsigned ();
+ unsigned *line_nos = XCNEWVEC (unsigned, length - 1);
- __read_long (&tmp, bbg_file, 4); /* ignore -1. */
-
- /* Read the cfg checksum. */
- __read_long (&cfg_checksum, bbg_file, 4);
+ if (blockno >= fn->num_blocks || fn->blocks[blockno].u.line.encoding)
+ goto corrupt;
- /* Read the number of blocks. */
- __read_long (&num_blocks, bbg_file, 4);
+ for (ix = 0; ; )
+ {
+ unsigned lineno = gcov_read_unsigned ();
- /* Create an array of size bb number of bb_info structs. */
- bb_graph = (struct bb_info *) xcalloc (num_blocks, sizeof (struct bb_info));
+ if (lineno)
+ {
+ if (!ix)
+ {
+ line_nos[ix++] = 0;
+ line_nos[ix++] = src->index;
+ }
+ line_nos[ix++] = lineno;
+ if (lineno >= src->num_lines)
+ src->num_lines = lineno + 1;
+ }
+ else
+ {
+ const char *file_name = gcov_read_string ();
- bptr->bb_graph = bb_graph;
- bptr->num_blocks = num_blocks;
+ if (!file_name)
+ break;
+ src = find_source (file_name);
- /* 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;
+ line_nos[ix++] = 0;
+ line_nos[ix++] = src->index;
+ }
+ }
- __read_long (&num_arcs_per_block, bbg_file, 4);
- for (j = 0; j < num_arcs_per_block; j++)
+ fn->blocks[blockno].u.line.encoding = line_nos;
+ fn->blocks[blockno].u.line.num = ix;
+ }
+ else if (current_tag && !GCOV_TAG_IS_SUBTAG (current_tag, tag))
+ {
+ fn = NULL;
+ current_tag = 0;
+ }
+ gcov_sync (base, length);
+ if (gcov_is_error ())
{
- 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);
+ corrupt:;
+ fnotice (stderr, "%s:corrupted\n", bbg_file_name);
+ gcov_close ();
+ return 1;
}
}
+ gcov_close ();
- 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 ();
+ /* We built everything backwards, so nreverse them all. */
- /* Must reverse the order of all succ arcs, to ensure that they match
- the order of the data in the .da file. */
+ /* Reverse sources. Not strictly necessary, but we'll then process
+ them in the 'expected' order. */
+ {
+ source_t *src, *src_p, *src_n;
- for (i = 0; i < num_blocks; i++)
- if (bb_graph[i].succ)
- bb_graph[i].succ = reverse_arcs (bb_graph[i].succ);
+ for (src_p = NULL, src = sources; src; src_p = src, src = src_n)
+ {
+ src_n = src->next;
+ src->next = src_p;
+ }
+ sources = src_p;
+ }
- /* Read profile from the .da file. */
+ /* Reverse functions. */
+ {
+ function_t *fn, *fn_p, *fn_n;
- profile = read_profile (function_name, cfg_checksum, instr_arcs);
+ for (fn_p = NULL, fn = functions; fn; fn_p = fn, fn = fn_n)
+ {
+ unsigned ix;
- /* For each arc not on the spanning tree, set its execution count from
- the .da file. */
+ fn_n = fn->next;
+ fn->next = fn_p;
- /* 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. */
+ /* Reverse the arcs. */
+ for (ix = fn->num_blocks; ix--;)
+ {
+ arc_t *arc, *arc_p, *arc_n;
- /* This duplicates code in branch_prob in profile.c. */
+ for (arc_p = NULL, arc = fn->blocks[ix].succ; arc;
+ arc_p = arc, arc = arc_n)
+ {
+ arc_n = arc->succ_next;
+ arc->succ_next = arc_p;
+ }
+ fn->blocks[ix].succ = arc_p;
- for (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);
+ for (arc_p = NULL, arc = fn->blocks[ix].pred; arc;
+ arc_p = arc, arc = arc_n)
+ {
+ arc_n = arc->pred_next;
+ arc->pred_next = arc_p;
+ }
+ fn->blocks[ix].pred = arc_p;
+ }
+ }
+ functions = fn_p;
+ }
+ return 0;
}
-static void
-solve_program_flow_graph (bptr)
- struct bb_info_list *bptr;
+/* Reads profiles from the count file and attach to each
+ function. Return nonzero if fatal error. */
+
+static int
+read_count_file (void)
{
- 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;
-
- /* 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)
+ unsigned ix;
+ unsigned version;
+ unsigned tag;
+ function_t *fn = NULL;
+ int error = 0;
+
+ if (!gcov_open (da_file_name, 1))
+ {
+ fnotice (stderr, "%s:cannot open data file, assuming not executed\n",
+ da_file_name);
+ no_data_file = 1;
+ return 0;
+ }
+ if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC))
{
- passes++;
- changes = 0;
+ fnotice (stderr, "%s:not a gcov data file\n", da_file_name);
+ cleanup:;
+ gcov_close ();
+ return 1;
+ }
+ version = gcov_read_unsigned ();
+ if (version != GCOV_VERSION)
+ {
+ char v[4], e[4];
+
+ GCOV_UNSIGNED2STRING (v, version);
+ GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
+
+ fnotice (stderr, "%s:version '%.4s', prefer version '%.4s'\n",
+ da_file_name, v, e);
+ }
+ tag = gcov_read_unsigned ();
+ if (tag != bbg_stamp)
+ {
+ fnotice (stderr, "%s:stamp mismatch with graph file\n", da_file_name);
+ goto cleanup;
+ }
- for (i = num_blocks - 1; i >= 0; i--)
+ while ((tag = gcov_read_unsigned ()))
+ {
+ unsigned length = gcov_read_unsigned ();
+ unsigned long base = gcov_position ();
+
+ if (tag == GCOV_TAG_OBJECT_SUMMARY)
+ gcov_read_summary (&object_summary);
+ else if (tag == GCOV_TAG_PROGRAM_SUMMARY)
+ program_count++;
+ else if (tag == GCOV_TAG_FUNCTION)
{
- if (! bb_graph[i].count_valid)
+ unsigned ident = gcov_read_unsigned ();
+ struct function_info *fn_n = functions;
+
+ for (fn = fn ? fn->next : NULL; ; fn = fn->next)
{
- 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)
+ if (fn)
+ ;
+ else if ((fn = fn_n))
+ fn_n = NULL;
+ else
{
- 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;
+ fnotice (stderr, "%s:unknown function '%u'\n",
+ da_file_name, ident);
+ break;
}
+ if (fn->ident == ident)
+ break;
}
- if (bb_graph[i].count_valid)
+
+ if (!fn)
+ ;
+ else if (gcov_read_unsigned () != fn->checksum)
{
- 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;
- }
+ mismatch:;
+ fnotice (stderr, "%s:profile mismatch for '%s'\n",
+ da_file_name, fn->name);
+ goto cleanup;
}
}
+ else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) && fn)
+ {
+ if (length != GCOV_TAG_COUNTER_LENGTH (fn->num_counts))
+ goto mismatch;
+
+ if (!fn->counts)
+ fn->counts = XCNEWVEC (gcov_type, fn->num_counts);
+
+ for (ix = 0; ix != fn->num_counts; ix++)
+ fn->counts[ix] += gcov_read_counter ();
+ }
+ gcov_sync (base, length);
+ if ((error = gcov_is_error ()))
+ {
+ fnotice (stderr, error < 0 ? "%s:overflowed\n" : "%s:corrupted\n",
+ da_file_name);
+ goto cleanup;
+ }
}
- /* 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 ();
+ gcov_close ();
+ return 0;
}
+/* Solve the flow graph. Propagate counts from the instrumented arcs
+ to the blocks and the uninstrumented arcs. */
static void
-read_files ()
+solve_flow_graph (function_t *fn)
{
- struct stat buf;
- struct bb_info_list *list_end = 0;
- struct bb_info_list *b_ptr;
-
- while (! feof (bbg_file))
+ unsigned ix;
+ arc_t *arc;
+ gcov_type *count_ptr = fn->counts;
+ block_t *blk;
+ block_t *valid_blocks = NULL; /* valid, but unpropagated blocks. */
+ block_t *invalid_blocks = NULL; /* invalid, but inferable blocks. */
+
+ if (fn->num_blocks < 2)
+ fnotice (stderr, "%s:'%s' lacks entry and/or exit blocks\n",
+ bbg_file_name, fn->name);
+ else
{
- b_ptr = (struct bb_info_list *) xmalloc (sizeof (struct bb_info_list));
-
- b_ptr->next = 0;
- if (list_end)
- list_end->next = b_ptr;
+ if (fn->blocks[0].num_pred)
+ fnotice (stderr, "%s:'%s' has arcs to entry block\n",
+ bbg_file_name, fn->name);
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);
+ /* We can't deduce the entry block counts from the lack of
+ predecessors. */
+ fn->blocks[0].num_pred = ~(unsigned)0;
- /* Set the EOF condition if at the end of file. */
- ungetc (getc (bbg_file), bbg_file);
+ if (fn->blocks[fn->num_blocks - 1].num_succ)
+ fnotice (stderr, "%s:'%s' has arcs from exit block\n",
+ bbg_file_name, fn->name);
+ else
+ /* Likewise, we can't deduce exit block counts from the lack
+ of its successors. */
+ fn->blocks[fn->num_blocks - 1].num_succ = ~(unsigned)0;
}
- /* Calculate all of the basic block execution counts and branch
- taken probabilities. */
+ /* Propagate the measured counts, this must be done in the same
+ order as the code in profile.c */
+ for (ix = 0, blk = fn->blocks; ix != fn->num_blocks; ix++, blk++)
+ {
+ block_t const *prev_dst = NULL;
+ int out_of_order = 0;
+ int non_fake_succ = 0;
- for (b_ptr = bb_graph_list; b_ptr; b_ptr = b_ptr->next)
- solve_program_flow_graph (b_ptr);
+ for (arc = blk->succ; arc; arc = arc->succ_next)
+ {
+ if (!arc->fake)
+ non_fake_succ++;
- /* 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;
+ if (!arc->on_tree)
+ {
+ if (count_ptr)
+ arc->count = *count_ptr++;
+ arc->count_valid = 1;
+ blk->num_succ--;
+ arc->dst->num_pred--;
+ }
+ if (prev_dst && prev_dst > arc->dst)
+ out_of_order = 1;
+ prev_dst = arc->dst;
+ }
+ if (non_fake_succ == 1)
+ {
+ /* If there is only one non-fake exit, it is an
+ unconditional branch. */
+ for (arc = blk->succ; arc; arc = arc->succ_next)
+ if (!arc->fake)
+ {
+ arc->is_unconditional = 1;
+ /* If this block is instrumenting a call, it might be
+ an artificial block. It is not artificial if it has
+ a non-fallthrough exit, or the destination of this
+ arc has more than one entry. Mark the destination
+ block as a return site, if none of those conditions
+ hold. */
+ if (blk->is_call_site && arc->fall_through
+ && arc->dst->pred == arc && !arc->pred_next)
+ arc->dst->is_call_return = 1;
+ }
+ }
- bb_data = (char *) xmalloc ((unsigned) buf.st_size);
- fread (bb_data, sizeof (char), buf.st_size, bb_file);
+ /* Sort the successor arcs into ascending dst order. profile.c
+ normally produces arcs in the right order, but sometimes with
+ one or two out of order. We're not using a particularly
+ smart sort. */
+ if (out_of_order)
+ {
+ arc_t *start = blk->succ;
+ unsigned changes = 1;
- fclose (bb_file);
- if (da_file)
- fclose (da_file);
- fclose (bbg_file);
-}
+ while (changes)
+ {
+ arc_t *arc, *arc_p, *arc_n;
+ changes = 0;
+ for (arc_p = NULL, arc = start; (arc_n = arc->succ_next);)
+ {
+ if (arc->dst > arc_n->dst)
+ {
+ changes = 1;
+ if (arc_p)
+ arc_p->succ_next = arc_n;
+ else
+ start = arc_n;
+ arc->succ_next = arc_n->succ_next;
+ arc_n->succ_next = arc;
+ arc_p = arc_n;
+ }
+ else
+ {
+ arc_p = arc;
+ arc = arc_n;
+ }
+ }
+ }
+ blk->succ = start;
+ }
-/* Scan the data in the .bb file to find all source files referenced,
- and the largest line number mentioned in each one. */
+ /* Place it on the invalid chain, it will be ignored if that's
+ wrong. */
+ blk->invalid_chain = 1;
+ blk->chain = invalid_blocks;
+ invalid_blocks = blk;
+ }
-static void
-scan_for_source_files ()
-{
- struct sourcefile *s_ptr = NULL;
- char *ptr;
- long 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++)
+ while (invalid_blocks || valid_blocks)
{
- __fetch_long (&line_num, ptr, 4);
- ptr += 4;
- if (line_num == -1)
+ while ((blk = invalid_blocks))
+ {
+ gcov_type total = 0;
+ const arc_t *arc;
+
+ invalid_blocks = blk->chain;
+ blk->invalid_chain = 0;
+ if (!blk->num_succ)
+ for (arc = blk->succ; arc; arc = arc->succ_next)
+ total += arc->count;
+ else if (!blk->num_pred)
+ for (arc = blk->pred; arc; arc = arc->pred_next)
+ total += arc->count;
+ else
+ continue;
+
+ blk->count = total;
+ blk->count_valid = 1;
+ blk->chain = valid_blocks;
+ blk->valid_chain = 1;
+ valid_blocks = blk;
+ }
+ while ((blk = valid_blocks))
{
- /* 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;
+ gcov_type total;
+ arc_t *arc, *inv_arc;
- if (s_ptr == 0)
+ valid_blocks = blk->chain;
+ blk->valid_chain = 0;
+ if (blk->num_succ == 1)
{
- /* 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 = xstrdup (ptr);
- s_ptr->maxlineno = 0;
- s_ptr->next = sources;
- sources = s_ptr;
+ block_t *dst;
+
+ total = blk->count;
+ inv_arc = NULL;
+ for (arc = blk->succ; arc; arc = arc->succ_next)
+ {
+ total -= arc->count;
+ if (!arc->count_valid)
+ inv_arc = arc;
+ }
+ dst = inv_arc->dst;
+ inv_arc->count_valid = 1;
+ inv_arc->count = total;
+ blk->num_succ--;
+ dst->num_pred--;
+ if (dst->count_valid)
+ {
+ if (dst->num_pred == 1 && !dst->valid_chain)
+ {
+ dst->chain = valid_blocks;
+ dst->valid_chain = 1;
+ valid_blocks = dst;
+ }
+ }
+ else
+ {
+ if (!dst->num_pred && !dst->invalid_chain)
+ {
+ dst->chain = invalid_blocks;
+ dst->invalid_chain = 1;
+ invalid_blocks = dst;
+ }
+ }
}
+ if (blk->num_pred == 1)
+ {
+ block_t *src;
- /* 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 ();
+ total = blk->count;
+ inv_arc = NULL;
+ for (arc = blk->pred; arc; arc = arc->pred_next)
+ {
+ total -= arc->count;
+ if (!arc->count_valid)
+ inv_arc = arc;
+ }
+ src = inv_arc->src;
+ inv_arc->count_valid = 1;
+ inv_arc->count = total;
+ blk->num_pred--;
+ src->num_succ--;
+ if (src->count_valid)
+ {
+ if (src->num_succ == 1 && !src->valid_chain)
+ {
+ src->chain = valid_blocks;
+ src->valid_chain = 1;
+ valid_blocks = src;
+ }
+ }
+ else
+ {
+ if (!src->num_succ && !src->invalid_chain)
+ {
+ src->chain = invalid_blocks;
+ src->invalid_chain = 1;
+ invalid_blocks = src;
+ }
+ }
+ }
}
}
+
+ /* If the graph has been correctly solved, every block will have a
+ valid count. */
+ for (ix = 0; ix < fn->num_blocks; ix++)
+ if (!fn->blocks[ix].count_valid)
+ {
+ fnotice (stderr, "%s:graph is unsolvable for '%s'\n",
+ bbg_file_name, fn->name);
+ break;
+ }
}
-\f
-/* 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;
+\f
-/* Calculate the branch taken probabilities for all arcs branches at the
- end of this block. */
+/* Increment totals in COVERAGE according to arc ARC. */
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;
+add_branch_counts (coverage_t *coverage, const arc_t *arc)
{
- gcov_type 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)
+ if (arc->is_call_non_return)
{
- /* Ignore fall through arcs as they aren't really branches. */
+ coverage->calls++;
+ if (arc->src->count)
+ coverage->calls_executed++;
+ }
+ else if (!arc->is_unconditional)
+ {
+ coverage->branches++;
+ if (arc->src->count)
+ coverage->branches_executed++;
+ if (arc->count)
+ coverage->branches_taken++;
+ }
+}
- if (arcptr->fall_through)
- continue;
+/* Format a HOST_WIDE_INT as either a percent ratio, or absolute
+ count. If dp >= 0, format TOP/BOTTOM * 100 to DP decimal places.
+ If DP is zero, no decimal point is printed. Only print 100% when
+ TOP==BOTTOM and only print 0% when TOP=0. If dp < 0, then simply
+ format TOP. Return pointer to a static string. */
- a_ptr = (struct arcdata *) xmalloc (sizeof (struct arcdata));
- a_ptr->total = total;
- if (total == 0)
- a_ptr->hits = 0;
- else
- a_ptr->hits = arcptr->arc_count;
- a_ptr->call_insn = arcptr->fake;
+static char const *
+format_gcov (gcov_type top, gcov_type bottom, int dp)
+{
+ static char buffer[20];
- if (output_function_summary)
+ 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)
{
- if (a_ptr->call_insn)
+ dp++;
+ do
{
- function_calls++;
- if (a_ptr->total != 0)
- function_calls_executed++;
+ buffer[ix+1] = buffer[ix];
+ ix--;
}
- 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;
+ while (dp--);
+ buffer[ix + 1] = '.';
}
}
+ else
+ sprintf (buffer, HOST_WIDEST_INT_PRINT_DEC, (HOST_WIDEST_INT)top);
+
+ return buffer;
}
+
/* Output summary info for a function. */
static void
-function_summary ()
+function_summary (const coverage_t *coverage, const char *title)
{
- if (function_source_lines)
- fnotice (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);
+ fnotice (stdout, "%s '%s'\n", title, coverage->name);
+
+ if (coverage->lines)
+ fnotice (stdout, "Lines executed:%s of %d\n",
+ format_gcov (coverage->lines_executed, coverage->lines, 2),
+ coverage->lines);
else
- fnotice (stdout, "No executable source lines in function %s\n",
- function_name);
+ fnotice (stdout, "No executable lines\n");
- if (output_branch_probs)
+ if (flag_branches)
{
- if (function_branches)
+ if (coverage->branches)
{
- fnotice (stdout, "%6.2f%% of %d branches executed in function %s\n",
- (((double) function_branches_executed / function_branches)
- * 100), function_branches, function_name);
- fnotice (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);
+ fnotice (stdout, "Branches executed:%s of %d\n",
+ format_gcov (coverage->branches_executed,
+ coverage->branches, 2),
+ coverage->branches);
+ fnotice (stdout, "Taken at least once:%s of %d\n",
+ format_gcov (coverage->branches_taken,
+ coverage->branches, 2),
+ coverage->branches);
}
else
- fnotice (stdout, "No branches in function %s\n", function_name);
- if (function_calls)
- fnotice (stdout, "%6.2f%% of %d calls executed in function %s\n",
- (((double) function_calls_executed / function_calls)
- * 100), function_calls, function_name);
+ fnotice (stdout, "No branches\n");
+ if (coverage->calls)
+ fnotice (stdout, "Calls executed:%s of %d\n",
+ format_gcov (coverage->calls_executed, coverage->calls, 2),
+ coverage->calls);
else
- fnotice (stdout, "No calls in function %s\n", function_name);
+ fnotice (stdout, "No calls\n");
}
}
-/* Calculate line execution counts, and output the data to a .tcov file. */
-
-static void
-output_data ()
+/* 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 '^'. */
+
+static char *
+make_gcov_file_name (const char *input_name, const 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;
- 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)
+ char *name = XNEWVEC (char, strlen (src_name) + strlen (input_name) + 10);
+
+ name[0] = 0;
+ if (flag_long_names && strcmp (src_name, input_name))
+ {
+ /* Generate the input filename part. */
+ cptr = flag_preserve_paths ? NULL : strrchr (input_name, '/');
+ strcat (name, cptr ? cptr + 1 : input_name);
+ strcat (name, "##");
+ }
+
+ /* Generate the source filename part. */
+ cptr = flag_preserve_paths ? NULL : strrchr (src_name, '/');
+ strcat (name, cptr ? cptr + 1 : src_name);
+
+ if (flag_preserve_paths)
{
- /* If this is a relative file name, and an object directory has been
- specified, then make it relative to the object directory name. */
- if (! IS_ABSOLUTE_PATHNAME (s_ptr->name)
- && object_directory != 0
- && *object_directory != '\0')
+ /* Convert '/' to '#', remove '/./', convert '/../' to '/^/' */
+ char *prev;
+
+ for (cptr = name; (cptr = strchr ((prev = cptr), '/'));)
{
- 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);
+ unsigned shift = 0;
+
+ if (prev + 1 == cptr && prev[0] == '.')
+ {
+ /* 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++);
+ }
}
- else
- source_file_name = s_ptr->name;
-
- line_counts = (gcov_type *) xcalloc (sizeof (gcov_type), s_ptr->maxlineno);
- line_exists = xcalloc (1, s_ptr->maxlineno);
- if (output_branch_probs)
- 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
- 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
- {
- 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);
- }
-
- 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. */
+ strcat (name, ".gcov");
+ return name;
+}
- if (block_num >= current_graph->num_blocks)
- {
- fnotice (stderr, "ERROR: too many basic blocks in .bb file %s\n",
- function_name);
- abort ();
- }
+/* Scan through the bb_data for each line in the block, increment
+ the line number execution count indicated by the execution count of
+ the appropriate basic block. */
- if (output_branch_probs && this_file)
- calculate_branch_probs (current_graph, block_num,
- branch_probs, last_line_num);
+static void
+add_line_counts (coverage_t *coverage, function_t *fn)
+{
+ unsigned ix;
+ line_t *line = NULL; /* This is propagated from one iteration to the
+ next. */
- block_num++;
- }
- else if (this_file)
+ /* Scan each basic block. */
+ for (ix = 0; ix != fn->num_blocks; ix++)
+ {
+ block_t *block = &fn->blocks[ix];
+ unsigned *encoding;
+ const source_t *src = NULL;
+ unsigned jx;
+
+ if (block->count && ix && ix + 1 != fn->num_blocks)
+ fn->blocks_executed++;
+ for (jx = 0, encoding = block->u.line.encoding;
+ jx != block->u.line.num; jx++, encoding++)
+ if (!*encoding)
+ {
+ unsigned src_n = *++encoding;
+
+ for (src = sources; src->index != src_n; src = src->next)
+ continue;
+ jx++;
+ }
+ else
+ {
+ line = &src->lines[*encoding];
+
+ if (coverage)
{
- if (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 (!line->exists)
+ coverage->lines++;
+ if (!line->count && block->count)
+ coverage->lines_executed++;
}
+ line->exists = 1;
+ line->count += block->count;
}
- }
-
- 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;
+ free (block->u.line.encoding);
+ block->u.cycle.arc = NULL;
+ block->u.cycle.ident = ~0U;
- for (count = 1; count < s_ptr->maxlineno; count++)
+ if (!ix || ix + 1 == fn->num_blocks)
+ /* Entry or exit block */;
+ else if (flag_all_blocks)
{
- 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->total != 0)
- total_calls_executed++;
- }
- else
- {
- total_branches++;
- if (a_ptr->total != 0)
- total_branches_executed++;
- if (a_ptr->hits > 0)
- total_branches_taken++;
- }
- }
- }
- }
+ line_t *block_line = line ? line : &fn->src->lines[fn->line];
- if (total_source_lines)
- fnotice (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
- fnotice (stdout, "No executable source lines in file %s\n",
- source_file_name);
-
- if (output_branch_probs)
+ block->chain = block_line->u.blocks;
+ block_line->u.blocks = block;
+ }
+ else if (flag_branches)
{
- if (total_branches)
+ arc_t *arc;
+
+ for (arc = block->succ; arc; arc = arc->succ_next)
{
- fnotice (stdout, "%6.2f%% of %d branches executed in file %s\n",
- (((double) total_branches_executed / total_branches)
- * 100), total_branches, source_file_name);
- fnotice (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);
+ arc->line_next = line->u.branches;
+ line->u.branches = arc;
+ if (coverage && !arc->is_unconditional)
+ add_branch_counts (coverage, arc);
}
- else
- fnotice (stdout, "No branches in file %s\n", source_file_name);
- if (total_calls)
- fnotice (stdout, "%6.2f%% of %d calls executed in file %s\n",
- (((double) total_calls_executed / total_calls)
- * 100), total_calls, source_file_name);
- else
- fnotice (stdout, "No calls in file %s\n", source_file_name);
}
+ }
+ if (!line)
+ fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->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. */
+/* Accumulate the line counts of a file. */
- source_file = fopen (source_file_name, "r");
- if (source_file == NULL)
- {
- fnotice (stderr, "Could not open source file %s.\n",
- source_file_name);
- free (line_counts);
- free (line_exists);
- continue;
- }
+static void
+accumulate_line_counts (source_t *src)
+{
+ line_t *line;
+ function_t *fn, *fn_p, *fn_n;
+ unsigned ix;
- count = strlen (source_file_name);
- cptr = strrchr (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));
+ /* Reverse the function order. */
+ for (fn = src->functions, fn_p = NULL; fn;
+ fn_p = fn, fn = fn_n)
+ {
+ fn_n = fn->line_next;
+ fn->line_next = fn_p;
+ }
+ src->functions = fn_p;
- cptr = strrchr (input_file_name, '/');
- if (cptr)
- strcpy (gcov_file_name, cptr + 1);
- else
- strcpy (gcov_file_name, input_file_name);
+ for (ix = src->num_lines, line = src->lines; ix--; line++)
+ {
+ if (!flag_all_blocks)
+ {
+ arc_t *arc, *arc_p, *arc_n;
- strcat (gcov_file_name, ".");
+ /* Total and reverse the branch information. */
+ for (arc = line->u.branches, arc_p = NULL; arc;
+ arc_p = arc, arc = arc_n)
+ {
+ arc_n = arc->line_next;
+ arc->line_next = arc_p;
- cptr = strrchr (source_file_name, '/');
- if (cptr)
- strcat (gcov_file_name, cptr + 1);
- else
- strcat (gcov_file_name, source_file_name);
+ add_branch_counts (&src->coverage, arc);
}
- else
+ line->u.branches = arc_p;
+ }
+ else if (line->u.blocks)
+ {
+ /* The user expects the line count to be the number of times
+ a line has been executed. Simply summing the block count
+ will give an artificially high number. The Right Thing
+ is to sum the entry counts to the graph of blocks on this
+ line, then find the elementary cycles of the local graph
+ and add the transition counts of those cycles. */
+ block_t *block, *block_p, *block_n;
+ gcov_type count = 0;
+
+ /* Reverse the block information. */
+ for (block = line->u.blocks, block_p = NULL; block;
+ block_p = block, block = block_n)
{
- gcov_file_name = xmalloc (count + 6);
- cptr = strrchr (source_file_name, '/');
- if (cptr)
- strcpy (gcov_file_name, cptr + 1);
- else
- strcpy (gcov_file_name, source_file_name);
+ block_n = block->chain;
+ block->chain = block_p;
+ block->u.cycle.ident = ix;
}
+ line->u.blocks = block_p;
- /* 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");
+ /* Sum the entry arcs. */
+ for (block = line->u.blocks; block; block = block->chain)
+ {
+ arc_t *arc;
- gcov_file = fopen (gcov_file_name, "w");
+ for (arc = block->pred; arc; arc = arc->pred_next)
+ {
+ if (arc->src->u.cycle.ident != ix)
+ count += arc->count;
+ if (flag_branches)
+ add_branch_counts (&src->coverage, arc);
+ }
- if (gcov_file == NULL)
- {
- fnotice (stderr, "Could not open output file %s.\n",
- gcov_file_name);
- fclose (source_file);
- free (line_counts);
- free (line_exists);
- continue;
+ /* Initialize the cs_count. */
+ for (arc = block->succ; arc; arc = arc->succ_next)
+ arc->cs_count = arc->count;
}
- fnotice (stdout, "Creating %s.\n", gcov_file_name);
-
- for (count = 1; count < s_ptr->maxlineno; count++)
+ /* Find the loops. This uses the algorithm described in
+ Tiernan 'An Efficient Search Algorithm to Find the
+ Elementary Circuits of a Graph', CACM Dec 1970. We hold
+ the P array by having each block point to the arc that
+ connects to the previous block. The H array is implicitly
+ held because of the arc ordering, and the block's
+ previous arc pointer.
+
+ Although the algorithm is O(N^3) for highly connected
+ graphs, at worst we'll have O(N^2), as most blocks have
+ only one or two exits. Most graphs will be small.
+
+ For each loop we find, locate the arc with the smallest
+ transition count, and add that to the cumulative
+ count. Decrease flow over the cycle and remove the arc
+ from consideration. */
+ for (block = line->u.blocks; block; block = block->chain)
{
- char *retval;
- int len;
-
- retval = fgets (string, STRING_SIZE, source_file);
+ block_t *head = block;
+ arc_t *arc;
- /* 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])
+ next_vertex:;
+ arc = head->succ;
+ current_vertex:;
+ while (arc)
{
- if (line_counts[count])
+ block_t *dst = arc->dst;
+ if (/* Already used that arc. */
+ arc->cycle
+ /* Not to same graph, or before first vertex. */
+ || dst->u.cycle.ident != ix
+ /* Already in path. */
+ || dst->u.cycle.arc)
{
- char c[20];
- sprintf (c, HOST_WIDEST_INT_PRINT_DEC, (HOST_WIDEST_INT)line_counts[count]);
- fprintf (gcov_file, "%12s %s", c,
- string);
+ arc = arc->succ_next;
+ continue;
}
- 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 (dst == block)
{
- if (a_ptr->call_insn)
+ /* Found a closing arc. */
+ gcov_type cycle_count = arc->cs_count;
+ arc_t *cycle_arc = arc;
+ arc_t *probe_arc;
+
+ /* Locate the smallest arc count of the loop. */
+ for (dst = head; (probe_arc = dst->u.cycle.arc);
+ dst = probe_arc->src)
+ if (cycle_count > probe_arc->cs_count)
+ {
+ cycle_count = probe_arc->cs_count;
+ cycle_arc = probe_arc;
+ }
+
+ count += cycle_count;
+ cycle_arc->cycle = 1;
+
+ /* Remove the flow from the cycle. */
+ arc->cs_count -= cycle_count;
+ for (dst = head; (probe_arc = dst->u.cycle.arc);
+ dst = probe_arc->src)
+ probe_arc->cs_count -= cycle_count;
+
+ /* Unwind to the cyclic arc. */
+ while (head != cycle_arc->src)
{
- if (a_ptr->total == 0)
- fnotice (gcov_file, "call %d never executed\n", i);
- else
- {
- if (output_branch_counts)
- {
- char c[20];
- sprintf (c, HOST_WIDEST_INT_PRINT_DEC,
- a_ptr->total - a_ptr->hits);
- fnotice (gcov_file,
- "call %d returns = %s\n", i, c);
- }
- else
- {
- char c[20];
- sprintf (c, HOST_WIDEST_INT_PRINT_DEC,
- 100 - ((a_ptr->hits * 100)
- + (a_ptr->total >> 1))
- / a_ptr->total);
- fnotice (gcov_file,
- "call %d returns = %s%%\n", i, c);
- }
- }
+ arc = head->u.cycle.arc;
+ head->u.cycle.arc = NULL;
+ head = arc->src;
}
- else
- {
- if (a_ptr->total == 0)
- fnotice (gcov_file, "branch %d never executed\n",
- i);
- else
- {
- if (output_branch_counts)
- {
- char c[20];
- sprintf (c, HOST_WIDEST_INT_PRINT_DEC,
- a_ptr->hits);
- fnotice (gcov_file,
- "branch %d taken = %s\n", i, c);
- }
- else
- {
- char c[20];
- sprintf (c, HOST_WIDEST_INT_PRINT_DEC,
- ((a_ptr->hits * 100)
- + (a_ptr->total >> 1))
- / a_ptr->total);
- fnotice (gcov_file,
- "branch %d taken = %s%%\n", i, c);
- }
- }
- }
- }
- }
+ /* Move on. */
+ arc = arc->succ_next;
+ continue;
+ }
- /* Gracefully handle errors while reading the source file. */
- if (retval == NULL)
+ /* Add new block to chain. */
+ dst->u.cycle.arc = arc;
+ head = dst;
+ goto next_vertex;
+ }
+ /* We could not add another vertex to the path. Remove
+ the last vertex from the list. */
+ arc = head->u.cycle.arc;
+ if (arc)
{
- fnotice (stderr,
- "Unexpected EOF while reading source file %s.\n",
- source_file_name);
- break;
+ /* It was not the first vertex. Move onto next arc. */
+ head->u.cycle.arc = NULL;
+ head = arc->src;
+ arc = arc->succ_next;
+ goto current_vertex;
}
+ /* Mark this block as unusable. */
+ block->u.cycle.ident = ~0U;
}
- /* Handle all remaining source lines. There may be lines
- after the last line of code. */
+ line->count = count;
+ }
- {
- char *retval = fgets (string, STRING_SIZE, source_file);
- while (retval != NULL)
- {
- int len;
+ if (line->exists)
+ {
+ src->coverage.lines++;
+ if (line->count)
+ src->coverage.lines_executed++;
+ }
+ }
+}
- fprintf (gcov_file, "\t\t%s", string);
+/* Output information about ARC number IX. Returns nonzero if
+ anything is output. */
- /* 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);
- }
+static int
+output_branch_count (FILE *gcov_file, int ix, const arc_t *arc)
+{
- retval = fgets (string, STRING_SIZE, source_file);
- }
- }
+ if (arc->is_call_non_return)
+ {
+ if (arc->src->count)
+ {
+ fnotice (gcov_file, "call %2d returned %s\n", ix,
+ format_gcov (arc->src->count - arc->count,
+ arc->src->count, -flag_counts));
+ }
+ else
+ fnotice (gcov_file, "call %2d never executed\n", ix);
+ }
+ else if (!arc->is_unconditional)
+ {
+ if (arc->src->count)
+ fnotice (gcov_file, "branch %2d taken %s%s\n", ix,
+ format_gcov (arc->count, arc->src->count, -flag_counts),
+ arc->fall_through ? " (fallthrough)" : "");
+ else
+ fnotice (gcov_file, "branch %2d never executed\n", ix);
+ }
+ else if (flag_unconditional && !arc->dst->is_call_return)
+ {
+ if (arc->src->count)
+ fnotice (gcov_file, "unconditional %2d taken %s\n", ix,
+ format_gcov (arc->count, arc->src->count, -flag_counts));
+ else
+ fnotice (gcov_file, "unconditional %2d never executed\n", ix);
+ }
+ else
+ return 0;
+ return 1;
- fclose (source_file);
- fclose (gcov_file);
+}
+
+/* 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. */
+
+static void
+output_lines (FILE *gcov_file, const source_t *src)
+{
+ FILE *source_file;
+ unsigned line_num; /* current line number. */
+ const line_t *line; /* current line info ptr. */
+ char string[STRING_SIZE]; /* line buffer. */
+ char const *retval = ""; /* status of source file reading. */
+ function_t *fn = NULL;
+
+ fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, src->name);
+ fprintf (gcov_file, "%9s:%5d:Graph:%s\n", "-", 0, bbg_file_name);
+ fprintf (gcov_file, "%9s:%5d:Data:%s\n", "-", 0,
+ no_data_file ? "-" : da_file_name);
+ fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0,
+ object_summary.ctrs[GCOV_COUNTER_ARCS].runs);
+ fprintf (gcov_file, "%9s:%5d:Programs:%u\n", "-", 0, program_count);
+
+ source_file = fopen (src->name, "r");
+ if (!source_file)
+ {
+ fnotice (stderr, "%s:cannot open source file\n", src->name);
+ retval = NULL;
+ }
+ else
+ {
+ struct stat status;
+
+ if (!fstat (fileno (source_file), &status)
+ && status.st_mtime > bbg_file_time)
+ {
+ fnotice (stderr, "%s:source file is newer than graph file '%s'\n",
+ src->name, bbg_file_name);
+ fprintf (gcov_file, "%9s:%5d:Source is newer than graph\n",
+ "-", 0);
+ }
+ }
+
+ if (flag_branches)
+ fn = src->functions;
+
+ for (line_num = 1, line = &src->lines[line_num];
+ line_num < src->num_lines; line_num++, line++)
+ {
+ for (; fn && fn->line == line_num; fn = fn->line_next)
+ {
+ arc_t *arc = fn->blocks[fn->num_blocks - 1].pred;
+ gcov_type return_count = fn->blocks[fn->num_blocks - 1].count;
+
+ for (; arc; arc = arc->pred_next)
+ if (arc->fake)
+ return_count -= arc->count;
+
+ fprintf (gcov_file, "function %s", fn->name);
+ fprintf (gcov_file, " called %s",
+ format_gcov (fn->blocks[0].count, 0, -1));
+ fprintf (gcov_file, " returned %s",
+ format_gcov (return_count, fn->blocks[0].count, 0));
+ fprintf (gcov_file, " blocks executed %s",
+ format_gcov (fn->blocks_executed, fn->num_blocks - 2, 0));
+ fprintf (gcov_file, "\n");
}
- free (line_counts);
- free (line_exists);
+ /* For lines which don't exist in the .bb file, print '-' before
+ the source line. For lines which exist but were never
+ executed, print '#####' before the source line. Otherwise,
+ print the execution count before the source line. There are
+ 16 spaces of indentation added before the source line so that
+ tabs won't be messed up. */
+ fprintf (gcov_file, "%9s:%5u:",
+ !line->exists ? "-" : !line->count ? "#####"
+ : format_gcov (line->count, 0, -1), line_num);
+
+ if (retval)
+ {
+ /* Copy source line. */
+ do
+ {
+ retval = fgets (string, STRING_SIZE, source_file);
+ if (!retval)
+ break;
+ fputs (retval, gcov_file);
+ }
+ while (!retval[0] || retval[strlen (retval) - 1] != '\n');
+ }
+ if (!retval)
+ fputs ("/*EOF*/\n", gcov_file);
+
+ if (flag_all_blocks)
+ {
+ block_t *block;
+ arc_t *arc;
+ int ix, jx;
+
+ for (ix = jx = 0, block = line->u.blocks; block;
+ block = block->chain)
+ {
+ if (!block->is_call_return)
+ fprintf (gcov_file, "%9s:%5u-block %2d\n",
+ !line->exists ? "-" : !block->count ? "$$$$$"
+ : format_gcov (block->count, 0, -1),
+ line_num, ix++);
+ if (flag_branches)
+ for (arc = block->succ; arc; arc = arc->succ_next)
+ jx += output_branch_count (gcov_file, jx, arc);
+ }
+ }
+ else if (flag_branches)
+ {
+ int ix;
+ arc_t *arc;
+
+ for (ix = 0, arc = line->u.branches; arc; arc = arc->line_next)
+ ix += output_branch_count (gcov_file, ix, arc);
+ }
+ }
+
+ /* Handle all remaining source lines. There may be lines after the
+ last line of code. */
+ if (retval)
+ {
+ for (; (retval = fgets (string, STRING_SIZE, source_file)); line_num++)
+ {
+ fprintf (gcov_file, "%9s:%5u:%s", "-", line_num, retval);
+
+ while (!retval[0] || retval[strlen (retval) - 1] != '\n')
+ {
+ retval = fgets (string, STRING_SIZE, source_file);
+ if (!retval)
+ break;
+ fputs (retval, gcov_file);
+ }
+ }
}
+
+ if (source_file)
+ fclose (source_file);
}