0
|
1 /* Gcov.c: prepend line execution counts and branch probabilities to a
|
|
2 source file.
|
|
3 Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999,
|
|
4 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
|
|
5 Free Software Foundation, Inc.
|
|
6 Contributed by James E. Wilson of Cygnus Support.
|
|
7 Mangled by Bob Manson of Cygnus Support.
|
|
8 Mangled further by Nathan Sidwell <nathan@codesourcery.com>
|
|
9
|
|
10 Gcov is free software; you can redistribute it and/or modify
|
|
11 it under the terms of the GNU General Public License as published by
|
|
12 the Free Software Foundation; either version 3, or (at your option)
|
|
13 any later version.
|
|
14
|
|
15 Gcov is distributed in the hope that it will be useful,
|
|
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
18 GNU General Public License for more details.
|
|
19
|
|
20 You should have received a copy of the GNU General Public License
|
|
21 along with Gcov; see the file COPYING3. If not see
|
|
22 <http://www.gnu.org/licenses/>. */
|
|
23
|
|
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. */
|
|
28
|
|
29 /* ??? Should have an option to print the number of basic blocks, and the
|
|
30 percent of them that are covered. */
|
|
31
|
|
32 /* Need an option to show individual block counts, and show
|
|
33 probabilities of fall through arcs. */
|
|
34
|
|
35 #include "config.h"
|
|
36 #include "system.h"
|
|
37 #include "coretypes.h"
|
|
38 #include "tm.h"
|
|
39 #include "intl.h"
|
|
40 #include "version.h"
|
|
41
|
|
42 #include <getopt.h>
|
|
43
|
|
44 #define IN_GCOV 1
|
|
45 #include "gcov-io.h"
|
|
46 #include "gcov-io.c"
|
|
47
|
|
48 /* The gcno file is generated by -ftest-coverage option. The gcda file is
|
|
49 generated by a program compiled with -fprofile-arcs. Their formats
|
|
50 are documented in gcov-io.h. */
|
|
51
|
|
52 /* The functions in this file for creating and solution program flow graphs
|
|
53 are very similar to functions in the gcc source file profile.c. In
|
|
54 some places we make use of the knowledge of how profile.c works to
|
|
55 select particular algorithms here. */
|
|
56
|
|
57 /* This is the size of the buffer used to read in source file lines. */
|
|
58
|
|
59 #define STRING_SIZE 200
|
|
60
|
|
61 struct function_info;
|
|
62 struct block_info;
|
|
63 struct source_info;
|
|
64
|
|
65 /* Describes an arc between two basic blocks. */
|
|
66
|
|
67 typedef struct arc_info
|
|
68 {
|
|
69 /* source and destination blocks. */
|
|
70 struct block_info *src;
|
|
71 struct block_info *dst;
|
|
72
|
|
73 /* transition counts. */
|
|
74 gcov_type count;
|
|
75 /* used in cycle search, so that we do not clobber original counts. */
|
|
76 gcov_type cs_count;
|
|
77
|
|
78 unsigned int count_valid : 1;
|
|
79 unsigned int on_tree : 1;
|
|
80 unsigned int fake : 1;
|
|
81 unsigned int fall_through : 1;
|
|
82
|
|
83 /* Arc is for a function that abnormally returns. */
|
|
84 unsigned int is_call_non_return : 1;
|
|
85
|
|
86 /* Arc is for catch/setjmp. */
|
|
87 unsigned int is_nonlocal_return : 1;
|
|
88
|
|
89 /* Is an unconditional branch. */
|
|
90 unsigned int is_unconditional : 1;
|
|
91
|
|
92 /* Loop making arc. */
|
|
93 unsigned int cycle : 1;
|
|
94
|
|
95 /* Next branch on line. */
|
|
96 struct arc_info *line_next;
|
|
97
|
|
98 /* Links to next arc on src and dst lists. */
|
|
99 struct arc_info *succ_next;
|
|
100 struct arc_info *pred_next;
|
|
101 } arc_t;
|
|
102
|
|
103 /* Describes a basic block. Contains lists of arcs to successor and
|
|
104 predecessor blocks. */
|
|
105
|
|
106 typedef struct block_info
|
|
107 {
|
|
108 /* Chain of exit and entry arcs. */
|
|
109 arc_t *succ;
|
|
110 arc_t *pred;
|
|
111
|
|
112 /* Number of unprocessed exit and entry arcs. */
|
|
113 gcov_type num_succ;
|
|
114 gcov_type num_pred;
|
|
115
|
|
116 /* Block execution count. */
|
|
117 gcov_type count;
|
|
118 unsigned flags : 13;
|
|
119 unsigned count_valid : 1;
|
|
120 unsigned valid_chain : 1;
|
|
121 unsigned invalid_chain : 1;
|
|
122
|
|
123 /* Block is a call instrumenting site. */
|
|
124 unsigned is_call_site : 1; /* Does the call. */
|
|
125 unsigned is_call_return : 1; /* Is the return. */
|
|
126
|
|
127 /* Block is a landing pad for longjmp or throw. */
|
|
128 unsigned is_nonlocal_return : 1;
|
|
129
|
|
130 union
|
|
131 {
|
|
132 struct
|
|
133 {
|
|
134 /* Array of line numbers and source files. source files are
|
|
135 introduced by a linenumber of zero, the next 'line number' is
|
|
136 the number of the source file. Always starts with a source
|
|
137 file. */
|
|
138 unsigned *encoding;
|
|
139 unsigned num;
|
|
140 } line; /* Valid until blocks are linked onto lines */
|
|
141 struct
|
|
142 {
|
|
143 /* Single line graph cycle workspace. Used for all-blocks
|
|
144 mode. */
|
|
145 arc_t *arc;
|
|
146 unsigned ident;
|
|
147 } cycle; /* Used in all-blocks mode, after blocks are linked onto
|
|
148 lines. */
|
|
149 } u;
|
|
150
|
|
151 /* Temporary chain for solving graph, and for chaining blocks on one
|
|
152 line. */
|
|
153 struct block_info *chain;
|
|
154
|
|
155 } block_t;
|
|
156
|
|
157 /* Describes a single function. Contains an array of basic blocks. */
|
|
158
|
|
159 typedef struct function_info
|
|
160 {
|
|
161 /* Name of function. */
|
|
162 char *name;
|
|
163 unsigned ident;
|
|
164 unsigned checksum;
|
|
165
|
|
166 /* Array of basic blocks. */
|
|
167 block_t *blocks;
|
|
168 unsigned num_blocks;
|
|
169 unsigned blocks_executed;
|
|
170
|
|
171 /* Raw arc coverage counts. */
|
|
172 gcov_type *counts;
|
|
173 unsigned num_counts;
|
|
174
|
|
175 /* First line number. */
|
|
176 unsigned line;
|
|
177 struct source_info *src;
|
|
178
|
|
179 /* Next function in same source file. */
|
|
180 struct function_info *line_next;
|
|
181
|
|
182 /* Next function. */
|
|
183 struct function_info *next;
|
|
184 } function_t;
|
|
185
|
|
186 /* Describes coverage of a file or function. */
|
|
187
|
|
188 typedef struct coverage_info
|
|
189 {
|
|
190 int lines;
|
|
191 int lines_executed;
|
|
192
|
|
193 int branches;
|
|
194 int branches_executed;
|
|
195 int branches_taken;
|
|
196
|
|
197 int calls;
|
|
198 int calls_executed;
|
|
199
|
|
200 char *name;
|
|
201 } coverage_t;
|
|
202
|
|
203 /* Describes a single line of source. Contains a chain of basic blocks
|
|
204 with code on it. */
|
|
205
|
|
206 typedef struct line_info
|
|
207 {
|
|
208 gcov_type count; /* execution count */
|
|
209 union
|
|
210 {
|
|
211 arc_t *branches; /* branches from blocks that end on this
|
|
212 line. Used for branch-counts when not
|
|
213 all-blocks mode. */
|
|
214 block_t *blocks; /* blocks which start on this line. Used
|
|
215 in all-blocks mode. */
|
|
216 } u;
|
|
217 unsigned exists : 1;
|
|
218 } line_t;
|
|
219
|
|
220 /* Describes a file mentioned in the block graph. Contains an array
|
|
221 of line info. */
|
|
222
|
|
223 typedef struct source_info
|
|
224 {
|
|
225 /* Name of source file. */
|
|
226 char *name;
|
|
227 unsigned index;
|
|
228 time_t file_time;
|
|
229
|
|
230 /* Array of line information. */
|
|
231 line_t *lines;
|
|
232 unsigned num_lines;
|
|
233
|
|
234 coverage_t coverage;
|
|
235
|
|
236 /* Functions in this source file. These are in ascending line
|
|
237 number order. */
|
|
238 function_t *functions;
|
|
239
|
|
240 /* Next source file. */
|
|
241 struct source_info *next;
|
|
242 } source_t;
|
|
243
|
|
244 /* Holds a list of function basic block graphs. */
|
|
245
|
|
246 static function_t *functions;
|
|
247
|
|
248 /* This points to the head of the sourcefile structure list. New elements
|
|
249 are always prepended. */
|
|
250
|
|
251 static source_t *sources;
|
|
252
|
|
253 /* Next index for a source file. */
|
|
254
|
|
255 static unsigned source_index;
|
|
256
|
|
257 /* This holds data summary information. */
|
|
258
|
|
259 static struct gcov_summary object_summary;
|
|
260 static unsigned program_count;
|
|
261
|
|
262 /* Modification time of graph file. */
|
|
263
|
|
264 static time_t bbg_file_time;
|
|
265
|
|
266 /* Name and file pointer of the input file for the basic block graph. */
|
|
267
|
|
268 static char *bbg_file_name;
|
|
269
|
|
270 /* Stamp of the bbg file */
|
|
271 static unsigned bbg_stamp;
|
|
272
|
|
273 /* Name and file pointer of the input file for the arc count data. */
|
|
274
|
|
275 static char *da_file_name;
|
|
276
|
|
277 /* Data file is missing. */
|
|
278
|
|
279 static int no_data_file;
|
|
280
|
|
281 /* If there is several input files, compute and display results after
|
|
282 reading all data files. This way if two or more gcda file refer to
|
|
283 the same source file (eg inline subprograms in a .h file), the
|
|
284 counts are added. */
|
|
285
|
|
286 static int multiple_files = 0;
|
|
287
|
|
288 /* Output branch probabilities. */
|
|
289
|
|
290 static int flag_branches = 0;
|
|
291
|
|
292 /* Show unconditional branches too. */
|
|
293 static int flag_unconditional = 0;
|
|
294
|
|
295 /* Output a gcov file if this is true. This is on by default, and can
|
|
296 be turned off by the -n option. */
|
|
297
|
|
298 static int flag_gcov_file = 1;
|
|
299
|
|
300 /* For included files, make the gcov output file name include the name
|
|
301 of the input source file. For example, if x.h is included in a.c,
|
|
302 then the output file name is a.c##x.h.gcov instead of x.h.gcov. */
|
|
303
|
|
304 static int flag_long_names = 0;
|
|
305
|
|
306 /* Output count information for every basic block, not merely those
|
|
307 that contain line number information. */
|
|
308
|
|
309 static int flag_all_blocks = 0;
|
|
310
|
|
311 /* Output summary info for each function. */
|
|
312
|
|
313 static int flag_function_summary = 0;
|
|
314
|
|
315 /* Object directory file prefix. This is the directory/file where the
|
|
316 graph and data files are looked for, if nonzero. */
|
|
317
|
|
318 static char *object_directory = 0;
|
|
319
|
|
320 /* Preserve all pathname components. Needed when object files and
|
|
321 source files are in subdirectories. '/' is mangled as '#', '.' is
|
|
322 elided and '..' mangled to '^'. */
|
|
323
|
|
324 static int flag_preserve_paths = 0;
|
|
325
|
|
326 /* Output the number of times a branch was taken as opposed to the percentage
|
|
327 of times it was taken. */
|
|
328
|
|
329 static int flag_counts = 0;
|
|
330
|
|
331 /* Forward declarations. */
|
|
332 static void fnotice (FILE *, const char *, ...) ATTRIBUTE_PRINTF_2;
|
|
333 static int process_args (int, char **);
|
|
334 static void print_usage (int) ATTRIBUTE_NORETURN;
|
|
335 static void print_version (void) ATTRIBUTE_NORETURN;
|
|
336 static void process_file (const char *);
|
|
337 static void generate_results (const char *);
|
|
338 static void create_file_names (const char *);
|
|
339 static source_t *find_source (const char *);
|
|
340 static int read_graph_file (void);
|
|
341 static int read_count_file (void);
|
|
342 static void solve_flow_graph (function_t *);
|
|
343 static void add_branch_counts (coverage_t *, const arc_t *);
|
|
344 static void add_line_counts (coverage_t *, function_t *);
|
|
345 static void function_summary (const coverage_t *, const char *);
|
|
346 static const char *format_gcov (gcov_type, gcov_type, int);
|
|
347 static void accumulate_line_counts (source_t *);
|
|
348 static int output_branch_count (FILE *, int, const arc_t *);
|
|
349 static void output_lines (FILE *, const source_t *);
|
|
350 static char *make_gcov_file_name (const char *, const char *);
|
|
351 static void release_structures (void);
|
|
352 extern int main (int, char **);
|
|
353
|
|
354 int
|
|
355 main (int argc, char **argv)
|
|
356 {
|
|
357 int argno;
|
|
358
|
|
359 /* Unlock the stdio streams. */
|
|
360 unlock_std_streams ();
|
|
361
|
|
362 gcc_init_libintl ();
|
|
363
|
|
364 /* Handle response files. */
|
|
365 expandargv (&argc, &argv);
|
|
366
|
|
367 argno = process_args (argc, argv);
|
|
368 if (optind == argc)
|
|
369 print_usage (true);
|
|
370
|
|
371 if (argc - argno > 1)
|
|
372 multiple_files = 1;
|
|
373
|
|
374 for (; argno != argc; argno++)
|
|
375 process_file (argv[argno]);
|
|
376
|
|
377 generate_results (multiple_files ? NULL : argv[argc - 1]);
|
|
378
|
|
379 release_structures ();
|
|
380
|
|
381 return 0;
|
|
382 }
|
|
383
|
|
384 static void
|
|
385 fnotice (FILE *file, const char *cmsgid, ...)
|
|
386 {
|
|
387 va_list ap;
|
|
388
|
|
389 va_start (ap, cmsgid);
|
|
390 vfprintf (file, _(cmsgid), ap);
|
|
391 va_end (ap);
|
|
392 }
|
|
393
|
|
394 /* Print a usage message and exit. If ERROR_P is nonzero, this is an error,
|
|
395 otherwise the output of --help. */
|
|
396
|
|
397 static void
|
|
398 print_usage (int error_p)
|
|
399 {
|
|
400 FILE *file = error_p ? stderr : stdout;
|
|
401 int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE;
|
|
402
|
|
403 fnotice (file, "Usage: gcov [OPTION]... SOURCEFILE...\n\n");
|
|
404 fnotice (file, "Print code coverage information.\n\n");
|
|
405 fnotice (file, " -h, --help Print this help, then exit\n");
|
|
406 fnotice (file, " -v, --version Print version number, then exit\n");
|
|
407 fnotice (file, " -a, --all-blocks Show information for every basic block\n");
|
|
408 fnotice (file, " -b, --branch-probabilities Include branch probabilities in output\n");
|
|
409 fnotice (file, " -c, --branch-counts Given counts of branches taken\n\
|
|
410 rather than percentages\n");
|
|
411 fnotice (file, " -n, --no-output Do not create an output file\n");
|
|
412 fnotice (file, " -l, --long-file-names Use long output file names for included\n\
|
|
413 source files\n");
|
|
414 fnotice (file, " -f, --function-summaries Output summaries for each function\n");
|
|
415 fnotice (file, " -o, --object-directory DIR|FILE Search for object files in DIR or called FILE\n");
|
|
416 fnotice (file, " -p, --preserve-paths Preserve all pathname components\n");
|
|
417 fnotice (file, " -u, --unconditional-branches Show unconditional branch counts too\n");
|
|
418 fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n",
|
|
419 bug_report_url);
|
|
420 exit (status);
|
|
421 }
|
|
422
|
|
423 /* Print version information and exit. */
|
|
424
|
|
425 static void
|
|
426 print_version (void)
|
|
427 {
|
|
428 fnotice (stdout, "gcov %s%s\n", pkgversion_string, version_string);
|
|
429 fprintf (stdout, "Copyright %s 2009 Free Software Foundation, Inc.\n",
|
|
430 _("(C)"));
|
|
431 fnotice (stdout,
|
|
432 _("This is free software; see the source for copying conditions.\n"
|
|
433 "There is NO warranty; not even for MERCHANTABILITY or \n"
|
|
434 "FITNESS FOR A PARTICULAR PURPOSE.\n\n"));
|
|
435 exit (SUCCESS_EXIT_CODE);
|
|
436 }
|
|
437
|
|
438 static const struct option options[] =
|
|
439 {
|
|
440 { "help", no_argument, NULL, 'h' },
|
|
441 { "version", no_argument, NULL, 'v' },
|
|
442 { "all-blocks", no_argument, NULL, 'a' },
|
|
443 { "branch-probabilities", no_argument, NULL, 'b' },
|
|
444 { "branch-counts", no_argument, NULL, 'c' },
|
|
445 { "no-output", no_argument, NULL, 'n' },
|
|
446 { "long-file-names", no_argument, NULL, 'l' },
|
|
447 { "function-summaries", no_argument, NULL, 'f' },
|
|
448 { "preserve-paths", no_argument, NULL, 'p' },
|
|
449 { "object-directory", required_argument, NULL, 'o' },
|
|
450 { "object-file", required_argument, NULL, 'o' },
|
|
451 { "unconditional-branches", no_argument, NULL, 'u' },
|
|
452 { 0, 0, 0, 0 }
|
|
453 };
|
|
454
|
|
455 /* Process args, return index to first non-arg. */
|
|
456
|
|
457 static int
|
|
458 process_args (int argc, char **argv)
|
|
459 {
|
|
460 int opt;
|
|
461
|
|
462 while ((opt = getopt_long (argc, argv, "abcfhlno:puv", options, NULL)) != -1)
|
|
463 {
|
|
464 switch (opt)
|
|
465 {
|
|
466 case 'a':
|
|
467 flag_all_blocks = 1;
|
|
468 break;
|
|
469 case 'b':
|
|
470 flag_branches = 1;
|
|
471 break;
|
|
472 case 'c':
|
|
473 flag_counts = 1;
|
|
474 break;
|
|
475 case 'f':
|
|
476 flag_function_summary = 1;
|
|
477 break;
|
|
478 case 'h':
|
|
479 print_usage (false);
|
|
480 /* print_usage will exit. */
|
|
481 case 'l':
|
|
482 flag_long_names = 1;
|
|
483 break;
|
|
484 case 'n':
|
|
485 flag_gcov_file = 0;
|
|
486 break;
|
|
487 case 'o':
|
|
488 object_directory = optarg;
|
|
489 break;
|
|
490 case 'p':
|
|
491 flag_preserve_paths = 1;
|
|
492 break;
|
|
493 case 'u':
|
|
494 flag_unconditional = 1;
|
|
495 break;
|
|
496 case 'v':
|
|
497 print_version ();
|
|
498 /* print_version will exit. */
|
|
499 default:
|
|
500 print_usage (true);
|
|
501 /* print_usage will exit. */
|
|
502 }
|
|
503 }
|
|
504
|
|
505 return optind;
|
|
506 }
|
|
507
|
|
508 /* Process a single source file. */
|
|
509
|
|
510 static void
|
|
511 process_file (const char *file_name)
|
|
512 {
|
|
513 function_t *fn;
|
|
514 function_t *fn_p;
|
|
515 function_t *old_functions;
|
|
516
|
|
517 /* Save and clear the list of current functions. They will be appended
|
|
518 later. */
|
|
519 old_functions = functions;
|
|
520 functions = NULL;
|
|
521
|
|
522 create_file_names (file_name);
|
|
523 if (read_graph_file ())
|
|
524 return;
|
|
525
|
|
526 if (!functions)
|
|
527 {
|
|
528 fnotice (stderr, "%s:no functions found\n", bbg_file_name);
|
|
529 return;
|
|
530 }
|
|
531
|
|
532 if (read_count_file ())
|
|
533 return;
|
|
534
|
|
535 for (fn_p = NULL, fn = functions; fn; fn_p = fn, fn = fn->next)
|
|
536 solve_flow_graph (fn);
|
|
537
|
|
538 if (fn_p)
|
|
539 fn_p->next = old_functions;
|
|
540 }
|
|
541
|
|
542 static void
|
|
543 generate_results (const char *file_name)
|
|
544 {
|
|
545 source_t *src;
|
|
546 function_t *fn;
|
|
547
|
|
548 for (src = sources; src; src = src->next)
|
|
549 src->lines = XCNEWVEC (line_t, src->num_lines);
|
|
550 for (fn = functions; fn; fn = fn->next)
|
|
551 {
|
|
552 coverage_t coverage;
|
|
553
|
|
554 memset (&coverage, 0, sizeof (coverage));
|
|
555 coverage.name = fn->name;
|
|
556 add_line_counts (flag_function_summary ? &coverage : NULL, fn);
|
|
557 if (flag_function_summary)
|
|
558 {
|
|
559 function_summary (&coverage, "Function");
|
|
560 fnotice (stdout, "\n");
|
|
561 }
|
|
562 }
|
|
563
|
|
564 for (src = sources; src; src = src->next)
|
|
565 {
|
|
566 accumulate_line_counts (src);
|
|
567 function_summary (&src->coverage, "File");
|
|
568 if (flag_gcov_file)
|
|
569 {
|
|
570 char *gcov_file_name = make_gcov_file_name (file_name, src->name);
|
|
571 FILE *gcov_file = fopen (gcov_file_name, "w");
|
|
572
|
|
573 if (gcov_file)
|
|
574 {
|
|
575 fnotice (stdout, "%s:creating '%s'\n",
|
|
576 src->name, gcov_file_name);
|
|
577 output_lines (gcov_file, src);
|
|
578 if (ferror (gcov_file))
|
|
579 fnotice (stderr, "%s:error writing output file '%s'\n",
|
|
580 src->name, gcov_file_name);
|
|
581 fclose (gcov_file);
|
|
582 }
|
|
583 else
|
|
584 fnotice (stderr, "%s:could not open output file '%s'\n",
|
|
585 src->name, gcov_file_name);
|
|
586 free (gcov_file_name);
|
|
587 }
|
|
588 fnotice (stdout, "\n");
|
|
589 }
|
|
590 }
|
|
591
|
|
592 /* Release all memory used. */
|
|
593
|
|
594 static void
|
|
595 release_structures (void)
|
|
596 {
|
|
597 function_t *fn;
|
|
598 source_t *src;
|
|
599
|
|
600 while ((src = sources))
|
|
601 {
|
|
602 sources = src->next;
|
|
603
|
|
604 free (src->name);
|
|
605 free (src->lines);
|
|
606 }
|
|
607
|
|
608 while ((fn = functions))
|
|
609 {
|
|
610 unsigned ix;
|
|
611 block_t *block;
|
|
612
|
|
613 functions = fn->next;
|
|
614 for (ix = fn->num_blocks, block = fn->blocks; ix--; block++)
|
|
615 {
|
|
616 arc_t *arc, *arc_n;
|
|
617
|
|
618 for (arc = block->succ; arc; arc = arc_n)
|
|
619 {
|
|
620 arc_n = arc->succ_next;
|
|
621 free (arc);
|
|
622 }
|
|
623 }
|
|
624 free (fn->blocks);
|
|
625 free (fn->counts);
|
|
626 }
|
|
627 }
|
|
628
|
|
629 /* Generate the names of the graph and data files. If OBJECT_DIRECTORY
|
|
630 is not specified, these are looked for in the current directory,
|
|
631 and named from the basename of the FILE_NAME sans extension. If
|
|
632 OBJECT_DIRECTORY is specified and is a directory, the files are in
|
|
633 that directory, but named from the basename of the FILE_NAME, sans
|
|
634 extension. Otherwise OBJECT_DIRECTORY is taken to be the name of
|
|
635 the object *file*, and the data files are named from that. */
|
|
636
|
|
637 static void
|
|
638 create_file_names (const char *file_name)
|
|
639 {
|
|
640 char *cptr;
|
|
641 char *name;
|
|
642 int length = strlen (file_name);
|
|
643 int base;
|
|
644
|
|
645 /* Free previous file names. */
|
|
646 if (bbg_file_name)
|
|
647 free (bbg_file_name);
|
|
648 if (da_file_name)
|
|
649 free (da_file_name);
|
|
650 da_file_name = bbg_file_name = NULL;
|
|
651 bbg_file_time = 0;
|
|
652 bbg_stamp = 0;
|
|
653
|
|
654 if (object_directory && object_directory[0])
|
|
655 {
|
|
656 struct stat status;
|
|
657
|
|
658 length += strlen (object_directory) + 2;
|
|
659 name = XNEWVEC (char, length);
|
|
660 name[0] = 0;
|
|
661
|
|
662 base = !stat (object_directory, &status) && S_ISDIR (status.st_mode);
|
|
663 strcat (name, object_directory);
|
|
664 if (base && (! IS_DIR_SEPARATOR (name[strlen (name) - 1])))
|
|
665 strcat (name, "/");
|
|
666 }
|
|
667 else
|
|
668 {
|
|
669 name = XNEWVEC (char, length + 1);
|
|
670 name[0] = 0;
|
|
671 base = 1;
|
|
672 }
|
|
673
|
|
674 if (base)
|
|
675 {
|
|
676 /* Append source file name. */
|
|
677 const char *cptr = lbasename (file_name);
|
|
678 strcat (name, cptr ? cptr : file_name);
|
|
679 }
|
|
680
|
|
681 /* Remove the extension. */
|
|
682 cptr = strrchr (name, '.');
|
|
683 if (cptr)
|
|
684 *cptr = 0;
|
|
685
|
|
686 length = strlen (name);
|
|
687
|
|
688 bbg_file_name = XNEWVEC (char, length + strlen (GCOV_NOTE_SUFFIX) + 1);
|
|
689 strcpy (bbg_file_name, name);
|
|
690 strcpy (bbg_file_name + length, GCOV_NOTE_SUFFIX);
|
|
691
|
|
692 da_file_name = XNEWVEC (char, length + strlen (GCOV_DATA_SUFFIX) + 1);
|
|
693 strcpy (da_file_name, name);
|
|
694 strcpy (da_file_name + length, GCOV_DATA_SUFFIX);
|
|
695
|
|
696 free (name);
|
|
697 return;
|
|
698 }
|
|
699
|
|
700 /* Find or create a source file structure for FILE_NAME. Copies
|
|
701 FILE_NAME on creation */
|
|
702
|
|
703 static source_t *
|
|
704 find_source (const char *file_name)
|
|
705 {
|
|
706 source_t *src;
|
|
707 struct stat status;
|
|
708
|
|
709 if (!file_name)
|
|
710 file_name = "<unknown>";
|
|
711
|
|
712 for (src = sources; src; src = src->next)
|
|
713 if (!strcmp (file_name, src->name))
|
|
714 break;
|
|
715
|
|
716 if (!src)
|
|
717 {
|
|
718 src = XCNEW (source_t);
|
|
719 src->name = xstrdup (file_name);
|
|
720 src->coverage.name = src->name;
|
|
721 src->index = source_index++;
|
|
722 src->next = sources;
|
|
723 sources = src;
|
|
724
|
|
725 if (!stat (file_name, &status))
|
|
726 src->file_time = status.st_mtime;
|
|
727 }
|
|
728
|
|
729 if (src->file_time > bbg_file_time)
|
|
730 {
|
|
731 static int info_emitted;
|
|
732
|
|
733 fnotice (stderr, "%s:source file is newer than graph file '%s'\n",
|
|
734 src->name, bbg_file_name);
|
|
735 if (!info_emitted)
|
|
736 {
|
|
737 fnotice (stderr,
|
|
738 "(the message is only displayed one per source file)\n");
|
|
739 info_emitted = 1;
|
|
740 }
|
|
741 src->file_time = 0;
|
|
742 }
|
|
743
|
|
744 return src;
|
|
745 }
|
|
746
|
|
747 /* Read the graph file. Return nonzero on fatal error. */
|
|
748
|
|
749 static int
|
|
750 read_graph_file (void)
|
|
751 {
|
|
752 unsigned version;
|
|
753 unsigned current_tag = 0;
|
|
754 struct function_info *fn = NULL;
|
|
755 function_t *old_functions_head = functions;
|
|
756 source_t *src = NULL;
|
|
757 unsigned ix;
|
|
758 unsigned tag;
|
|
759
|
|
760 if (!gcov_open (bbg_file_name, 1))
|
|
761 {
|
|
762 fnotice (stderr, "%s:cannot open graph file\n", bbg_file_name);
|
|
763 return 1;
|
|
764 }
|
|
765 bbg_file_time = gcov_time ();
|
|
766 if (!gcov_magic (gcov_read_unsigned (), GCOV_NOTE_MAGIC))
|
|
767 {
|
|
768 fnotice (stderr, "%s:not a gcov graph file\n", bbg_file_name);
|
|
769 gcov_close ();
|
|
770 return 1;
|
|
771 }
|
|
772
|
|
773 version = gcov_read_unsigned ();
|
|
774 if (version != GCOV_VERSION)
|
|
775 {
|
|
776 char v[4], e[4];
|
|
777
|
|
778 GCOV_UNSIGNED2STRING (v, version);
|
|
779 GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
|
|
780
|
|
781 fnotice (stderr, "%s:version '%.4s', prefer '%.4s'\n",
|
|
782 bbg_file_name, v, e);
|
|
783 }
|
|
784 bbg_stamp = gcov_read_unsigned ();
|
|
785
|
|
786 while ((tag = gcov_read_unsigned ()))
|
|
787 {
|
|
788 unsigned length = gcov_read_unsigned ();
|
|
789 gcov_position_t base = gcov_position ();
|
|
790
|
|
791 if (tag == GCOV_TAG_FUNCTION)
|
|
792 {
|
|
793 char *function_name;
|
|
794 unsigned ident, checksum, lineno;
|
|
795 source_t *src;
|
|
796 function_t *probe, *prev;
|
|
797
|
|
798 ident = gcov_read_unsigned ();
|
|
799 checksum = gcov_read_unsigned ();
|
|
800 function_name = xstrdup (gcov_read_string ());
|
|
801 src = find_source (gcov_read_string ());
|
|
802 lineno = gcov_read_unsigned ();
|
|
803
|
|
804 fn = XCNEW (function_t);
|
|
805 fn->name = function_name;
|
|
806 fn->ident = ident;
|
|
807 fn->checksum = checksum;
|
|
808 fn->src = src;
|
|
809 fn->line = lineno;
|
|
810
|
|
811 fn->next = functions;
|
|
812 functions = fn;
|
|
813 current_tag = tag;
|
|
814
|
|
815 if (lineno >= src->num_lines)
|
|
816 src->num_lines = lineno + 1;
|
|
817 /* Now insert it into the source file's list of
|
|
818 functions. Normally functions will be encountered in
|
|
819 ascending order, so a simple scan is quick. */
|
|
820 for (probe = src->functions, prev = NULL;
|
|
821 probe && probe->line > lineno;
|
|
822 prev = probe, probe = probe->line_next)
|
|
823 continue;
|
|
824 fn->line_next = probe;
|
|
825 if (prev)
|
|
826 prev->line_next = fn;
|
|
827 else
|
|
828 src->functions = fn;
|
|
829 }
|
|
830 else if (fn && tag == GCOV_TAG_BLOCKS)
|
|
831 {
|
|
832 if (fn->blocks)
|
|
833 fnotice (stderr, "%s:already seen blocks for '%s'\n",
|
|
834 bbg_file_name, fn->name);
|
|
835 else
|
|
836 {
|
|
837 unsigned ix, num_blocks = GCOV_TAG_BLOCKS_NUM (length);
|
|
838 fn->num_blocks = num_blocks;
|
|
839
|
|
840 fn->blocks = XCNEWVEC (block_t, fn->num_blocks);
|
|
841 for (ix = 0; ix != num_blocks; ix++)
|
|
842 fn->blocks[ix].flags = gcov_read_unsigned ();
|
|
843 }
|
|
844 }
|
|
845 else if (fn && tag == GCOV_TAG_ARCS)
|
|
846 {
|
|
847 unsigned src = gcov_read_unsigned ();
|
|
848 unsigned num_dests = GCOV_TAG_ARCS_NUM (length);
|
|
849
|
|
850 if (src >= fn->num_blocks || fn->blocks[src].succ)
|
|
851 goto corrupt;
|
|
852
|
|
853 while (num_dests--)
|
|
854 {
|
|
855 struct arc_info *arc;
|
|
856 unsigned dest = gcov_read_unsigned ();
|
|
857 unsigned flags = gcov_read_unsigned ();
|
|
858
|
|
859 if (dest >= fn->num_blocks)
|
|
860 goto corrupt;
|
|
861 arc = XCNEW (arc_t);
|
|
862
|
|
863 arc->dst = &fn->blocks[dest];
|
|
864 arc->src = &fn->blocks[src];
|
|
865
|
|
866 arc->count = 0;
|
|
867 arc->count_valid = 0;
|
|
868 arc->on_tree = !!(flags & GCOV_ARC_ON_TREE);
|
|
869 arc->fake = !!(flags & GCOV_ARC_FAKE);
|
|
870 arc->fall_through = !!(flags & GCOV_ARC_FALLTHROUGH);
|
|
871
|
|
872 arc->succ_next = fn->blocks[src].succ;
|
|
873 fn->blocks[src].succ = arc;
|
|
874 fn->blocks[src].num_succ++;
|
|
875
|
|
876 arc->pred_next = fn->blocks[dest].pred;
|
|
877 fn->blocks[dest].pred = arc;
|
|
878 fn->blocks[dest].num_pred++;
|
|
879
|
|
880 if (arc->fake)
|
|
881 {
|
|
882 if (src)
|
|
883 {
|
|
884 /* Exceptional exit from this function, the
|
|
885 source block must be a call. */
|
|
886 fn->blocks[src].is_call_site = 1;
|
|
887 arc->is_call_non_return = 1;
|
|
888 }
|
|
889 else
|
|
890 {
|
|
891 /* Non-local return from a callee of this
|
|
892 function. The destination block is a catch or
|
|
893 setjmp. */
|
|
894 arc->is_nonlocal_return = 1;
|
|
895 fn->blocks[dest].is_nonlocal_return = 1;
|
|
896 }
|
|
897 }
|
|
898
|
|
899 if (!arc->on_tree)
|
|
900 fn->num_counts++;
|
|
901 }
|
|
902 }
|
|
903 else if (fn && tag == GCOV_TAG_LINES)
|
|
904 {
|
|
905 unsigned blockno = gcov_read_unsigned ();
|
|
906 unsigned *line_nos = XCNEWVEC (unsigned, length - 1);
|
|
907
|
|
908 if (blockno >= fn->num_blocks || fn->blocks[blockno].u.line.encoding)
|
|
909 goto corrupt;
|
|
910
|
|
911 for (ix = 0; ; )
|
|
912 {
|
|
913 unsigned lineno = gcov_read_unsigned ();
|
|
914
|
|
915 if (lineno)
|
|
916 {
|
|
917 if (!ix)
|
|
918 {
|
|
919 line_nos[ix++] = 0;
|
|
920 line_nos[ix++] = src->index;
|
|
921 }
|
|
922 line_nos[ix++] = lineno;
|
|
923 if (lineno >= src->num_lines)
|
|
924 src->num_lines = lineno + 1;
|
|
925 }
|
|
926 else
|
|
927 {
|
|
928 const char *file_name = gcov_read_string ();
|
|
929
|
|
930 if (!file_name)
|
|
931 break;
|
|
932 src = find_source (file_name);
|
|
933
|
|
934 line_nos[ix++] = 0;
|
|
935 line_nos[ix++] = src->index;
|
|
936 }
|
|
937 }
|
|
938
|
|
939 fn->blocks[blockno].u.line.encoding = line_nos;
|
|
940 fn->blocks[blockno].u.line.num = ix;
|
|
941 }
|
|
942 else if (current_tag && !GCOV_TAG_IS_SUBTAG (current_tag, tag))
|
|
943 {
|
|
944 fn = NULL;
|
|
945 current_tag = 0;
|
|
946 }
|
|
947 gcov_sync (base, length);
|
|
948 if (gcov_is_error ())
|
|
949 {
|
|
950 corrupt:;
|
|
951 fnotice (stderr, "%s:corrupted\n", bbg_file_name);
|
|
952 gcov_close ();
|
|
953 return 1;
|
|
954 }
|
|
955 }
|
|
956 gcov_close ();
|
|
957
|
|
958 /* We built everything backwards, so nreverse them all. */
|
|
959
|
|
960 /* Reverse sources. Not strictly necessary, but we'll then process
|
|
961 them in the 'expected' order. */
|
|
962 {
|
|
963 source_t *src, *src_p, *src_n;
|
|
964
|
|
965 for (src_p = NULL, src = sources; src; src_p = src, src = src_n)
|
|
966 {
|
|
967 src_n = src->next;
|
|
968 src->next = src_p;
|
|
969 }
|
|
970 sources = src_p;
|
|
971 }
|
|
972
|
|
973 /* Reverse functions. */
|
|
974 {
|
|
975 function_t *fn, *fn_p, *fn_n;
|
|
976
|
|
977 for (fn_p = old_functions_head, fn = functions;
|
|
978 fn != old_functions_head;
|
|
979 fn_p = fn, fn = fn_n)
|
|
980 {
|
|
981 unsigned ix;
|
|
982
|
|
983 fn_n = fn->next;
|
|
984 fn->next = fn_p;
|
|
985
|
|
986 /* Reverse the arcs. */
|
|
987 for (ix = fn->num_blocks; ix--;)
|
|
988 {
|
|
989 arc_t *arc, *arc_p, *arc_n;
|
|
990
|
|
991 for (arc_p = NULL, arc = fn->blocks[ix].succ; arc;
|
|
992 arc_p = arc, arc = arc_n)
|
|
993 {
|
|
994 arc_n = arc->succ_next;
|
|
995 arc->succ_next = arc_p;
|
|
996 }
|
|
997 fn->blocks[ix].succ = arc_p;
|
|
998
|
|
999 for (arc_p = NULL, arc = fn->blocks[ix].pred; arc;
|
|
1000 arc_p = arc, arc = arc_n)
|
|
1001 {
|
|
1002 arc_n = arc->pred_next;
|
|
1003 arc->pred_next = arc_p;
|
|
1004 }
|
|
1005 fn->blocks[ix].pred = arc_p;
|
|
1006 }
|
|
1007 }
|
|
1008 functions = fn_p;
|
|
1009 }
|
|
1010 return 0;
|
|
1011 }
|
|
1012
|
|
1013 /* Reads profiles from the count file and attach to each
|
|
1014 function. Return nonzero if fatal error. */
|
|
1015
|
|
1016 static int
|
|
1017 read_count_file (void)
|
|
1018 {
|
|
1019 unsigned ix;
|
|
1020 unsigned version;
|
|
1021 unsigned tag;
|
|
1022 function_t *fn = NULL;
|
|
1023 int error = 0;
|
|
1024
|
|
1025 if (!gcov_open (da_file_name, 1))
|
|
1026 {
|
|
1027 fnotice (stderr, "%s:cannot open data file, assuming not executed\n",
|
|
1028 da_file_name);
|
|
1029 no_data_file = 1;
|
|
1030 return 0;
|
|
1031 }
|
|
1032 if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC))
|
|
1033 {
|
|
1034 fnotice (stderr, "%s:not a gcov data file\n", da_file_name);
|
|
1035 cleanup:;
|
|
1036 gcov_close ();
|
|
1037 return 1;
|
|
1038 }
|
|
1039 version = gcov_read_unsigned ();
|
|
1040 if (version != GCOV_VERSION)
|
|
1041 {
|
|
1042 char v[4], e[4];
|
|
1043
|
|
1044 GCOV_UNSIGNED2STRING (v, version);
|
|
1045 GCOV_UNSIGNED2STRING (e, GCOV_VERSION);
|
|
1046
|
|
1047 fnotice (stderr, "%s:version '%.4s', prefer version '%.4s'\n",
|
|
1048 da_file_name, v, e);
|
|
1049 }
|
|
1050 tag = gcov_read_unsigned ();
|
|
1051 if (tag != bbg_stamp)
|
|
1052 {
|
|
1053 fnotice (stderr, "%s:stamp mismatch with graph file\n", da_file_name);
|
|
1054 goto cleanup;
|
|
1055 }
|
|
1056
|
|
1057 while ((tag = gcov_read_unsigned ()))
|
|
1058 {
|
|
1059 unsigned length = gcov_read_unsigned ();
|
|
1060 unsigned long base = gcov_position ();
|
|
1061
|
|
1062 if (tag == GCOV_TAG_OBJECT_SUMMARY)
|
|
1063 gcov_read_summary (&object_summary);
|
|
1064 else if (tag == GCOV_TAG_PROGRAM_SUMMARY)
|
|
1065 program_count++;
|
|
1066 else if (tag == GCOV_TAG_FUNCTION)
|
|
1067 {
|
|
1068 unsigned ident = gcov_read_unsigned ();
|
|
1069 struct function_info *fn_n = functions;
|
|
1070
|
|
1071 /* Try to find the function in the list.
|
|
1072 To speed up the search, first start from the last function
|
|
1073 found. */
|
|
1074 for (fn = fn ? fn->next : NULL; ; fn = fn->next)
|
|
1075 {
|
|
1076 if (fn)
|
|
1077 ;
|
|
1078 else if ((fn = fn_n))
|
|
1079 fn_n = NULL;
|
|
1080 else
|
|
1081 {
|
|
1082 fnotice (stderr, "%s:unknown function '%u'\n",
|
|
1083 da_file_name, ident);
|
|
1084 break;
|
|
1085 }
|
|
1086 if (fn->ident == ident)
|
|
1087 break;
|
|
1088 }
|
|
1089
|
|
1090 if (!fn)
|
|
1091 ;
|
|
1092 else if (gcov_read_unsigned () != fn->checksum)
|
|
1093 {
|
|
1094 mismatch:;
|
|
1095 fnotice (stderr, "%s:profile mismatch for '%s'\n",
|
|
1096 da_file_name, fn->name);
|
|
1097 goto cleanup;
|
|
1098 }
|
|
1099 }
|
|
1100 else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) && fn)
|
|
1101 {
|
|
1102 if (length != GCOV_TAG_COUNTER_LENGTH (fn->num_counts))
|
|
1103 goto mismatch;
|
|
1104
|
|
1105 if (!fn->counts)
|
|
1106 fn->counts = XCNEWVEC (gcov_type, fn->num_counts);
|
|
1107
|
|
1108 for (ix = 0; ix != fn->num_counts; ix++)
|
|
1109 fn->counts[ix] += gcov_read_counter ();
|
|
1110 }
|
|
1111 gcov_sync (base, length);
|
|
1112 if ((error = gcov_is_error ()))
|
|
1113 {
|
|
1114 fnotice (stderr, error < 0 ? "%s:overflowed\n" : "%s:corrupted\n",
|
|
1115 da_file_name);
|
|
1116 goto cleanup;
|
|
1117 }
|
|
1118 }
|
|
1119
|
|
1120 gcov_close ();
|
|
1121 return 0;
|
|
1122 }
|
|
1123
|
|
1124 /* Solve the flow graph. Propagate counts from the instrumented arcs
|
|
1125 to the blocks and the uninstrumented arcs. */
|
|
1126
|
|
1127 static void
|
|
1128 solve_flow_graph (function_t *fn)
|
|
1129 {
|
|
1130 unsigned ix;
|
|
1131 arc_t *arc;
|
|
1132 gcov_type *count_ptr = fn->counts;
|
|
1133 block_t *blk;
|
|
1134 block_t *valid_blocks = NULL; /* valid, but unpropagated blocks. */
|
|
1135 block_t *invalid_blocks = NULL; /* invalid, but inferable blocks. */
|
|
1136
|
|
1137 if (fn->num_blocks < 2)
|
|
1138 fnotice (stderr, "%s:'%s' lacks entry and/or exit blocks\n",
|
|
1139 bbg_file_name, fn->name);
|
|
1140 else
|
|
1141 {
|
|
1142 if (fn->blocks[0].num_pred)
|
|
1143 fnotice (stderr, "%s:'%s' has arcs to entry block\n",
|
|
1144 bbg_file_name, fn->name);
|
|
1145 else
|
|
1146 /* We can't deduce the entry block counts from the lack of
|
|
1147 predecessors. */
|
|
1148 fn->blocks[0].num_pred = ~(unsigned)0;
|
|
1149
|
|
1150 if (fn->blocks[fn->num_blocks - 1].num_succ)
|
|
1151 fnotice (stderr, "%s:'%s' has arcs from exit block\n",
|
|
1152 bbg_file_name, fn->name);
|
|
1153 else
|
|
1154 /* Likewise, we can't deduce exit block counts from the lack
|
|
1155 of its successors. */
|
|
1156 fn->blocks[fn->num_blocks - 1].num_succ = ~(unsigned)0;
|
|
1157 }
|
|
1158
|
|
1159 /* Propagate the measured counts, this must be done in the same
|
|
1160 order as the code in profile.c */
|
|
1161 for (ix = 0, blk = fn->blocks; ix != fn->num_blocks; ix++, blk++)
|
|
1162 {
|
|
1163 block_t const *prev_dst = NULL;
|
|
1164 int out_of_order = 0;
|
|
1165 int non_fake_succ = 0;
|
|
1166
|
|
1167 for (arc = blk->succ; arc; arc = arc->succ_next)
|
|
1168 {
|
|
1169 if (!arc->fake)
|
|
1170 non_fake_succ++;
|
|
1171
|
|
1172 if (!arc->on_tree)
|
|
1173 {
|
|
1174 if (count_ptr)
|
|
1175 arc->count = *count_ptr++;
|
|
1176 arc->count_valid = 1;
|
|
1177 blk->num_succ--;
|
|
1178 arc->dst->num_pred--;
|
|
1179 }
|
|
1180 if (prev_dst && prev_dst > arc->dst)
|
|
1181 out_of_order = 1;
|
|
1182 prev_dst = arc->dst;
|
|
1183 }
|
|
1184 if (non_fake_succ == 1)
|
|
1185 {
|
|
1186 /* If there is only one non-fake exit, it is an
|
|
1187 unconditional branch. */
|
|
1188 for (arc = blk->succ; arc; arc = arc->succ_next)
|
|
1189 if (!arc->fake)
|
|
1190 {
|
|
1191 arc->is_unconditional = 1;
|
|
1192 /* If this block is instrumenting a call, it might be
|
|
1193 an artificial block. It is not artificial if it has
|
|
1194 a non-fallthrough exit, or the destination of this
|
|
1195 arc has more than one entry. Mark the destination
|
|
1196 block as a return site, if none of those conditions
|
|
1197 hold. */
|
|
1198 if (blk->is_call_site && arc->fall_through
|
|
1199 && arc->dst->pred == arc && !arc->pred_next)
|
|
1200 arc->dst->is_call_return = 1;
|
|
1201 }
|
|
1202 }
|
|
1203
|
|
1204 /* Sort the successor arcs into ascending dst order. profile.c
|
|
1205 normally produces arcs in the right order, but sometimes with
|
|
1206 one or two out of order. We're not using a particularly
|
|
1207 smart sort. */
|
|
1208 if (out_of_order)
|
|
1209 {
|
|
1210 arc_t *start = blk->succ;
|
|
1211 unsigned changes = 1;
|
|
1212
|
|
1213 while (changes)
|
|
1214 {
|
|
1215 arc_t *arc, *arc_p, *arc_n;
|
|
1216
|
|
1217 changes = 0;
|
|
1218 for (arc_p = NULL, arc = start; (arc_n = arc->succ_next);)
|
|
1219 {
|
|
1220 if (arc->dst > arc_n->dst)
|
|
1221 {
|
|
1222 changes = 1;
|
|
1223 if (arc_p)
|
|
1224 arc_p->succ_next = arc_n;
|
|
1225 else
|
|
1226 start = arc_n;
|
|
1227 arc->succ_next = arc_n->succ_next;
|
|
1228 arc_n->succ_next = arc;
|
|
1229 arc_p = arc_n;
|
|
1230 }
|
|
1231 else
|
|
1232 {
|
|
1233 arc_p = arc;
|
|
1234 arc = arc_n;
|
|
1235 }
|
|
1236 }
|
|
1237 }
|
|
1238 blk->succ = start;
|
|
1239 }
|
|
1240
|
|
1241 /* Place it on the invalid chain, it will be ignored if that's
|
|
1242 wrong. */
|
|
1243 blk->invalid_chain = 1;
|
|
1244 blk->chain = invalid_blocks;
|
|
1245 invalid_blocks = blk;
|
|
1246 }
|
|
1247
|
|
1248 while (invalid_blocks || valid_blocks)
|
|
1249 {
|
|
1250 while ((blk = invalid_blocks))
|
|
1251 {
|
|
1252 gcov_type total = 0;
|
|
1253 const arc_t *arc;
|
|
1254
|
|
1255 invalid_blocks = blk->chain;
|
|
1256 blk->invalid_chain = 0;
|
|
1257 if (!blk->num_succ)
|
|
1258 for (arc = blk->succ; arc; arc = arc->succ_next)
|
|
1259 total += arc->count;
|
|
1260 else if (!blk->num_pred)
|
|
1261 for (arc = blk->pred; arc; arc = arc->pred_next)
|
|
1262 total += arc->count;
|
|
1263 else
|
|
1264 continue;
|
|
1265
|
|
1266 blk->count = total;
|
|
1267 blk->count_valid = 1;
|
|
1268 blk->chain = valid_blocks;
|
|
1269 blk->valid_chain = 1;
|
|
1270 valid_blocks = blk;
|
|
1271 }
|
|
1272 while ((blk = valid_blocks))
|
|
1273 {
|
|
1274 gcov_type total;
|
|
1275 arc_t *arc, *inv_arc;
|
|
1276
|
|
1277 valid_blocks = blk->chain;
|
|
1278 blk->valid_chain = 0;
|
|
1279 if (blk->num_succ == 1)
|
|
1280 {
|
|
1281 block_t *dst;
|
|
1282
|
|
1283 total = blk->count;
|
|
1284 inv_arc = NULL;
|
|
1285 for (arc = blk->succ; arc; arc = arc->succ_next)
|
|
1286 {
|
|
1287 total -= arc->count;
|
|
1288 if (!arc->count_valid)
|
|
1289 inv_arc = arc;
|
|
1290 }
|
|
1291 dst = inv_arc->dst;
|
|
1292 inv_arc->count_valid = 1;
|
|
1293 inv_arc->count = total;
|
|
1294 blk->num_succ--;
|
|
1295 dst->num_pred--;
|
|
1296 if (dst->count_valid)
|
|
1297 {
|
|
1298 if (dst->num_pred == 1 && !dst->valid_chain)
|
|
1299 {
|
|
1300 dst->chain = valid_blocks;
|
|
1301 dst->valid_chain = 1;
|
|
1302 valid_blocks = dst;
|
|
1303 }
|
|
1304 }
|
|
1305 else
|
|
1306 {
|
|
1307 if (!dst->num_pred && !dst->invalid_chain)
|
|
1308 {
|
|
1309 dst->chain = invalid_blocks;
|
|
1310 dst->invalid_chain = 1;
|
|
1311 invalid_blocks = dst;
|
|
1312 }
|
|
1313 }
|
|
1314 }
|
|
1315 if (blk->num_pred == 1)
|
|
1316 {
|
|
1317 block_t *src;
|
|
1318
|
|
1319 total = blk->count;
|
|
1320 inv_arc = NULL;
|
|
1321 for (arc = blk->pred; arc; arc = arc->pred_next)
|
|
1322 {
|
|
1323 total -= arc->count;
|
|
1324 if (!arc->count_valid)
|
|
1325 inv_arc = arc;
|
|
1326 }
|
|
1327 src = inv_arc->src;
|
|
1328 inv_arc->count_valid = 1;
|
|
1329 inv_arc->count = total;
|
|
1330 blk->num_pred--;
|
|
1331 src->num_succ--;
|
|
1332 if (src->count_valid)
|
|
1333 {
|
|
1334 if (src->num_succ == 1 && !src->valid_chain)
|
|
1335 {
|
|
1336 src->chain = valid_blocks;
|
|
1337 src->valid_chain = 1;
|
|
1338 valid_blocks = src;
|
|
1339 }
|
|
1340 }
|
|
1341 else
|
|
1342 {
|
|
1343 if (!src->num_succ && !src->invalid_chain)
|
|
1344 {
|
|
1345 src->chain = invalid_blocks;
|
|
1346 src->invalid_chain = 1;
|
|
1347 invalid_blocks = src;
|
|
1348 }
|
|
1349 }
|
|
1350 }
|
|
1351 }
|
|
1352 }
|
|
1353
|
|
1354 /* If the graph has been correctly solved, every block will have a
|
|
1355 valid count. */
|
|
1356 for (ix = 0; ix < fn->num_blocks; ix++)
|
|
1357 if (!fn->blocks[ix].count_valid)
|
|
1358 {
|
|
1359 fnotice (stderr, "%s:graph is unsolvable for '%s'\n",
|
|
1360 bbg_file_name, fn->name);
|
|
1361 break;
|
|
1362 }
|
|
1363 }
|
|
1364
|
|
1365
|
|
1366
|
|
1367 /* Increment totals in COVERAGE according to arc ARC. */
|
|
1368
|
|
1369 static void
|
|
1370 add_branch_counts (coverage_t *coverage, const arc_t *arc)
|
|
1371 {
|
|
1372 if (arc->is_call_non_return)
|
|
1373 {
|
|
1374 coverage->calls++;
|
|
1375 if (arc->src->count)
|
|
1376 coverage->calls_executed++;
|
|
1377 }
|
|
1378 else if (!arc->is_unconditional)
|
|
1379 {
|
|
1380 coverage->branches++;
|
|
1381 if (arc->src->count)
|
|
1382 coverage->branches_executed++;
|
|
1383 if (arc->count)
|
|
1384 coverage->branches_taken++;
|
|
1385 }
|
|
1386 }
|
|
1387
|
|
1388 /* Format a HOST_WIDE_INT as either a percent ratio, or absolute
|
|
1389 count. If dp >= 0, format TOP/BOTTOM * 100 to DP decimal places.
|
|
1390 If DP is zero, no decimal point is printed. Only print 100% when
|
|
1391 TOP==BOTTOM and only print 0% when TOP=0. If dp < 0, then simply
|
|
1392 format TOP. Return pointer to a static string. */
|
|
1393
|
|
1394 static char const *
|
|
1395 format_gcov (gcov_type top, gcov_type bottom, int dp)
|
|
1396 {
|
|
1397 static char buffer[20];
|
|
1398
|
|
1399 if (dp >= 0)
|
|
1400 {
|
|
1401 float ratio = bottom ? (float)top / bottom : 0;
|
|
1402 int ix;
|
|
1403 unsigned limit = 100;
|
|
1404 unsigned percent;
|
|
1405
|
|
1406 for (ix = dp; ix--; )
|
|
1407 limit *= 10;
|
|
1408
|
|
1409 percent = (unsigned) (ratio * limit + (float)0.5);
|
|
1410 if (percent <= 0 && top)
|
|
1411 percent = 1;
|
|
1412 else if (percent >= limit && top != bottom)
|
|
1413 percent = limit - 1;
|
|
1414 ix = sprintf (buffer, "%.*u%%", dp + 1, percent);
|
|
1415 if (dp)
|
|
1416 {
|
|
1417 dp++;
|
|
1418 do
|
|
1419 {
|
|
1420 buffer[ix+1] = buffer[ix];
|
|
1421 ix--;
|
|
1422 }
|
|
1423 while (dp--);
|
|
1424 buffer[ix + 1] = '.';
|
|
1425 }
|
|
1426 }
|
|
1427 else
|
|
1428 sprintf (buffer, HOST_WIDEST_INT_PRINT_DEC, (HOST_WIDEST_INT)top);
|
|
1429
|
|
1430 return buffer;
|
|
1431 }
|
|
1432
|
|
1433
|
|
1434 /* Output summary info for a function. */
|
|
1435
|
|
1436 static void
|
|
1437 function_summary (const coverage_t *coverage, const char *title)
|
|
1438 {
|
|
1439 fnotice (stdout, "%s '%s'\n", title, coverage->name);
|
|
1440
|
|
1441 if (coverage->lines)
|
|
1442 fnotice (stdout, "Lines executed:%s of %d\n",
|
|
1443 format_gcov (coverage->lines_executed, coverage->lines, 2),
|
|
1444 coverage->lines);
|
|
1445 else
|
|
1446 fnotice (stdout, "No executable lines\n");
|
|
1447
|
|
1448 if (flag_branches)
|
|
1449 {
|
|
1450 if (coverage->branches)
|
|
1451 {
|
|
1452 fnotice (stdout, "Branches executed:%s of %d\n",
|
|
1453 format_gcov (coverage->branches_executed,
|
|
1454 coverage->branches, 2),
|
|
1455 coverage->branches);
|
|
1456 fnotice (stdout, "Taken at least once:%s of %d\n",
|
|
1457 format_gcov (coverage->branches_taken,
|
|
1458 coverage->branches, 2),
|
|
1459 coverage->branches);
|
|
1460 }
|
|
1461 else
|
|
1462 fnotice (stdout, "No branches\n");
|
|
1463 if (coverage->calls)
|
|
1464 fnotice (stdout, "Calls executed:%s of %d\n",
|
|
1465 format_gcov (coverage->calls_executed, coverage->calls, 2),
|
|
1466 coverage->calls);
|
|
1467 else
|
|
1468 fnotice (stdout, "No calls\n");
|
|
1469 }
|
|
1470 }
|
|
1471
|
|
1472 /* Generate an output file name. LONG_OUTPUT_NAMES and PRESERVE_PATHS
|
|
1473 affect name generation. With preserve_paths we create a filename
|
|
1474 from all path components of the source file, replacing '/' with
|
|
1475 '#', without it we simply take the basename component. With
|
|
1476 long_output_names we prepend the processed name of the input file
|
|
1477 to each output name (except when the current source file is the
|
|
1478 input file, so you don't get a double concatenation). The two
|
|
1479 components are separated by '##'. Also '.' filename components are
|
|
1480 removed and '..' components are renamed to '^'. */
|
|
1481
|
|
1482 static char *
|
|
1483 make_gcov_file_name (const char *input_name, const char *src_name)
|
|
1484 {
|
|
1485 const char *cptr;
|
|
1486 char *name;
|
|
1487
|
|
1488 if (flag_long_names && input_name && strcmp (src_name, input_name))
|
|
1489 {
|
|
1490 name = XNEWVEC (char, strlen (src_name) + strlen (input_name) + 10);
|
|
1491 name[0] = 0;
|
|
1492 /* Generate the input filename part. */
|
|
1493 cptr = flag_preserve_paths ? NULL : lbasename (input_name);
|
|
1494 strcat (name, cptr ? cptr : input_name);
|
|
1495 strcat (name, "##");
|
|
1496 }
|
|
1497 else
|
|
1498 {
|
|
1499 name = XNEWVEC (char, strlen (src_name) + 10);
|
|
1500 name[0] = 0;
|
|
1501 }
|
|
1502
|
|
1503 /* Generate the source filename part. */
|
|
1504
|
|
1505 cptr = flag_preserve_paths ? NULL : lbasename (src_name);
|
|
1506 strcat (name, cptr ? cptr : src_name);
|
|
1507
|
|
1508 if (flag_preserve_paths)
|
|
1509 {
|
|
1510 /* Convert '/' and '\' to '#', remove '/./', convert '/../' to '/^/',
|
|
1511 convert ':' to '~' on DOS based file system. */
|
|
1512 char *pnew = name, *pold = name;
|
|
1513
|
|
1514 /* First check for leading drive separator. */
|
|
1515
|
|
1516 while (*pold != '\0')
|
|
1517 {
|
|
1518 if (*pold == '/' || *pold == '\\')
|
|
1519 {
|
|
1520 *pnew++ = '#';
|
|
1521 pold++;
|
|
1522 }
|
|
1523 #if defined (HAVE_DOS_BASED_FILE_SYSTEM)
|
|
1524 else if (*pold == ':')
|
|
1525 {
|
|
1526 *pnew++ = '~';
|
|
1527 pold++;
|
|
1528 }
|
|
1529 #endif
|
|
1530 else if ((*pold == '/' && strstr (pold, "/./") == pold)
|
|
1531 || (*pold == '\\' && strstr (pold, "\\.\\") == pold))
|
|
1532 pold += 3;
|
|
1533 else if (*pold == '/' && strstr (pold, "/../") == pold)
|
|
1534 {
|
|
1535 strcpy (pnew, "/^/");
|
|
1536 pnew += 3;
|
|
1537 pold += 4;
|
|
1538 }
|
|
1539 else if (*pold == '\\' && strstr (pold, "\\..\\") == pold)
|
|
1540 {
|
|
1541 strcpy (pnew, "\\^\\");
|
|
1542 pnew += 3;
|
|
1543 pold += 4;
|
|
1544 }
|
|
1545 else
|
|
1546 *pnew++ = *pold++;
|
|
1547 }
|
|
1548
|
|
1549 *pnew = '\0';
|
|
1550 }
|
|
1551
|
|
1552 strcat (name, ".gcov");
|
|
1553 return name;
|
|
1554 }
|
|
1555
|
|
1556 /* Scan through the bb_data for each line in the block, increment
|
|
1557 the line number execution count indicated by the execution count of
|
|
1558 the appropriate basic block. */
|
|
1559
|
|
1560 static void
|
|
1561 add_line_counts (coverage_t *coverage, function_t *fn)
|
|
1562 {
|
|
1563 unsigned ix;
|
|
1564 line_t *line = NULL; /* This is propagated from one iteration to the
|
|
1565 next. */
|
|
1566
|
|
1567 /* Scan each basic block. */
|
|
1568 for (ix = 0; ix != fn->num_blocks; ix++)
|
|
1569 {
|
|
1570 block_t *block = &fn->blocks[ix];
|
|
1571 unsigned *encoding;
|
|
1572 const source_t *src = NULL;
|
|
1573 unsigned jx;
|
|
1574
|
|
1575 if (block->count && ix && ix + 1 != fn->num_blocks)
|
|
1576 fn->blocks_executed++;
|
|
1577 for (jx = 0, encoding = block->u.line.encoding;
|
|
1578 jx != block->u.line.num; jx++, encoding++)
|
|
1579 if (!*encoding)
|
|
1580 {
|
|
1581 unsigned src_n = *++encoding;
|
|
1582
|
|
1583 for (src = sources; src->index != src_n; src = src->next)
|
|
1584 continue;
|
|
1585 jx++;
|
|
1586 }
|
|
1587 else
|
|
1588 {
|
|
1589 line = &src->lines[*encoding];
|
|
1590
|
|
1591 if (coverage)
|
|
1592 {
|
|
1593 if (!line->exists)
|
|
1594 coverage->lines++;
|
|
1595 if (!line->count && block->count)
|
|
1596 coverage->lines_executed++;
|
|
1597 }
|
|
1598 line->exists = 1;
|
|
1599 line->count += block->count;
|
|
1600 }
|
|
1601 free (block->u.line.encoding);
|
|
1602 block->u.cycle.arc = NULL;
|
|
1603 block->u.cycle.ident = ~0U;
|
|
1604
|
|
1605 if (!ix || ix + 1 == fn->num_blocks)
|
|
1606 /* Entry or exit block */;
|
|
1607 else if (flag_all_blocks)
|
|
1608 {
|
|
1609 line_t *block_line = line ? line : &fn->src->lines[fn->line];
|
|
1610
|
|
1611 block->chain = block_line->u.blocks;
|
|
1612 block_line->u.blocks = block;
|
|
1613 }
|
|
1614 else if (flag_branches)
|
|
1615 {
|
|
1616 arc_t *arc;
|
|
1617
|
|
1618 for (arc = block->succ; arc; arc = arc->succ_next)
|
|
1619 {
|
|
1620 arc->line_next = line->u.branches;
|
|
1621 line->u.branches = arc;
|
|
1622 if (coverage && !arc->is_unconditional)
|
|
1623 add_branch_counts (coverage, arc);
|
|
1624 }
|
|
1625 }
|
|
1626 }
|
|
1627 if (!line)
|
|
1628 fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name);
|
|
1629 }
|
|
1630
|
|
1631 /* Accumulate the line counts of a file. */
|
|
1632
|
|
1633 static void
|
|
1634 accumulate_line_counts (source_t *src)
|
|
1635 {
|
|
1636 line_t *line;
|
|
1637 function_t *fn, *fn_p, *fn_n;
|
|
1638 unsigned ix;
|
|
1639
|
|
1640 /* Reverse the function order. */
|
|
1641 for (fn = src->functions, fn_p = NULL; fn;
|
|
1642 fn_p = fn, fn = fn_n)
|
|
1643 {
|
|
1644 fn_n = fn->line_next;
|
|
1645 fn->line_next = fn_p;
|
|
1646 }
|
|
1647 src->functions = fn_p;
|
|
1648
|
|
1649 for (ix = src->num_lines, line = src->lines; ix--; line++)
|
|
1650 {
|
|
1651 if (!flag_all_blocks)
|
|
1652 {
|
|
1653 arc_t *arc, *arc_p, *arc_n;
|
|
1654
|
|
1655 /* Total and reverse the branch information. */
|
|
1656 for (arc = line->u.branches, arc_p = NULL; arc;
|
|
1657 arc_p = arc, arc = arc_n)
|
|
1658 {
|
|
1659 arc_n = arc->line_next;
|
|
1660 arc->line_next = arc_p;
|
|
1661
|
|
1662 add_branch_counts (&src->coverage, arc);
|
|
1663 }
|
|
1664 line->u.branches = arc_p;
|
|
1665 }
|
|
1666 else if (line->u.blocks)
|
|
1667 {
|
|
1668 /* The user expects the line count to be the number of times
|
|
1669 a line has been executed. Simply summing the block count
|
|
1670 will give an artificially high number. The Right Thing
|
|
1671 is to sum the entry counts to the graph of blocks on this
|
|
1672 line, then find the elementary cycles of the local graph
|
|
1673 and add the transition counts of those cycles. */
|
|
1674 block_t *block, *block_p, *block_n;
|
|
1675 gcov_type count = 0;
|
|
1676
|
|
1677 /* Reverse the block information. */
|
|
1678 for (block = line->u.blocks, block_p = NULL; block;
|
|
1679 block_p = block, block = block_n)
|
|
1680 {
|
|
1681 block_n = block->chain;
|
|
1682 block->chain = block_p;
|
|
1683 block->u.cycle.ident = ix;
|
|
1684 }
|
|
1685 line->u.blocks = block_p;
|
|
1686
|
|
1687 /* Sum the entry arcs. */
|
|
1688 for (block = line->u.blocks; block; block = block->chain)
|
|
1689 {
|
|
1690 arc_t *arc;
|
|
1691
|
|
1692 for (arc = block->pred; arc; arc = arc->pred_next)
|
|
1693 {
|
|
1694 if (arc->src->u.cycle.ident != ix)
|
|
1695 count += arc->count;
|
|
1696 if (flag_branches)
|
|
1697 add_branch_counts (&src->coverage, arc);
|
|
1698 }
|
|
1699
|
|
1700 /* Initialize the cs_count. */
|
|
1701 for (arc = block->succ; arc; arc = arc->succ_next)
|
|
1702 arc->cs_count = arc->count;
|
|
1703 }
|
|
1704
|
|
1705 /* Find the loops. This uses the algorithm described in
|
|
1706 Tiernan 'An Efficient Search Algorithm to Find the
|
|
1707 Elementary Circuits of a Graph', CACM Dec 1970. We hold
|
|
1708 the P array by having each block point to the arc that
|
|
1709 connects to the previous block. The H array is implicitly
|
|
1710 held because of the arc ordering, and the block's
|
|
1711 previous arc pointer.
|
|
1712
|
|
1713 Although the algorithm is O(N^3) for highly connected
|
|
1714 graphs, at worst we'll have O(N^2), as most blocks have
|
|
1715 only one or two exits. Most graphs will be small.
|
|
1716
|
|
1717 For each loop we find, locate the arc with the smallest
|
|
1718 transition count, and add that to the cumulative
|
|
1719 count. Decrease flow over the cycle and remove the arc
|
|
1720 from consideration. */
|
|
1721 for (block = line->u.blocks; block; block = block->chain)
|
|
1722 {
|
|
1723 block_t *head = block;
|
|
1724 arc_t *arc;
|
|
1725
|
|
1726 next_vertex:;
|
|
1727 arc = head->succ;
|
|
1728 current_vertex:;
|
|
1729 while (arc)
|
|
1730 {
|
|
1731 block_t *dst = arc->dst;
|
|
1732 if (/* Already used that arc. */
|
|
1733 arc->cycle
|
|
1734 /* Not to same graph, or before first vertex. */
|
|
1735 || dst->u.cycle.ident != ix
|
|
1736 /* Already in path. */
|
|
1737 || dst->u.cycle.arc)
|
|
1738 {
|
|
1739 arc = arc->succ_next;
|
|
1740 continue;
|
|
1741 }
|
|
1742
|
|
1743 if (dst == block)
|
|
1744 {
|
|
1745 /* Found a closing arc. */
|
|
1746 gcov_type cycle_count = arc->cs_count;
|
|
1747 arc_t *cycle_arc = arc;
|
|
1748 arc_t *probe_arc;
|
|
1749
|
|
1750 /* Locate the smallest arc count of the loop. */
|
|
1751 for (dst = head; (probe_arc = dst->u.cycle.arc);
|
|
1752 dst = probe_arc->src)
|
|
1753 if (cycle_count > probe_arc->cs_count)
|
|
1754 {
|
|
1755 cycle_count = probe_arc->cs_count;
|
|
1756 cycle_arc = probe_arc;
|
|
1757 }
|
|
1758
|
|
1759 count += cycle_count;
|
|
1760 cycle_arc->cycle = 1;
|
|
1761
|
|
1762 /* Remove the flow from the cycle. */
|
|
1763 arc->cs_count -= cycle_count;
|
|
1764 for (dst = head; (probe_arc = dst->u.cycle.arc);
|
|
1765 dst = probe_arc->src)
|
|
1766 probe_arc->cs_count -= cycle_count;
|
|
1767
|
|
1768 /* Unwind to the cyclic arc. */
|
|
1769 while (head != cycle_arc->src)
|
|
1770 {
|
|
1771 arc = head->u.cycle.arc;
|
|
1772 head->u.cycle.arc = NULL;
|
|
1773 head = arc->src;
|
|
1774 }
|
|
1775 /* Move on. */
|
|
1776 arc = arc->succ_next;
|
|
1777 continue;
|
|
1778 }
|
|
1779
|
|
1780 /* Add new block to chain. */
|
|
1781 dst->u.cycle.arc = arc;
|
|
1782 head = dst;
|
|
1783 goto next_vertex;
|
|
1784 }
|
|
1785 /* We could not add another vertex to the path. Remove
|
|
1786 the last vertex from the list. */
|
|
1787 arc = head->u.cycle.arc;
|
|
1788 if (arc)
|
|
1789 {
|
|
1790 /* It was not the first vertex. Move onto next arc. */
|
|
1791 head->u.cycle.arc = NULL;
|
|
1792 head = arc->src;
|
|
1793 arc = arc->succ_next;
|
|
1794 goto current_vertex;
|
|
1795 }
|
|
1796 /* Mark this block as unusable. */
|
|
1797 block->u.cycle.ident = ~0U;
|
|
1798 }
|
|
1799
|
|
1800 line->count = count;
|
|
1801 }
|
|
1802
|
|
1803 if (line->exists)
|
|
1804 {
|
|
1805 src->coverage.lines++;
|
|
1806 if (line->count)
|
|
1807 src->coverage.lines_executed++;
|
|
1808 }
|
|
1809 }
|
|
1810 }
|
|
1811
|
|
1812 /* Output information about ARC number IX. Returns nonzero if
|
|
1813 anything is output. */
|
|
1814
|
|
1815 static int
|
|
1816 output_branch_count (FILE *gcov_file, int ix, const arc_t *arc)
|
|
1817 {
|
|
1818
|
|
1819 if (arc->is_call_non_return)
|
|
1820 {
|
|
1821 if (arc->src->count)
|
|
1822 {
|
|
1823 fnotice (gcov_file, "call %2d returned %s\n", ix,
|
|
1824 format_gcov (arc->src->count - arc->count,
|
|
1825 arc->src->count, -flag_counts));
|
|
1826 }
|
|
1827 else
|
|
1828 fnotice (gcov_file, "call %2d never executed\n", ix);
|
|
1829 }
|
|
1830 else if (!arc->is_unconditional)
|
|
1831 {
|
|
1832 if (arc->src->count)
|
|
1833 fnotice (gcov_file, "branch %2d taken %s%s\n", ix,
|
|
1834 format_gcov (arc->count, arc->src->count, -flag_counts),
|
|
1835 arc->fall_through ? " (fallthrough)" : "");
|
|
1836 else
|
|
1837 fnotice (gcov_file, "branch %2d never executed\n", ix);
|
|
1838 }
|
|
1839 else if (flag_unconditional && !arc->dst->is_call_return)
|
|
1840 {
|
|
1841 if (arc->src->count)
|
|
1842 fnotice (gcov_file, "unconditional %2d taken %s\n", ix,
|
|
1843 format_gcov (arc->count, arc->src->count, -flag_counts));
|
|
1844 else
|
|
1845 fnotice (gcov_file, "unconditional %2d never executed\n", ix);
|
|
1846 }
|
|
1847 else
|
|
1848 return 0;
|
|
1849 return 1;
|
|
1850
|
|
1851 }
|
|
1852
|
|
1853 /* Read in the source file one line at a time, and output that line to
|
|
1854 the gcov file preceded by its execution count and other
|
|
1855 information. */
|
|
1856
|
|
1857 static void
|
|
1858 output_lines (FILE *gcov_file, const source_t *src)
|
|
1859 {
|
|
1860 FILE *source_file;
|
|
1861 unsigned line_num; /* current line number. */
|
|
1862 const line_t *line; /* current line info ptr. */
|
|
1863 char string[STRING_SIZE]; /* line buffer. */
|
|
1864 char const *retval = ""; /* status of source file reading. */
|
|
1865 function_t *fn = NULL;
|
|
1866
|
|
1867 fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, src->name);
|
|
1868 if (!multiple_files)
|
|
1869 {
|
|
1870 fprintf (gcov_file, "%9s:%5d:Graph:%s\n", "-", 0, bbg_file_name);
|
|
1871 fprintf (gcov_file, "%9s:%5d:Data:%s\n", "-", 0,
|
|
1872 no_data_file ? "-" : da_file_name);
|
|
1873 fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0,
|
|
1874 object_summary.ctrs[GCOV_COUNTER_ARCS].runs);
|
|
1875 }
|
|
1876 fprintf (gcov_file, "%9s:%5d:Programs:%u\n", "-", 0, program_count);
|
|
1877
|
|
1878 source_file = fopen (src->name, "r");
|
|
1879 if (!source_file)
|
|
1880 {
|
|
1881 fnotice (stderr, "%s:cannot open source file\n", src->name);
|
|
1882 retval = NULL;
|
|
1883 }
|
|
1884 else if (src->file_time == 0)
|
|
1885 fprintf (gcov_file, "%9s:%5d:Source is newer than graph\n", "-", 0);
|
|
1886
|
|
1887 if (flag_branches)
|
|
1888 fn = src->functions;
|
|
1889
|
|
1890 for (line_num = 1, line = &src->lines[line_num];
|
|
1891 line_num < src->num_lines; line_num++, line++)
|
|
1892 {
|
|
1893 for (; fn && fn->line == line_num; fn = fn->line_next)
|
|
1894 {
|
|
1895 arc_t *arc = fn->blocks[fn->num_blocks - 1].pred;
|
|
1896 gcov_type return_count = fn->blocks[fn->num_blocks - 1].count;
|
|
1897
|
|
1898 for (; arc; arc = arc->pred_next)
|
|
1899 if (arc->fake)
|
|
1900 return_count -= arc->count;
|
|
1901
|
|
1902 fprintf (gcov_file, "function %s", fn->name);
|
|
1903 fprintf (gcov_file, " called %s",
|
|
1904 format_gcov (fn->blocks[0].count, 0, -1));
|
|
1905 fprintf (gcov_file, " returned %s",
|
|
1906 format_gcov (return_count, fn->blocks[0].count, 0));
|
|
1907 fprintf (gcov_file, " blocks executed %s",
|
|
1908 format_gcov (fn->blocks_executed, fn->num_blocks - 2, 0));
|
|
1909 fprintf (gcov_file, "\n");
|
|
1910 }
|
|
1911
|
|
1912 /* For lines which don't exist in the .bb file, print '-' before
|
|
1913 the source line. For lines which exist but were never
|
|
1914 executed, print '#####' before the source line. Otherwise,
|
|
1915 print the execution count before the source line. There are
|
|
1916 16 spaces of indentation added before the source line so that
|
|
1917 tabs won't be messed up. */
|
|
1918 fprintf (gcov_file, "%9s:%5u:",
|
|
1919 !line->exists ? "-" : !line->count ? "#####"
|
|
1920 : format_gcov (line->count, 0, -1), line_num);
|
|
1921
|
|
1922 if (retval)
|
|
1923 {
|
|
1924 /* Copy source line. */
|
|
1925 do
|
|
1926 {
|
|
1927 retval = fgets (string, STRING_SIZE, source_file);
|
|
1928 if (!retval)
|
|
1929 break;
|
|
1930 fputs (retval, gcov_file);
|
|
1931 }
|
|
1932 while (!retval[0] || retval[strlen (retval) - 1] != '\n');
|
|
1933 }
|
|
1934 if (!retval)
|
|
1935 fputs ("/*EOF*/\n", gcov_file);
|
|
1936
|
|
1937 if (flag_all_blocks)
|
|
1938 {
|
|
1939 block_t *block;
|
|
1940 arc_t *arc;
|
|
1941 int ix, jx;
|
|
1942
|
|
1943 for (ix = jx = 0, block = line->u.blocks; block;
|
|
1944 block = block->chain)
|
|
1945 {
|
|
1946 if (!block->is_call_return)
|
|
1947 fprintf (gcov_file, "%9s:%5u-block %2d\n",
|
|
1948 !line->exists ? "-" : !block->count ? "$$$$$"
|
|
1949 : format_gcov (block->count, 0, -1),
|
|
1950 line_num, ix++);
|
|
1951 if (flag_branches)
|
|
1952 for (arc = block->succ; arc; arc = arc->succ_next)
|
|
1953 jx += output_branch_count (gcov_file, jx, arc);
|
|
1954 }
|
|
1955 }
|
|
1956 else if (flag_branches)
|
|
1957 {
|
|
1958 int ix;
|
|
1959 arc_t *arc;
|
|
1960
|
|
1961 for (ix = 0, arc = line->u.branches; arc; arc = arc->line_next)
|
|
1962 ix += output_branch_count (gcov_file, ix, arc);
|
|
1963 }
|
|
1964 }
|
|
1965
|
|
1966 /* Handle all remaining source lines. There may be lines after the
|
|
1967 last line of code. */
|
|
1968 if (retval)
|
|
1969 {
|
|
1970 for (; (retval = fgets (string, STRING_SIZE, source_file)); line_num++)
|
|
1971 {
|
|
1972 fprintf (gcov_file, "%9s:%5u:%s", "-", line_num, retval);
|
|
1973
|
|
1974 while (!retval[0] || retval[strlen (retval) - 1] != '\n')
|
|
1975 {
|
|
1976 retval = fgets (string, STRING_SIZE, source_file);
|
|
1977 if (!retval)
|
|
1978 break;
|
|
1979 fputs (retval, gcov_file);
|
|
1980 }
|
|
1981 }
|
|
1982 }
|
|
1983
|
|
1984 if (source_file)
|
|
1985 fclose (source_file);
|
|
1986 }
|