diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2013-04-11 09:13:11 +0000 |
---|---|---|
committer | <> | 2014-04-23 12:05:38 +0000 |
commit | 6af3fdec2262dd94954acc5e426ef71cbd4521d3 (patch) | |
tree | 9be02de9a80f7935892a2d03741adee44723e65d /gcc/gcov.c | |
parent | 19be2b4342ac32e9edc78ce6fed8f61b63ae98d1 (diff) | |
download | gcc-tarball-6af3fdec2262dd94954acc5e426ef71cbd4521d3.tar.gz |
Imported from /home/lorry/working-area/delta_gcc-tarball/gcc-4.7.3.tar.bz2.gcc-4.7.3
Diffstat (limited to 'gcc/gcov.c')
-rw-r--r-- | gcc/gcov.c | 1070 |
1 files changed, 706 insertions, 364 deletions
diff --git a/gcc/gcov.c b/gcc/gcov.c index 2fbeaf55e5..97071115be 100644 --- a/gcc/gcov.c +++ b/gcc/gcov.c @@ -1,7 +1,7 @@ /* 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, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 + 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. Contributed by James E. Wilson of Cygnus Support. Mangled by Bob Manson of Cygnus Support. @@ -37,6 +37,7 @@ along with Gcov; see the file COPYING3. If not see #include "coretypes.h" #include "tm.h" #include "intl.h" +#include "diagnostic.h" #include "version.h" #include <getopt.h> @@ -54,9 +55,14 @@ along with Gcov; see the file COPYING3. If not see some places we make use of the knowledge of how profile.c works to select particular algorithms here. */ -/* This is the size of the buffer used to read in source file lines. */ +/* The code validates that the profile information read in corresponds + to the code currently being compiled. Rather than checking for + identical files, the code below computes a checksum on the CFG + (based on the order of basic blocks and the arcs in the CFG). If + the CFG checksum in the gcda file match the CFG checksum for the + code currently being compiled, the profile data will be used. */ -#define STRING_SIZE 200 +/* This is the size of the buffer used to read in source file lines. */ struct function_info; struct block_info; @@ -80,6 +86,9 @@ typedef struct arc_info unsigned int fake : 1; unsigned int fall_through : 1; + /* Arc to a catch handler. */ + unsigned int is_throw : 1; + /* Arc is for a function that abnormally returns. */ unsigned int is_call_non_return : 1; @@ -115,10 +124,11 @@ typedef struct block_info /* Block execution count. */ gcov_type count; - unsigned flags : 13; + unsigned flags : 12; unsigned count_valid : 1; unsigned valid_chain : 1; unsigned invalid_chain : 1; + unsigned exceptional : 1; /* Block is a call instrumenting site. */ unsigned is_call_site : 1; /* Does the call. */ @@ -161,7 +171,11 @@ typedef struct function_info /* Name of function. */ char *name; unsigned ident; - unsigned checksum; + unsigned lineno_checksum; + unsigned cfg_checksum; + + /* The graph contains at least one fake incoming edge. */ + unsigned has_catch : 1; /* Array of basic blocks. */ block_t *blocks; @@ -172,9 +186,9 @@ typedef struct function_info gcov_type *counts; unsigned num_counts; - /* First line number. */ + /* First line number & file. */ unsigned line; - struct source_info *src; + unsigned src; /* Next function in same source file. */ struct function_info *line_next; @@ -215,6 +229,7 @@ typedef struct line_info in all-blocks mode. */ } u; unsigned exists : 1; + unsigned unexceptional : 1; } line_t; /* Describes a file mentioned in the block graph. Contains an array @@ -222,9 +237,8 @@ typedef struct line_info typedef struct source_info { - /* Name of source file. */ + /* Canonical name of source file. */ char *name; - unsigned index; time_t file_time; /* Array of line information. */ @@ -236,29 +250,35 @@ 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; +typedef struct name_map +{ + char *name; /* Source file name */ + unsigned src; /* Source file */ +} name_map_t; + /* Holds a list of function basic block graphs. */ static function_t *functions; +static function_t **fn_end = &functions; -/* 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 source_t *sources; /* Array of source files */ +static unsigned n_sources; /* Number of sources */ +static unsigned a_sources; /* Allocated sources */ -static unsigned source_index; +static name_map_t *names; /* Mapping of file names to sources */ +static unsigned n_names; /* Number of names */ +static unsigned a_names; /* Allocated names */ /* This holds data summary information. */ -static struct gcov_summary object_summary; +static unsigned object_runs; static unsigned program_count; +static unsigned total_lines; +static unsigned total_executed; + /* Modification time of graph file. */ static time_t bbg_file_time; @@ -322,6 +342,17 @@ static int flag_function_summary = 0; static char *object_directory = 0; +/* Source directory prefix. This is removed from source pathnames + that match, when generating the output file name. */ + +static char *source_prefix = 0; +static size_t source_length = 0; + +/* Only show data for sources with relative pathnames. Absolute ones + usually indicate a system header file, which although it may + contain inline functions, is usually uninteresting. */ +static int flag_relative_only = 0; + /* Preserve all pathname components. Needed when object files and source files are in subdirectories. '/' is mangled as '#', '.' is elided and '..' mangled to '^'. */ @@ -334,26 +365,32 @@ static int flag_preserve_paths = 0; static int flag_counts = 0; /* Forward declarations. */ -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 int name_search (const void *, const void *); +static int name_sort (const void *, const void *); +static char *canonicalize_name (const char *); +static unsigned find_source (const char *); +static function_t *read_graph_file (void); +static int read_count_file (function_t *); static void solve_flow_graph (function_t *); +static void find_exception_blocks (function_t *); static void add_branch_counts (coverage_t *, const arc_t *); static void add_line_counts (coverage_t *, function_t *); +static void executed_summary (unsigned, unsigned); 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 char *mangle_name (const char *, char *); static void release_structures (void); +static void release_function (function_t *); extern int main (int, char **); int @@ -361,15 +398,30 @@ main (int argc, char **argv) { int argno; int first_arg; + const char *p; + + p = argv[0] + strlen (argv[0]); + while (p != argv[0] && !IS_DIR_SEPARATOR (p[-1])) + --p; + progname = p; + + xmalloc_set_program_name (progname); /* Unlock the stdio streams. */ unlock_std_streams (); gcc_init_libintl (); + diagnostic_initialize (global_dc, 0); + /* Handle response files. */ expandargv (&argc, &argv); + a_names = 10; + names = XNEWVEC (name_map_t, a_names); + a_sources = 10; + sources = XNEWVEC (source_t, a_sources); + argno = process_args (argc, argv); if (optind == argc) print_usage (true); @@ -393,16 +445,6 @@ main (int argc, char **argv) return 0; } - -static void -fnotice (FILE *file, const char *cmsgid, ...) -{ - va_list 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. */ @@ -413,7 +455,7 @@ 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]... SOURCE|OBJ...\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"); @@ -426,6 +468,8 @@ print_usage (int error_p) 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, " -s, --source-prefix DIR Source prefix to elide\n"); + fnotice (file, " -r, --relative-only Only show data for relative sources\n"); fnotice (file, " -p, --preserve-paths Preserve all pathname components\n"); fnotice (file, " -u, --unconditional-branches Show unconditional branch counts too\n"); fnotice (file, " -d, --display-progress Display progress information\n"); @@ -440,7 +484,7 @@ static void print_version (void) { fnotice (stdout, "gcov %s%s\n", pkgversion_string, version_string); - fprintf (stdout, "Copyright %s 2011 Free Software Foundation, Inc.\n", + fprintf (stdout, "Copyright %s 2012 Free Software Foundation, Inc.\n", _("(C)")); fnotice (stdout, _("This is free software; see the source for copying conditions.\n" @@ -460,8 +504,10 @@ static const struct option options[] = { "long-file-names", no_argument, NULL, 'l' }, { "function-summaries", no_argument, NULL, 'f' }, { "preserve-paths", no_argument, NULL, 'p' }, + { "relative-only", no_argument, NULL, 'r' }, { "object-directory", required_argument, NULL, 'o' }, { "object-file", required_argument, NULL, 'o' }, + { "source-prefix", required_argument, NULL, 's' }, { "unconditional-branches", no_argument, NULL, 'u' }, { "display-progress", no_argument, NULL, 'd' }, { 0, 0, 0, 0 } @@ -474,7 +520,7 @@ process_args (int argc, char **argv) { int opt; - while ((opt = getopt_long (argc, argv, "abcdfhlno:puv", options, NULL)) != -1) + while ((opt = getopt_long (argc, argv, "abcdfhlno:s:pruv", options, NULL)) != -1) { switch (opt) { @@ -502,6 +548,13 @@ process_args (int argc, char **argv) case 'o': object_directory = optarg; break; + case 's': + source_prefix = optarg; + source_length = strlen (source_prefix); + break; + case 'r': + flag_relative_only = 1; + break; case 'p': flag_preserve_paths = 1; break; @@ -523,48 +576,92 @@ process_args (int argc, char **argv) return optind; } -/* Process a single source file. */ +/* Process a single input file. */ static void process_file (const char *file_name) { - 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; + function_t *fns; create_file_names (file_name); - if (read_graph_file ()) + fns = read_graph_file (); + if (!fns) return; - - if (!functions) + + read_count_file (fns); + while (fns) { - fnotice (stderr, "%s:no functions found\n", bbg_file_name); - return; - } + function_t *fn = fns; - if (read_count_file ()) - return; + fns = fn->next; + fn->next = NULL; + if (fn->counts) + { + unsigned src = fn->src; + unsigned line = fn->line; + unsigned block_no; + function_t *probe, **prev; + + /* 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. Note we're + building this list in reverse order. */ + for (prev = &sources[src].functions; + (probe = *prev); prev = &probe->line_next) + if (probe->line <= line) + break; + fn->line_next = probe; + *prev = fn; - for (fn_p = NULL, fn = functions; fn; fn_p = fn, fn = fn->next) - solve_flow_graph (fn); + /* Mark last line in files touched by function. */ + for (block_no = 0; block_no != fn->num_blocks; block_no++) + { + unsigned *enc = fn->blocks[block_no].u.line.encoding; + unsigned num = fn->blocks[block_no].u.line.num; - if (fn_p) - fn_p->next = old_functions; + for (; num--; enc++) + if (!*enc) + { + if (enc[1] != src) + { + if (line >= sources[src].num_lines) + sources[src].num_lines = line + 1; + line = 0; + src = enc[1]; + } + enc++; + num--; + } + else if (*enc > line) + line = *enc; + } + if (line >= sources[src].num_lines) + sources[src].num_lines = line + 1; + + solve_flow_graph (fn); + if (fn->has_catch) + find_exception_blocks (fn); + *fn_end = fn; + fn_end = &fn->next; + } + else + /* The function was not in the executable -- some other + instance must have been selected. */ + release_function (fn); + } } static void generate_results (const char *file_name) { + unsigned ix; source_t *src; function_t *fn; - for (src = sources; src; src = src->next) - src->lines = XCNEWVEC (line_t, src->num_lines); + for (ix = n_sources, src = sources; ix--; src++) + if (src->num_lines) + src->lines = XCNEWVEC (line_t, src->num_lines); + for (fn = functions; fn; fn = fn->next) { coverage_t coverage; @@ -579,78 +676,123 @@ generate_results (const char *file_name) } } - for (src = sources; src; src = src->next) + if (file_name) + { + name_map_t *name_map = (name_map_t *)bsearch + (file_name, names, n_names, sizeof (*names), name_search); + if (name_map) + file_name = sources[name_map->src].coverage.name; + else + file_name = canonicalize_name (file_name); + } + + for (ix = n_sources, src = sources; ix--; src++) { + if (flag_relative_only) + { + /* Ignore this source, if it is an absolute path (after + source prefix removal). */ + char first = src->coverage.name[0]; + +#if HAVE_DOS_BASED_FILE_SYSTEM + if (first && src->coverage.name[1] == ':') + first = src->coverage.name[2]; +#endif + if (IS_DIR_SEPARATOR (first)) + continue; + } + accumulate_line_counts (src); function_summary (&src->coverage, "File"); + total_lines += src->coverage.lines; + total_executed += src->coverage.lines_executed; if (flag_gcov_file) { - char *gcov_file_name = make_gcov_file_name (file_name, src->name); - FILE *gcov_file = fopen (gcov_file_name, "w"); + char *gcov_file_name + = make_gcov_file_name (file_name, src->coverage.name); - if (gcov_file) + if (src->coverage.lines) { - 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); + FILE *gcov_file = fopen (gcov_file_name, "w"); + + if (gcov_file) + { + fnotice (stdout, "Creating '%s'\n", gcov_file_name); + output_lines (gcov_file, src); + if (ferror (gcov_file)) + fnotice (stderr, "Error writing output file '%s'\n", + gcov_file_name); + fclose (gcov_file); + } + else + fnotice (stderr, "Could not open output file '%s'\n", + gcov_file_name); } else - fnotice (stderr, "%s:could not open output file '%s'\n", - src->name, gcov_file_name); + { + unlink (gcov_file_name); + fnotice (stdout, "Removing '%s'\n", gcov_file_name); + } free (gcov_file_name); } fnotice (stdout, "\n"); } + + if (!file_name) + executed_summary (total_lines, total_executed); } -/* Release all memory used. */ +/* Release a function structure */ static void -release_structures (void) +release_function (function_t *fn) { - function_t *fn; - source_t *src; + unsigned ix; + block_t *block; - while ((src = sources)) + for (ix = fn->num_blocks, block = fn->blocks; ix--; block++) { - sources = src->next; + arc_t *arc, *arc_n; - free (src->name); - free (src->lines); + for (arc = block->succ; arc; arc = arc_n) + { + arc_n = arc->succ_next; + free (arc); + } } + free (fn->blocks); + free (fn->counts); +} + +/* Release all memory used. */ + +static void +release_structures (void) +{ + unsigned ix; + function_t *fn; + + for (ix = n_sources; ix--;) + free (sources[ix].lines); + free (sources); + + for (ix = n_names; ix--;) + free (names[ix].name); + free (names); while ((fn = functions)) { - 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); + release_function (fn); } } -/* 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. */ +/* Generate the names of the graph and data files. If OBJECT_DIRECTORY + is not specified, these are named from 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 create_file_names (const char *file_name) @@ -661,10 +803,8 @@ create_file_names (const char *file_name) int base; /* Free previous file names. */ - if (bbg_file_name) - free (bbg_file_name); - if (da_file_name) - free (da_file_name); + free (bbg_file_name); + free (da_file_name); da_file_name = bbg_file_name = NULL; bbg_file_time = 0; bbg_stamp = 0; @@ -685,8 +825,8 @@ create_file_names (const char *file_name) else { name = XNEWVEC (char, length + 1); - name[0] = 0; - base = 1; + strcpy (name, file_name); + base = 0; } if (base) @@ -715,77 +855,163 @@ create_file_names (const char *file_name) return; } +/* A is a string and B is a pointer to name_map_t. Compare for file + name orderability. */ + +static int +name_search (const void *a_, const void *b_) +{ + const char *a = (const char *)a_; + const name_map_t *b = (const name_map_t *)b_; + +#if HAVE_DOS_BASED_FILE_SYSTEM + return strcasecmp (a, b->name); +#else + return strcmp (a, b->name); +#endif +} + +/* A and B are a pointer to name_map_t. Compare for file name + orderability. */ + +static int +name_sort (const void *a_, const void *b_) +{ + const name_map_t *a = (const name_map_t *)a_; + return name_search (a->name, b_); +} + /* Find or create a source file structure for FILE_NAME. Copies FILE_NAME on creation */ -static source_t * +static unsigned find_source (const char *file_name) { - source_t *src; + name_map_t *name_map; + char *canon; + unsigned idx; struct stat status; if (!file_name) file_name = "<unknown>"; + name_map = (name_map_t *)bsearch + (file_name, names, n_names, sizeof (*names), name_search); + if (name_map) + { + idx = name_map->src; + goto check_date; + } - for (src = sources; src; src = src->next) - if (!strcmp (file_name, src->name)) - break; - - if (!src) + if (n_names + 2 > a_names) { - src = XCNEW (source_t); - src->name = xstrdup (file_name); - src->coverage.name = src->name; - src->index = source_index++; - src->next = sources; - sources = src; + /* Extend the name map array -- we'll be inserting one or two + entries. */ + a_names *= 2; + name_map = XNEWVEC (name_map_t, a_names); + memcpy (name_map, names, n_names * sizeof (*names)); + free (names); + names = name_map; + } + + /* Not found, try the canonical name. */ + canon = canonicalize_name (file_name); + name_map = (name_map_t *)bsearch + (canon, names, n_names, sizeof (*names), name_search); + if (!name_map) + { + /* Not found with canonical name, create a new source. */ + source_t *src; + + if (n_sources == a_sources) + { + a_sources *= 2; + src = XNEWVEC (source_t, a_sources); + memcpy (src, sources, n_sources * sizeof (*sources)); + free (sources); + sources = src; + } - if (!stat (file_name, &status)) + idx = n_sources; + + name_map = &names[n_names++]; + name_map->name = canon; + name_map->src = idx; + + src = &sources[n_sources++]; + memset (src, 0, sizeof (*src)); + src->name = canon; + src->coverage.name = src->name; + if (source_length +#if HAVE_DOS_BASED_FILE_SYSTEM + /* You lose if separators don't match exactly in the + prefix. */ + && !strncasecmp (source_prefix, src->coverage.name, source_length) +#else + && !strncmp (source_prefix, src->coverage.name, source_length) +#endif + && IS_DIR_SEPARATOR (src->coverage.name[source_length])) + src->coverage.name += source_length + 1; + if (!stat (src->name, &status)) src->file_time = status.st_mtime; } + else + idx = name_map->src; - if (src->file_time > bbg_file_time) + if (name_search (file_name, name_map)) + { + /* Append the non-canonical name. */ + name_map = &names[n_names++]; + name_map->name = xstrdup (file_name); + name_map->src = idx; + } + + /* Resort the name map. */ + qsort (names, n_names, sizeof (*names), name_sort); + + check_date: + if (sources[idx].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); + file_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; + sources[idx].file_time = 0; } - return src; + return idx; } -/* Read the graph file. Return nonzero on fatal error. */ +/* Read the graph file. Return list of functions read -- in reverse order. */ -static int +static function_t * 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; + function_t *fn = NULL; + function_t *fns = NULL; + function_t **fns_end = &fns; + unsigned src_idx = 0; unsigned ix; unsigned tag; if (!gcov_open (bbg_file_name, 1)) { fnotice (stderr, "%s:cannot open graph file\n", bbg_file_name); - return 1; + return fns; } 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; + return fns; } version = gcov_read_unsigned (); @@ -809,41 +1035,29 @@ read_graph_file (void) if (tag == GCOV_TAG_FUNCTION) { char *function_name; - unsigned ident, checksum, lineno; - source_t *src; - function_t *probe, *prev; + unsigned ident, lineno; + unsigned lineno_checksum, cfg_checksum; ident = gcov_read_unsigned (); - checksum = gcov_read_unsigned (); + lineno_checksum = gcov_read_unsigned (); + cfg_checksum = gcov_read_unsigned (); function_name = xstrdup (gcov_read_string ()); - src = find_source (gcov_read_string ()); + src_idx = 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->lineno_checksum = lineno_checksum; + fn->cfg_checksum = cfg_checksum; + fn->src = src_idx; fn->line = lineno; - fn->next = functions; - functions = fn; + fn->line_next = NULL; + fn->next = NULL; + *fns_end = fn; + fns_end = &fn->next; current_tag = tag; - - if (lineno >= src->num_lines) - src->num_lines = lineno + 1; - /* Now insert it into the source file's list of - functions. Normally functions will be encountered in - ascending order, so a simple scan is quick. */ - for (probe = src->functions, prev = NULL; - probe && probe->line > lineno; - prev = probe, probe = probe->line_next) - continue; - fn->line_next = probe; - if (prev) - prev->line_next = fn; - else - src->functions = fn; } else if (fn && tag == GCOV_TAG_BLOCKS) { @@ -864,13 +1078,15 @@ read_graph_file (void) { unsigned src = gcov_read_unsigned (); unsigned num_dests = GCOV_TAG_ARCS_NUM (length); + block_t *src_blk = &fn->blocks[src]; + unsigned mark_catches = 0; + struct arc_info *arc; 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 (); @@ -879,7 +1095,7 @@ read_graph_file (void) arc = XCNEW (arc_t); arc->dst = &fn->blocks[dest]; - arc->src = &fn->blocks[src]; + arc->src = src_blk; arc->count = 0; arc->count_valid = 0; @@ -887,9 +1103,9 @@ read_graph_file (void) 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->succ_next = src_blk->succ; + src_blk->succ = arc; + src_blk->num_succ++; arc->pred_next = fn->blocks[dest].pred; fn->blocks[dest].pred = arc; @@ -903,12 +1119,12 @@ read_graph_file (void) source block must be a call. */ fn->blocks[src].is_call_site = 1; arc->is_call_non_return = 1; + mark_catches = 1; } else { /* Non-local return from a callee of this - function. The destination block is a catch or - setjmp. */ + function. The destination block is a setjmp. */ arc->is_nonlocal_return = 1; fn->blocks[dest].is_nonlocal_return = 1; } @@ -917,6 +1133,20 @@ read_graph_file (void) if (!arc->on_tree) fn->num_counts++; } + + if (mark_catches) + { + /* We have a fake exit from this block. The other + non-fall through exits must be to catch handlers. + Mark them as catch arcs. */ + + for (arc = src_blk->succ; arc; arc = arc->succ_next) + if (!arc->fake && !arc->fall_through) + { + arc->is_throw = 1; + fn->has_catch = 1; + } + } } else if (fn && tag == GCOV_TAG_LINES) { @@ -935,11 +1165,9 @@ read_graph_file (void) if (!ix) { line_nos[ix++] = 0; - line_nos[ix++] = src->index; + line_nos[ix++] = src_idx; } line_nos[ix++] = lineno; - if (lineno >= src->num_lines) - src->num_lines = lineno + 1; } else { @@ -947,10 +1175,9 @@ read_graph_file (void) if (!file_name) break; - src = find_source (file_name); - + src_idx = find_source (file_name); line_nos[ix++] = 0; - line_nos[ix++] = src->index; + line_nos[ix++] = src_idx; } } @@ -967,72 +1194,22 @@ read_graph_file (void) { corrupt:; fnotice (stderr, "%s:corrupted\n", bbg_file_name); - gcov_close (); - return 1; + break; } } gcov_close (); - /* We built everything backwards, so nreverse them all. */ - - /* Reverse sources. Not strictly necessary, but we'll then process - them in the 'expected' order. */ - { - source_t *src, *src_p, *src_n; - - for (src_p = NULL, src = sources; src; src_p = src, src = src_n) - { - src_n = src->next; - src->next = src_p; - } - sources = src_p; - } - - /* Reverse functions. */ - { - function_t *fn, *fn_p, *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; - - /* Reverse the arcs. */ - for (ix = fn->num_blocks; ix--;) - { - arc_t *arc, *arc_p, *arc_n; + if (!fns) + fnotice (stderr, "%s:no functions found\n", bbg_file_name); - 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; + return fns; } /* Reads profiles from the count file and attach to each function. Return nonzero if fatal error. */ static int -read_count_file (void) +read_count_file (function_t *fns) { unsigned ix; unsigned version; @@ -1077,39 +1254,44 @@ read_count_file (void) unsigned length = gcov_read_unsigned (); unsigned long base = gcov_position (); - if (tag == GCOV_TAG_OBJECT_SUMMARY) - gcov_read_summary (&object_summary); - else if (tag == GCOV_TAG_PROGRAM_SUMMARY) - program_count++; - else if (tag == GCOV_TAG_FUNCTION) + if (tag == GCOV_TAG_PROGRAM_SUMMARY) { - { - unsigned ident = gcov_read_unsigned (); - struct function_info *fn_n = functions; + struct gcov_summary summary; + gcov_read_summary (&summary); + object_runs += summary.ctrs[GCOV_COUNTER_ARCS].runs; + program_count++; + } + else if (tag == GCOV_TAG_FUNCTION && !length) + ; /* placeholder */ + else if (tag == GCOV_TAG_FUNCTION && length == GCOV_TAG_FUNCTION_LENGTH) + { + unsigned ident; + struct function_info *fn_n; - /* 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) + /* Try to find the function in the list. To speed up the + search, first start from the last function found. */ + ident = gcov_read_unsigned (); + fn_n = fns; + 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) + else if (gcov_read_unsigned () != fn->lineno_checksum + || gcov_read_unsigned () != fn->cfg_checksum) { mismatch:; fnotice (stderr, "%s:profile mismatch for '%s'\n", @@ -1154,6 +1336,28 @@ solve_flow_graph (function_t *fn) block_t *valid_blocks = NULL; /* valid, but unpropagated blocks. */ block_t *invalid_blocks = NULL; /* invalid, but inferable blocks. */ + /* The arcs were built in reverse order. Fix that now. */ + for (ix = fn->num_blocks; ix--;) + { + arc_t *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; + } + if (fn->num_blocks < 2) fnotice (stderr, "%s:'%s' lacks entry and/or exit blocks\n", bbg_file_name, fn->name); @@ -1382,6 +1586,34 @@ solve_flow_graph (function_t *fn) } } +/* Mark all the blocks only reachable via an incoming catch. */ + +static void +find_exception_blocks (function_t *fn) +{ + unsigned ix; + block_t **queue = XALLOCAVEC (block_t *, fn->num_blocks); + + /* First mark all blocks as exceptional. */ + for (ix = fn->num_blocks; ix--;) + fn->blocks[ix].exceptional = 1; + + /* Now mark all the blocks reachable via non-fake edges */ + queue[0] = fn->blocks; + queue[0]->exceptional = 0; + for (ix = 1; ix;) + { + block_t *block = queue[--ix]; + const arc_t *arc; + + for (arc = block->succ; arc; arc = arc->succ_next) + if (!arc->fake && !arc->is_throw && arc->dst->exceptional) + { + arc->dst->exceptional = 0; + queue[ix++] = arc->dst; + } + } +} /* Increment totals in COVERAGE according to arc ARC. */ @@ -1450,20 +1682,25 @@ format_gcov (gcov_type top, gcov_type bottom, int dp) return buffer; } - -/* Output summary info for a function. */ +/* Summary of execution */ static void -function_summary (const coverage_t *coverage, const char *title) +executed_summary (unsigned lines, unsigned executed) { - fnotice (stdout, "%s '%s'\n", title, coverage->name); - - if (coverage->lines) + if (lines) fnotice (stdout, "Lines executed:%s of %d\n", - format_gcov (coverage->lines_executed, coverage->lines, 2), - coverage->lines); + format_gcov (executed, lines, 2), lines); else fnotice (stdout, "No executable lines\n"); +} + +/* Output summary info for a function or file. */ + +static void +function_summary (const coverage_t *coverage, const char *title) +{ + fnotice (stdout, "%s '%s'\n", title, coverage->name); + executed_summary (coverage->lines, coverage->lines_executed); if (flag_branches) { @@ -1489,88 +1726,174 @@ function_summary (const coverage_t *coverage, const char *title) } } -/* 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 +/* Canonicalize the filename NAME by canonicalizing directory + separators, eliding . components and resolving .. components + appropriately. Always returns a unique string. */ + +static char * +canonicalize_name (const char *name) +{ + /* The canonical name cannot be longer than the incoming name. */ + char *result = XNEWVEC (char, strlen (name) + 1); + const char *base = name, *probe; + char *ptr = result; + char *dd_base; + int slash = 0; + +#if HAVE_DOS_BASED_FILE_SYSTEM + if (base[0] && base[1] == ':') + { + result[0] = base[0]; + result[1] = ':'; + base += 2; + ptr += 2; + } +#endif + for (dd_base = ptr; *base; base = probe) + { + size_t len; + + for (probe = base; *probe; probe++) + if (IS_DIR_SEPARATOR (*probe)) + break; + + len = probe - base; + if (len == 1 && base[0] == '.') + /* Elide a '.' directory */ + ; + else if (len == 2 && base[0] == '.' && base[1] == '.') + { + /* '..', we can only elide it and the previous directory, if + we're not a symlink. */ + struct stat ATTRIBUTE_UNUSED buf; + + *ptr = 0; + if (dd_base == ptr +#if defined (S_ISLNK) + /* S_ISLNK is not POSIX.1-1996. */ + || stat (result, &buf) || S_ISLNK (buf.st_mode) +#endif + ) + { + /* Cannot elide, or unreadable or a symlink. */ + dd_base = ptr + 2 + slash; + goto regular; + } + while (ptr != dd_base && *ptr != '/') + ptr--; + slash = ptr != result; + } + else + { + regular: + /* Regular pathname component. */ + if (slash) + *ptr++ = '/'; + memcpy (ptr, base, len); + ptr += len; + slash = 1; + } + + for (; IS_DIR_SEPARATOR (*probe); probe++) + continue; + } + *ptr = 0; + + return result; +} + +/* Generate an output file name. INPUT_NAME is the canonicalized main + input file and SRC_NAME is the canonicalized file name. + LONG_OUTPUT_NAMES and PRESERVE_PATHS affect name generation. 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 '^'. */ + components are separated by '##'. With preserve_paths we create a + filename from all path components of the source file, replacing '/' + with '#', and .. with '^', without it we simply take the basename + component. (Remember, the canonicalized name will already have + elided '.' components and converted \\ separators.) */ static char * make_gcov_file_name (const char *input_name, const char *src_name) { - const char *cptr; - char *name; + char *ptr; + char *result; 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 : lbasename (input_name); - strcat (name, cptr ? cptr : input_name); - strcat (name, "##"); + result = XNEWVEC (char, strlen (input_name) + strlen (src_name) + 10); + + ptr = result; + ptr = mangle_name (input_name, ptr); + ptr[0] = ptr[1] = '#'; + ptr += 2; } else { - name = XNEWVEC (char, strlen (src_name) + 10); - name[0] = 0; + result = XNEWVEC (char, strlen (src_name) + 10); + ptr = result; } - /* Generate the source filename part. */ - - cptr = flag_preserve_paths ? NULL : lbasename (src_name); - strcat (name, cptr ? cptr : src_name); + ptr = mangle_name (src_name, ptr); + strcpy (ptr, ".gcov"); + + return result; +} - if (flag_preserve_paths) +static char * +mangle_name (char const *base, char *ptr) +{ + size_t len; + + /* Generate the source filename part. */ + if (!flag_preserve_paths) { - /* Convert '/' and '\' to '#', remove '/./', convert '/../' to '/^/', + base = lbasename (base); + len = strlen (base); + memcpy (ptr, base, len); + ptr += len; + } + else + { + /* Convert '/' to '#', convert '..' to '^', convert ':' to '~' on DOS based file system. */ - char *pnew = name, *pold = name; - - /* First check for leading drive separator. */ + const char *probe; - while (*pold != '\0') +#if HAVE_DOS_BASED_FILE_SYSTEM + if (base[0] && base[1] == ':') { - if (*pold == '/' || *pold == '\\') - { - *pnew++ = '#'; - pold++; - } -#if defined (HAVE_DOS_BASED_FILE_SYSTEM) - else if (*pold == ':') - { - *pnew++ = '~'; - pold++; - } + ptr[0] = base[0]; + ptr[1] = '~'; + ptr += 2; + base += 2; + } #endif - else if ((*pold == '/' && strstr (pold, "/./") == pold) - || (*pold == '\\' && strstr (pold, "\\.\\") == pold)) - pold += 3; - else if (*pold == '/' && strstr (pold, "/../") == pold) + for (; *base; base = probe) + { + size_t len; + + for (probe = base; *probe; probe++) + if (*probe == '/') + break; + len = probe - base; + if (len == 2 && base[0] == '.' && base[1] == '.') + *ptr++ = '^'; + else { - strcpy (pnew, "/^/"); - pnew += 3; - pold += 4; + memcpy (ptr, base, len); + ptr += len; } - else if (*pold == '\\' && strstr (pold, "\\..\\") == pold) + if (*probe) { - strcpy (pnew, "\\^\\"); - pnew += 3; - pold += 4; + *ptr++ = '#'; + probe++; } - else - *pnew++ = *pold++; } - - *pnew = '\0'; } - - strcat (name, ".gcov"); - return name; + + return ptr; } /* Scan through the bb_data for each line in the block, increment @@ -1598,10 +1921,7 @@ add_line_counts (coverage_t *coverage, function_t *fn) jx != block->u.line.num; jx++, encoding++) if (!*encoding) { - unsigned src_n = *++encoding; - - for (src = sources; src->index != src_n; src = src->next) - continue; + src = &sources[*++encoding]; jx++; } else @@ -1616,6 +1936,8 @@ add_line_counts (coverage_t *coverage, function_t *fn) coverage->lines_executed++; } line->exists = 1; + if (!block->exceptional) + line->unexceptional = 1; line->count += block->count; } free (block->u.line.encoding); @@ -1626,7 +1948,10 @@ add_line_counts (coverage_t *coverage, function_t *fn) /* Entry or exit block */; else if (flag_all_blocks) { - line_t *block_line = line ? line : &fn->src->lines[fn->line]; + line_t *block_line = line; + + if (!block_line) + block_line = &sources[fn->src].lines[fn->line]; block->chain = block_line->u.blocks; block_line->u.blocks = block; @@ -1835,7 +2160,6 @@ accumulate_line_counts (source_t *src) static int output_branch_count (FILE *gcov_file, int ix, const arc_t *arc) { - if (arc->is_call_non_return) { if (arc->src->count) @@ -1852,7 +2176,8 @@ output_branch_count (FILE *gcov_file, int ix, const arc_t *arc) 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)" : ""); + arc->fall_through ? " (fallthrough)" + : arc->is_throw ? " (throw)" : ""); else fnotice (gcov_file, "branch %2d never executed\n", ix); } @@ -1870,6 +2195,44 @@ output_branch_count (FILE *gcov_file, int ix, const arc_t *arc) } +static const char * +read_line (FILE *file) +{ + static char *string; + static size_t string_len; + size_t pos = 0; + char *ptr; + + if (!string_len) + { + string_len = 200; + string = XNEWVEC (char, string_len); + } + + while ((ptr = fgets (string + pos, string_len - pos, file))) + { + size_t len = strlen (string + pos); + + if (string[pos + len - 1] == '\n') + { + string[pos + len - 1] = 0; + return string; + } + pos += len; + ptr = XNEWVEC (char, string_len * 2); + if (ptr) + { + memcpy (ptr, string, pos); + string = ptr; + string_len += 2; + } + else + pos = 0; + } + + return pos ? string : NULL; +} + /* 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. */ @@ -1880,25 +2243,23 @@ 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. */ + const char *retval = ""; /* status of source file reading. */ function_t *fn = NULL; - fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, src->name); + fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, src->coverage.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:Runs:%u\n", "-", 0, object_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); + fnotice (stderr, "Cannot open source file %s\n", src->name); retval = NULL; } else if (src->file_time == 0) @@ -1929,30 +2290,20 @@ output_lines (FILE *gcov_file, const source_t *src) fprintf (gcov_file, "\n"); } + if (retval) + retval = read_line (source_file); + /* 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); + executed, print '#####' or '=====' 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:%s\n", + !line->exists ? "-" : line->count + ? format_gcov (line->count, 0, -1) + : line->unexceptional ? "#####" : "=====", line_num, + retval ? retval : "/*EOF*/"); if (flag_all_blocks) { @@ -1965,8 +2316,9 @@ output_lines (FILE *gcov_file, const source_t *src) { if (!block->is_call_return) fprintf (gcov_file, "%9s:%5u-block %2d\n", - !line->exists ? "-" : !block->count ? "$$$$$" - : format_gcov (block->count, 0, -1), + !line->exists ? "-" : block->count + ? format_gcov (block->count, 0, -1) + : block->exceptional ? "%%%%%" : "$$$$$", line_num, ix++); if (flag_branches) for (arc = block->succ; arc; arc = arc->succ_next) @@ -1987,18 +2339,8 @@ output_lines (FILE *gcov_file, const source_t *src) 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); - } - } + for (; (retval = read_line (source_file)); line_num++) + fprintf (gcov_file, "%9s:%5u:%s\n", "-", line_num, retval); } if (source_file) |