X-Git-Url: http://git.sourceforge.jp/view?p=pf3gnuchains%2Fgcc-fork.git;a=blobdiff_plain;f=gcc%2Fgcov.c;h=4f259a9228f6a55a87b4865bbddd3909d076bd9c;hp=5dbd4a468ea726909cf3c630d9eb885ac54e7a25;hb=530ed22e1f194ba502a65e92cb177332bd699071;hpb=fadec241213f193c6e1ffcf2033412fe356de0de diff --git a/gcc/gcov.c b/gcc/gcov.c index 5dbd4a468ea..4f259a9228f 100644 --- a/gcc/gcov.c +++ b/gcc/gcov.c @@ -1,12 +1,15 @@ /* Gcov.c: prepend line execution counts and branch probabilities to a source file. - Copyright (C) 1990, 91-94, 96, 97, 98, 1999 Free Software Foundation, Inc. + Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999, + 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 + Free Software Foundation, Inc. Contributed by James E. Wilson of Cygnus Support. Mangled by Bob Manson of Cygnus Support. + Mangled further by Nathan Sidwell 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) +the Free Software Foundation; either version 3, or (at your option) any later version. Gcov is distributed in the hope that it will be useful, @@ -15,17 +18,8 @@ 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, 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. */ +along with Gcov; see the file COPYING3. If not see +. */ /* ??? Print a list of the ten blocks with the highest execution counts, and list the line numbers corresponding to those blocks. Also, perhaps @@ -35,1390 +29,1942 @@ Boston, MA 02111-1307, USA. */ /* ??? 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. */ +/* 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" -#undef abort +#include "version.h" +#include + +#define IN_GCOV 1 #include "gcov-io.h" +#include "gcov-io.c" + +/* The gcno file is generated by -ftest-coverage option. The gcda file is + generated by a program compiled with -fprofile-arcs. Their formats + are documented in 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. +/* The functions in this file for creating and solution program flow graphs + 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. */ - 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. +/* This is the size of the buffer used to read in source file lines. */ - 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. +#define STRING_SIZE 200 - The .da file contains the execution count for each instrumented branch. +struct function_info; +struct block_info; +struct source_info; - 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. */ +/* Describes an arc between two basic blocks. */ -/* The functions in this file for creating and solution program flow graphs - are very similar to functions in the gcc source file profile.c. */ +typedef struct arc_info +{ + /* source and destination blocks. */ + struct block_info *src; + struct block_info *dst; -char gcov_version_string[] = "GNU gcov version 1.5\n"; + /* transition counts. */ + gcov_type count; + /* used in cycle search, so that we do not clobber original counts. */ + gcov_type cs_count; -/* This is the size of the buffer used to read in source file lines. */ + unsigned int count_valid : 1; + unsigned int on_tree : 1; + unsigned int fake : 1; + unsigned int fall_through : 1; -#define STRING_SIZE 200 + /* Arc is for a function that abnormally returns. */ + unsigned int is_call_non_return : 1; + + /* Arc is for catch/setjump. */ + unsigned int is_nonlocal_return : 1; -/* One copy of this structure is created for each source file mentioned in the - .bb file. */ + /* Is an unconditional branch. */ + unsigned int is_unconditional : 1; -struct sourcefile + /* Loop making arc. */ + unsigned int cycle : 1; + + /* Next branch on line. */ + struct arc_info *line_next; + + /* Links to next arc on src and dst lists. */ + struct arc_info *succ_next; + struct arc_info *pred_next; +} arc_t; + +/* Describes a basic block. Contains lists of arcs to successor and + predecessor blocks. */ + +typedef struct block_info +{ + /* 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; + + /* Temporary chain for solving graph, and for chaining blocks on one + line. */ + struct block_info *chain; + +} block_t; + +/* Describes a single function. Contains an array of basic blocks. */ + +typedef struct function_info { + /* Name of function. */ char *name; - int maxlineno; - struct sourcefile *next; -}; + unsigned ident; + unsigned checksum; -/* This points to the head of the sourcefile structure list. */ + /* Array of basic blocks. */ + block_t *blocks; + unsigned num_blocks; + unsigned blocks_executed; -struct sourcefile *sources; + /* Raw arc coverage counts. */ + gcov_type *counts; + unsigned num_counts; -/* One of these is dynamically created whenever we identify an arc in the - function. */ + /* First line number. */ + unsigned line; + struct source_info *src; -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; -}; + /* Next function in same source file. */ + struct function_info *line_next; -/* Count the number of basic blocks, and create an array of these structures, - one for each bb in the function. */ + /* Next function. */ + struct function_info *next; +} function_t; -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 -}; +/* 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; -/* When outputting branch probabilities, one of these structures is created - for each branch/call. */ + char *name; +} coverage_t; + +/* Describes a single line of source. Contains a chain of basic blocks + with code on it. */ -struct arcdata +typedef struct line_info { - int hits; - int total; - int call_insn; - struct arcdata *next; -}; + 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; + time_t file_time; -/* Used to save the list of bb_graphs, one per function. */ + /* Array of line information. */ + line_t *lines; + unsigned num_lines; -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; -}; + 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. New elements + are always prepended. */ -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. */ +/* Next index for a source file. */ -static char *da_file_name; -static FILE *da_file; +static unsigned source_index; + +/* This holds data summary information. */ + +static struct gcov_summary object_summary; +static unsigned program_count; + +/* Modification time of graph file. */ + +static time_t bbg_file_time; -/* Name and file pointer of the input file for the basic block line counts. */ +/* Name and file pointer of the input file for the basic block graph. */ -static char *bb_file_name; -static FILE *bb_file; +static char *bbg_file_name; -/* Holds the entire contents of the bb_file read into memory. */ +/* Stamp of the bbg file */ +static unsigned bbg_stamp; -static char *bb_data; +/* Name and file pointer of the input file for the arc count data. */ -/* Size of bb_data array in longs. */ +static char *da_file_name; -static long bb_data_size; +/* Data file is missing. */ -/* Name and file pointer of the output file. */ +static int no_data_file; -static char *gcov_file_name; -static FILE *gcov_file; +/* If there is several input files, compute and display results after + reading all data files. This way if two or more gcda file refer to + the same source file (eg inline subprograms in a .h file), the + counts are added. */ -/* Name of the file mentioned on the command line. */ +static int multiple_files = 0; -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. This works only - when a single source file is specified. */ +/* 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. */ -static int output_long_names = 0; +static int flag_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 */ - -static int output_branch_counts = 0; + of times it was taken. */ + +static int flag_counts = 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)) ATTRIBUTE_NORETURN; -static void init_arc PROTO ((struct adj_list *, int, int, struct bb_info *)); -static struct adj_list *reverse_arcs PROTO ((struct adj_list *)); -static void create_program_flow_graph PROTO ((struct bb_info_list *)); -static void solve_program_flow_graph PROTO ((struct bb_info_list *)); -static void calculate_branch_probs PROTO ((struct bb_info_list *, int, - struct arcdata **, int)); -static void function_summary PROTO ((void)); - -extern int main PROTO ((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 generate_results (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) { -#ifdef HAVE_LC_MESSAGES - setlocale (LC_MESSAGES, ""); -#endif - (void) bindtextdomain (PACKAGE, localedir); - (void) textdomain (PACKAGE); + int argno; + + /* Unlock the stdio streams. */ + unlock_std_streams (); - process_args (argc, argv); + gcc_init_libintl (); - open_files (); + argno = process_args (argc, argv); + if (optind == argc) + print_usage (true); - read_files (); + if (argc - argno > 1) + multiple_files = 1; - scan_for_source_files (); + for (; argno != argc; argno++) + process_file (argv[argno]); - output_data (); + generate_results (multiple_files ? NULL : argv[argc - 1]); + + release_structures (); return 0; } -static void fnotice PVPROTO ((FILE *, const char *, ...)) ATTRIBUTE_PRINTF_2; static void -fnotice VPROTO ((FILE *file, const char *msgid, ...)) +fnotice (FILE *file, const char *cmsgid, ...) { -#ifndef ANSI_PROTOTYPES - FILE *file; - const char *msgid; -#endif va_list ap; - VA_START (ap, msgid); - -#ifndef ANSI_PROTOTYPES - file = va_arg (ap, FILE *); - msgid = va_arg (ap, const char *); -#endif - - vfprintf (file, _(msgid), ap); + va_start (ap, cmsgid); + vfprintf (file, _(cmsgid), ap); va_end (ap); } + +/* Print a usage message and exit. If ERROR_P is nonzero, this is an error, + otherwise the output of --help. */ -/* 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 PROTO ((void)) ATTRIBUTE_NORETURN; - -void -fancy_abort () +static void +print_usage (int error_p) { - fnotice (stderr, "Internal gcov abort.\n"); - exit (FATAL_EXIT_CODE); + FILE *file = error_p ? stderr : stdout; + int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE; + + fnotice (file, "Usage: gcov [OPTION]... SOURCEFILE...\n\n"); + fnotice (file, "Print code coverage information.\n\n"); + fnotice (file, " -h, --help Print this help, then exit\n"); + fnotice (file, " -v, --version Print version number, then exit\n"); + fnotice (file, " -a, --all-blocks Show information for every basic block\n"); + fnotice (file, " -b, --branch-probabilities Include branch probabilities in output\n"); + fnotice (file, " -c, --branch-counts Given counts of branches taken\n\ + rather than percentages\n"); + fnotice (file, " -n, --no-output Do not create an output file\n"); + fnotice (file, " -l, --long-file-names Use long output file names for included\n\ + source files\n"); + fnotice (file, " -f, --function-summaries Output summaries for each function\n"); + fnotice (file, " -o, --object-directory DIR|FILE Search for object files in DIR or called FILE\n"); + fnotice (file, " -p, --preserve-paths Preserve all pathname components\n"); + fnotice (file, " -u, --unconditional-branches Show unconditional branch counts too\n"); + fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n", + bug_report_url); + exit (status); } - -/* Print a usage message and exit. */ + +/* Print version information and exit. */ static void -print_usage () +print_version (void) { - fnotice (stderr, "gcov [-b] [-v] [-n] [-l] [-f] [-o OBJDIR] file\n"); - exit (FATAL_EXIT_CODE); + fnotice (stdout, "gcov %s%s\n", pkgversion_string, version_string); + fprintf (stdout, "Copyright %s 2008 Free Software Foundation, Inc.\n", + _("(C)")); + fnotice (stdout, + _("This is free software; see the source for copying conditions.\n" + "There is NO warranty; not even for MERCHANTABILITY or \n" + "FITNESS FOR A PARTICULAR PURPOSE.\n\n")); + exit (SUCCESS_EXIT_CODE); } -/* Parse the command line. */ +static const struct option options[] = +{ + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { "all-blocks", no_argument, NULL, 'a' }, + { "branch-probabilities", no_argument, NULL, 'b' }, + { "branch-counts", no_argument, NULL, 'c' }, + { "no-output", no_argument, NULL, 'n' }, + { "long-file-names", no_argument, NULL, 'l' }, + { "function-summaries", no_argument, NULL, 'f' }, + { "preserve-paths", no_argument, NULL, 'p' }, + { "object-directory", required_argument, NULL, 'o' }, + { "object-file", required_argument, NULL, 'o' }, + { "unconditional-branches", no_argument, NULL, 'u' }, + { 0, 0, 0, 0 } +}; + +/* 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 i; + int opt; - for (i = 1; i < argc; i++) + while ((opt = getopt_long (argc, argv, "abcfhlno:puv", options, NULL)) != -1) { - if (argv[i][0] == '-') + switch (opt) { - if (argv[i][1] == 'b') - output_branch_probs = 1; - else if (argv[i][1] == 'c') - output_branch_counts = 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 (); + case 'a': + flag_all_blocks = 1; + break; + case 'b': + flag_branches = 1; + break; + case 'c': + flag_counts = 1; + break; + case 'f': + flag_function_summary = 1; + break; + case 'h': + print_usage (false); + /* print_usage will exit. */ + case 'l': + flag_long_names = 1; + break; + case 'n': + flag_gcov_file = 0; + break; + case 'o': + object_directory = optarg; + break; + case 'p': + flag_preserve_paths = 1; + break; + case 'u': + flag_unconditional = 1; + break; + case 'v': + print_version (); + /* print_version will exit. */ + default: + print_usage (true); + /* print_usage will exit. */ } - else if (! input_file_name) - input_file_name = argv[i]; - else - print_usage (); } - if (! input_file_name) - print_usage (); + 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; + function_t *fn; + function_t *fn_p; + function_t *old_functions; - /* 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; + /* Save and clear the list of current functions. They will be appended + later. */ + old_functions = functions; + functions = NULL; - da_file_name = xmalloc (count + objdir_count + 4); - bb_file_name = xmalloc (count + objdir_count + 4); - bbg_file_name = xmalloc (count + objdir_count + 5); + create_file_names (file_name); + if (read_graph_file ()) + return; - if (object_directory) + if (!functions) { - strcpy (da_file_name, object_directory); - strcpy (bb_file_name, object_directory); - strcpy (bbg_file_name, object_directory); + fnotice (stderr, "%s:no functions found\n", bbg_file_name); + return; + } - if (object_directory[objdir_count - 1] != '/') - { - strcat (da_file_name, "/"); - strcat (bb_file_name, "/"); - strcat (bbg_file_name, "/"); - } + if (read_count_file ()) + return; - 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 + for (fn_p = NULL, fn = functions; fn; fn_p = fn, fn = fn->next) + solve_flow_graph (fn); + + if (fn_p) + fn_p->next = old_functions; +} + +static void +generate_results (const char *file_name) +{ + source_t *src; + function_t *fn; + + for (src = sources; src; src = src->next) + src->lines = XCNEWVEC (line_t, src->num_lines); + for (fn = functions; fn; fn = fn->next) + { + coverage_t coverage; + + memset (&coverage, 0, sizeof (coverage)); + coverage.name = fn->name; + add_line_counts (flag_function_summary ? &coverage : NULL, fn); + if (flag_function_summary) { - strcat (da_file_name, input_file_name); - strcat (bb_file_name, input_file_name); - strcat (bbg_file_name, input_file_name); + function_summary (&coverage, "Function"); + fnotice (stdout, "\n"); } } - else + + for (src = sources; src; src = src->next) { - strcpy (da_file_name, input_file_name); - strcpy (bb_file_name, input_file_name); - strcpy (bbg_file_name, input_file_name); - } + accumulate_line_counts (src); + function_summary (&src->coverage, "File"); + if (flag_gcov_file) + { + char *gcov_file_name = make_gcov_file_name (file_name, src->name); + FILE *gcov_file = fopen (gcov_file_name, "w"); - cptr = rindex (bb_file_name, '.'); - if (cptr) - strcpy (cptr, ".bb"); - else - strcat (bb_file_name, ".bb"); + 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"); + } +} - cptr = rindex (da_file_name, '.'); - if (cptr) - strcpy (cptr, ".da"); - else - strcat (da_file_name, ".da"); +/* Release all memory used. */ - cptr = rindex (bbg_file_name, '.'); - if (cptr) - strcpy (cptr, ".bbg"); - else - strcat (bbg_file_name, ".bbg"); +static void +release_structures (void) +{ + function_t *fn; + source_t *src; - 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"); - } - - bbg_file = fopen (bbg_file_name, "rb"); - if (bbg_file == NULL) - { - fnotice (stderr, "Could not open program flow graph file %s.\n", - bbg_file_name); - exit (FATAL_EXIT_CODE); + free (src->name); + free (src->lines); } - /* 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)) + while ((fn = functions)) { - fnotice (stderr, "No executable code associated with file %s.\n", - input_file_name); - exit (FATAL_EXIT_CODE); + unsigned ix; + block_t *block; + + functions = fn->next; + for (ix = fn->num_blocks, block = fn->blocks; ix--; block++) + { + arc_t *arc, *arc_n; + + for (arc = block->succ; arc; arc = arc_n) + { + arc_n = arc->succ_next; + free (arc); + } + } + free (fn->blocks); + free (fn->counts); } } - -/* 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; + + /* Free previous file names. */ + if (bbg_file_name) + free (bbg_file_name); + if (da_file_name) + free (da_file_name); + da_file_name = bbg_file_name = NULL; + bbg_file_time = 0; + bbg_stamp = 0; + + if (object_directory && object_directory[0]) + { + struct stat status; -/* Reverse the arcs on a 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; + + length = strlen (name); + + bbg_file_name = XNEWVEC (char, length + strlen (GCOV_NOTE_SUFFIX) + 1); + strcpy (bbg_file_name, name); + strcpy (bbg_file_name + length, GCOV_NOTE_SUFFIX); + + da_file_name = XNEWVEC (char, length + strlen (GCOV_DATA_SUFFIX) + 1); + strcpy (da_file_name, name); + strcpy (da_file_name + length, GCOV_DATA_SUFFIX); + free (name); + return; +} -/* Construct the program flow graph from the .bbg file, and read in the data - in the .da file. */ +/* Find or create a source file structure for FILE_NAME. Copies + FILE_NAME on creation */ -static void -create_program_flow_graph (bptr) - struct bb_info_list *bptr; +static source_t * +find_source (const char *file_name) { - long num_blocks, number_arcs, src, dest, flag_bits, num_arcs_per_block; - int i; - struct adj_list *arcptr; - struct bb_info *bb_graph; + source_t *src; + struct stat status; - /* Read the number of blocks. */ - __read_long (&num_blocks, bbg_file, 4); + if (!file_name) + file_name = ""; - /* Create an array of size bb number of bb_info structs. */ - bb_graph = (struct bb_info *) xcalloc (num_blocks, sizeof (struct bb_info)); + for (src = sources; src; src = src->next) + if (!strcmp (file_name, src->name)) + break; - bptr->bb_graph = bb_graph; - bptr->num_blocks = num_blocks; + if (!src) + { + src = XCNEW (source_t); + src->name = xstrdup (file_name); + src->coverage.name = src->name; + src->index = source_index++; + src->next = sources; + sources = src; + + if (!stat (file_name, &status)) + src->file_time = status.st_mtime; + } - /* Read and create each arc from the .bbg file. */ - __read_long (&number_arcs, bbg_file, 4); - for (i = 0; i < num_blocks; i++) + if (src->file_time > bbg_file_time) { - int j; + static int info_emitted; - __read_long (&num_arcs_per_block, bbg_file, 4); - for (j = 0; j < num_arcs_per_block; j++) + fnotice (stderr, "%s:source file is newer than graph file '%s'\n", + src->name, bbg_file_name); + if (!info_emitted) { - 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); + fnotice (stderr, + "(the message is only displayed one per source file)\n"); + info_emitted = 1; } + src->file_time = 0; } - if (number_arcs) - abort (); + return src; +} - /* 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 (); +/* Read the graph file. Return nonzero on fatal error. */ - /* Must reverse the order of all succ arcs, to ensure that they match - the order of the data in the .da file. */ +static int +read_graph_file (void) +{ + unsigned version; + unsigned current_tag = 0; + struct function_info *fn = NULL; + function_t *old_functions_head = functions; + 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 < num_blocks; i++) - if (bb_graph[i].succ) - bb_graph[i].succ = reverse_arcs (bb_graph[i].succ); + version = gcov_read_unsigned (); + if (version != GCOV_VERSION) + { + char v[4], e[4]; - /* For each arc not on the spanning tree, set its execution count from - the .da file. */ + GCOV_UNSIGNED2STRING (v, version); + GCOV_UNSIGNED2STRING (e, GCOV_VERSION); - /* 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. */ + fnotice (stderr, "%s:version '%.4s', prefer '%.4s'\n", + bbg_file_name, v, e); + } + bbg_stamp = gcov_read_unsigned (); - /* This duplicates code in branch_prob in profile.c. */ + while ((tag = gcov_read_unsigned ())) + { + unsigned length = gcov_read_unsigned (); + gcov_position_t base = gcov_position (); - for (i = 0; i < num_blocks; i++) - for (arcptr = bb_graph[i].succ; arcptr; arcptr = arcptr->succ_next) - if (! arcptr->on_tree) + if (tag == GCOV_TAG_FUNCTION) { - 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--; + 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; } -} - -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; + else if (fn && tag == GCOV_TAG_BLOCKS) + { + if (fn->blocks) + fnotice (stderr, "%s:already seen blocks for '%s'\n", + bbg_file_name, fn->name); + else + { + unsigned ix, num_blocks = GCOV_TAG_BLOCKS_NUM (length); + fn->num_blocks = num_blocks; - for (i = num_blocks - 1; i >= 0; i--) + 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) { - if (! bb_graph[i].count_valid) + unsigned src = gcov_read_unsigned (); + unsigned num_dests = GCOV_TAG_ARCS_NUM (length); + + if (src >= fn->num_blocks || fn->blocks[src].succ) + goto corrupt; + + while (num_dests--) { - 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) + struct arc_info *arc; + unsigned dest = gcov_read_unsigned (); + unsigned flags = gcov_read_unsigned (); + + if (dest >= fn->num_blocks) + goto corrupt; + arc = XCNEW (arc_t); + + arc->dst = &fn->blocks[dest]; + arc->src = &fn->blocks[src]; + + arc->count = 0; + arc->count_valid = 0; + arc->on_tree = !!(flags & GCOV_ARC_ON_TREE); + arc->fake = !!(flags & GCOV_ARC_FAKE); + arc->fall_through = !!(flags & GCOV_ARC_FALLTHROUGH); + + arc->succ_next = fn->blocks[src].succ; + fn->blocks[src].succ = arc; + fn->blocks[src].num_succ++; + + arc->pred_next = fn->blocks[dest].pred; + fn->blocks[dest].pred = arc; + fn->blocks[dest].num_pred++; + + if (arc->fake) { - 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 (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 (!arc->on_tree) + fn->num_counts++; } - if (bb_graph[i].count_valid) + } + else if (fn && tag == GCOV_TAG_LINES) + { + unsigned blockno = gcov_read_unsigned (); + unsigned *line_nos = XCNEWVEC (unsigned, length - 1); + + if (blockno >= fn->num_blocks || fn->blocks[blockno].u.line.encoding) + goto corrupt; + + for (ix = 0; ; ) { - if (bb_graph[i].succ_count == 1) + unsigned lineno = gcov_read_unsigned (); + + if (lineno) { - 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 (!ix) + { + line_nos[ix++] = 0; + line_nos[ix++] = src->index; + } + line_nos[ix++] = lineno; + if (lineno >= src->num_lines) + src->num_lines = lineno + 1; } - if (bb_graph[i].pred_count == 1) + else { - 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; + const char *file_name = gcov_read_string (); + + if (!file_name) + break; + src = find_source (file_name); + + line_nos[ix++] = 0; + line_nos[ix++] = src->index; } } + + fn->blocks[blockno].u.line.encoding = line_nos; + fn->blocks[blockno].u.line.num = ix; + } + else if (current_tag && !GCOV_TAG_IS_SUBTAG (current_tag, tag)) + { + fn = NULL; + current_tag = 0; + } + gcov_sync (base, length); + if (gcov_is_error ()) + { + corrupt:; + fnotice (stderr, "%s:corrupted\n", bbg_file_name); + gcov_close (); + return 1; } } - - /* 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 (); + /* We built everything backwards, so nreverse them all. */ -static void -read_files () -{ - struct stat buf; - struct bb_info_list *list_end = 0; - struct bb_info_list *b_ptr; - long total; + /* Reverse sources. Not strictly necessary, but we'll then process + them in the 'expected' order. */ + { + source_t *src, *src_p, *src_n; - /* 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(); + for (src_p = NULL, src = sources; src; src_p = src, src = src_n) + { + src_n = src->next; + src->next = src_p; + } + sources = src_p; + } - while (! feof (bbg_file)) - { - b_ptr = (struct bb_info_list *) xmalloc (sizeof (struct bb_info_list)); + /* Reverse functions. */ + { + function_t *fn, *fn_p, *fn_n; - b_ptr->next = 0; - if (list_end) - list_end->next = b_ptr; - else - bb_graph_list = b_ptr; - list_end = b_ptr; + for (fn_p = old_functions_head, fn = functions; + fn != old_functions_head; + fn_p = fn, fn = fn_n) + { + unsigned ix; - /* Read in the data in the .bbg file and reconstruct the program flow - graph for one function. */ - create_program_flow_graph (b_ptr); + fn_n = fn->next; + fn->next = fn_p; - /* Set the EOF condition if at the end of file. */ - ungetc (getc (bbg_file), bbg_file); - } + /* Reverse the arcs. */ + for (ix = fn->num_blocks; ix--;) + { + arc_t *arc, *arc_p, *arc_n; + + for (arc_p = NULL, arc = fn->blocks[ix].succ; arc; + arc_p = arc, arc = arc_n) + { + arc_n = arc->succ_next; + arc->succ_next = arc_p; + } + fn->blocks[ix].succ = arc_p; + + for (arc_p = NULL, arc = fn->blocks[ix].pred; arc; + arc_p = arc, arc = arc_n) + { + arc_n = arc->pred_next; + arc->pred_next = arc_p; + } + fn->blocks[ix].pred = arc_p; + } + } + functions = fn_p; + } + return 0; +} + +/* Reads profiles from the count file and attach to each + function. Return nonzero if fatal error. */ + +static int +read_count_file (void) +{ + unsigned ix; + unsigned version; + unsigned tag; + function_t *fn = NULL; + int error = 0; - /* Check to make sure the .da file data is valid. */ + if (!gcov_open (da_file_name, 1)) + { + fnotice (stderr, "%s:cannot open data file, assuming not executed\n", + da_file_name); + no_data_file = 1; + return 0; + } + if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC)) + { + fnotice (stderr, "%s:not a gcov data file\n", da_file_name); + cleanup:; + gcov_close (); + return 1; + } + version = gcov_read_unsigned (); + if (version != GCOV_VERSION) + { + char v[4], e[4]; - if (da_file) + 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) { - if (feof (da_file)) - fnotice (stderr, ".da file contents exhausted too early\n"); - /* Should be at end of file now. */ - if (__read_long (&total, da_file, 8) == 0) - fnotice (stderr, ".da file contents not exhausted\n"); + fnotice (stderr, "%s:stamp mismatch with graph file\n", da_file_name); + goto cleanup; } - /* Calculate all of the basic block execution counts and branch - taken probabilities. */ + while ((tag = gcov_read_unsigned ())) + { + unsigned length = gcov_read_unsigned (); + unsigned long base = gcov_position (); + + if (tag == GCOV_TAG_OBJECT_SUMMARY) + gcov_read_summary (&object_summary); + else if (tag == GCOV_TAG_PROGRAM_SUMMARY) + program_count++; + else if (tag == GCOV_TAG_FUNCTION) + { + unsigned ident = gcov_read_unsigned (); + struct function_info *fn_n = functions; + + /* Try to find the function in the list. + To speed up the search, first start from the last function + found. */ + for (fn = fn ? fn->next : NULL; ; fn = fn->next) + { + if (fn) + ; + else if ((fn = fn_n)) + fn_n = NULL; + else + { + fnotice (stderr, "%s:unknown function '%u'\n", + da_file_name, ident); + break; + } + if (fn->ident == ident) + break; + } + + if (!fn) + ; + else if (gcov_read_unsigned () != fn->checksum) + { + mismatch:; + fnotice (stderr, "%s:profile mismatch for '%s'\n", + da_file_name, fn->name); + goto cleanup; + } + } + else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) && fn) + { + if (length != GCOV_TAG_COUNTER_LENGTH (fn->num_counts)) + goto mismatch; - for (b_ptr = bb_graph_list; b_ptr; b_ptr = b_ptr->next) - solve_program_flow_graph (b_ptr); + if (!fn->counts) + fn->counts = XCNEWVEC (gcov_type, fn->num_counts); - /* 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; + 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; + } + } - 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); + gcov_close (); + return 0; } - -/* Scan the data in the .bb file to find all source files referenced, - and the largest line number mentioned in each one. */ +/* Solve the flow graph. Propagate counts from the instrumented arcs + to the blocks and the uninstrumented arcs. */ static void -scan_for_source_files () +solve_flow_graph (function_t *fn) { - 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++) + 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 + { + if (fn->blocks[0].num_pred) + fnotice (stderr, "%s:'%s' has arcs to entry block\n", + bbg_file_name, fn->name); + else + /* We can't deduce the entry block counts from the lack of + predecessors. */ + fn->blocks[0].num_pred = ~(unsigned)0; + + if (fn->blocks[fn->num_blocks - 1].num_succ) + fnotice (stderr, "%s:'%s' has arcs from exit block\n", + bbg_file_name, fn->name); + else + /* Likewise, we can't deduce exit block counts from the lack + of its successors. */ + fn->blocks[fn->num_blocks - 1].num_succ = ~(unsigned)0; + } + + /* 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++) { - __fetch_long (&line_num, ptr, 4); - ptr += 4; - if (line_num == -1) + block_t const *prev_dst = NULL; + int out_of_order = 0; + int non_fake_succ = 0; + + for (arc = blk->succ; arc; arc = arc->succ_next) { - /* 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 (!arc->fake) + non_fake_succ++; - if (s_ptr == 0) + if (!arc->on_tree) { - /* 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; + if (count_ptr) + arc->count = *count_ptr++; + arc->count_valid = 1; + blk->num_succ--; + arc->dst->num_pred--; } - - /* Scan past the file name. */ - { - long delim; - do { - count++; - __fetch_long (&delim, ptr, 4); - ptr += 4; - } while (delim != line_num); - } + if (prev_dst && prev_dst > arc->dst) + out_of_order = 1; + prev_dst = arc->dst; } - else if (line_num == -2) + if (non_fake_succ == 1) { - long delim; - - /* A function name follows. Ignore it. */ - do { - count++; - __fetch_long (&delim, ptr, 4); - ptr += 4; - } while (delim != line_num); + /* 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; + } + } + + /* 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; + + 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; } - /* 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) + + /* 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; + } + + while (invalid_blocks || valid_blocks) + { + while ((blk = invalid_blocks)) { - if (s_ptr->maxlineno <= line_num) - s_ptr->maxlineno = line_num + 1; + 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; } - else if (line_num < 0) + while ((blk = valid_blocks)) { - /* Don't know what this is, but it's garbage. */ - abort(); + gcov_type total; + arc_t *arc, *inv_arc; + + valid_blocks = blk->chain; + blk->valid_chain = 0; + if (blk->num_succ == 1) + { + block_t *dst; + + total = blk->count; + inv_arc = NULL; + for (arc = blk->succ; arc; arc = arc->succ_next) + { + total -= arc->count; + if (!arc->count_valid) + inv_arc = arc; + } + dst = inv_arc->dst; + inv_arc->count_valid = 1; + inv_arc->count = total; + blk->num_succ--; + dst->num_pred--; + if (dst->count_valid) + { + if (dst->num_pred == 1 && !dst->valid_chain) + { + dst->chain = valid_blocks; + dst->valid_chain = 1; + valid_blocks = dst; + } + } + else + { + if (!dst->num_pred && !dst->invalid_chain) + { + dst->chain = invalid_blocks; + dst->invalid_chain = 1; + invalid_blocks = dst; + } + } + } + if (blk->num_pred == 1) + { + block_t *src; + + total = blk->count; + inv_arc = NULL; + for (arc = blk->pred; arc; arc = arc->pred_next) + { + total -= arc->count; + if (!arc->count_valid) + inv_arc = arc; + } + src = inv_arc->src; + inv_arc->count_valid = 1; + inv_arc->count = total; + blk->num_pred--; + src->num_succ--; + if (src->count_valid) + { + if (src->num_succ == 1 && !src->valid_chain) + { + src->chain = valid_blocks; + src->valid_chain = 1; + valid_blocks = src; + } + } + else + { + if (!src->num_succ && !src->invalid_chain) + { + src->chain = invalid_blocks; + src->invalid_chain = 1; + invalid_blocks = src; + } + } + } } } + + /* 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; + } } - -/* 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. */ +/* 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) { - 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) + if (arc->is_call_non_return) { - /* Ignore fall through arcs as they aren't really branches. */ - - if (arcptr->fall_through) - continue; - - 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; + 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 (output_function_summary) +/* 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. */ + +static char const * +format_gcov (gcov_type top, gcov_type bottom, int dp) +{ + static char buffer[20]; + + if (dp >= 0) + { + float ratio = bottom ? (float)top / bottom : 0; + int ix; + unsigned limit = 100; + unsigned percent; + + for (ix = dp; ix--; ) + limit *= 10; + + percent = (unsigned) (ratio * limit + (float)0.5); + if (percent <= 0 && top) + percent = 1; + else if (percent >= limit && top != bottom) + percent = limit - 1; + ix = sprintf (buffer, "%.*u%%", dp + 1, percent); + if (dp) { - 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. */ - 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) + char *name; + + if (flag_long_names && input_name && strcmp (src_name, input_name)) + { + name = XNEWVEC (char, strlen (src_name) + strlen (input_name) + 10); + name[0] = 0; + /* Generate the input filename part. */ + cptr = flag_preserve_paths ? NULL : strrchr (input_name, '/'); + strcat (name, cptr ? cptr + 1 : input_name); + strcat (name, "##"); + } + else + { + name = XNEWVEC (char, strlen (src_name) + 10); + name[0] = 0; + } + + /* 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 (! (*s_ptr->name == '/' || *s_ptr->name == DIR_SEPARATOR - /* Check for disk name on MS-DOS-based systems. */ - || (DIR_SEPARATOR == '\\' - && s_ptr->name[1] == ':' - && (s_ptr->name[2] == DIR_SEPARATOR - || s_ptr->name[2] == '/'))) - && 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 = (long *) xcalloc (sizeof (long), 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++) + strcat (name, ".gcov"); + return name; +} + +/* 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. */ + +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. */ + + /* 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) { - long delim; + unsigned src_n = *++encoding; - __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. */ - - if (block_num >= current_graph->num_blocks) - { - fnotice (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) + 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. */ + free (block->u.line.encoding); + block->u.cycle.arc = NULL; + block->u.cycle.ident = ~0U; - 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; + if (!ix || ix + 1 == fn->num_blocks) + /* Entry or exit block */; + else if (flag_all_blocks) + { + line_t *block_line = line ? line : &fn->src->lines[fn->line]; - for (count = 1; count < s_ptr->maxlineno; count++) + block->chain = block_line->u.blocks; + block_line->u.blocks = block; + } + else if (flag_branches) { - if (line_exists[count]) - { - total_source_lines++; - if (line_counts[count]) - total_source_lines_executed++; - } - if (output_branch_probs) + arc_t *arc; + + for (arc = block->succ; arc; arc = arc->succ_next) { - 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++; - } - } + arc->line_next = line->u.branches; + line->u.branches = arc; + if (coverage && !arc->is_unconditional) + add_branch_counts (coverage, arc); } } + } + if (!line) + fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name); +} - 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); +/* Accumulate the line counts of a file. */ - if (output_branch_probs) - { - if (total_branches) - { - 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); - } - 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); - } +static void +accumulate_line_counts (source_t *src) +{ + line_t *line; + function_t *fn, *fn_p, *fn_n; + unsigned ix; + + /* 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; - if (output_gcov_file) + for (ix = src->num_lines, line = src->lines; ix--; line++) + { + if (!flag_all_blocks) { - /* 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) - { - fnotice (stderr, "Could not open source file %s.\n", - source_file_name); - free (line_counts); - free (line_exists); - continue; - } + arc_t *arc, *arc_p, *arc_n; - 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)) + /* Total and reverse the branch information. */ + for (arc = line->u.branches, arc_p = NULL; arc; + arc_p = arc, arc = arc_n) { - 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, "."); + arc_n = arc->line_next; + arc->line_next = arc_p; - cptr = rindex (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 = rindex (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); - - /* 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); + block_t *head = block; + arc_t *arc; - /* 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) + next_vertex:; + arc = head->succ; + current_vertex:; + while (arc) { - retval = fgets (string, STRING_SIZE, source_file); - fputs (string, gcov_file); - } + 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) + { + arc = arc->succ_next; + continue; + } - 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) - fnotice (gcov_file, - "call %d returns = %d\n", - i, a_ptr->total - a_ptr->hits); - else - fnotice (gcov_file, - "call %d returns = %d%%\n", - i, 100 - ((a_ptr->hits * 100) + - (a_ptr->total >> 1))/a_ptr->total); - } + 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) - fnotice (gcov_file, - "branch %d taken = %d\n", - i, a_ptr->hits); - else - fnotice (gcov_file, - "branch %d taken = %d%%\n", i, - ((a_ptr->hits * 100) + - (a_ptr->total >> 1))/ - a_ptr->total); - - } - } - } - } + /* 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; + +} + +/* 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); + if (!multiple_files) + { + 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 if (src->file_time == 0) + 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"); + } + + /* 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); - fclose (source_file); - fclose (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); + } + } - free (line_counts); - free (line_exists); + /* 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); }