1 /* Gcov.c: prepend line execution counts and branch probabilities to a
3 Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999,
4 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
5 Contributed by James E. Wilson of Cygnus Support.
6 Mangled by Bob Manson of Cygnus Support.
7 Mangled further by Nathan Sidwell <nathan@codesourcery.com>
9 Gcov is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
14 Gcov is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Gcov; see the file COPYING. If not, write to
21 the Free Software Foundation, 51 Franklin Street, Fifth Floor,
22 Boston, MA 02110-1301, USA. */
24 /* ??? Print a list of the ten blocks with the highest execution counts,
25 and list the line numbers corresponding to those blocks. Also, perhaps
26 list the line numbers with the highest execution counts, only printing
27 the first if there are several which are all listed in the same block. */
29 /* ??? Should have an option to print the number of basic blocks, and the
30 percent of them that are covered. */
32 /* ??? Does not correctly handle the case where two .bb files refer to
33 the same included source file. For example, if one has a short
34 file containing only inline functions, which is then included in
35 two other files, then there will be two .bb files which refer to
36 the include file, but there is no way to get the total execution
37 counts for the included file, can only get execution counts for one
38 or the other of the including files. this can be fixed by --ratios
39 --long-file-names --preserve-paths and perl. */
41 /* Need an option to show individual block counts, and show
42 probabilities of fall through arcs. */
46 #include "coretypes.h"
57 /* The bbg file is generated by -ftest-coverage option. The da file is
58 generated by a program compiled with -fprofile-arcs. Their formats
59 are documented in gcov-io.h. */
61 /* The functions in this file for creating and solution program flow graphs
62 are very similar to functions in the gcc source file profile.c. In
63 some places we make use of the knowledge of how profile.c works to
64 select particular algorithms here. */
66 /* This is the size of the buffer used to read in source file lines. */
68 #define STRING_SIZE 200
74 /* Describes an arc between two basic blocks. */
76 typedef struct arc_info
78 /* source and destination blocks. */
79 struct block_info
*src
;
80 struct block_info
*dst
;
82 /* transition counts. */
84 /* used in cycle search, so that we do not clobber original counts. */
87 unsigned int count_valid
: 1;
88 unsigned int on_tree
: 1;
89 unsigned int fake
: 1;
90 unsigned int fall_through
: 1;
92 /* Arc is for a function that abnormally returns. */
93 unsigned int is_call_non_return
: 1;
95 /* Arc is for catch/setjump. */
96 unsigned int is_nonlocal_return
: 1;
98 /* Is an unconditional branch. */
99 unsigned int is_unconditional
: 1;
101 /* Loop making arc. */
102 unsigned int cycle
: 1;
104 /* Next branch on line. */
105 struct arc_info
*line_next
;
107 /* Links to next arc on src and dst lists. */
108 struct arc_info
*succ_next
;
109 struct arc_info
*pred_next
;
112 /* Describes a basic block. Contains lists of arcs to successor and
113 predecessor blocks. */
115 typedef struct block_info
117 /* Chain of exit and entry arcs. */
121 /* Number of unprocessed exit and entry arcs. */
125 /* Block execution count. */
128 unsigned count_valid
: 1;
129 unsigned valid_chain
: 1;
130 unsigned invalid_chain
: 1;
132 /* Block is a call instrumenting site. */
133 unsigned is_call_site
: 1; /* Does the call. */
134 unsigned is_call_return
: 1; /* Is the return. */
136 /* Block is a landing pad for longjmp or throw. */
137 unsigned is_nonlocal_return
: 1;
143 /* Array of line numbers and source files. source files are
144 introduced by a linenumber of zero, the next 'line number' is
145 the number of the source file. Always starts with a source
149 } line
; /* Valid until blocks are linked onto lines */
152 /* Single line graph cycle workspace. Used for all-blocks
156 } cycle
; /* Used in all-blocks mode, after blocks are linked onto
160 /* Temporary chain for solving graph, and for chaining blocks on one
162 struct block_info
*chain
;
166 /* Describes a single function. Contains an array of basic blocks. */
168 typedef struct function_info
170 /* Name of function. */
175 /* Array of basic blocks. */
178 unsigned blocks_executed
;
180 /* Raw arc coverage counts. */
184 /* First line number. */
186 struct source_info
*src
;
188 /* Next function in same source file. */
189 struct function_info
*line_next
;
192 struct function_info
*next
;
195 /* Describes coverage of a file or function. */
197 typedef struct coverage_info
203 int branches_executed
;
212 /* Describes a single line of source. Contains a chain of basic blocks
215 typedef struct line_info
217 gcov_type count
; /* execution count */
220 arc_t
*branches
; /* branches from blocks that end on this
221 line. Used for branch-counts when not
223 block_t
*blocks
; /* blocks which start on this line. Used
224 in all-blocks mode. */
229 /* Describes a file mentioned in the block graph. Contains an array
232 typedef struct source_info
234 /* Name of source file. */
238 /* Array of line information. */
244 /* Functions in this source file. These are in ascending line
246 function_t
*functions
;
248 /* Next source file. */
249 struct source_info
*next
;
252 /* Holds a list of function basic block graphs. */
254 static function_t
*functions
;
256 /* This points to the head of the sourcefile structure list. */
258 static source_t
*sources
;
260 /* This holds data summary information. */
262 static struct gcov_summary object_summary
;
263 static unsigned program_count
;
265 /* Modification time of graph file. */
267 static time_t bbg_file_time
;
269 /* Name and file pointer of the input file for the basic block graph. */
271 static char *bbg_file_name
;
273 /* Stamp of the bbg file */
274 static unsigned bbg_stamp
;
276 /* Name and file pointer of the input file for the arc count data. */
278 static char *da_file_name
;
280 /* Data file is missing. */
282 static int no_data_file
;
284 /* Output branch probabilities. */
286 static int flag_branches
= 0;
288 /* Show unconditional branches too. */
289 static int flag_unconditional
= 0;
291 /* Output a gcov file if this is true. This is on by default, and can
292 be turned off by the -n option. */
294 static int flag_gcov_file
= 1;
296 /* For included files, make the gcov output file name include the name
297 of the input source file. For example, if x.h is included in a.c,
298 then the output file name is a.c##x.h.gcov instead of x.h.gcov. */
300 static int flag_long_names
= 0;
302 /* Output count information for every basic block, not merely those
303 that contain line number information. */
305 static int flag_all_blocks
= 0;
307 /* Output summary info for each function. */
309 static int flag_function_summary
= 0;
311 /* Object directory file prefix. This is the directory/file where the
312 graph and data files are looked for, if nonzero. */
314 static char *object_directory
= 0;
316 /* Preserve all pathname components. Needed when object files and
317 source files are in subdirectories. '/' is mangled as '#', '.' is
318 elided and '..' mangled to '^'. */
320 static int flag_preserve_paths
= 0;
322 /* Output the number of times a branch was taken as opposed to the percentage
323 of times it was taken. */
325 static int flag_counts
= 0;
327 /* Forward declarations. */
328 static void fnotice (FILE *, const char *, ...) ATTRIBUTE_PRINTF_2
;
329 static int process_args (int, char **);
330 static void print_usage (int) ATTRIBUTE_NORETURN
;
331 static void print_version (void) ATTRIBUTE_NORETURN
;
332 static void process_file (const char *);
333 static void create_file_names (const char *);
334 static source_t
*find_source (const char *);
335 static int read_graph_file (void);
336 static int read_count_file (void);
337 static void solve_flow_graph (function_t
*);
338 static void add_branch_counts (coverage_t
*, const arc_t
*);
339 static void add_line_counts (coverage_t
*, function_t
*);
340 static void function_summary (const coverage_t
*, const char *);
341 static const char *format_gcov (gcov_type
, gcov_type
, int);
342 static void accumulate_line_counts (source_t
*);
343 static int output_branch_count (FILE *, int, const arc_t
*);
344 static void output_lines (FILE *, const source_t
*);
345 static char *make_gcov_file_name (const char *, const char *);
346 static void release_structures (void);
347 extern int main (int, char **);
350 main (int argc
, char **argv
)
354 /* Unlock the stdio streams. */
355 unlock_std_streams ();
359 argno
= process_args (argc
, argv
);
363 for (; argno
!= argc
; argno
++)
365 release_structures ();
367 process_file (argv
[argno
]);
374 fnotice (FILE *file
, const char *cmsgid
, ...)
378 va_start (ap
, cmsgid
);
379 vfprintf (file
, _(cmsgid
), ap
);
383 /* Print a usage message and exit. If ERROR_P is nonzero, this is an error,
384 otherwise the output of --help. */
387 print_usage (int error_p
)
389 FILE *file
= error_p
? stderr
: stdout
;
390 int status
= error_p
? FATAL_EXIT_CODE
: SUCCESS_EXIT_CODE
;
392 fnotice (file
, "Usage: gcov [OPTION]... SOURCEFILE\n\n");
393 fnotice (file
, "Print code coverage information.\n\n");
394 fnotice (file
, " -h, --help Print this help, then exit\n");
395 fnotice (file
, " -v, --version Print version number, then exit\n");
396 fnotice (file
, " -a, --all-blocks Show information for every basic block\n");
397 fnotice (file
, " -b, --branch-probabilities Include branch probabilities in output\n");
398 fnotice (file
, " -c, --branch-counts Given counts of branches taken\n\
399 rather than percentages\n");
400 fnotice (file
, " -n, --no-output Do not create an output file\n");
401 fnotice (file
, " -l, --long-file-names Use long output file names for included\n\
403 fnotice (file
, " -f, --function-summaries Output summaries for each function\n");
404 fnotice (file
, " -o, --object-directory DIR|FILE Search for object files in DIR or called FILE\n");
405 fnotice (file
, " -p, --preserve-paths Preserve all pathname components\n");
406 fnotice (file
, " -u, --unconditional-branches Show unconditional branch counts too\n");
407 fnotice (file
, "\nFor bug reporting instructions, please see:\n%s.\n",
412 /* Print version information and exit. */
417 fnotice (stdout
, "gcov (GCC) %s\n", version_string
);
418 fprintf (stdout
, "Copyright %s 2006 Free Software Foundation, Inc.\n",
421 _("This is free software; see the source for copying conditions.\n"
422 "There is NO warranty; not even for MERCHANTABILITY or \n"
423 "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
424 exit (SUCCESS_EXIT_CODE
);
427 static const struct option options
[] =
429 { "help", no_argument
, NULL
, 'h' },
430 { "version", no_argument
, NULL
, 'v' },
431 { "all-blocks", no_argument
, NULL
, 'a' },
432 { "branch-probabilities", no_argument
, NULL
, 'b' },
433 { "branch-counts", no_argument
, NULL
, 'c' },
434 { "no-output", no_argument
, NULL
, 'n' },
435 { "long-file-names", no_argument
, NULL
, 'l' },
436 { "function-summaries", no_argument
, NULL
, 'f' },
437 { "preserve-paths", no_argument
, NULL
, 'p' },
438 { "object-directory", required_argument
, NULL
, 'o' },
439 { "object-file", required_argument
, NULL
, 'o' },
440 { "unconditional-branches", no_argument
, NULL
, 'u' },
444 /* Process args, return index to first non-arg. */
447 process_args (int argc
, char **argv
)
451 while ((opt
= getopt_long (argc
, argv
, "abcfhlno:puv", options
, NULL
)) != -1)
465 flag_function_summary
= 1;
469 /* print_usage will exit. */
477 object_directory
= optarg
;
480 flag_preserve_paths
= 1;
483 flag_unconditional
= 1;
487 /* print_version will exit. */
490 /* print_usage will exit. */
497 /* Process a single source file. */
500 process_file (const char *file_name
)
505 create_file_names (file_name
);
506 if (read_graph_file ())
511 fnotice (stderr
, "%s:no functions found\n", bbg_file_name
);
515 if (read_count_file ())
518 for (fn
= functions
; fn
; fn
= fn
->next
)
519 solve_flow_graph (fn
);
520 for (src
= sources
; src
; src
= src
->next
)
521 src
->lines
= XCNEWVEC (line_t
, src
->num_lines
);
522 for (fn
= functions
; fn
; fn
= fn
->next
)
526 memset (&coverage
, 0, sizeof (coverage
));
527 coverage
.name
= fn
->name
;
528 add_line_counts (flag_function_summary
? &coverage
: NULL
, fn
);
529 if (flag_function_summary
)
531 function_summary (&coverage
, "Function");
532 fnotice (stdout
, "\n");
536 for (src
= sources
; src
; src
= src
->next
)
538 accumulate_line_counts (src
);
539 function_summary (&src
->coverage
, "File");
542 char *gcov_file_name
= make_gcov_file_name (file_name
, src
->name
);
543 FILE *gcov_file
= fopen (gcov_file_name
, "w");
547 fnotice (stdout
, "%s:creating '%s'\n",
548 src
->name
, gcov_file_name
);
549 output_lines (gcov_file
, src
);
550 if (ferror (gcov_file
))
551 fnotice (stderr
, "%s:error writing output file '%s'\n",
552 src
->name
, gcov_file_name
);
556 fnotice (stderr
, "%s:could not open output file '%s'\n",
557 src
->name
, gcov_file_name
);
558 free (gcov_file_name
);
560 fnotice (stdout
, "\n");
564 /* Release all memory used. */
567 release_structures (void)
572 free (bbg_file_name
);
574 da_file_name
= bbg_file_name
= NULL
;
578 while ((src
= sources
))
586 while ((fn
= functions
))
591 functions
= fn
->next
;
592 for (ix
= fn
->num_blocks
, block
= fn
->blocks
; ix
--; block
++)
596 for (arc
= block
->succ
; arc
; arc
= arc_n
)
598 arc_n
= arc
->succ_next
;
607 /* Generate the names of the graph and data files. If OBJECT_DIRECTORY
608 is not specified, these are looked for in the current directory,
609 and named from the basename of the FILE_NAME sans extension. If
610 OBJECT_DIRECTORY is specified and is a directory, the files are in
611 that directory, but named from the basename of the FILE_NAME, sans
612 extension. Otherwise OBJECT_DIRECTORY is taken to be the name of
613 the object *file*, and the data files are named from that. */
616 create_file_names (const char *file_name
)
620 int length
= strlen (file_name
);
623 if (object_directory
&& object_directory
[0])
627 length
+= strlen (object_directory
) + 2;
628 name
= XNEWVEC (char, length
);
631 base
= !stat (object_directory
, &status
) && S_ISDIR (status
.st_mode
);
632 strcat (name
, object_directory
);
633 if (base
&& name
[strlen (name
) - 1] != '/')
638 name
= XNEWVEC (char, length
+ 1);
645 /* Append source file name. */
646 cptr
= strrchr (file_name
, '/');
647 strcat (name
, cptr
? cptr
+ 1 : file_name
);
650 /* Remove the extension. */
651 cptr
= strrchr (name
, '.');
655 length
= strlen (name
);
657 bbg_file_name
= XNEWVEC (char, length
+ strlen (GCOV_NOTE_SUFFIX
) + 1);
658 strcpy (bbg_file_name
, name
);
659 strcpy (bbg_file_name
+ length
, GCOV_NOTE_SUFFIX
);
661 da_file_name
= XNEWVEC (char, length
+ strlen (GCOV_DATA_SUFFIX
) + 1);
662 strcpy (da_file_name
, name
);
663 strcpy (da_file_name
+ length
, GCOV_DATA_SUFFIX
);
669 /* Find or create a source file structure for FILE_NAME. Copies
670 FILE_NAME on creation */
673 find_source (const char *file_name
)
678 file_name
= "<unknown>";
680 for (src
= sources
; src
; src
= src
->next
)
681 if (!strcmp (file_name
, src
->name
))
684 src
= XCNEW (source_t
);
685 src
->name
= xstrdup (file_name
);
686 src
->coverage
.name
= src
->name
;
687 src
->index
= sources
? sources
->index
+ 1 : 1;
694 /* Read the graph file. Return nonzero on fatal error. */
697 read_graph_file (void)
700 unsigned current_tag
= 0;
701 struct function_info
*fn
= NULL
;
702 source_t
*src
= NULL
;
706 if (!gcov_open (bbg_file_name
, 1))
708 fnotice (stderr
, "%s:cannot open graph file\n", bbg_file_name
);
711 bbg_file_time
= gcov_time ();
712 if (!gcov_magic (gcov_read_unsigned (), GCOV_NOTE_MAGIC
))
714 fnotice (stderr
, "%s:not a gcov graph file\n", bbg_file_name
);
719 version
= gcov_read_unsigned ();
720 if (version
!= GCOV_VERSION
)
724 GCOV_UNSIGNED2STRING (v
, version
);
725 GCOV_UNSIGNED2STRING (e
, GCOV_VERSION
);
727 fnotice (stderr
, "%s:version '%.4s', prefer '%.4s'\n",
728 bbg_file_name
, v
, e
);
730 bbg_stamp
= gcov_read_unsigned ();
732 while ((tag
= gcov_read_unsigned ()))
734 unsigned length
= gcov_read_unsigned ();
735 gcov_position_t base
= gcov_position ();
737 if (tag
== GCOV_TAG_FUNCTION
)
740 unsigned ident
, checksum
, lineno
;
742 function_t
*probe
, *prev
;
744 ident
= gcov_read_unsigned ();
745 checksum
= gcov_read_unsigned ();
746 function_name
= xstrdup (gcov_read_string ());
747 src
= find_source (gcov_read_string ());
748 lineno
= gcov_read_unsigned ();
750 fn
= XCNEW (function_t
);
751 fn
->name
= function_name
;
753 fn
->checksum
= checksum
;
757 fn
->next
= functions
;
761 if (lineno
>= src
->num_lines
)
762 src
->num_lines
= lineno
+ 1;
763 /* Now insert it into the source file's list of
764 functions. Normally functions will be encountered in
765 ascending order, so a simple scan is quick. */
766 for (probe
= src
->functions
, prev
= NULL
;
767 probe
&& probe
->line
> lineno
;
768 prev
= probe
, probe
= probe
->line_next
)
770 fn
->line_next
= probe
;
772 prev
->line_next
= fn
;
776 else if (fn
&& tag
== GCOV_TAG_BLOCKS
)
779 fnotice (stderr
, "%s:already seen blocks for '%s'\n",
780 bbg_file_name
, fn
->name
);
783 unsigned ix
, num_blocks
= GCOV_TAG_BLOCKS_NUM (length
);
784 fn
->num_blocks
= num_blocks
;
786 fn
->blocks
= XCNEWVEC (block_t
, fn
->num_blocks
);
787 for (ix
= 0; ix
!= num_blocks
; ix
++)
788 fn
->blocks
[ix
].flags
= gcov_read_unsigned ();
791 else if (fn
&& tag
== GCOV_TAG_ARCS
)
793 unsigned src
= gcov_read_unsigned ();
794 unsigned num_dests
= GCOV_TAG_ARCS_NUM (length
);
796 if (src
>= fn
->num_blocks
|| fn
->blocks
[src
].succ
)
801 struct arc_info
*arc
;
802 unsigned dest
= gcov_read_unsigned ();
803 unsigned flags
= gcov_read_unsigned ();
805 if (dest
>= fn
->num_blocks
)
809 arc
->dst
= &fn
->blocks
[dest
];
810 arc
->src
= &fn
->blocks
[src
];
813 arc
->count_valid
= 0;
814 arc
->on_tree
= !!(flags
& GCOV_ARC_ON_TREE
);
815 arc
->fake
= !!(flags
& GCOV_ARC_FAKE
);
816 arc
->fall_through
= !!(flags
& GCOV_ARC_FALLTHROUGH
);
818 arc
->succ_next
= fn
->blocks
[src
].succ
;
819 fn
->blocks
[src
].succ
= arc
;
820 fn
->blocks
[src
].num_succ
++;
822 arc
->pred_next
= fn
->blocks
[dest
].pred
;
823 fn
->blocks
[dest
].pred
= arc
;
824 fn
->blocks
[dest
].num_pred
++;
830 /* Exceptional exit from this function, the
831 source block must be a call. */
832 fn
->blocks
[src
].is_call_site
= 1;
833 arc
->is_call_non_return
= 1;
837 /* Non-local return from a callee of this
838 function. The destination block is a catch or
840 arc
->is_nonlocal_return
= 1;
841 fn
->blocks
[dest
].is_nonlocal_return
= 1;
849 else if (fn
&& tag
== GCOV_TAG_LINES
)
851 unsigned blockno
= gcov_read_unsigned ();
852 unsigned *line_nos
= XCNEWVEC (unsigned, length
- 1);
854 if (blockno
>= fn
->num_blocks
|| fn
->blocks
[blockno
].u
.line
.encoding
)
859 unsigned lineno
= gcov_read_unsigned ();
866 line_nos
[ix
++] = src
->index
;
868 line_nos
[ix
++] = lineno
;
869 if (lineno
>= src
->num_lines
)
870 src
->num_lines
= lineno
+ 1;
874 const char *file_name
= gcov_read_string ();
878 src
= find_source (file_name
);
881 line_nos
[ix
++] = src
->index
;
885 fn
->blocks
[blockno
].u
.line
.encoding
= line_nos
;
886 fn
->blocks
[blockno
].u
.line
.num
= ix
;
888 else if (current_tag
&& !GCOV_TAG_IS_SUBTAG (current_tag
, tag
))
893 gcov_sync (base
, length
);
894 if (gcov_is_error ())
897 fnotice (stderr
, "%s:corrupted\n", bbg_file_name
);
904 /* We built everything backwards, so nreverse them all. */
906 /* Reverse sources. Not strictly necessary, but we'll then process
907 them in the 'expected' order. */
909 source_t
*src
, *src_p
, *src_n
;
911 for (src_p
= NULL
, src
= sources
; src
; src_p
= src
, src
= src_n
)
919 /* Reverse functions. */
921 function_t
*fn
, *fn_p
, *fn_n
;
923 for (fn_p
= NULL
, fn
= functions
; fn
; fn_p
= fn
, fn
= fn_n
)
930 /* Reverse the arcs. */
931 for (ix
= fn
->num_blocks
; ix
--;)
933 arc_t
*arc
, *arc_p
, *arc_n
;
935 for (arc_p
= NULL
, arc
= fn
->blocks
[ix
].succ
; arc
;
936 arc_p
= arc
, arc
= arc_n
)
938 arc_n
= arc
->succ_next
;
939 arc
->succ_next
= arc_p
;
941 fn
->blocks
[ix
].succ
= arc_p
;
943 for (arc_p
= NULL
, arc
= fn
->blocks
[ix
].pred
; arc
;
944 arc_p
= arc
, arc
= arc_n
)
946 arc_n
= arc
->pred_next
;
947 arc
->pred_next
= arc_p
;
949 fn
->blocks
[ix
].pred
= arc_p
;
957 /* Reads profiles from the count file and attach to each
958 function. Return nonzero if fatal error. */
961 read_count_file (void)
966 function_t
*fn
= NULL
;
969 if (!gcov_open (da_file_name
, 1))
971 fnotice (stderr
, "%s:cannot open data file, assuming not executed\n",
976 if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC
))
978 fnotice (stderr
, "%s:not a gcov data file\n", da_file_name
);
983 version
= gcov_read_unsigned ();
984 if (version
!= GCOV_VERSION
)
988 GCOV_UNSIGNED2STRING (v
, version
);
989 GCOV_UNSIGNED2STRING (e
, GCOV_VERSION
);
991 fnotice (stderr
, "%s:version '%.4s', prefer version '%.4s'\n",
994 tag
= gcov_read_unsigned ();
995 if (tag
!= bbg_stamp
)
997 fnotice (stderr
, "%s:stamp mismatch with graph file\n", da_file_name
);
1001 while ((tag
= gcov_read_unsigned ()))
1003 unsigned length
= gcov_read_unsigned ();
1004 unsigned long base
= gcov_position ();
1006 if (tag
== GCOV_TAG_OBJECT_SUMMARY
)
1007 gcov_read_summary (&object_summary
);
1008 else if (tag
== GCOV_TAG_PROGRAM_SUMMARY
)
1010 else if (tag
== GCOV_TAG_FUNCTION
)
1012 unsigned ident
= gcov_read_unsigned ();
1013 struct function_info
*fn_n
= functions
;
1015 for (fn
= fn
? fn
->next
: NULL
; ; fn
= fn
->next
)
1019 else if ((fn
= fn_n
))
1023 fnotice (stderr
, "%s:unknown function '%u'\n",
1024 da_file_name
, ident
);
1027 if (fn
->ident
== ident
)
1033 else if (gcov_read_unsigned () != fn
->checksum
)
1036 fnotice (stderr
, "%s:profile mismatch for '%s'\n",
1037 da_file_name
, fn
->name
);
1041 else if (tag
== GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS
) && fn
)
1043 if (length
!= GCOV_TAG_COUNTER_LENGTH (fn
->num_counts
))
1047 fn
->counts
= XCNEWVEC (gcov_type
, fn
->num_counts
);
1049 for (ix
= 0; ix
!= fn
->num_counts
; ix
++)
1050 fn
->counts
[ix
] += gcov_read_counter ();
1052 gcov_sync (base
, length
);
1053 if ((error
= gcov_is_error ()))
1055 fnotice (stderr
, error
< 0 ? "%s:overflowed\n" : "%s:corrupted\n",
1065 /* Solve the flow graph. Propagate counts from the instrumented arcs
1066 to the blocks and the uninstrumented arcs. */
1069 solve_flow_graph (function_t
*fn
)
1073 gcov_type
*count_ptr
= fn
->counts
;
1075 block_t
*valid_blocks
= NULL
; /* valid, but unpropagated blocks. */
1076 block_t
*invalid_blocks
= NULL
; /* invalid, but inferable blocks. */
1078 if (fn
->num_blocks
< 2)
1079 fnotice (stderr
, "%s:'%s' lacks entry and/or exit blocks\n",
1080 bbg_file_name
, fn
->name
);
1083 if (fn
->blocks
[0].num_pred
)
1084 fnotice (stderr
, "%s:'%s' has arcs to entry block\n",
1085 bbg_file_name
, fn
->name
);
1087 /* We can't deduce the entry block counts from the lack of
1089 fn
->blocks
[0].num_pred
= ~(unsigned)0;
1091 if (fn
->blocks
[fn
->num_blocks
- 1].num_succ
)
1092 fnotice (stderr
, "%s:'%s' has arcs from exit block\n",
1093 bbg_file_name
, fn
->name
);
1095 /* Likewise, we can't deduce exit block counts from the lack
1096 of its successors. */
1097 fn
->blocks
[fn
->num_blocks
- 1].num_succ
= ~(unsigned)0;
1100 /* Propagate the measured counts, this must be done in the same
1101 order as the code in profile.c */
1102 for (ix
= 0, blk
= fn
->blocks
; ix
!= fn
->num_blocks
; ix
++, blk
++)
1104 block_t
const *prev_dst
= NULL
;
1105 int out_of_order
= 0;
1106 int non_fake_succ
= 0;
1108 for (arc
= blk
->succ
; arc
; arc
= arc
->succ_next
)
1116 arc
->count
= *count_ptr
++;
1117 arc
->count_valid
= 1;
1119 arc
->dst
->num_pred
--;
1121 if (prev_dst
&& prev_dst
> arc
->dst
)
1123 prev_dst
= arc
->dst
;
1125 if (non_fake_succ
== 1)
1127 /* If there is only one non-fake exit, it is an
1128 unconditional branch. */
1129 for (arc
= blk
->succ
; arc
; arc
= arc
->succ_next
)
1132 arc
->is_unconditional
= 1;
1133 /* If this block is instrumenting a call, it might be
1134 an artificial block. It is not artificial if it has
1135 a non-fallthrough exit, or the destination of this
1136 arc has more than one entry. Mark the destination
1137 block as a return site, if none of those conditions
1139 if (blk
->is_call_site
&& arc
->fall_through
1140 && arc
->dst
->pred
== arc
&& !arc
->pred_next
)
1141 arc
->dst
->is_call_return
= 1;
1145 /* Sort the successor arcs into ascending dst order. profile.c
1146 normally produces arcs in the right order, but sometimes with
1147 one or two out of order. We're not using a particularly
1151 arc_t
*start
= blk
->succ
;
1152 unsigned changes
= 1;
1156 arc_t
*arc
, *arc_p
, *arc_n
;
1159 for (arc_p
= NULL
, arc
= start
; (arc_n
= arc
->succ_next
);)
1161 if (arc
->dst
> arc_n
->dst
)
1165 arc_p
->succ_next
= arc_n
;
1168 arc
->succ_next
= arc_n
->succ_next
;
1169 arc_n
->succ_next
= arc
;
1182 /* Place it on the invalid chain, it will be ignored if that's
1184 blk
->invalid_chain
= 1;
1185 blk
->chain
= invalid_blocks
;
1186 invalid_blocks
= blk
;
1189 while (invalid_blocks
|| valid_blocks
)
1191 while ((blk
= invalid_blocks
))
1193 gcov_type total
= 0;
1196 invalid_blocks
= blk
->chain
;
1197 blk
->invalid_chain
= 0;
1199 for (arc
= blk
->succ
; arc
; arc
= arc
->succ_next
)
1200 total
+= arc
->count
;
1201 else if (!blk
->num_pred
)
1202 for (arc
= blk
->pred
; arc
; arc
= arc
->pred_next
)
1203 total
+= arc
->count
;
1208 blk
->count_valid
= 1;
1209 blk
->chain
= valid_blocks
;
1210 blk
->valid_chain
= 1;
1213 while ((blk
= valid_blocks
))
1216 arc_t
*arc
, *inv_arc
;
1218 valid_blocks
= blk
->chain
;
1219 blk
->valid_chain
= 0;
1220 if (blk
->num_succ
== 1)
1226 for (arc
= blk
->succ
; arc
; arc
= arc
->succ_next
)
1228 total
-= arc
->count
;
1229 if (!arc
->count_valid
)
1233 inv_arc
->count_valid
= 1;
1234 inv_arc
->count
= total
;
1237 if (dst
->count_valid
)
1239 if (dst
->num_pred
== 1 && !dst
->valid_chain
)
1241 dst
->chain
= valid_blocks
;
1242 dst
->valid_chain
= 1;
1248 if (!dst
->num_pred
&& !dst
->invalid_chain
)
1250 dst
->chain
= invalid_blocks
;
1251 dst
->invalid_chain
= 1;
1252 invalid_blocks
= dst
;
1256 if (blk
->num_pred
== 1)
1262 for (arc
= blk
->pred
; arc
; arc
= arc
->pred_next
)
1264 total
-= arc
->count
;
1265 if (!arc
->count_valid
)
1269 inv_arc
->count_valid
= 1;
1270 inv_arc
->count
= total
;
1273 if (src
->count_valid
)
1275 if (src
->num_succ
== 1 && !src
->valid_chain
)
1277 src
->chain
= valid_blocks
;
1278 src
->valid_chain
= 1;
1284 if (!src
->num_succ
&& !src
->invalid_chain
)
1286 src
->chain
= invalid_blocks
;
1287 src
->invalid_chain
= 1;
1288 invalid_blocks
= src
;
1295 /* If the graph has been correctly solved, every block will have a
1297 for (ix
= 0; ix
< fn
->num_blocks
; ix
++)
1298 if (!fn
->blocks
[ix
].count_valid
)
1300 fnotice (stderr
, "%s:graph is unsolvable for '%s'\n",
1301 bbg_file_name
, fn
->name
);
1308 /* Increment totals in COVERAGE according to arc ARC. */
1311 add_branch_counts (coverage_t
*coverage
, const arc_t
*arc
)
1313 if (arc
->is_call_non_return
)
1316 if (arc
->src
->count
)
1317 coverage
->calls_executed
++;
1319 else if (!arc
->is_unconditional
)
1321 coverage
->branches
++;
1322 if (arc
->src
->count
)
1323 coverage
->branches_executed
++;
1325 coverage
->branches_taken
++;
1329 /* Format a HOST_WIDE_INT as either a percent ratio, or absolute
1330 count. If dp >= 0, format TOP/BOTTOM * 100 to DP decimal places.
1331 If DP is zero, no decimal point is printed. Only print 100% when
1332 TOP==BOTTOM and only print 0% when TOP=0. If dp < 0, then simply
1333 format TOP. Return pointer to a static string. */
1336 format_gcov (gcov_type top
, gcov_type bottom
, int dp
)
1338 static char buffer
[20];
1342 float ratio
= bottom
? (float)top
/ bottom
: 0;
1344 unsigned limit
= 100;
1347 for (ix
= dp
; ix
--; )
1350 percent
= (unsigned) (ratio
* limit
+ (float)0.5);
1351 if (percent
<= 0 && top
)
1353 else if (percent
>= limit
&& top
!= bottom
)
1354 percent
= limit
- 1;
1355 ix
= sprintf (buffer
, "%.*u%%", dp
+ 1, percent
);
1361 buffer
[ix
+1] = buffer
[ix
];
1365 buffer
[ix
+ 1] = '.';
1369 sprintf (buffer
, HOST_WIDEST_INT_PRINT_DEC
, (HOST_WIDEST_INT
)top
);
1375 /* Output summary info for a function. */
1378 function_summary (const coverage_t
*coverage
, const char *title
)
1380 fnotice (stdout
, "%s '%s'\n", title
, coverage
->name
);
1382 if (coverage
->lines
)
1383 fnotice (stdout
, "Lines executed:%s of %d\n",
1384 format_gcov (coverage
->lines_executed
, coverage
->lines
, 2),
1387 fnotice (stdout
, "No executable lines\n");
1391 if (coverage
->branches
)
1393 fnotice (stdout
, "Branches executed:%s of %d\n",
1394 format_gcov (coverage
->branches_executed
,
1395 coverage
->branches
, 2),
1396 coverage
->branches
);
1397 fnotice (stdout
, "Taken at least once:%s of %d\n",
1398 format_gcov (coverage
->branches_taken
,
1399 coverage
->branches
, 2),
1400 coverage
->branches
);
1403 fnotice (stdout
, "No branches\n");
1404 if (coverage
->calls
)
1405 fnotice (stdout
, "Calls executed:%s of %d\n",
1406 format_gcov (coverage
->calls_executed
, coverage
->calls
, 2),
1409 fnotice (stdout
, "No calls\n");
1413 /* Generate an output file name. LONG_OUTPUT_NAMES and PRESERVE_PATHS
1414 affect name generation. With preserve_paths we create a filename
1415 from all path components of the source file, replacing '/' with
1416 '#', without it we simply take the basename component. With
1417 long_output_names we prepend the processed name of the input file
1418 to each output name (except when the current source file is the
1419 input file, so you don't get a double concatenation). The two
1420 components are separated by '##'. Also '.' filename components are
1421 removed and '..' components are renamed to '^'. */
1424 make_gcov_file_name (const char *input_name
, const char *src_name
)
1427 char *name
= XNEWVEC (char, strlen (src_name
) + strlen (input_name
) + 10);
1430 if (flag_long_names
&& strcmp (src_name
, input_name
))
1432 /* Generate the input filename part. */
1433 cptr
= flag_preserve_paths
? NULL
: strrchr (input_name
, '/');
1434 strcat (name
, cptr
? cptr
+ 1 : input_name
);
1435 strcat (name
, "##");
1438 /* Generate the source filename part. */
1439 cptr
= flag_preserve_paths
? NULL
: strrchr (src_name
, '/');
1440 strcat (name
, cptr
? cptr
+ 1 : src_name
);
1442 if (flag_preserve_paths
)
1444 /* Convert '/' to '#', remove '/./', convert '/../' to '/^/' */
1447 for (cptr
= name
; (cptr
= strchr ((prev
= cptr
), '/'));)
1451 if (prev
+ 1 == cptr
&& prev
[0] == '.')
1456 else if (prev
+ 2 == cptr
&& prev
[0] == '.' && prev
[1] == '.')
1468 prev
[0] = prev
[shift
];
1474 strcat (name
, ".gcov");
1478 /* Scan through the bb_data for each line in the block, increment
1479 the line number execution count indicated by the execution count of
1480 the appropriate basic block. */
1483 add_line_counts (coverage_t
*coverage
, function_t
*fn
)
1486 line_t
*line
= NULL
; /* This is propagated from one iteration to the
1489 /* Scan each basic block. */
1490 for (ix
= 0; ix
!= fn
->num_blocks
; ix
++)
1492 block_t
*block
= &fn
->blocks
[ix
];
1494 const source_t
*src
= NULL
;
1497 if (block
->count
&& ix
&& ix
+ 1 != fn
->num_blocks
)
1498 fn
->blocks_executed
++;
1499 for (jx
= 0, encoding
= block
->u
.line
.encoding
;
1500 jx
!= block
->u
.line
.num
; jx
++, encoding
++)
1503 unsigned src_n
= *++encoding
;
1505 for (src
= sources
; src
->index
!= src_n
; src
= src
->next
)
1511 line
= &src
->lines
[*encoding
];
1517 if (!line
->count
&& block
->count
)
1518 coverage
->lines_executed
++;
1521 line
->count
+= block
->count
;
1523 free (block
->u
.line
.encoding
);
1524 block
->u
.cycle
.arc
= NULL
;
1525 block
->u
.cycle
.ident
= ~0U;
1527 if (!ix
|| ix
+ 1 == fn
->num_blocks
)
1528 /* Entry or exit block */;
1529 else if (flag_all_blocks
)
1531 line_t
*block_line
= line
? line
: &fn
->src
->lines
[fn
->line
];
1533 block
->chain
= block_line
->u
.blocks
;
1534 block_line
->u
.blocks
= block
;
1536 else if (flag_branches
)
1540 for (arc
= block
->succ
; arc
; arc
= arc
->succ_next
)
1542 arc
->line_next
= line
->u
.branches
;
1543 line
->u
.branches
= arc
;
1544 if (coverage
&& !arc
->is_unconditional
)
1545 add_branch_counts (coverage
, arc
);
1550 fnotice (stderr
, "%s:no lines for '%s'\n", bbg_file_name
, fn
->name
);
1553 /* Accumulate the line counts of a file. */
1556 accumulate_line_counts (source_t
*src
)
1559 function_t
*fn
, *fn_p
, *fn_n
;
1562 /* Reverse the function order. */
1563 for (fn
= src
->functions
, fn_p
= NULL
; fn
;
1564 fn_p
= fn
, fn
= fn_n
)
1566 fn_n
= fn
->line_next
;
1567 fn
->line_next
= fn_p
;
1569 src
->functions
= fn_p
;
1571 for (ix
= src
->num_lines
, line
= src
->lines
; ix
--; line
++)
1573 if (!flag_all_blocks
)
1575 arc_t
*arc
, *arc_p
, *arc_n
;
1577 /* Total and reverse the branch information. */
1578 for (arc
= line
->u
.branches
, arc_p
= NULL
; arc
;
1579 arc_p
= arc
, arc
= arc_n
)
1581 arc_n
= arc
->line_next
;
1582 arc
->line_next
= arc_p
;
1584 add_branch_counts (&src
->coverage
, arc
);
1586 line
->u
.branches
= arc_p
;
1588 else if (line
->u
.blocks
)
1590 /* The user expects the line count to be the number of times
1591 a line has been executed. Simply summing the block count
1592 will give an artificially high number. The Right Thing
1593 is to sum the entry counts to the graph of blocks on this
1594 line, then find the elementary cycles of the local graph
1595 and add the transition counts of those cycles. */
1596 block_t
*block
, *block_p
, *block_n
;
1597 gcov_type count
= 0;
1599 /* Reverse the block information. */
1600 for (block
= line
->u
.blocks
, block_p
= NULL
; block
;
1601 block_p
= block
, block
= block_n
)
1603 block_n
= block
->chain
;
1604 block
->chain
= block_p
;
1605 block
->u
.cycle
.ident
= ix
;
1607 line
->u
.blocks
= block_p
;
1609 /* Sum the entry arcs. */
1610 for (block
= line
->u
.blocks
; block
; block
= block
->chain
)
1614 for (arc
= block
->pred
; arc
; arc
= arc
->pred_next
)
1616 if (arc
->src
->u
.cycle
.ident
!= ix
)
1617 count
+= arc
->count
;
1619 add_branch_counts (&src
->coverage
, arc
);
1622 /* Initialize the cs_count. */
1623 for (arc
= block
->succ
; arc
; arc
= arc
->succ_next
)
1624 arc
->cs_count
= arc
->count
;
1627 /* Find the loops. This uses the algorithm described in
1628 Tiernan 'An Efficient Search Algorithm to Find the
1629 Elementary Circuits of a Graph', CACM Dec 1970. We hold
1630 the P array by having each block point to the arc that
1631 connects to the previous block. The H array is implicitly
1632 held because of the arc ordering, and the block's
1633 previous arc pointer.
1635 Although the algorithm is O(N^3) for highly connected
1636 graphs, at worst we'll have O(N^2), as most blocks have
1637 only one or two exits. Most graphs will be small.
1639 For each loop we find, locate the arc with the smallest
1640 transition count, and add that to the cumulative
1641 count. Decrease flow over the cycle and remove the arc
1642 from consideration. */
1643 for (block
= line
->u
.blocks
; block
; block
= block
->chain
)
1645 block_t
*head
= block
;
1653 block_t
*dst
= arc
->dst
;
1654 if (/* Already used that arc. */
1656 /* Not to same graph, or before first vertex. */
1657 || dst
->u
.cycle
.ident
!= ix
1658 /* Already in path. */
1659 || dst
->u
.cycle
.arc
)
1661 arc
= arc
->succ_next
;
1667 /* Found a closing arc. */
1668 gcov_type cycle_count
= arc
->cs_count
;
1669 arc_t
*cycle_arc
= arc
;
1672 /* Locate the smallest arc count of the loop. */
1673 for (dst
= head
; (probe_arc
= dst
->u
.cycle
.arc
);
1674 dst
= probe_arc
->src
)
1675 if (cycle_count
> probe_arc
->cs_count
)
1677 cycle_count
= probe_arc
->cs_count
;
1678 cycle_arc
= probe_arc
;
1681 count
+= cycle_count
;
1682 cycle_arc
->cycle
= 1;
1684 /* Remove the flow from the cycle. */
1685 arc
->cs_count
-= cycle_count
;
1686 for (dst
= head
; (probe_arc
= dst
->u
.cycle
.arc
);
1687 dst
= probe_arc
->src
)
1688 probe_arc
->cs_count
-= cycle_count
;
1690 /* Unwind to the cyclic arc. */
1691 while (head
!= cycle_arc
->src
)
1693 arc
= head
->u
.cycle
.arc
;
1694 head
->u
.cycle
.arc
= NULL
;
1698 arc
= arc
->succ_next
;
1702 /* Add new block to chain. */
1703 dst
->u
.cycle
.arc
= arc
;
1707 /* We could not add another vertex to the path. Remove
1708 the last vertex from the list. */
1709 arc
= head
->u
.cycle
.arc
;
1712 /* It was not the first vertex. Move onto next arc. */
1713 head
->u
.cycle
.arc
= NULL
;
1715 arc
= arc
->succ_next
;
1716 goto current_vertex
;
1718 /* Mark this block as unusable. */
1719 block
->u
.cycle
.ident
= ~0U;
1722 line
->count
= count
;
1727 src
->coverage
.lines
++;
1729 src
->coverage
.lines_executed
++;
1734 /* Output information about ARC number IX. Returns nonzero if
1735 anything is output. */
1738 output_branch_count (FILE *gcov_file
, int ix
, const arc_t
*arc
)
1741 if (arc
->is_call_non_return
)
1743 if (arc
->src
->count
)
1745 fnotice (gcov_file
, "call %2d returned %s\n", ix
,
1746 format_gcov (arc
->src
->count
- arc
->count
,
1747 arc
->src
->count
, -flag_counts
));
1750 fnotice (gcov_file
, "call %2d never executed\n", ix
);
1752 else if (!arc
->is_unconditional
)
1754 if (arc
->src
->count
)
1755 fnotice (gcov_file
, "branch %2d taken %s%s\n", ix
,
1756 format_gcov (arc
->count
, arc
->src
->count
, -flag_counts
),
1757 arc
->fall_through
? " (fallthrough)" : "");
1759 fnotice (gcov_file
, "branch %2d never executed\n", ix
);
1761 else if (flag_unconditional
&& !arc
->dst
->is_call_return
)
1763 if (arc
->src
->count
)
1764 fnotice (gcov_file
, "unconditional %2d taken %s\n", ix
,
1765 format_gcov (arc
->count
, arc
->src
->count
, -flag_counts
));
1767 fnotice (gcov_file
, "unconditional %2d never executed\n", ix
);
1775 /* Read in the source file one line at a time, and output that line to
1776 the gcov file preceded by its execution count and other
1780 output_lines (FILE *gcov_file
, const source_t
*src
)
1783 unsigned line_num
; /* current line number. */
1784 const line_t
*line
; /* current line info ptr. */
1785 char string
[STRING_SIZE
]; /* line buffer. */
1786 char const *retval
= ""; /* status of source file reading. */
1787 function_t
*fn
= NULL
;
1789 fprintf (gcov_file
, "%9s:%5d:Source:%s\n", "-", 0, src
->name
);
1790 fprintf (gcov_file
, "%9s:%5d:Graph:%s\n", "-", 0, bbg_file_name
);
1791 fprintf (gcov_file
, "%9s:%5d:Data:%s\n", "-", 0,
1792 no_data_file
? "-" : da_file_name
);
1793 fprintf (gcov_file
, "%9s:%5d:Runs:%u\n", "-", 0,
1794 object_summary
.ctrs
[GCOV_COUNTER_ARCS
].runs
);
1795 fprintf (gcov_file
, "%9s:%5d:Programs:%u\n", "-", 0, program_count
);
1797 source_file
= fopen (src
->name
, "r");
1800 fnotice (stderr
, "%s:cannot open source file\n", src
->name
);
1807 if (!fstat (fileno (source_file
), &status
)
1808 && status
.st_mtime
> bbg_file_time
)
1810 fnotice (stderr
, "%s:source file is newer than graph file '%s'\n",
1811 src
->name
, bbg_file_name
);
1812 fprintf (gcov_file
, "%9s:%5d:Source is newer than graph\n",
1818 fn
= src
->functions
;
1820 for (line_num
= 1, line
= &src
->lines
[line_num
];
1821 line_num
< src
->num_lines
; line_num
++, line
++)
1823 for (; fn
&& fn
->line
== line_num
; fn
= fn
->line_next
)
1825 arc_t
*arc
= fn
->blocks
[fn
->num_blocks
- 1].pred
;
1826 gcov_type return_count
= fn
->blocks
[fn
->num_blocks
- 1].count
;
1828 for (; arc
; arc
= arc
->pred_next
)
1830 return_count
-= arc
->count
;
1832 fprintf (gcov_file
, "function %s", fn
->name
);
1833 fprintf (gcov_file
, " called %s",
1834 format_gcov (fn
->blocks
[0].count
, 0, -1));
1835 fprintf (gcov_file
, " returned %s",
1836 format_gcov (return_count
, fn
->blocks
[0].count
, 0));
1837 fprintf (gcov_file
, " blocks executed %s",
1838 format_gcov (fn
->blocks_executed
, fn
->num_blocks
- 2, 0));
1839 fprintf (gcov_file
, "\n");
1842 /* For lines which don't exist in the .bb file, print '-' before
1843 the source line. For lines which exist but were never
1844 executed, print '#####' before the source line. Otherwise,
1845 print the execution count before the source line. There are
1846 16 spaces of indentation added before the source line so that
1847 tabs won't be messed up. */
1848 fprintf (gcov_file
, "%9s:%5u:",
1849 !line
->exists
? "-" : !line
->count
? "#####"
1850 : format_gcov (line
->count
, 0, -1), line_num
);
1854 /* Copy source line. */
1857 retval
= fgets (string
, STRING_SIZE
, source_file
);
1860 fputs (retval
, gcov_file
);
1862 while (!retval
[0] || retval
[strlen (retval
) - 1] != '\n');
1865 fputs ("/*EOF*/\n", gcov_file
);
1867 if (flag_all_blocks
)
1873 for (ix
= jx
= 0, block
= line
->u
.blocks
; block
;
1874 block
= block
->chain
)
1876 if (!block
->is_call_return
)
1877 fprintf (gcov_file
, "%9s:%5u-block %2d\n",
1878 !line
->exists
? "-" : !block
->count
? "$$$$$"
1879 : format_gcov (block
->count
, 0, -1),
1882 for (arc
= block
->succ
; arc
; arc
= arc
->succ_next
)
1883 jx
+= output_branch_count (gcov_file
, jx
, arc
);
1886 else if (flag_branches
)
1891 for (ix
= 0, arc
= line
->u
.branches
; arc
; arc
= arc
->line_next
)
1892 ix
+= output_branch_count (gcov_file
, ix
, arc
);
1896 /* Handle all remaining source lines. There may be lines after the
1897 last line of code. */
1900 for (; (retval
= fgets (string
, STRING_SIZE
, source_file
)); line_num
++)
1902 fprintf (gcov_file
, "%9s:%5u:%s", "-", line_num
, retval
);
1904 while (!retval
[0] || retval
[strlen (retval
) - 1] != '\n')
1906 retval
= fgets (string
, STRING_SIZE
, source_file
);
1909 fputs (retval
, gcov_file
);
1915 fclose (source_file
);