X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fgcov.c;h=1c1403d927ddd4cc14dee5afdef22da8ef58e3dc;hb=dd90e580a01914c35fe4d2538bf53aa14fc9bcce;hp=adce8c8fb8fd5da5c20b2ee5834a4625b69843ad;hpb=67ce556b47830dd825524e8370969b814c355216;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/gcov.c b/gcc/gcov.c index adce8c8fb8f..1c1403d927d 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, 2003, 2004, 2005 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, 2010 + 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, 51 Franklin Street, Fifth Floor, -Boston, MA 02110-1301, 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 02110-1301, 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. */ @@ -54,7 +45,7 @@ Boston, MA 02110-1301, 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. */ @@ -92,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. */ @@ -234,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; @@ -253,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; @@ -277,6 +274,17 @@ static unsigned bbg_stamp; 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; @@ -326,6 +334,7 @@ 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); @@ -352,16 +361,22 @@ main (int argc, char **argv) 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; } @@ -385,7 +400,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]... 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"); @@ -410,8 +425,8 @@ print_usage (int error_p) static void print_version (void) { - fnotice (stdout, "gcov (GCC) %s\n", version_string); - fprintf (stdout, "Copyright %s 2004 Free Software Foundation, Inc.\n", + fnotice (stdout, "gcov %s%s\n", pkgversion_string, version_string); + fprintf (stdout, "Copyright %s 2010 Free Software Foundation, Inc.\n", _("(C)")); fnotice (stdout, _("This is free software; see the source for copying conditions.\n" @@ -495,8 +510,14 @@ process_args (int argc, char **argv) static void 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 ()) @@ -511,10 +532,21 @@ process_file (const char *file_name) 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 = 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; @@ -565,12 +597,6 @@ 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; - bbg_stamp = 0; - while ((src = sources)) { sources = src->next; @@ -616,22 +642,31 @@ create_file_names (const char *file_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; } @@ -639,8 +674,8 @@ create_file_names (const char *file_name) 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. */ @@ -649,15 +684,16 @@ create_file_names (const char *file_name) *cptr = 0; length = strlen (name); - - bbg_file_name = xmalloc (length + strlen (GCOV_NOTE_SUFFIX) + 1); + + 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 = 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; } @@ -668,20 +704,42 @@ static source_t * 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; + 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; + } - src = 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; + 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; } @@ -694,6 +752,7 @@ 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; @@ -742,7 +801,7 @@ read_graph_file (void) src = find_source (gcov_read_string ()); lineno = gcov_read_unsigned (); - fn = xcalloc (1, sizeof (function_t)); + fn = XCNEW (function_t); fn->name = function_name; fn->ident = ident; fn->checksum = checksum; @@ -778,7 +837,7 @@ read_graph_file (void) unsigned ix, num_blocks = GCOV_TAG_BLOCKS_NUM (length); fn->num_blocks = num_blocks; - fn->blocks = 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 (); } @@ -799,7 +858,7 @@ read_graph_file (void) if (dest >= fn->num_blocks) goto corrupt; - arc = xcalloc (1, sizeof (arc_t)); + arc = XCNEW (arc_t); arc->dst = &fn->blocks[dest]; arc->src = &fn->blocks[src]; @@ -844,7 +903,7 @@ read_graph_file (void) else if (fn && tag == GCOV_TAG_LINES) { unsigned blockno = gcov_read_unsigned (); - unsigned *line_nos = xcalloc (length - 1, sizeof (unsigned)); + unsigned *line_nos = XCNEWVEC (unsigned, length - 1); if (blockno >= fn->num_blocks || fn->blocks[blockno].u.line.encoding) goto corrupt; @@ -915,7 +974,9 @@ read_graph_file (void) { 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; @@ -963,8 +1024,10 @@ read_count_file (void) 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_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC)) { @@ -980,7 +1043,7 @@ read_count_file (void) GCOV_UNSIGNED2STRING (v, version); GCOV_UNSIGNED2STRING (e, GCOV_VERSION); - + fnotice (stderr, "%s:version '%.4s', prefer version '%.4s'\n", da_file_name, v, e); } @@ -1002,24 +1065,29 @@ read_count_file (void) program_count++; else if (tag == GCOV_TAG_FUNCTION) { - unsigned ident = gcov_read_unsigned (); - struct function_info *fn_n = functions; + { + unsigned ident = gcov_read_unsigned (); + struct function_info *fn_n = functions; - 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); + /* 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->ident == ident) - break; - } + } + } if (!fn) ; @@ -1037,7 +1105,7 @@ read_count_file (void) goto mismatch; if (!fn->counts) - fn->counts = 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 (); @@ -1416,52 +1484,71 @@ function_summary (const coverage_t *coverage, const char *title) static char * make_gcov_file_name (const char *input_name, const char *src_name) { - char *cptr; - char *name = xmalloc (strlen (src_name) + strlen (input_name) + 10); + const char *cptr; + char *name; - name[0] = 0; - if (flag_long_names && strcmp (src_name, input_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; + /* Convert '/' and '\' to '#', remove '/./', convert '/../' to '/^/', + convert ':' to '~' on DOS based file system. */ + char *pnew = name, *pold = name; - for (cptr = name; (cptr = strchr ((prev = cptr), '/'));) - { - unsigned shift = 0; + /* First check for leading drive separator. */ - if (prev + 1 == cptr && prev[0] == '.') + while (*pold != '\0') + { + if (*pold == '/' || *pold == '\\') { - /* Remove '.' */ - shift = 2; + *pnew++ = '#'; + pold++; } - else if (prev + 2 == cptr && prev[0] == '.' && prev[1] == '.') +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + else if (*pold == ':') { - /* Convert '..' */ - shift = 1; - prev[1] = '^'; + *pnew++ = '~'; + pold++; } - else - *cptr++ = '#'; - if (shift) +#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) { - cptr = prev; - do - prev[0] = prev[shift]; - while (*prev++); + strcpy (pnew, "\\^\\"); + pnew += 3; + pold += 4; } + else + *pnew++ = *pold++; } + + *pnew = '\0'; } strcat (name, ".gcov"); @@ -1780,10 +1867,14 @@ output_lines (FILE *gcov_file, const source_t *src) 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"); @@ -1792,19 +1883,8 @@ output_lines (FILE *gcov_file, const source_t *src) 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; @@ -1816,11 +1896,11 @@ output_lines (FILE *gcov_file, const source_t *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; - + fprintf (gcov_file, "function %s", fn->name); fprintf (gcov_file, " called %s", format_gcov (fn->blocks[0].count, 0, -1));