OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / gcov.c
index 78f0ad4..a92ce91 100644 (file)
@@ -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, 2006 Free Software Foundation, Inc.
+   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+   Free Software Foundation, Inc.
    Contributed by James E. Wilson of Cygnus Support.
    Mangled by Bob Manson of Cygnus Support.
    Mangled further by Nathan Sidwell <nathan@codesourcery.com>
 
 Gcov is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-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
+<http://www.gnu.org/licenses/>.  */
 
 /* ??? 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 2006 Free Software Foundation, Inc.\n",
+  fnotice (stdout, "gcov %s%s\n", pkgversion_string, version_string);
+  fprintf (stdout, "Copyright %s 2008 Free Software Foundation, Inc.\n",
           _("(C)"));
   fnotice (stdout,
           _("This is free software; see the source for copying conditions.\n"
@@ -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,8 +532,19 @@ 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 = XCNEWVEC (line_t, src->num_lines);
   for (fn = functions; fn; fn = fn->next)
@@ -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,6 +642,15 @@ 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;
@@ -626,7 +661,7 @@ create_file_names (const char *file_name)
 
       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
@@ -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.  */
@@ -658,6 +693,7 @@ create_file_names (const char *file_name)
   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 = "<unknown>";
 
   for (src = sources; src; src = src->next)
     if (!strcmp (file_name, src->name))
-      return src;
+      break;
 
-  src = XCNEW (source_t);
-  src->name = xstrdup (file_name);
-  src->coverage.name = src->name;
-  src->index = sources ? sources->index + 1 : 1;
-  src->next = sources;
-  sources = src;
+  if (!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;
 }
@@ -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;
@@ -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))
     {
@@ -1005,6 +1068,9 @@ read_count_file (void)
          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)
@@ -1416,52 +1482,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 = XNEWVEC (char, 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 +1865,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 +1881,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;