X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fgcov.c;h=61ac7ed697482f3be48cefae2ea004c7fc9fcfb4;hb=27f97b121b8499a35e7f1d915649d099ebe7fcfe;hp=9288ca570f97b8eca34cd4d52745d88744e9f6d6;hpb=2358393e3a0db63122498ce79a712bc28fa95f9e;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/gcov.c b/gcc/gcov.c index 9288ca570f9..61ac7ed6974 100644 --- a/gcc/gcov.c +++ b/gcc/gcov.c @@ -1,14 +1,15 @@ /* 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, 2007, 2008, 2009 + 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, @@ -17,9 +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. */ +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 @@ -29,15 +29,6 @@ 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. 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. */ @@ -47,7 +38,6 @@ Boston, MA 02111-1307, USA. */ #include "tm.h" #include "intl.h" #include "version.h" -#undef abort #include @@ -55,7 +45,7 @@ Boston, MA 02111-1307, USA. */ #include "gcov-io.h" #include "gcov-io.c" -/* The bbg file is generated by -ftest-coverage option. The da file is +/* 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. */ @@ -82,6 +72,8 @@ typedef struct arc_info /* transition counts. */ gcov_type count; + /* used in cycle search, so that we do not clobber original counts. */ + gcov_type cs_count; unsigned int count_valid : 1; unsigned int on_tree : 1; @@ -91,7 +83,7 @@ typedef struct arc_info /* Arc is for a function that abnormally returns. */ unsigned int is_call_non_return : 1; - /* Arc is for catch/setjump. */ + /* Arc is for catch/setjmp. */ unsigned int is_nonlocal_return : 1; /* Is an unconditional branch. */ @@ -102,7 +94,7 @@ typedef struct arc_info /* 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; @@ -159,7 +151,7 @@ typedef struct block_info /* 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. */ @@ -186,7 +178,7 @@ typedef struct function_info /* Next function in same source file. */ struct function_info *line_next; - + /* Next function. */ struct function_info *next; } function_t; @@ -197,14 +189,14 @@ 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; @@ -216,7 +208,7 @@ typedef struct line_info gcov_type count; /* execution count */ union { - arc_t *branches; /* branches from blocks that end on this + 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 @@ -233,6 +225,7 @@ typedef struct source_info /* Name of source file. */ char *name; unsigned index; + time_t file_time; /* Array of line information. */ line_t *lines; @@ -243,7 +236,7 @@ typedef struct source_info /* Functions in this source file. These are in ascending line number order. */ function_t *functions; - + /* Next source file. */ struct source_info *next; } source_t; @@ -252,10 +245,15 @@ typedef struct source_info static function_t *functions; -/* This points to the head of the sourcefile structure list. */ +/* This points to the head of the sourcefile structure list. New elements + are always prepended. */ static source_t *sources; +/* Next index for a source file. */ + +static unsigned source_index; + /* This holds data summary information. */ static struct gcov_summary object_summary; @@ -269,15 +267,29 @@ static time_t bbg_file_time; static char *bbg_file_name; +/* Stamp of the bbg file */ +static unsigned bbg_stamp; + /* Name and file pointer of the input file for the arc count data. */ static char *da_file_name; +/* Data file is missing. */ + +static int no_data_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. */ + +static int multiple_files = 0; + /* Output branch probabilities. */ static int flag_branches = 0; -/* Show unconditional branches too. */ +/* Show unconditional branches too. */ static int flag_unconditional = 0; /* Output a gcov file if this is true. This is on by default, and can @@ -317,82 +329,78 @@ static int flag_preserve_paths = 0; static int flag_counts = 0; /* Forward declarations. */ -static void fnotice PARAMS ((FILE *, const char *, ...)) ATTRIBUTE_PRINTF_2; -static int process_args PARAMS ((int, char **)); -static void print_usage PARAMS ((int)) ATTRIBUTE_NORETURN; -static void print_version PARAMS ((void)) ATTRIBUTE_NORETURN; -static void process_file PARAMS ((const char *)); -static void create_file_names PARAMS ((const char *)); -static source_t *find_source PARAMS ((const char *)); -static int read_graph_file PARAMS ((void)); -static int read_count_file PARAMS ((void)); -static void solve_flow_graph PARAMS ((function_t *)); -static void add_branch_counts PARAMS ((coverage_t *, const arc_t *)); -static void add_line_counts PARAMS ((coverage_t *, function_t *)); -static void function_summary PARAMS ((const coverage_t *, const char *)); -static const char *format_gcov PARAMS ((gcov_type, gcov_type, int)); -static void accumulate_line_counts PARAMS ((source_t *)); -static int output_branch_count PARAMS ((FILE *, int, const arc_t *)); -static void output_lines PARAMS ((FILE *, const source_t *)); -static char *make_gcov_file_name PARAMS ((const char *, const char *)); -static void release_structures 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 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) { int argno; - + + /* Unlock the stdio streams. */ + unlock_std_streams (); + gcc_init_libintl (); + /* Handle response files. */ + expandargv (&argc, &argv); + argno = process_args (argc, argv); if (optind == argc) print_usage (true); + if (argc - argno > 1) + multiple_files = 1; + for (; argno != argc; argno++) - { - release_structures (); - - process_file (argv[argno]); - } - + process_file (argv[argno]); + + generate_results (multiple_files ? NULL : argv[argc - 1]); + + release_structures (); + return 0; } static void -fnotice (FILE *file, const char *msgid, ...) +fnotice (FILE *file, const char *cmsgid, ...) { va_list ap; - - va_start (ap, msgid); - vfprintf (file, _(msgid), ap); - va_end (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); } /* 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, "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"); @@ -415,19 +423,15 @@ print_usage (error_p) /* Print version information and exit. */ static void -print_version () +print_version (void) { - char v[4]; - unsigned version = GCOV_VERSION; - unsigned ix; - - for (ix = 4; ix--; version >>= 8) - v[ix] = version; - fnotice (stdout, "gcov %.4s (GCC %s)\n", v, version_string); - fnotice (stdout, "Copyright (C) 2002 Free Software Foundation, Inc.\n"); + fnotice (stdout, "gcov %s%s\n", pkgversion_string, version_string); + fprintf (stdout, "Copyright %s 2009 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); } @@ -451,9 +455,7 @@ static const struct option options[] = /* Process args, return index to first non-arg. */ static int -process_args (argc, argv) - int argc; - char **argv; +process_args (int argc, char **argv) { int opt; @@ -506,33 +508,49 @@ process_args (argc, argv) /* Process a single source file. */ static void -process_file (file_name) - const char *file_name; +process_file (const char *file_name) { - source_t *src; function_t *fn; - + function_t *fn_p; + function_t *old_functions; + + /* Save and clear the list of current functions. They will be appended + later. */ + old_functions = functions; + functions = NULL; + create_file_names (file_name); if (read_graph_file ()) return; - + if (!functions) { fnotice (stderr, "%s:no functions found\n", bbg_file_name); return; } - + if (read_count_file ()) return; - - for (fn = functions; fn; fn = fn->next) + + 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 = (line_t *) xcalloc (src->num_lines, sizeof (line_t)); + 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); @@ -542,7 +560,7 @@ process_file (file_name) fnotice (stdout, "\n"); } } - + for (src = sources; src; src = src->next) { accumulate_line_counts (src); @@ -551,19 +569,19 @@ process_file (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", + 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", + 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", + fnotice (stderr, "%s:could not open output file '%s'\n", src->name, gcov_file_name); free (gcov_file_name); } @@ -574,16 +592,11 @@ process_file (file_name) /* Release all memory used. */ static void -release_structures () +release_structures (void) { function_t *fn; source_t *src; - - free (bbg_file_name); - free (da_file_name); - da_file_name = bbg_file_name = NULL; - bbg_file_time = 0; - + while ((src = sources)) { sources = src->next; @@ -591,12 +604,12 @@ release_structures () free (src->name); free (src->lines); } - + while ((fn = functions)) { unsigned ix; block_t *block; - + functions = fn->next; for (ix = fn->num_blocks, block = fn->blocks; ix--; block++) { @@ -622,56 +635,65 @@ release_structures () the object *file*, and the data files are named from that. */ static void -create_file_names (file_name) - const char *file_name; +create_file_names (const char *file_name) { 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; length += strlen (object_directory) + 2; - name = xmalloc (length); + name = XNEWVEC (char, length); name[0] = 0; - + base = !stat (object_directory, &status) && S_ISDIR (status.st_mode); strcat (name, object_directory); - if (base && name[strlen (name) - 1] != '/') + if (base && (! IS_DIR_SEPARATOR (name[strlen (name) - 1]))) strcat (name, "/"); } else { - name = xmalloc (length + 1); + name = XNEWVEC (char, length + 1); name[0] = 0; base = 1; } - + if (base) { /* Append source file name. */ - cptr = strrchr (file_name, '/'); - strcat (name, cptr ? cptr + 1 : file_name); + const char *cptr = lbasename (file_name); + strcat (name, cptr ? cptr : file_name); } - + /* Remove the extension. */ cptr = strrchr (name, '.'); if (cptr) *cptr = 0; - + length = strlen (name); - bbg_file_name = xmalloc (length + strlen (GCOV_GRAPH_SUFFIX) + 1); + bbg_file_name = XNEWVEC (char, length + strlen (GCOV_NOTE_SUFFIX) + 1); strcpy (bbg_file_name, name); - strcpy (bbg_file_name + length, GCOV_GRAPH_SUFFIX); + strcpy (bbg_file_name + length, GCOV_NOTE_SUFFIX); - da_file_name = xmalloc (length + strlen (GCOV_DATA_SUFFIX) + 1); + 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; } @@ -679,24 +701,45 @@ create_file_names (file_name) FILE_NAME on creation */ static source_t * -find_source (file_name) - const char *file_name; +find_source (const char *file_name) { source_t *src; + struct stat status; if (!file_name) file_name = ""; - + for (src = sources; src; src = src->next) if (!strcmp (file_name, src->name)) - return src; - - src = (source_t *)xcalloc (1, sizeof (source_t)); - src->name = xstrdup (file_name); - src->coverage.name = src->name; - src->index = sources ? sources->index + 1 : 1; - src->next = sources; - sources = src; + break; + + 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; + } + + if (src->file_time > bbg_file_time) + { + static int info_emitted; + + fnotice (stderr, "%s:source file is newer than graph file '%s'\n", + src->name, bbg_file_name); + if (!info_emitted) + { + fnotice (stderr, + "(the message is only displayed one per source file)\n"); + info_emitted = 1; + } + src->file_time = 0; + } return src; } @@ -704,22 +747,23 @@ find_source (file_name) /* Read the graph file. Return nonzero on fatal error. */ static int -read_graph_file () +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_read_unsigned () != GCOV_GRAPH_MAGIC) + if (!gcov_magic (gcov_read_unsigned (), GCOV_NOTE_MAGIC)) { fnotice (stderr, "%s:not a gcov graph file\n", bbg_file_name); gcov_close (); @@ -730,17 +774,15 @@ read_graph_file () if (version != GCOV_VERSION) { char v[4], e[4]; - unsigned required = GCOV_VERSION; - - for (ix = 4; ix--; required >>= 8, version >>= 8) - { - v[ix] = version; - e[ix] = required; - } - fnotice (stderr, "%s:version `%.4s', prefer `%.4s'\n", + + GCOV_UNSIGNED2STRING (v, version); + GCOV_UNSIGNED2STRING (e, GCOV_VERSION); + + fnotice (stderr, "%s:version '%.4s', prefer '%.4s'\n", bbg_file_name, v, e); } - + bbg_stamp = gcov_read_unsigned (); + while ((tag = gcov_read_unsigned ())) { unsigned length = gcov_read_unsigned (); @@ -758,8 +800,8 @@ read_graph_file () function_name = xstrdup (gcov_read_string ()); src = find_source (gcov_read_string ()); lineno = gcov_read_unsigned (); - - fn = (function_t *)xcalloc (1, sizeof (function_t)); + + fn = XCNEW (function_t); fn->name = function_name; fn->ident = ident; fn->checksum = checksum; @@ -769,7 +811,7 @@ read_graph_file () 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 @@ -788,15 +830,14 @@ read_graph_file () else if (fn && tag == GCOV_TAG_BLOCKS) { if (fn->blocks) - fnotice (stderr, "%s:already seen blocks for `%s'\n", + fnotice (stderr, "%s:already seen blocks for '%s'\n", bbg_file_name, fn->name); else { - unsigned ix, num_blocks = length / 4; + unsigned ix, num_blocks = GCOV_TAG_BLOCKS_NUM (length); fn->num_blocks = num_blocks; - - fn->blocks - = (block_t *)xcalloc (fn->num_blocks, sizeof (block_t)); + + fn->blocks = XCNEWVEC (block_t, fn->num_blocks); for (ix = 0; ix != num_blocks; ix++) fn->blocks[ix].flags = gcov_read_unsigned (); } @@ -804,34 +845,34 @@ read_graph_file () else if (fn && tag == GCOV_TAG_ARCS) { unsigned src = gcov_read_unsigned (); - unsigned num_dests = (length - 4) / 8; + unsigned num_dests = GCOV_TAG_ARCS_NUM (length); 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 (); - + if (dest >= fn->num_blocks) goto corrupt; - arc = (arc_t *) xcalloc (1, sizeof (arc_t)); - + 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++; @@ -848,13 +889,13 @@ read_graph_file () else { /* Non-local return from a callee of this - function. The destination block is a catch or - setjmp. */ + 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++; } @@ -862,16 +903,15 @@ read_graph_file () else if (fn && tag == GCOV_TAG_LINES) { unsigned blockno = gcov_read_unsigned (); - unsigned *line_nos - = (unsigned *)xcalloc ((length - 4) / 4, sizeof (unsigned)); + unsigned *line_nos = XCNEWVEC (unsigned, length - 1); if (blockno >= fn->num_blocks || fn->blocks[blockno].u.line.encoding) goto corrupt; - + for (ix = 0; ; ) { unsigned lineno = gcov_read_unsigned (); - + if (lineno) { if (!ix) @@ -886,16 +926,16 @@ read_graph_file () else { 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; } @@ -906,19 +946,17 @@ read_graph_file () } gcov_sync (base, length); if (gcov_is_error ()) - break; - } - if (!gcov_is_eof ()) - { - corrupt:; - fnotice (stderr, "%s:corrupted\n", bbg_file_name); - gcov_close (); - return 1; + { + corrupt:; + fnotice (stderr, "%s:corrupted\n", bbg_file_name); + gcov_close (); + return 1; + } } gcov_close (); - - /* We built everything backwards, so nreverse them all */ - + + /* We built everything backwards, so nreverse them all. */ + /* Reverse sources. Not strictly necessary, but we'll then process them in the 'expected' order. */ { @@ -936,10 +974,12 @@ read_graph_file () { function_t *fn, *fn_p, *fn_n; - for (fn_p = NULL, fn = functions; fn; fn_p = fn, fn = fn_n) + for (fn_p = old_functions_head, fn = functions; + fn != old_functions_head; + fn_p = fn, fn = fn_n) { unsigned ix; - + fn_n = fn->next; fn->next = fn_p; @@ -947,7 +987,7 @@ read_graph_file () 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) { @@ -974,7 +1014,7 @@ read_graph_file () function. Return nonzero if fatal error. */ static int -read_count_file () +read_count_file (void) { unsigned ix; unsigned version; @@ -984,10 +1024,12 @@ read_count_file () if (!gcov_open (da_file_name, 1)) { - fnotice (stderr, "%s:cannot open data file\n", da_file_name); - return 1; + fnotice (stderr, "%s:cannot open data file, assuming not executed\n", + da_file_name); + no_data_file = 1; + return 0; } - if (gcov_read_unsigned () != GCOV_DATA_MAGIC) + if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC)) { fnotice (stderr, "%s:not a gcov data file\n", da_file_name); cleanup:; @@ -998,17 +1040,20 @@ read_count_file () if (version != GCOV_VERSION) { char v[4], e[4]; - unsigned desired = GCOV_VERSION; + + GCOV_UNSIGNED2STRING (v, version); + GCOV_UNSIGNED2STRING (e, GCOV_VERSION); - for (ix = 4; ix--; desired >>= 8, version >>= 8) - { - v[ix] = version; - e[ix] = desired; - } - fnotice (stderr, "%s:version `%.4s', prefer version `%.4s'\n", + 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; + } + while ((tag = gcov_read_unsigned ())) { unsigned length = gcov_read_unsigned (); @@ -1023,6 +1068,9 @@ read_count_file () 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) @@ -1031,7 +1079,7 @@ read_count_file () fn_n = NULL; else { - fnotice (stderr, "%s:unknown function `%u'\n", + fnotice (stderr, "%s:unknown function '%u'\n", da_file_name, ident); break; } @@ -1044,35 +1092,31 @@ read_count_file () else if (gcov_read_unsigned () != fn->checksum) { mismatch:; - fnotice (stderr, "%s:profile mismatch for `%s'\n", + 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 != 8 * fn->num_counts) + if (length != GCOV_TAG_COUNTER_LENGTH (fn->num_counts)) goto mismatch; - + if (!fn->counts) - fn->counts - = (gcov_type *)xcalloc (fn->num_counts, sizeof (gcov_type)); - + 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 ())) - break; + { + fnotice (stderr, error < 0 ? "%s:overflowed\n" : "%s:corrupted\n", + da_file_name); + goto cleanup; + } } - if (!gcov_is_eof ()) - { - fnotice (stderr, error < 0 ? "%s:overflowed\n" : "%s:corrupted\n", - da_file_name); - goto cleanup; - } - gcov_close (); return 0; } @@ -1081,8 +1125,7 @@ read_count_file () to the blocks and the uninstrumented arcs. */ static void -solve_flow_graph (fn) - function_t *fn; +solve_flow_graph (function_t *fn) { unsigned ix; arc_t *arc; @@ -1090,22 +1133,22 @@ solve_flow_graph (fn) 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", + 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", + 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", + 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 @@ -1120,12 +1163,12 @@ solve_flow_graph (fn) 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) { if (!arc->fake) non_fake_succ++; - + if (!arc->on_tree) { if (count_ptr) @@ -1147,7 +1190,7 @@ solve_flow_graph (fn) { arc->is_unconditional = 1; /* If this block is instrumenting a call, it might be - an artifical block. It is not artificial if it has + 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 @@ -1157,7 +1200,7 @@ solve_flow_graph (fn) 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 @@ -1166,11 +1209,11 @@ solve_flow_graph (fn) { 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);) { @@ -1194,7 +1237,7 @@ solve_flow_graph (fn) } blk->succ = start; } - + /* Place it on the invalid chain, it will be ignored if that's wrong. */ blk->invalid_chain = 1; @@ -1208,7 +1251,7 @@ solve_flow_graph (fn) { gcov_type total = 0; const arc_t *arc; - + invalid_blocks = blk->chain; blk->invalid_chain = 0; if (!blk->num_succ) @@ -1219,7 +1262,7 @@ solve_flow_graph (fn) total += arc->count; else continue; - + blk->count = total; blk->count_valid = 1; blk->chain = valid_blocks; @@ -1236,7 +1279,7 @@ solve_flow_graph (fn) if (blk->num_succ == 1) { block_t *dst; - + total = blk->count; inv_arc = NULL; for (arc = blk->succ; arc; arc = arc->succ_next) @@ -1272,7 +1315,7 @@ solve_flow_graph (fn) if (blk->num_pred == 1) { block_t *src; - + total = blk->count; inv_arc = NULL; for (arc = blk->pred; arc; arc = arc->pred_next) @@ -1307,13 +1350,13 @@ solve_flow_graph (fn) } } } - + /* 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", + fnotice (stderr, "%s:graph is unsolvable for '%s'\n", bbg_file_name, fn->name); break; } @@ -1324,9 +1367,7 @@ solve_flow_graph (fn) /* Increment totals in COVERAGE according to arc ARC. */ static void -add_branch_counts (coverage, arc) - coverage_t *coverage; - const arc_t *arc; +add_branch_counts (coverage_t *coverage, const arc_t *arc) { if (arc->is_call_non_return) { @@ -1351,22 +1392,20 @@ add_branch_counts (coverage, arc) format TOP. Return pointer to a static string. */ static char const * -format_gcov (top, bottom, dp) - gcov_type top, bottom; - int dp; +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; @@ -1387,7 +1426,7 @@ format_gcov (top, bottom, dp) } else sprintf (buffer, HOST_WIDEST_INT_PRINT_DEC, (HOST_WIDEST_INT)top); - + return buffer; } @@ -1395,18 +1434,16 @@ format_gcov (top, bottom, dp) /* Output summary info for a function. */ static void -function_summary (coverage, title) - const coverage_t *coverage; - const char *title; +function_summary (const coverage_t *coverage, const char *title) { - fnotice (stdout, "%s `%s'\n", title, coverage->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 lines"); + fnotice (stdout, "No executable lines\n"); if (flag_branches) { @@ -1443,58 +1480,75 @@ function_summary (coverage, title) removed and '..' components are renamed to '^'. */ static char * -make_gcov_file_name (input_name, src_name) - const char *input_name; - const char *src_name; +make_gcov_file_name (const char *input_name, const char *src_name) { - char *cptr; - char *name = xmalloc (strlen (src_name) + strlen (input_name) + 10); - - name[0] = 0; - if (flag_long_names && strcmp (src_name, input_name)) + const char *cptr; + 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); + cptr = flag_preserve_paths ? NULL : lbasename (input_name); + strcat (name, cptr ? cptr : 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); - + + cptr = flag_preserve_paths ? NULL : lbasename (src_name); + strcat (name, cptr ? cptr : src_name); + if (flag_preserve_paths) { - /* Convert '/' to '#', remove '/./', convert '/../' to '/^/' */ - char *prev; - - for (cptr = name; (cptr = strchr ((prev = cptr), '/'));) - { - 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++); - } - } + /* Convert '/' and '\' to '#', remove '/./', convert '/../' to '/^/', + convert ':' to '~' on DOS based file system. */ + char *pnew = name, *pold = name; + + /* First check for leading drive separator. */ + + while (*pold != '\0') + { + if (*pold == '/' || *pold == '\\') + { + *pnew++ = '#'; + pold++; + } +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + else if (*pold == ':') + { + *pnew++ = '~'; + pold++; + } +#endif + else if ((*pold == '/' && strstr (pold, "/./") == pold) + || (*pold == '\\' && strstr (pold, "\\.\\") == pold)) + pold += 3; + else if (*pold == '/' && strstr (pold, "/../") == pold) + { + strcpy (pnew, "/^/"); + pnew += 3; + pold += 4; + } + else if (*pold == '\\' && strstr (pold, "\\..\\") == pold) + { + strcpy (pnew, "\\^\\"); + pnew += 3; + pold += 4; + } + else + *pnew++ = *pold++; + } + + *pnew = '\0'; } - + strcat (name, ".gcov"); return name; } @@ -1504,12 +1558,10 @@ make_gcov_file_name (input_name, src_name) the appropriate basic block. */ static void -add_line_counts (coverage, fn) - coverage_t *coverage; - function_t *fn; +add_line_counts (coverage_t *coverage, function_t *fn) { unsigned ix; - line_t *line = NULL; /* this is propagated from one iteration to the + line_t *line = NULL; /* This is propagated from one iteration to the next. */ /* Scan each basic block. */ @@ -1549,13 +1601,13 @@ add_line_counts (coverage, fn) free (block->u.line.encoding); block->u.cycle.arc = NULL; block->u.cycle.ident = ~0U; - + 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]; - + block->chain = block_line->u.blocks; block_line->u.blocks = block; } @@ -1573,14 +1625,13 @@ add_line_counts (coverage, fn) } } if (!line) - fnotice (stderr, "%s:no lines for `%s'\n", bbg_file_name, fn->name); + fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name); } /* Accumulate the line counts of a file. */ static void -accumulate_line_counts (src) - source_t *src; +accumulate_line_counts (source_t *src) { line_t *line; function_t *fn, *fn_p, *fn_n; @@ -1594,20 +1645,20 @@ accumulate_line_counts (src) fn->line_next = fn_p; } src->functions = fn_p; - + for (ix = src->num_lines, line = src->lines; ix--; line++) { if (!flag_all_blocks) { arc_t *arc, *arc_p, *arc_n; - + /* 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; - + add_branch_counts (&src->coverage, arc); } line->u.branches = arc_p; @@ -1622,7 +1673,7 @@ accumulate_line_counts (src) 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) @@ -1632,7 +1683,7 @@ accumulate_line_counts (src) block->u.cycle.ident = ix; } line->u.blocks = block_p; - + /* Sum the entry arcs. */ for (block = line->u.blocks; block; block = block->chain) { @@ -1645,6 +1696,10 @@ accumulate_line_counts (src) if (flag_branches) add_branch_counts (&src->coverage, arc); } + + /* Initialize the cs_count. */ + for (arc = block->succ; arc; arc = arc->succ_next) + arc->cs_count = arc->count; } /* Find the loops. This uses the algorithm described in @@ -1661,12 +1716,13 @@ accumulate_line_counts (src) For each loop we find, locate the arc with the smallest transition count, and add that to the cumulative - count. Remove the arc from consideration. */ + count. Decrease flow over the cycle and remove the arc + from consideration. */ for (block = line->u.blocks; block; block = block->chain) { block_t *head = block; arc_t *arc; - + next_vertex:; arc = head->succ; current_vertex:; @@ -1683,36 +1739,44 @@ accumulate_line_counts (src) arc = arc->succ_next; continue; } - + if (dst == block) { /* Found a closing arc. */ - gcov_type cycle_count = arc->count; + 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->count) + if (cycle_count > probe_arc->cs_count) { - cycle_count = probe_arc->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) { arc = head->u.cycle.arc; + head->u.cycle.arc = NULL; head = arc->src; } /* Move on. */ arc = arc->succ_next; continue; } - + /* Add new block to chain. */ dst->u.cycle.arc = arc; head = dst; @@ -1735,7 +1799,7 @@ accumulate_line_counts (src) line->count = count; } - + if (line->exists) { src->coverage.lines++; @@ -1745,16 +1809,13 @@ accumulate_line_counts (src) } } -/* Ouput information about ARC number IX. Returns nonzero if +/* Output information about ARC number IX. Returns nonzero if anything is output. */ static int -output_branch_count (gcov_file, ix, arc) - FILE *gcov_file; - int ix; - const arc_t *arc; +output_branch_count (FILE *gcov_file, int ix, const arc_t *arc) { - + if (arc->is_call_non_return) { if (arc->src->count) @@ -1786,7 +1847,7 @@ output_branch_count (gcov_file, ix, arc) else return 0; return 1; - + } /* Read in the source file one line at a time, and output that line to @@ -1794,43 +1855,37 @@ output_branch_count (gcov_file, ix, arc) information. */ static void -output_lines (gcov_file, src) - FILE *gcov_file; - const source_t *src; +output_lines (FILE *gcov_file, const source_t *src) { FILE *source_file; - unsigned line_num; /* current line number. */ + 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 = src->functions; + 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, da_file_name); - fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0, - object_summary.ctrs[GCOV_COUNTER_ARCS].runs); + 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 - { - 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); - } - } + 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++) @@ -1839,7 +1894,7 @@ output_lines (gcov_file, src) { 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; @@ -1853,17 +1908,17 @@ output_lines (gcov_file, src) 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. */ + 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. */ @@ -1884,7 +1939,7 @@ output_lines (gcov_file, src) block_t *block; arc_t *arc; int ix, jx; - + for (ix = jx = 0, block = line->u.blocks; block; block = block->chain) { @@ -1902,12 +1957,12 @@ output_lines (gcov_file, src) { 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) @@ -1915,7 +1970,7 @@ output_lines (gcov_file, src) 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); @@ -1925,7 +1980,7 @@ output_lines (gcov_file, src) } } } - + if (source_file) fclose (source_file); }