[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