OSDN Git Service

PR c++/50811
[pf3gnuchains/gcc-fork.git] / gcc / gcov.c
index 34ad98d..94a1c35 100644 (file)
@@ -1,7 +1,8 @@
 /* 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 Free Software Foundation, Inc.
+   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
+   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>
@@ -36,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>
@@ -53,6 +55,13 @@ 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.  */
 
+/* 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.  */
+
 /* This is the size of the buffer used to read in source file lines.  */
 
 #define STRING_SIZE 200
@@ -82,7 +91,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.  */
@@ -160,7 +169,8 @@ typedef struct function_info
   /* Name of function.  */
   char *name;
   unsigned ident;
-  unsigned checksum;
+  unsigned lineno_checksum;
+  unsigned cfg_checksum;
 
   /* Array of basic blocks.  */
   block_t *blocks;
@@ -296,6 +306,11 @@ static int flag_unconditional = 0;
 
 static int flag_gcov_file = 1;
 
+/* Output progress indication if this is true.  This is off by default
+   and can be turned on by the -d option.  */
+
+static int flag_display_progress = 0;
+
 /* For included files, make the gcov output file name include the name
    of the input source file.  For example, if x.h is included in a.c,
    then the output file name is a.c##x.h.gcov instead of x.h.gcov.  */
@@ -328,7 +343,6 @@ 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;
@@ -354,12 +368,26 @@ int
 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);
+
   argno = process_args (argc, argv);
   if (optind == argc)
     print_usage (true);
@@ -367,8 +395,15 @@ main (int argc, char **argv)
   if (argc - argno > 1)
     multiple_files = 1;
 
+  first_arg = argno;
+  
   for (; argno != argc; argno++)
-    process_file (argv[argno]);
+    {
+      if (flag_display_progress)
+        printf("Processing file %d out of %d\n",  
+               argno - first_arg + 1, argc - first_arg);
+      process_file (argv[argno]);
+    }
 
   generate_results (multiple_files ? NULL : argv[argc - 1]);
 
@@ -376,16 +411,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);
-}
 \f
 /* Print a usage message and exit.  If ERROR_P is nonzero, this is an error,
    otherwise the output of --help.  */
@@ -411,6 +436,7 @@ print_usage (int error_p)
   fnotice (file, "  -o, --object-directory DIR|FILE Search for object files in DIR or called FILE\n");
   fnotice (file, "  -p, --preserve-paths            Preserve all pathname components\n");
   fnotice (file, "  -u, --unconditional-branches    Show unconditional branch counts too\n");
+  fnotice (file, "  -d, --display-progress          Display progress information\n");
   fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
           bug_report_url);
   exit (status);
@@ -422,7 +448,7 @@ static void
 print_version (void)
 {
   fnotice (stdout, "gcov %s%s\n", pkgversion_string, version_string);
-  fprintf (stdout, "Copyright %s 2007 Free Software Foundation, Inc.\n",
+  fprintf (stdout, "Copyright %s 2011 Free Software Foundation, Inc.\n",
           _("(C)"));
   fnotice (stdout,
           _("This is free software; see the source for copying conditions.\n"
@@ -445,6 +471,7 @@ static const struct option options[] =
   { "object-directory",     required_argument, NULL, 'o' },
   { "object-file",          required_argument, NULL, 'o' },
   { "unconditional-branches", no_argument,     NULL, 'u' },
+  { "display-progress",     no_argument,       NULL, 'd' },
   { 0, 0, 0, 0 }
 };
 
@@ -455,7 +482,7 @@ process_args (int argc, char **argv)
 {
   int opt;
 
-  while ((opt = getopt_long (argc, argv, "abcfhlno:puv", options, NULL)) != -1)
+  while ((opt = getopt_long (argc, argv, "abcdfhlno:puv", options, NULL)) != -1)
     {
       switch (opt)
        {
@@ -489,6 +516,9 @@ process_args (int argc, char **argv)
        case 'u':
          flag_unconditional = 1;
          break;
+        case 'd':
+          flag_display_progress = 1;
+          break;
        case 'v':
          print_version ();
          /* print_version will exit.  */
@@ -622,13 +652,12 @@ release_structures (void)
     }
 }
 
-/* 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)
@@ -639,10 +668,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;
@@ -657,21 +684,21 @@ 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
     {
       name = XNEWVEC (char, length + 1);
-      name[0] = 0;
-      base = 1;
+      strcpy (name, file_name);
+      base = 0;
     }
 
   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.  */
@@ -680,7 +707,7 @@ create_file_names (const char *file_name)
     *cptr = 0;
 
   length = strlen (name);
-  
+
   bbg_file_name = XNEWVEC (char, length + strlen (GCOV_NOTE_SUFFIX) + 1);
   strcpy (bbg_file_name, name);
   strcpy (bbg_file_name + length, GCOV_NOTE_SUFFIX);
@@ -706,7 +733,7 @@ find_source (const char *file_name)
     file_name = "<unknown>";
 
   for (src = sources; src; src = src->next)
-    if (!strcmp (file_name, src->name))
+    if (!filename_cmp (file_name, src->name))
       break;
 
   if (!src)
@@ -717,7 +744,7 @@ find_source (const char *file_name)
       src->index = source_index++;
       src->next = sources;
       sources = src;
-      
+
       if (!stat (file_name, &status))
        src->file_time = status.st_mtime;
     }
@@ -787,12 +814,14 @@ read_graph_file (void)
       if (tag == GCOV_TAG_FUNCTION)
        {
          char *function_name;
-         unsigned ident, checksum, lineno;
+         unsigned ident, lineno;
+         unsigned lineno_checksum, cfg_checksum;
          source_t *src;
          function_t *probe, *prev;
 
          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 ());
          lineno = gcov_read_unsigned ();
@@ -800,7 +829,8 @@ read_graph_file (void)
          fn = XCNEW (function_t);
          fn->name = function_name;
          fn->ident = ident;
-         fn->checksum = checksum;
+         fn->lineno_checksum = lineno_checksum;
+         fn->cfg_checksum = cfg_checksum;
          fn->src = src;
          fn->line = lineno;
 
@@ -1039,7 +1069,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);
     }
@@ -1061,31 +1091,34 @@ 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;
 
-         /* 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);
+           /* 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)
            ;
-         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",
@@ -1478,7 +1511,7 @@ 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;
+  const char *cptr;
   char *name;
 
   if (flag_long_names && input_name && strcmp (src_name, input_name))
@@ -1486,8 +1519,8 @@ make_gcov_file_name (const char *input_name, const char *src_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
@@ -1497,39 +1530,61 @@ make_gcov_file_name (const char *input_name, const char *src_name)
     }
 
   /* 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 defined (HAVE_DOS_BASED_FILE_SYSTEM)
+         if (*pold == ':')
            {
-             /* Remove '.' */
-             shift = 2;
+             *pnew++ = '~';
+             pold++;
            }
-         else if (prev + 2 == cptr && prev[0] == '.' && prev[1] == '.')
+         else
+#endif
+         if ((*pold == '/'
+                   && (strstr (pold, "/./") == pold
+                       || strstr (pold, "/.\\") == pold))
+                  || (*pold == '\\'
+                      && (strstr (pold, "\\.\\") == pold
+                          || strstr (pold, "\\./") == pold)))
+             pold += 3;
+         else if (*pold == '/'
+                  && (strstr (pold, "/../") == pold
+                      || strstr (pold, "/..\\") == pold))
            {
-             /* Convert '..' */
-             shift = 1;
-             prev[1] = '^';
+             strcpy (pnew, "#^#");
+             pnew += 3;
+             pold += 4;
            }
-         else
-           *cptr++ = '#';
-         if (shift)
+         else if (*pold == '\\'
+                  && (strstr (pold, "\\..\\") == pold
+                      || strstr (pold, "\\../") == pold))
+           {
+             strcpy (pnew, "#^#");
+             pnew += 3;
+             pold += 4;
+           }
+         else if (*pold == '/' || *pold == '\\')
            {
-             cptr = prev;
-             do
-               prev[0] = prev[shift];
-             while (*prev++);
+             *pnew++ = '#';
+             pold++;
            }
+         else
+           *pnew++ = *pold++;
        }
+
+      *pnew = '\0';
     }
 
   strcat (name, ".gcov");
@@ -1877,11 +1932,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));