[gcov] Cope with subdirectories

Nathan Sidwell nathan@codesourcery.com
Mon Aug 5 07:50:00 GMT 2002


Hi,
I've installed this patch which makes gcov cope with object
files in subdirectories. It also improves the rounding of
percentages so that 0% means exactly 0 and 100% means exactly
100 - thus you can distinguish never/sometimes/always.

tested on i686-pc-linux-gnu

nathan
-- 
Dr Nathan Sidwell   ::   http://www.codesourcery.com   ::   CodeSourcery LLC
         'But that's a lie.' - 'Yes it is. What's your point?'
nathan@codesourcery.com : http://www.cs.bris.ac.uk/~nathan/ : nathan@acm.org
-------------- next part --------------
2002-07-05  Nathan Sidwell  <nathan@codesourcery.com>

	* gcov.c (bb_file_time): New static variable.
	(object_directory): May also be object file.
	(preserve_paths): New static variable.
	(print_usage): Adjust.
	(options): Adjust.
	(process_args): Adjust.
	(open_files): Simplify. Cope when OBJECT_DIRECTORY is an object
	file. Find modification date on bb file.
	(read_profile): Don't rewind a NULL file.
	(format_hwint): New static function.
	(function_summary): Use format_hwint.
	(output_data): SOURCE_FILE_NAME is never relative to
	OBJECT_DIRECTORY. Use format_hwint. Adjust gcov file name
	mangling. Adjust output format to make it more machine readable.
	* doc/gcov.texi: Document & clarify semantics.
	
Index: gcov.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/gcov.c,v
retrieving revision 1.43
diff -c -3 -p -r1.43 gcov.c
*** gcov.c	4 Jun 2002 11:30:27 -0000	1.43
--- gcov.c	4 Aug 2002 16:55:17 -0000
*************** struct bb_info_list {
*** 160,165 ****
--- 160,169 ----
  
  static struct bb_info_list *bb_graph_list = 0;
  
+ /* Modification time of data files. */
+ 
+ static time_t bb_file_time;
+ 
  /* Name and file pointer of the input file for the basic block graph.  */
  
  static char *bbg_file_name;
*************** static int output_long_names = 0;
*** 212,222 ****
  
  static int output_function_summary = 0;
  
! /* Object directory file prefix.  This is the directory where .bb and .bbg
!    files are looked for, if non-zero.  */
  
  static char *object_directory = 0;
  
  /* Output the number of times a branch was taken as opposed to the percentage
     of times it was taken.  Turned on by the -c option */
  
--- 216,230 ----
  
  static int output_function_summary = 0;
  
! /* Object directory file prefix.  This is the directory/file
!    where .bb and .bbg files are looked for, if non-zero.  */
  
  static char *object_directory = 0;
  
+ /* Preserve all pathname components. Needed when object files and
+    source files are in subdirectories.  */
+ static int preserve_paths = 0;
+ 
  /* Output the number of times a branch was taken as opposed to the percentage
     of times it was taken.  Turned on by the -c option */
  
*************** static void solve_program_flow_graph PAR
*** 238,243 ****
--- 246,252 ----
  static void calculate_branch_probs PARAMS ((struct bb_info_list *, int,
  					    struct arcdata **, int));
  static void function_summary PARAMS ((void));
+ static const char *format_hwint PARAMS ((HOST_WIDEST_INT, HOST_WIDEST_INT, int));
  
  extern int main PARAMS ((int, char **));
  
*************** print_usage (error_p)
*** 304,310 ****
    fnotice (file, "  -l, --long-file-names           Use long output file names for included\n\
                                      source files\n");
    fnotice (file, "  -f, --function-summaries        Output summaries for each function\n");
!   fnotice (file, "  -o, --object-directory OBJDIR   Search for object files in OBJDIR\n");
    fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
  	   GCCBUGURL);
    exit (status);
--- 313,320 ----
    fnotice (file, "  -l, --long-file-names           Use long output file names for included\n\
                                      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, "  -p, --preserve-paths            Preserve all pathname components\n");
    fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
  	   GCCBUGURL);
    exit (status);
*************** static const struct option options[] =
*** 332,338 ****
    { "no-output",            no_argument,       NULL, 'n' },
    { "long-file-names",      no_argument,       NULL, 'l' },
    { "function-summaries",   no_argument,       NULL, 'f' },
!   { "object-directory",     required_argument, NULL, 'o' }
  };
  
  /* Parse the command line.  */
--- 342,350 ----
    { "no-output",            no_argument,       NULL, 'n' },
    { "long-file-names",      no_argument,       NULL, 'l' },
    { "function-summaries",   no_argument,       NULL, 'f' },
!   { "preserve-paths",       no_argument,       NULL, 'p' },
!   { "object-directory",     required_argument, NULL, 'o' },
!   { "object-file",          required_argument, NULL, 'o' },
  };
  
  /* Parse the command line.  */
*************** process_args (argc, argv)
*** 344,350 ****
  {
    int opt;
  
!   while ((opt = getopt_long (argc, argv, "hvbclnfo:", options, NULL)) != -1)
      {
        switch (opt)
  	{
--- 356,362 ----
  {
    int opt;
  
!   while ((opt = getopt_long (argc, argv, "hvbclnfo:p", options, NULL)) != -1)
      {
        switch (opt)
  	{
*************** process_args (argc, argv)
*** 372,377 ****
--- 384,392 ----
  	case 'o':
  	  object_directory = optarg;
  	  break;
+ 	case 'p':
+ 	  preserve_paths = 1;
+ 	  break;
  	default:
  	  print_usage (true);
  	  /* print_usage will exit.  */
*************** process_args (argc, argv)
*** 385,461 ****
  }
  
  
! /* Find and open the .bb, .da, and .bbg files.  */
  
  static void
  open_files ()
  {
-   int count, objdir_count;
    char *cptr;
! 
!   /* Determine the names of the .bb, .bbg, and .da files.  Strip off the
!      extension, if any, and append the new extensions.  */
!   count = strlen (input_file_name);
!   if (object_directory)
!     objdir_count = strlen (object_directory);
!   else
!     objdir_count = 0;
! 
!   da_file_name = xmalloc (count + objdir_count + 4);
!   bb_file_name = xmalloc (count + objdir_count + 4);
!   bbg_file_name = xmalloc (count + objdir_count + 5);
! 
!   if (object_directory)
      {
!       strcpy (da_file_name, object_directory);
!       strcpy (bb_file_name, object_directory);
!       strcpy (bbg_file_name, object_directory);
  
!       if (object_directory[objdir_count - 1] != '/')
! 	{
! 	  strcat (da_file_name, "/");
! 	  strcat (bb_file_name, "/");
! 	  strcat (bbg_file_name, "/");
! 	}
! 
!       cptr = strrchr (input_file_name, '/');
!       if (cptr)
! 	{
! 	  strcat (da_file_name, cptr + 1);
! 	  strcat (bb_file_name, cptr + 1);
! 	  strcat (bbg_file_name, cptr + 1);
! 	}
!       else
! 	{
! 	  strcat (da_file_name, input_file_name);
! 	  strcat (bb_file_name, input_file_name);
! 	  strcat (bbg_file_name, input_file_name);
! 	}
      }
    else
      {
!       strcpy (da_file_name, input_file_name);
!       strcpy (bb_file_name, input_file_name);
!       strcpy (bbg_file_name, input_file_name);
      }
  
!   cptr = strrchr (bb_file_name, '.');
!   if (cptr)
!     strcpy (cptr, ".bb");
!   else
!     strcat (bb_file_name, ".bb");
! 
!   cptr = strrchr (da_file_name, '.');
!   if (cptr)
!     strcpy (cptr, ".da");
!   else
!     strcat (da_file_name, ".da");
! 
!   cptr = strrchr (bbg_file_name, '.');
    if (cptr)
!     strcpy (cptr, ".bbg");
!   else
!     strcat (bbg_file_name, ".bbg");
  
    bb_file = fopen (bb_file_name, "rb");
    if (bb_file == NULL)
--- 400,465 ----
  }
  
  
! /* Find and open the .bb, .da, and .bbg files. If OBJECT_DIRECTORY is
!    not specified, these are looked for in the current directory, and
!    named from the basename of the input_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 input_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
  open_files ()
  {
    char *cptr;
!   char *name;
!   int length = strlen (input_file_name);
!   int base;
!   
!   if (object_directory && object_directory[0])
      {
!       struct stat status;
  
!       length += strlen (object_directory) + 2;
!       name = xmalloc (length);
!       name[0] = 0;
!       
!       base = !stat (object_directory, &status) && S_ISDIR (status.st_mode);
!       strcat (name, object_directory);
!       if (base && name[strlen (name) - 1] != '/')
! 	strcat (name, "/");
      }
    else
      {
!       name = xmalloc (length + 1);
!       name[0] = 0;
!       base = 1;
      }
+   
+   if (base)
+     {
+       /* Append source file name */
+       cptr = strrchr (input_file_name, '/');
+       cptr = cptr ? cptr + 1 : input_file_name;
  
!       strcat (name, cptr);
!     }
!   /* Remove the extension. */
!   cptr = strrchr (name, '.');
    if (cptr)
!     *cptr = 0;
!   
!   length = strlen (name);
!   da_file_name = xmalloc (length + 4);
!   bb_file_name = xmalloc (length + 4);
!   bbg_file_name = xmalloc (length + 5);
! 
!   strcpy (da_file_name, name);
!   strcpy (bb_file_name, name);
!   strcpy (bbg_file_name, name);
!   strcpy (da_file_name + length, ".da");
!   strcpy (bb_file_name + length, ".bb");
!   strcpy (bbg_file_name + length, ".bbg");
  
    bb_file = fopen (bb_file_name, "rb");
    if (bb_file == NULL)
*************** open_files ()
*** 464,469 ****
--- 468,488 ----
        exit (FATAL_EXIT_CODE);
      }
  
+   bbg_file = fopen (bbg_file_name, "rb");
+   if (bbg_file == NULL)
+     {
+       fnotice (stderr, "Could not open program flow graph file %s.\n",
+ 	       bbg_file_name);
+       exit (FATAL_EXIT_CODE);
+     }
+   
+   {
+     struct stat status;
+ 
+     if (!fstat (fileno (bb_file), &status))
+       bb_file_time = status.st_mtime;
+   }
+   
    /* If none of the functions in the file were executed, then there won't
       be a .da file.  Just assume that all counts are zero in this case.  */
    da_file = fopen (da_file_name, "rb");
*************** open_files ()
*** 473,486 ****
        fnotice (stderr, "Assuming that all execution counts are zero.\n");
      }
  
-   bbg_file = fopen (bbg_file_name, "rb");
-   if (bbg_file == NULL)
-     {
-       fnotice (stderr, "Could not open program flow graph file %s.\n",
- 	       bbg_file_name);
-       exit (FATAL_EXIT_CODE);
-     }
- 
    /* Check for empty .bbg file.  This indicates that there is no executable
       code in this source file.  */
    /* Set the EOF condition if at the end of file.  */
--- 492,497 ----
*************** read_profile (function_name, cfg_checksu
*** 554,560 ****
    int function_name_buffer_len;
  
    profile = xmalloc (sizeof (gcov_type) * instr_arcs);
-   rewind (da_file);
    function_name_buffer_len = strlen (function_name) + 1;
    function_name_buffer = xmalloc (function_name_buffer_len + 1);
  
--- 565,570 ----
*************** read_profile (function_name, cfg_checksu
*** 564,569 ****
--- 574,580 ----
    if (!da_file)
      return profile;
  
+   rewind (da_file);
    while (1)
      {
        long magic, extra_bytes;
*************** calculate_branch_probs (current_graph, b
*** 1100,1114 ****
      }
  }
  
  /* Output summary info for a function.  */
  
  static void
  function_summary ()
  {
    if (function_source_lines)
!     fnotice (stdout, "%6.2f%% of %d source lines executed in function %s\n",
! 	     (((double) function_source_lines_executed / function_source_lines)
! 	      * 100), function_source_lines, function_name);
    else
      fnotice (stdout, "No executable source lines in function %s\n",
  	     function_name);
--- 1111,1174 ----
      }
  }
  
+ /* Format a HOST_WIDE_INT as either a percent ratio, or absolute
+    count.  If dp >= 0, format TOP/BOTTOM * 100 to DP decimal places.
+    If DP is zero, no decimal point is printed. Only print 100% when
+    TOP==BOTTOM and only print 0% when TOP=0.  If dp < 0, then simply
+    format TOP.  Return pointer to a static string.  */
+ 
+ static char const *
+ format_hwint (top, bottom, dp)
+      HOST_WIDEST_INT top, bottom;
+      int dp;
+ {
+   static char buffer[20];
+   
+   if (dp >= 0)
+     {
+       float ratio = bottom ? (float)top / bottom : 0;
+       int ix;
+       unsigned limit = 100;
+       unsigned percent;
+   
+       for (ix = dp; ix--; )
+ 	limit *= 10;
+       
+       percent = (unsigned) (ratio * limit);
+       if (!percent && top)
+ 	percent = 1;
+       else if (percent == limit && top != bottom)
+ 	percent = limit - 1;
+       ix = sprintf (buffer, "%.*u%%", dp + 1, percent);
+       if (dp)
+ 	{
+ 	  dp++;
+ 	  do
+ 	    {
+ 	      buffer[ix+1] = buffer[ix];
+ 	      ix--;
+ 	    }
+ 	  while (dp--);
+ 	  buffer[ix + 1] = '.';
+ 	}
+     }
+   else
+     sprintf (buffer, HOST_WIDEST_INT_PRINT_DEC, top);
+   
+   return buffer;
+ }
+ 
+ 
  /* Output summary info for a function.  */
  
  static void
  function_summary ()
  {
    if (function_source_lines)
!     fnotice (stdout, "%s of %d source lines executed in function %s\n",
! 	     format_hwint (function_source_lines_executed,
! 			   function_source_lines, 2),
! 	     function_source_lines, function_name);
    else
      fnotice (stdout, "No executable source lines in function %s\n",
  	     function_name);
*************** function_summary ()
*** 1117,1136 ****
      {
        if (function_branches)
  	{
! 	  fnotice (stdout, "%6.2f%% of %d branches executed in function %s\n",
! 		   (((double) function_branches_executed / function_branches)
! 		    * 100), function_branches, function_name);
  	  fnotice (stdout,
! 		"%6.2f%% of %d branches taken at least once in function %s\n",
! 		   (((double) function_branches_taken / function_branches)
! 		    * 100), function_branches, function_name);
  	}
        else
  	fnotice (stdout, "No branches in function %s\n", function_name);
        if (function_calls)
! 	fnotice (stdout, "%6.2f%% of %d calls executed in function %s\n",
! 		 (((double) function_calls_executed / function_calls)
! 		  * 100), function_calls, function_name);
        else
  	fnotice (stdout, "No calls in function %s\n", function_name);
      }
--- 1177,1199 ----
      {
        if (function_branches)
  	{
! 	  fnotice (stdout, "%s of %d branches executed in function %s\n",
! 		   format_hwint (function_branches_executed,
! 				 function_branches, 2),
! 		   function_branches, function_name);
  	  fnotice (stdout,
! 		"%s of %d branches taken at least once in function %s\n",
! 		   format_hwint (function_branches_taken,
! 				 function_branches, 2),
! 		   function_branches, function_name);
  	}
        else
  	fnotice (stdout, "No branches in function %s\n", function_name);
        if (function_calls)
! 	fnotice (stdout, "%s of %d calls executed in function %s\n",
! 		 format_hwint (function_calls_executed,
! 			       function_calls, 2),
! 		 function_calls, function_name);
        else
  	fnotice (stdout, "No calls in function %s\n", function_name);
      }
*************** output_data ()
*** 1185,1205 ****
  
    for (s_ptr = sources; s_ptr; s_ptr = s_ptr->next)
      {
!       /* If this is a relative file name, and an object directory has been
! 	 specified, then make it relative to the object directory name.  */
!       if (! IS_ABSOLUTE_PATHNAME (s_ptr->name)
! 	  && object_directory != 0
! 	  && *object_directory != '\0')
! 	{
! 	  int objdir_count = strlen (object_directory);
! 	  source_file_name = xmalloc (objdir_count + strlen (s_ptr->name) + 2);
! 	  strcpy (source_file_name, object_directory);
! 	  if (object_directory[objdir_count - 1] != '/')
! 	    source_file_name[objdir_count++] = '/';
! 	  strcpy (source_file_name + objdir_count, s_ptr->name);
! 	}
!       else
! 	source_file_name = s_ptr->name;
  
        line_counts = (gcov_type *) xcalloc (sizeof (gcov_type), s_ptr->maxlineno);
        line_exists = xcalloc (1, s_ptr->maxlineno);
--- 1248,1254 ----
  
    for (s_ptr = sources; s_ptr; s_ptr = s_ptr->next)
      {
!       source_file_name = s_ptr->name;
  
        line_counts = (gcov_type *) xcalloc (sizeof (gcov_type), s_ptr->maxlineno);
        line_exists = xcalloc (1, s_ptr->maxlineno);
*************** output_data ()
*** 1376,1384 ****
  
        if (total_source_lines)
  	fnotice (stdout,
! 		 "%6.2f%% of %d source lines executed in file %s\n",
! 		 (((double) total_source_lines_executed / total_source_lines)
! 		  * 100), total_source_lines, source_file_name);
        else
  	fnotice (stdout, "No executable source lines in file %s\n",
  		 source_file_name);
--- 1425,1434 ----
  
        if (total_source_lines)
  	fnotice (stdout,
! 		 "%s of %d source lines executed in file %s\n",
! 		 format_hwint (total_source_lines_executed,
! 			       total_source_lines, 2),
! 		 total_source_lines, source_file_name);
        else
  	fnotice (stdout, "No executable source lines in file %s\n",
  		 source_file_name);
*************** output_data ()
*** 1387,1406 ****
  	{
  	  if (total_branches)
  	    {
! 	      fnotice (stdout, "%6.2f%% of %d branches executed in file %s\n",
! 		       (((double) total_branches_executed / total_branches)
! 			* 100), total_branches, source_file_name);
  	      fnotice (stdout,
! 		    "%6.2f%% of %d branches taken at least once in file %s\n",
! 		       (((double) total_branches_taken / total_branches)
! 			* 100), total_branches, source_file_name);
  	    }
  	  else
  	    fnotice (stdout, "No branches in file %s\n", source_file_name);
  	  if (total_calls)
! 	    fnotice (stdout, "%6.2f%% of %d calls executed in file %s\n",
! 		     (((double) total_calls_executed / total_calls)
! 		      * 100), total_calls, source_file_name);
  	  else
  	    fnotice (stdout, "No calls in file %s\n", source_file_name);
  	}
--- 1437,1458 ----
  	{
  	  if (total_branches)
  	    {
! 	      fnotice (stdout, "%s of %d branches executed in file %s\n",
! 		       format_hwint (total_branches_executed,
! 				     total_branches, 2),
! 		       total_branches, source_file_name);
  	      fnotice (stdout,
! 		       "%s of %d branches taken at least once in file %s\n",
! 		       format_hwint (total_branches_taken,
! 				     total_branches, 2),
! 		       total_branches, source_file_name);
  	    }
  	  else
  	    fnotice (stdout, "No branches in file %s\n", source_file_name);
  	  if (total_calls)
! 	    fnotice (stdout, "%s of %d calls executed in file %s\n",
! 		     format_hwint (total_calls_executed, total_calls, 2),
! 		     total_calls, source_file_name);
  	  else
  	    fnotice (stdout, "No calls in file %s\n", source_file_name);
  	}
*************** output_data ()
*** 1410,1460 ****
  	  /* Now the statistics are ready.  Read in the source file one line
  	     at a time, and output that line to the gcov file preceded by
  	     its execution count if non zero.  */
  
! 	  source_file = fopen (source_file_name, "r");
! 	  if (source_file == NULL)
  	    {
! 	      fnotice (stderr, "Could not open source file %s.\n",
! 		       source_file_name);
! 	      free (line_counts);
! 	      free (line_exists);
! 	      continue;
  	    }
  
! 	  count = strlen (source_file_name);
! 	  cptr = strrchr (s_ptr->name, '/');
! 	  if (cptr)
! 	    cptr = cptr + 1;
! 	  else
! 	    cptr = s_ptr->name;
! 	  if (output_long_names && strcmp (cptr, input_file_name))
! 	    {
! 	      gcov_file_name = xmalloc (count + 7 + strlen (input_file_name));
! 
! 	      cptr = strrchr (input_file_name, '/');
! 	      if (cptr)
! 		strcpy (gcov_file_name, cptr + 1);
! 	      else
! 		strcpy (gcov_file_name, input_file_name);
! 
! 	      strcat (gcov_file_name, ".");
! 
! 	      cptr = strrchr (source_file_name, '/');
! 	      if (cptr)
! 		strcat (gcov_file_name, cptr + 1);
! 	      else
! 		strcat (gcov_file_name, source_file_name);
! 	    }
! 	  else
  	    {
! 	      gcov_file_name = xmalloc (count + 6);
! 	      cptr = strrchr (source_file_name, '/');
! 	      if (cptr)
! 		strcpy (gcov_file_name, cptr + 1);
! 	      else
! 		strcpy (gcov_file_name, source_file_name);
  	    }
! 
  	  /* Don't strip off the ending for compatibility with tcov, since
  	     this results in confusion if there is more than one file with
  	     the same basename, e.g. tmp.c and tmp.h.  */
--- 1462,1531 ----
  	  /* Now the statistics are ready.  Read in the source file one line
  	     at a time, and output that line to the gcov file preceded by
  	     its execution count if non zero.  */
+ 	  char const *retval;
  
! 	  /* 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
! 	     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 '^'. */
! 	  gcov_file_name = xmalloc (strlen (source_file_name)
! 				    + strlen (input_file_name) + 10);
! 	  gcov_file_name[0] = 0;
! 	  if (output_long_names && strcmp (source_file_name, input_file_name))
  	    {
! 	      /* Generate the input filename part.  */
! 	      cptr = preserve_paths ? NULL : strrchr (input_file_name, '/');
! 	      cptr = cptr ? cptr + 1 : input_file_name;
! 	      strcat (gcov_file_name, cptr);
! 	      strcat (gcov_file_name, "##");
  	    }
+ 	  /* Generate the source filename part. */
+ 	  cptr = preserve_paths ? NULL : strrchr (source_file_name, '/');
+ 	  cptr = cptr ? cptr + 1 : source_file_name;
+ 	  strcat (gcov_file_name, cptr);
  
! 	  if (preserve_paths)
  	    {
! 	      /* Convert '/' to '#', remove '/./', convert '/../' to
! 		 '/^/' */
! 	      char *prev;
! 	      
! 	      for (cptr = gcov_file_name;
! 		   (cptr = strchr ((prev = cptr), '/'));)
! 		{
! 		  unsigned shift = 0;
! 		  
! 		  if (prev + 1 == cptr && prev[0] == '.')
! 		    {
! 		      /* Remove '.' */
! 		      shift = 2;
! 		    }
! 		  else if (prev + 2 == cptr
! 			   && prev[0] == '.' && prev[1] == '.')
! 		    {
! 		      /* Convert '..' */
! 		      shift = 1;
! 		      prev[1] = '^';
! 		    }
! 		  else
! 		    *cptr++ = '#';
! 		  if (shift)
! 		    {
! 		      cptr = prev;
! 		      do
! 			prev[0] = prev[shift];
! 		      while (*prev++);
! 		    }
! 		}
  	    }
! 	  
  	  /* Don't strip off the ending for compatibility with tcov, since
  	     this results in confusion if there is more than one file with
  	     the same basename, e.g. tmp.c and tmp.h.  */
*************** output_data ()
*** 1466,1472 ****
  	    {
  	      fnotice (stderr, "Could not open output file %s.\n",
  		       gcov_file_name);
- 	      fclose (source_file);
  	      free (line_counts);
  	      free (line_exists);
  	      continue;
--- 1537,1542 ----
*************** output_data ()
*** 1474,1517 ****
  
  	  fnotice (stdout, "Creating %s.\n", gcov_file_name);
  
! 	  for (count = 1; count < s_ptr->maxlineno; count++)
  	    {
! 	      char *retval;
! 	      int len;
! 
! 	      retval = fgets (string, STRING_SIZE, source_file);
  
! 	      /* For lines which don't exist in the .bb file, print nothing
! 		 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.  */
! 	      if (line_exists[count])
  		{
! 		  if (line_counts[count])
  		    {
! 		      char c[20];
! 		      sprintf (c, HOST_WIDEST_INT_PRINT_DEC, (HOST_WIDEST_INT)line_counts[count]);
! 		      fprintf (gcov_file, "%12s    %s", c,
! 			       string);
  		    }
! 		  else
! 		    fprintf (gcov_file, "      ######    %s", string);
! 		}
! 	      else
! 		fprintf (gcov_file, "\t\t%s", string);
! 
! 	      /* In case the source file line is larger than our buffer, keep
! 		 reading and outputting lines until we get a newline.  */
! 	      len = strlen (string);
! 	      while ((len == 0 || string[strlen (string) - 1] != '\n')
! 		     && retval != NULL)
! 		{
! 		  retval = fgets (string, STRING_SIZE, source_file);
! 		  fputs (string, gcov_file);
  		}
! 
  	      if (output_branch_probs)
  		{
  		  for (i = 0, a_ptr = branch_probs[count]; a_ptr;
--- 1544,1604 ----
  
  	  fnotice (stdout, "Creating %s.\n", gcov_file_name);
  
! 	  fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, source_file_name);
! 	  fprintf (gcov_file, "%9s:%5d:Object:%s\n", "-", 0, bb_file_name);
! 	  
! 	  source_file = fopen (source_file_name, "r");
! 	  if (source_file == NULL)
! 	    fnotice (stderr, "Could not open source file %s.\n",
! 		     source_file_name);
! 	  else
  	    {
! 	      struct stat status;
  
! 	      if (!fstat (fileno (source_file), &status)
! 		  && status.st_mtime > bb_file_time)
! 		{
! 		  fnotice (stderr, "Warning: source file %s is newer than %s\n",
! 			   source_file_name, bb_file_name);
! 		  fprintf (gcov_file, "%9s:%5d:Source is newer than compiler output\n", "-", 0);
! 		}
! 	    }
! 	  
! 	  for (retval = source_file ? "" : NULL, count = 1;
! 	       count < s_ptr->maxlineno; count++)
! 	    {
! 	      /* 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:%5ld:",
! 		       !line_exists[count] ? "-"
! 		       : !line_counts[count] ? "#####"
! 		       : format_hwint (line_counts[count], 0, -1), count);
! 	      
! 	      if (retval)
  		{
! 		  do
  		    {
! 		      retval = fgets (string, STRING_SIZE, source_file);
! 		      if (!retval)
! 			{
! 			  fnotice (stderr,
! 				   "Unexpected EOF while reading source file %s.\n",
! 				   source_file_name);
! 			  break;
! 			}
! 		      fputs (retval, gcov_file);
  		    }
! 		  while (!retval[0] || retval[strlen (retval) - 1] != '\n');
  		}
! 	      if (!retval)
! 		fputs ("??\n", gcov_file);
! 	      
  	      if (output_branch_probs)
  		{
  		  for (i = 0, a_ptr = branch_probs[count]; a_ptr;
*************** output_data ()
*** 1520,1614 ****
  		      if (a_ptr->call_insn)
  			{
  			  if (a_ptr->total == 0)
! 			    fnotice (gcov_file, "call %d never executed\n", i);
! 		            else
! 			      {
! 				if (output_branch_counts)
! 				  {
! 				    char c[20];
! 				    sprintf (c, HOST_WIDEST_INT_PRINT_DEC,
! 					     a_ptr->total - a_ptr->hits);
! 				    fnotice (gcov_file,
! 					     "call %d returns = %s\n", i, c);
! 				  }
! 			        else
! 				  {
! 				    char c[20];
! 				    sprintf (c, HOST_WIDEST_INT_PRINT_DEC,
! 					     100 - ((a_ptr->hits * 100)
! 						    + (a_ptr->total >> 1))
! 					     / a_ptr->total);
! 				    fnotice (gcov_file,
! 					     "call %d returns = %s%%\n", i, c);
! 				  }
! 			      }
  			}
  		      else
  			{
  			  if (a_ptr->total == 0)
! 			    fnotice (gcov_file, "branch %d never executed\n",
  				     i);
  			  else
! 			    {
! 			      if (output_branch_counts)
! 				{
! 				  char c[20];
! 				  sprintf (c, HOST_WIDEST_INT_PRINT_DEC,
! 					   a_ptr->hits);
! 				  fnotice (gcov_file,
! 					   "branch %d taken = %s\n", i, c);
! 				}
! 			      else
! 				{
! 				  char c[20];
! 				  sprintf (c, HOST_WIDEST_INT_PRINT_DEC,
! 					   ((a_ptr->hits * 100)
! 					    + (a_ptr->total >> 1))
! 					   / a_ptr->total);
! 				  fnotice (gcov_file,
! 					   "branch %d taken = %s%%\n", i, c);
! 				}
! 			    }
  			}
  		   }
  	      }
- 
- 	      /* Gracefully handle errors while reading the source file.  */
- 	      if (retval == NULL)
- 		{
- 		  fnotice (stderr,
- 			   "Unexpected EOF while reading source file %s.\n",
- 			   source_file_name);
- 		  break;
- 		}
  	    }
  
  	  /* Handle all remaining source lines.  There may be lines
  	     after the last line of code.  */
  
! 	  {
! 	    char *retval = fgets (string, STRING_SIZE, source_file);
! 	    while (retval != NULL)
! 	      {
! 		int len;
! 
! 		fprintf (gcov_file, "\t\t%s", string);
! 
! 		/* In case the source file line is larger than our buffer, keep
! 		   reading and outputting lines until we get a newline.  */
! 		len = strlen (string);
! 		while ((len == 0 || string[strlen (string) - 1] != '\n')
! 		       && retval != NULL)
! 		  {
! 		    retval = fgets (string, STRING_SIZE, source_file);
! 		    fputs (string, gcov_file);
! 		  }
! 
! 		retval = fgets (string, STRING_SIZE, source_file);
! 	      }
! 	  }
  
! 	  fclose (source_file);
  	  fclose (gcov_file);
  	}
  
--- 1607,1659 ----
  		      if (a_ptr->call_insn)
  			{
  			  if (a_ptr->total == 0)
! 			    fnotice (gcov_file, "call   %2d: never executed\n", i);
! 			  else
! 			    fnotice
! 			      (gcov_file, "call   %2d: returns %s\n", i,
! 			       format_hwint (a_ptr->total - a_ptr->hits,
! 					     a_ptr->total,
! 					     -output_branch_counts));
  			}
  		      else
  			{
  			  if (a_ptr->total == 0)
! 			    fnotice (gcov_file, "branch %2d: never executed\n",
  				     i);
  			  else
! 			    fnotice
! 			      (gcov_file, "branch %2d: taken %s\n", i,
! 			       format_hwint (a_ptr->hits, a_ptr->total,
! 					     -output_branch_counts));
  			}
  		   }
  	      }
  	    }
  
  	  /* Handle all remaining source lines.  There may be lines
  	     after the last line of code.  */
+ 	  if (retval)
+ 	    {
+ 	      for (; (retval = fgets (string, STRING_SIZE, source_file));
+ 		   count++)
+ 		{
+ 		  fprintf (gcov_file, "%9s:%5ld:%s", "-", count, retval);
  
! 		  while (!retval[0] || retval[strlen (retval) - 1] != '\n')
! 		    {
! 		      retval = fgets (string, STRING_SIZE, source_file);
! 		      if (!retval)
! 			break;
! 		      fputs (retval, gcov_file);
! 		    }
! 		}
! 	    }
  
! 	  if (source_file)
! 	    fclose (source_file);
! 	  if (ferror (gcov_file))
! 	    fnotice (stderr, "Error writing output file %s.\n",
! 		     gcov_file_name);
  	  fclose (gcov_file);
  	}
  
Index: doc/gcov.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/gcov.texi,v
retrieving revision 1.9
diff -c -3 -p -r1.9 gcov.texi
*** doc/gcov.texi	29 Jul 2002 18:40:45 -0000	1.9
--- doc/gcov.texi	4 Aug 2002 16:55:18 -0000
***************
*** 1,10 ****
! @c Copyright (C) 1996, 1997, 1999, 2000, 2001 Free Software Foundation, Inc.
  @c This is part of the GCC manual.
  @c For copying conditions, see the file gcc.texi.
  
  @ignore
  @c man begin COPYRIGHT
! Copyright @copyright{} 1996, 1997, 1999, 2000, 2001 Free Software Foundation, Inc.
  
  Permission is granted to copy, distribute and/or modify this document
  under the terms of the GNU Free Documentation License, Version 1.1 or
--- 1,11 ----
! @c Copyright (C) 1996, 1997, 1999, 2000, 2001,
! @c 2002 Free Software Foundation, Inc.
  @c This is part of the GCC manual.
  @c For copying conditions, see the file gcc.texi.
  
  @ignore
  @c man begin COPYRIGHT
! Copyright @copyright{} 1996, 1997, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
  
  Permission is granted to copy, distribute and/or modify this document
  under the terms of the GNU Free Documentation License, Version 1.1 or
*************** test code coverage in your programs.
*** 47,58 ****
  @c man begin DESCRIPTION
  
  @command{gcov} is a test coverage program.  Use it in concert with GCC
! to analyze your programs to help create more efficient, faster
! running code.  You can use @command{gcov} as a profiling tool to help
! discover where your optimization efforts will best affect your code.  You
! can also use @command{gcov} along with the other profiling tool,
! @command{gprof}, to assess which parts of your code use the greatest amount
! of computing time.
  
  Profiling tools help you analyze your code's performance.  Using a
  profiler such as @command{gcov} or @command{gprof}, you can find out some
--- 48,60 ----
  @c man begin DESCRIPTION
  
  @command{gcov} is a test coverage program.  Use it in concert with GCC
! to analyze your programs to help create more efficient, faster running
! code and to discover untested parts of your program.  You can use
! @command{gcov} as a profiling tool to help discover where your
! optimization efforts will best affect your code.  You can also use
! @command{gcov} along with the other profiling tool, @command{gprof}, to
! assess which parts of your code use the greatest amount of computing
! time.
  
  Profiling tools help you analyze your code's performance.  Using a
  profiler such as @command{gcov} or @command{gprof}, you can find out some
*************** gcov @r{[}@var{options}@r{]} @var{source
*** 117,126 ****
  @ignore
  @c man begin SYNOPSIS
  gcov [@option{-v}|@option{--version}] [@option{-h}|@option{--help}]
!      [@option{-b}|@option{--branch-probabilities}] [@option{-c}|@option{--branch-counts}]
!      [@option{-n}|@option{--no-output}] [@option{-l}|@option{--long-file-names}]
       [@option{-f}|@option{--function-summaries}]
!      [@option{-o}|@option{--object-directory} @var{directory}] @var{sourcefile}
  @c man end
  @c man begin SEEALSO
  gpl(7), gfdl(7), fsf-funding(7), gcc(1) and the Info entry for @file{gcc}.
--- 119,131 ----
  @ignore
  @c man begin SYNOPSIS
  gcov [@option{-v}|@option{--version}] [@option{-h}|@option{--help}]
!      [@option{-b}|@option{--branch-probabilities}]
!      [@option{-c}|@option{--branch-counts}]
!      [@option{-n}|@option{--no-output}]
!      [@option{-l}|@option{--long-file-names}]
!      [@option{-p}|@option{--preserve-paths}]
       [@option{-f}|@option{--function-summaries}]
!      [@option{-o}|@option{--object-directory} @var{directory|file}] @var{sourcefile}
  @c man end
  @c man begin SEEALSO
  gpl(7), gfdl(7), fsf-funding(7), gcc(1) and the Info entry for @file{gcc}.
*************** Do not create the @command{gcov} output 
*** 159,189 ****
  Create long file names for included source files.  For example, if the
  header file @file{x.h} contains code, and was included in the file
  @file{a.c}, then running @command{gcov} on the file @file{a.c} will produce
! an output file called @file{a.c.x.h.gcov} instead of @file{x.h.gcov}.
  This can be useful if @file{x.h} is included in multiple source files.
  
  @item -f
  @itemx --function-summaries
  Output summaries for each function in addition to the file level summary.
  
! @item -o @var{directory}
  @itemx --object-directory @var{directory}
! The directory where the object files live.  Gcov will search for @file{.bb},
! @file{.bbg}, and @file{.da} files in this directory.
  @end table
  
! @need 3000
  When using @command{gcov}, you must first compile your program with two
  special GCC options: @samp{-fprofile-arcs -ftest-coverage}.
  This tells the compiler to generate additional information needed by
  gcov (basically a flow graph of the program) and also includes
  additional code in the object files for generating the extra profiling
  information needed by gcov.  These additional files are placed in the
! directory where the source code is located.
  
  Running the program will cause profile output to be generated.  For each
  source file compiled with @option{-fprofile-arcs}, an accompanying @file{.da}
! file will be placed in the source directory.
  
  Running @command{gcov} with your program's source file names as arguments
  will now produce a listing of the code along with frequency of execution
--- 164,233 ----
  Create long file names for included source files.  For example, if the
  header file @file{x.h} contains code, and was included in the file
  @file{a.c}, then running @command{gcov} on the file @file{a.c} will produce
! an output file called @file{a.c##x.h.gcov} instead of @file{x.h.gcov}.
  This can be useful if @file{x.h} is included in multiple source files.
  
+ @item -p
+ @itemx --preserve-paths
+ Preserve complete path information in the names of generated
+ @file{.gcov} files. Without this option, just the filename component is
+ used. With this option, all directories are used, with '/' characters
+ translated to '#' characters, '.' directory components removed and '..'
+ components renamed to '^'. This is useful if sourcefiles are in several
+ different directories. It also affects the @samp{-l} option.
+ 
  @item -f
  @itemx --function-summaries
  Output summaries for each function in addition to the file level summary.
  
! @item -o @var{directory|file}
  @itemx --object-directory @var{directory}
! @itemx --object-file @var{file}
! Specify either the directory containing the gcov data files, or the
! object path name. The @file{.bb}, @file{.bbg}, and
! @file{.da} data files are searched for using this option. If a directory
! is specified, the data files are in that directory and named after the
! source file name, without its extension. If a file is specified here,
! the data files are named after that file, without its extension. If this
! option is not supplied, it defaults to the current directory.
! 
  @end table
  
! Gcov should be run with the current directory the same as that when you
! invoked the compiler. Otherwise it will not be able to locate the source
! files. Gcov produces files called @file{@var{mangledname}.gcov} in the
! current directory. These contain the coverage information of the source
! file they correspond to. One @file{.gcov} file is produced for each
! source file containing code, which was compiled to produce the data
! files. The @file{.gcov} files contain the ':' separated fields along
! with program source code. The format is
! 
! @smallexample
! @var{execution_count}:@var{line_number}:@var{source line text}
! @end smallexample
! 
! Additional block information may succeed each line, when requested by
! command line option. The @var{execution_count} is @samp{-} for lines
! containing no code and @samp{#####} for lines which were never
! executed. Some lines of information at the start have @var{line_number}
! of zero.
! 
! When printing percentages, 0% and 100% are only printed when the values
! are @emph{exactly} 0% and 100% respectively. Other values which would
! conventionally be rounded to 0% or 100% are instead printed as the
! nearest non-boundary value.
! 
  When using @command{gcov}, you must first compile your program with two
  special GCC options: @samp{-fprofile-arcs -ftest-coverage}.
  This tells the compiler to generate additional information needed by
  gcov (basically a flow graph of the program) and also includes
  additional code in the object files for generating the extra profiling
  information needed by gcov.  These additional files are placed in the
! directory where the object file is located.
  
  Running the program will cause profile output to be generated.  For each
  source file compiled with @option{-fprofile-arcs}, an accompanying @file{.da}
! file will be placed in the object file directory.
  
  Running @command{gcov} with your program's source file names as arguments
  will now produce a listing of the code along with frequency of execution
*************** is what you see when you use the basic @
*** 194,200 ****
  $ gcc -fprofile-arcs -ftest-coverage tmp.c
  $ a.out
  $ gcov tmp.c
!  87.50% of 8 source lines executed in file tmp.c
  Creating tmp.c.gcov.
  @end smallexample
  
--- 238,244 ----
  $ gcc -fprofile-arcs -ftest-coverage tmp.c
  $ a.out
  $ gcov tmp.c
! 90.00% of 10 source lines executed in file tmp.c
  Creating tmp.c.gcov.
  @end smallexample
  
*************** The file @file{tmp.c.gcov} contains outp
*** 202,221 ****
  Here is a sample:
  
  @smallexample
!                 main()
!                 @{
!            1      int i, total;
! 
!            1      total = 0;
! 
!           11      for (i = 0; i < 10; i++)
!           10        total += i;
! 
!            1      if (total != 45)
!       ######        printf ("Failure\n");
!                   else
!            1        printf ("Success\n");
!            1    @}
  @end smallexample
  
  @need 450
--- 246,270 ----
  Here is a sample:
  
  @smallexample
!         -:    0:Source:tmp.c
!         -:    0:Object:tmp.bb
!         -:    1:#include <stdio.h>
!         -:    2:
!         -:    3:int main (void)
!         1:    4:@{
!         1:    5:  int i, total;
!         -:    6:  
!         1:    7:  total = 0;
!         -:    8:  
!        11:    9:  for (i = 0; i < 10; i++)
!        10:   10:    total += i;
!         -:   11:  
!         1:   12:  if (total != 45)
!     #####:   13:    printf ("Failure\n");
!         -:   14:  else
!         1:   15:    printf ("Success\n");
!         1:   16:  return 0;
!         1:   17:@}
  @end smallexample
  
  @need 450
*************** When you use the @option{-b} option, you
*** 223,259 ****
  
  @smallexample
  $ gcov -b tmp.c
!  87.50% of 8 source lines executed in file tmp.c
!  80.00% of 5 branches executed in file tmp.c
!  80.00% of 5 branches taken at least once in file tmp.c
!  50.00% of 2 calls executed in file tmp.c
  Creating tmp.c.gcov.
  @end smallexample
  
  Here is a sample of a resulting @file{tmp.c.gcov} file:
  
  @smallexample
!                 main()
!                 @{
!            1      int i, total;
! 
!            1      total = 0;
! 
!           11      for (i = 0; i < 10; i++)
! branch 0 taken = 91%
! branch 1 taken = 100%
! branch 2 taken = 100%
!           10        total += i;
! 
!            1      if (total != 45)
! branch 0 taken = 100%
!       ######        printf ("Failure\n");
! call 0 never executed
! branch 1 never executed
!                   else
!            1        printf ("Success\n");
! call 0 returns = 100%
!            1    @}
  @end smallexample
  
  For each basic block, a line is printed after the last line of the basic
--- 272,313 ----
  
  @smallexample
  $ gcov -b tmp.c
! 90.00% of 10 source lines executed in file tmp.c
! 80.00% of 5 branches executed in file tmp.c
! 80.00% of 5 branches taken at least once in file tmp.c
! 50.00% of 2 calls executed in file tmp.c
  Creating tmp.c.gcov.
  @end smallexample
  
  Here is a sample of a resulting @file{tmp.c.gcov} file:
  
  @smallexample
!         -:    0:Source:tmp.c
!         -:    0:Object:tmp.bb
!         -:    1:#include <stdio.h>
!         -:    2:
!         -:    3:int main (void)
!         1:    4:@{
!         1:    5:  int i, total;
!         -:    6:  
!         1:    7:  total = 0;
!         -:    8:  
!        11:    9:  for (i = 0; i < 10; i++)
! branch  0: taken 90%
! branch  1: taken 100%
! branch  2: taken 100%
!        10:   10:    total += i;
!         -:   11:  
!         1:   12:  if (total != 45)
! branch  0: taken 100%
!     #####:   13:    printf ("Failure\n");
! call    0: never executed
! branch  1: never executed
!         -:   14:  else
!         1:   15:    printf ("Success\n");
! call    0: returns 100%
!         1:   16:  return 0;
!         1:   17:@}
  @end smallexample
  
  For each basic block, a line is printed after the last line of the basic
*************** provide more accurate long-term informat
*** 286,296 ****
  program runs.
  
  The data in the @file{.da} files is saved immediately before the program
! exits.  For each source file compiled with @option{-fprofile-arcs}, the profiling
! code first attempts to read in an existing @file{.da} file; if the file
! doesn't match the executable (differing number of basic block counts) it
! will ignore the contents of the file.  It then adds in the new execution
! counts and finally writes the data to the file.
  
  @node Gcov and Optimization
  @section Using @command{gcov} with GCC Optimization
--- 340,350 ----
  program runs.
  
  The data in the @file{.da} files is saved immediately before the program
! exits.  For each source file compiled with @option{-fprofile-arcs}, the
! profiling code first attempts to read in an existing @file{.da} file; if
! the file doesn't match the executable (differing number of basic block
! counts) it will ignore the contents of the file.  It then adds in the
! new execution counts and finally writes the data to the file.
  
  @node Gcov and Optimization
  @section Using @command{gcov} with GCC Optimization
*************** the @command{gcov} output looks like thi
*** 319,328 ****
  optimization:
  
  @smallexample
!       100  if (a != b)
!       100    c = 1;
!       100  else
!       100    c = 0;
  @end smallexample
  
  The output shows that this block of code, combined by optimization,
--- 373,382 ----
  optimization:
  
  @smallexample
!       100:   12:if (a != b)
!       100:   13:  c = 1;
!       100:   14:else
!       100:   15:  c = 0;
  @end smallexample
  
  The output shows that this block of code, combined by optimization,


More information about the Gcc-patches mailing list