Mercurial > hg > CbC > CbC_gcc
annotate gcc/gcov.c @ 55:77e2b8dfacca gcc-4.4.5
update it from 4.4.3 to 4.5.0
author | ryoma <e075725@ie.u-ryukyu.ac.jp> |
---|---|
date | Fri, 12 Feb 2010 23:39:51 +0900 |
parents | 3bfb6c00c1e0 |
children | b7f97abdc517 |
rev | line source |
---|---|
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, | |
55
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
4 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 |
0 | 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); | |
55
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
429 fprintf (stdout, "Copyright %s 2009 Free Software Foundation, Inc.\n", |
0 | 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); | |
55
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
687 |
0 | 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; | |
55
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
724 |
0 | 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); | |
55
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1046 |
0 | 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 { | |
55
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1068 { |
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1069 unsigned ident = gcov_read_unsigned (); |
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1070 struct function_info *fn_n = functions; |
0 | 1071 |
55
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1072 /* Try to find the function in the list. |
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1073 To speed up the search, first start from the last function |
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1074 found. */ |
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1075 for (fn = fn ? fn->next : NULL; ; fn = fn->next) |
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1076 { |
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1077 if (fn) |
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1078 ; |
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1079 else if ((fn = fn_n)) |
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1080 fn_n = NULL; |
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1081 else |
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1082 { |
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1083 fnotice (stderr, "%s:unknown function '%u'\n", |
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1084 da_file_name, ident); |
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1085 break; |
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1086 } |
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1087 if (fn->ident == ident) |
0 | 1088 break; |
55
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1089 } |
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1090 } |
0 | 1091 |
1092 if (!fn) | |
1093 ; | |
1094 else if (gcov_read_unsigned () != fn->checksum) | |
1095 { | |
1096 mismatch:; | |
1097 fnotice (stderr, "%s:profile mismatch for '%s'\n", | |
1098 da_file_name, fn->name); | |
1099 goto cleanup; | |
1100 } | |
1101 } | |
1102 else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) && fn) | |
1103 { | |
1104 if (length != GCOV_TAG_COUNTER_LENGTH (fn->num_counts)) | |
1105 goto mismatch; | |
1106 | |
1107 if (!fn->counts) | |
1108 fn->counts = XCNEWVEC (gcov_type, fn->num_counts); | |
1109 | |
1110 for (ix = 0; ix != fn->num_counts; ix++) | |
1111 fn->counts[ix] += gcov_read_counter (); | |
1112 } | |
1113 gcov_sync (base, length); | |
1114 if ((error = gcov_is_error ())) | |
1115 { | |
1116 fnotice (stderr, error < 0 ? "%s:overflowed\n" : "%s:corrupted\n", | |
1117 da_file_name); | |
1118 goto cleanup; | |
1119 } | |
1120 } | |
1121 | |
1122 gcov_close (); | |
1123 return 0; | |
1124 } | |
1125 | |
1126 /* Solve the flow graph. Propagate counts from the instrumented arcs | |
1127 to the blocks and the uninstrumented arcs. */ | |
1128 | |
1129 static void | |
1130 solve_flow_graph (function_t *fn) | |
1131 { | |
1132 unsigned ix; | |
1133 arc_t *arc; | |
1134 gcov_type *count_ptr = fn->counts; | |
1135 block_t *blk; | |
1136 block_t *valid_blocks = NULL; /* valid, but unpropagated blocks. */ | |
1137 block_t *invalid_blocks = NULL; /* invalid, but inferable blocks. */ | |
1138 | |
1139 if (fn->num_blocks < 2) | |
1140 fnotice (stderr, "%s:'%s' lacks entry and/or exit blocks\n", | |
1141 bbg_file_name, fn->name); | |
1142 else | |
1143 { | |
1144 if (fn->blocks[0].num_pred) | |
1145 fnotice (stderr, "%s:'%s' has arcs to entry block\n", | |
1146 bbg_file_name, fn->name); | |
1147 else | |
1148 /* We can't deduce the entry block counts from the lack of | |
1149 predecessors. */ | |
1150 fn->blocks[0].num_pred = ~(unsigned)0; | |
1151 | |
1152 if (fn->blocks[fn->num_blocks - 1].num_succ) | |
1153 fnotice (stderr, "%s:'%s' has arcs from exit block\n", | |
1154 bbg_file_name, fn->name); | |
1155 else | |
1156 /* Likewise, we can't deduce exit block counts from the lack | |
1157 of its successors. */ | |
1158 fn->blocks[fn->num_blocks - 1].num_succ = ~(unsigned)0; | |
1159 } | |
1160 | |
1161 /* Propagate the measured counts, this must be done in the same | |
1162 order as the code in profile.c */ | |
1163 for (ix = 0, blk = fn->blocks; ix != fn->num_blocks; ix++, blk++) | |
1164 { | |
1165 block_t const *prev_dst = NULL; | |
1166 int out_of_order = 0; | |
1167 int non_fake_succ = 0; | |
1168 | |
1169 for (arc = blk->succ; arc; arc = arc->succ_next) | |
1170 { | |
1171 if (!arc->fake) | |
1172 non_fake_succ++; | |
1173 | |
1174 if (!arc->on_tree) | |
1175 { | |
1176 if (count_ptr) | |
1177 arc->count = *count_ptr++; | |
1178 arc->count_valid = 1; | |
1179 blk->num_succ--; | |
1180 arc->dst->num_pred--; | |
1181 } | |
1182 if (prev_dst && prev_dst > arc->dst) | |
1183 out_of_order = 1; | |
1184 prev_dst = arc->dst; | |
1185 } | |
1186 if (non_fake_succ == 1) | |
1187 { | |
1188 /* If there is only one non-fake exit, it is an | |
1189 unconditional branch. */ | |
1190 for (arc = blk->succ; arc; arc = arc->succ_next) | |
1191 if (!arc->fake) | |
1192 { | |
1193 arc->is_unconditional = 1; | |
1194 /* If this block is instrumenting a call, it might be | |
1195 an artificial block. It is not artificial if it has | |
1196 a non-fallthrough exit, or the destination of this | |
1197 arc has more than one entry. Mark the destination | |
1198 block as a return site, if none of those conditions | |
1199 hold. */ | |
1200 if (blk->is_call_site && arc->fall_through | |
1201 && arc->dst->pred == arc && !arc->pred_next) | |
1202 arc->dst->is_call_return = 1; | |
1203 } | |
1204 } | |
1205 | |
1206 /* Sort the successor arcs into ascending dst order. profile.c | |
1207 normally produces arcs in the right order, but sometimes with | |
1208 one or two out of order. We're not using a particularly | |
1209 smart sort. */ | |
1210 if (out_of_order) | |
1211 { | |
1212 arc_t *start = blk->succ; | |
1213 unsigned changes = 1; | |
1214 | |
1215 while (changes) | |
1216 { | |
1217 arc_t *arc, *arc_p, *arc_n; | |
1218 | |
1219 changes = 0; | |
1220 for (arc_p = NULL, arc = start; (arc_n = arc->succ_next);) | |
1221 { | |
1222 if (arc->dst > arc_n->dst) | |
1223 { | |
1224 changes = 1; | |
1225 if (arc_p) | |
1226 arc_p->succ_next = arc_n; | |
1227 else | |
1228 start = arc_n; | |
1229 arc->succ_next = arc_n->succ_next; | |
1230 arc_n->succ_next = arc; | |
1231 arc_p = arc_n; | |
1232 } | |
1233 else | |
1234 { | |
1235 arc_p = arc; | |
1236 arc = arc_n; | |
1237 } | |
1238 } | |
1239 } | |
1240 blk->succ = start; | |
1241 } | |
1242 | |
1243 /* Place it on the invalid chain, it will be ignored if that's | |
1244 wrong. */ | |
1245 blk->invalid_chain = 1; | |
1246 blk->chain = invalid_blocks; | |
1247 invalid_blocks = blk; | |
1248 } | |
1249 | |
1250 while (invalid_blocks || valid_blocks) | |
1251 { | |
1252 while ((blk = invalid_blocks)) | |
1253 { | |
1254 gcov_type total = 0; | |
1255 const arc_t *arc; | |
1256 | |
1257 invalid_blocks = blk->chain; | |
1258 blk->invalid_chain = 0; | |
1259 if (!blk->num_succ) | |
1260 for (arc = blk->succ; arc; arc = arc->succ_next) | |
1261 total += arc->count; | |
1262 else if (!blk->num_pred) | |
1263 for (arc = blk->pred; arc; arc = arc->pred_next) | |
1264 total += arc->count; | |
1265 else | |
1266 continue; | |
1267 | |
1268 blk->count = total; | |
1269 blk->count_valid = 1; | |
1270 blk->chain = valid_blocks; | |
1271 blk->valid_chain = 1; | |
1272 valid_blocks = blk; | |
1273 } | |
1274 while ((blk = valid_blocks)) | |
1275 { | |
1276 gcov_type total; | |
1277 arc_t *arc, *inv_arc; | |
1278 | |
1279 valid_blocks = blk->chain; | |
1280 blk->valid_chain = 0; | |
1281 if (blk->num_succ == 1) | |
1282 { | |
1283 block_t *dst; | |
1284 | |
1285 total = blk->count; | |
1286 inv_arc = NULL; | |
1287 for (arc = blk->succ; arc; arc = arc->succ_next) | |
1288 { | |
1289 total -= arc->count; | |
1290 if (!arc->count_valid) | |
1291 inv_arc = arc; | |
1292 } | |
1293 dst = inv_arc->dst; | |
1294 inv_arc->count_valid = 1; | |
1295 inv_arc->count = total; | |
1296 blk->num_succ--; | |
1297 dst->num_pred--; | |
1298 if (dst->count_valid) | |
1299 { | |
1300 if (dst->num_pred == 1 && !dst->valid_chain) | |
1301 { | |
1302 dst->chain = valid_blocks; | |
1303 dst->valid_chain = 1; | |
1304 valid_blocks = dst; | |
1305 } | |
1306 } | |
1307 else | |
1308 { | |
1309 if (!dst->num_pred && !dst->invalid_chain) | |
1310 { | |
1311 dst->chain = invalid_blocks; | |
1312 dst->invalid_chain = 1; | |
1313 invalid_blocks = dst; | |
1314 } | |
1315 } | |
1316 } | |
1317 if (blk->num_pred == 1) | |
1318 { | |
1319 block_t *src; | |
1320 | |
1321 total = blk->count; | |
1322 inv_arc = NULL; | |
1323 for (arc = blk->pred; arc; arc = arc->pred_next) | |
1324 { | |
1325 total -= arc->count; | |
1326 if (!arc->count_valid) | |
1327 inv_arc = arc; | |
1328 } | |
1329 src = inv_arc->src; | |
1330 inv_arc->count_valid = 1; | |
1331 inv_arc->count = total; | |
1332 blk->num_pred--; | |
1333 src->num_succ--; | |
1334 if (src->count_valid) | |
1335 { | |
1336 if (src->num_succ == 1 && !src->valid_chain) | |
1337 { | |
1338 src->chain = valid_blocks; | |
1339 src->valid_chain = 1; | |
1340 valid_blocks = src; | |
1341 } | |
1342 } | |
1343 else | |
1344 { | |
1345 if (!src->num_succ && !src->invalid_chain) | |
1346 { | |
1347 src->chain = invalid_blocks; | |
1348 src->invalid_chain = 1; | |
1349 invalid_blocks = src; | |
1350 } | |
1351 } | |
1352 } | |
1353 } | |
1354 } | |
1355 | |
1356 /* If the graph has been correctly solved, every block will have a | |
1357 valid count. */ | |
1358 for (ix = 0; ix < fn->num_blocks; ix++) | |
1359 if (!fn->blocks[ix].count_valid) | |
1360 { | |
1361 fnotice (stderr, "%s:graph is unsolvable for '%s'\n", | |
1362 bbg_file_name, fn->name); | |
1363 break; | |
1364 } | |
1365 } | |
1366 | |
1367 | |
1368 | |
1369 /* Increment totals in COVERAGE according to arc ARC. */ | |
1370 | |
1371 static void | |
1372 add_branch_counts (coverage_t *coverage, const arc_t *arc) | |
1373 { | |
1374 if (arc->is_call_non_return) | |
1375 { | |
1376 coverage->calls++; | |
1377 if (arc->src->count) | |
1378 coverage->calls_executed++; | |
1379 } | |
1380 else if (!arc->is_unconditional) | |
1381 { | |
1382 coverage->branches++; | |
1383 if (arc->src->count) | |
1384 coverage->branches_executed++; | |
1385 if (arc->count) | |
1386 coverage->branches_taken++; | |
1387 } | |
1388 } | |
1389 | |
1390 /* Format a HOST_WIDE_INT as either a percent ratio, or absolute | |
1391 count. If dp >= 0, format TOP/BOTTOM * 100 to DP decimal places. | |
1392 If DP is zero, no decimal point is printed. Only print 100% when | |
1393 TOP==BOTTOM and only print 0% when TOP=0. If dp < 0, then simply | |
1394 format TOP. Return pointer to a static string. */ | |
1395 | |
1396 static char const * | |
1397 format_gcov (gcov_type top, gcov_type bottom, int dp) | |
1398 { | |
1399 static char buffer[20]; | |
1400 | |
1401 if (dp >= 0) | |
1402 { | |
1403 float ratio = bottom ? (float)top / bottom : 0; | |
1404 int ix; | |
1405 unsigned limit = 100; | |
1406 unsigned percent; | |
1407 | |
1408 for (ix = dp; ix--; ) | |
1409 limit *= 10; | |
1410 | |
1411 percent = (unsigned) (ratio * limit + (float)0.5); | |
1412 if (percent <= 0 && top) | |
1413 percent = 1; | |
1414 else if (percent >= limit && top != bottom) | |
1415 percent = limit - 1; | |
1416 ix = sprintf (buffer, "%.*u%%", dp + 1, percent); | |
1417 if (dp) | |
1418 { | |
1419 dp++; | |
1420 do | |
1421 { | |
1422 buffer[ix+1] = buffer[ix]; | |
1423 ix--; | |
1424 } | |
1425 while (dp--); | |
1426 buffer[ix + 1] = '.'; | |
1427 } | |
1428 } | |
1429 else | |
1430 sprintf (buffer, HOST_WIDEST_INT_PRINT_DEC, (HOST_WIDEST_INT)top); | |
1431 | |
1432 return buffer; | |
1433 } | |
1434 | |
1435 | |
1436 /* Output summary info for a function. */ | |
1437 | |
1438 static void | |
1439 function_summary (const coverage_t *coverage, const char *title) | |
1440 { | |
1441 fnotice (stdout, "%s '%s'\n", title, coverage->name); | |
1442 | |
1443 if (coverage->lines) | |
1444 fnotice (stdout, "Lines executed:%s of %d\n", | |
1445 format_gcov (coverage->lines_executed, coverage->lines, 2), | |
1446 coverage->lines); | |
1447 else | |
1448 fnotice (stdout, "No executable lines\n"); | |
1449 | |
1450 if (flag_branches) | |
1451 { | |
1452 if (coverage->branches) | |
1453 { | |
1454 fnotice (stdout, "Branches executed:%s of %d\n", | |
1455 format_gcov (coverage->branches_executed, | |
1456 coverage->branches, 2), | |
1457 coverage->branches); | |
1458 fnotice (stdout, "Taken at least once:%s of %d\n", | |
1459 format_gcov (coverage->branches_taken, | |
1460 coverage->branches, 2), | |
1461 coverage->branches); | |
1462 } | |
1463 else | |
1464 fnotice (stdout, "No branches\n"); | |
1465 if (coverage->calls) | |
1466 fnotice (stdout, "Calls executed:%s of %d\n", | |
1467 format_gcov (coverage->calls_executed, coverage->calls, 2), | |
1468 coverage->calls); | |
1469 else | |
1470 fnotice (stdout, "No calls\n"); | |
1471 } | |
1472 } | |
1473 | |
1474 /* Generate an output file name. LONG_OUTPUT_NAMES and PRESERVE_PATHS | |
1475 affect name generation. With preserve_paths we create a filename | |
1476 from all path components of the source file, replacing '/' with | |
1477 '#', without it we simply take the basename component. With | |
1478 long_output_names we prepend the processed name of the input file | |
1479 to each output name (except when the current source file is the | |
1480 input file, so you don't get a double concatenation). The two | |
1481 components are separated by '##'. Also '.' filename components are | |
1482 removed and '..' components are renamed to '^'. */ | |
1483 | |
1484 static char * | |
1485 make_gcov_file_name (const char *input_name, const char *src_name) | |
1486 { | |
1487 const char *cptr; | |
1488 char *name; | |
1489 | |
1490 if (flag_long_names && input_name && strcmp (src_name, input_name)) | |
1491 { | |
1492 name = XNEWVEC (char, strlen (src_name) + strlen (input_name) + 10); | |
1493 name[0] = 0; | |
1494 /* Generate the input filename part. */ | |
1495 cptr = flag_preserve_paths ? NULL : lbasename (input_name); | |
1496 strcat (name, cptr ? cptr : input_name); | |
1497 strcat (name, "##"); | |
1498 } | |
1499 else | |
1500 { | |
1501 name = XNEWVEC (char, strlen (src_name) + 10); | |
1502 name[0] = 0; | |
1503 } | |
1504 | |
1505 /* Generate the source filename part. */ | |
1506 | |
1507 cptr = flag_preserve_paths ? NULL : lbasename (src_name); | |
1508 strcat (name, cptr ? cptr : src_name); | |
1509 | |
1510 if (flag_preserve_paths) | |
1511 { | |
1512 /* Convert '/' and '\' to '#', remove '/./', convert '/../' to '/^/', | |
1513 convert ':' to '~' on DOS based file system. */ | |
1514 char *pnew = name, *pold = name; | |
1515 | |
1516 /* First check for leading drive separator. */ | |
1517 | |
1518 while (*pold != '\0') | |
1519 { | |
1520 if (*pold == '/' || *pold == '\\') | |
1521 { | |
1522 *pnew++ = '#'; | |
1523 pold++; | |
1524 } | |
1525 #if defined (HAVE_DOS_BASED_FILE_SYSTEM) | |
1526 else if (*pold == ':') | |
1527 { | |
1528 *pnew++ = '~'; | |
1529 pold++; | |
1530 } | |
1531 #endif | |
1532 else if ((*pold == '/' && strstr (pold, "/./") == pold) | |
1533 || (*pold == '\\' && strstr (pold, "\\.\\") == pold)) | |
1534 pold += 3; | |
1535 else if (*pold == '/' && strstr (pold, "/../") == pold) | |
1536 { | |
1537 strcpy (pnew, "/^/"); | |
1538 pnew += 3; | |
1539 pold += 4; | |
1540 } | |
1541 else if (*pold == '\\' && strstr (pold, "\\..\\") == pold) | |
1542 { | |
1543 strcpy (pnew, "\\^\\"); | |
1544 pnew += 3; | |
1545 pold += 4; | |
1546 } | |
1547 else | |
1548 *pnew++ = *pold++; | |
1549 } | |
1550 | |
1551 *pnew = '\0'; | |
1552 } | |
1553 | |
1554 strcat (name, ".gcov"); | |
1555 return name; | |
1556 } | |
1557 | |
1558 /* Scan through the bb_data for each line in the block, increment | |
1559 the line number execution count indicated by the execution count of | |
1560 the appropriate basic block. */ | |
1561 | |
1562 static void | |
1563 add_line_counts (coverage_t *coverage, function_t *fn) | |
1564 { | |
1565 unsigned ix; | |
1566 line_t *line = NULL; /* This is propagated from one iteration to the | |
1567 next. */ | |
1568 | |
1569 /* Scan each basic block. */ | |
1570 for (ix = 0; ix != fn->num_blocks; ix++) | |
1571 { | |
1572 block_t *block = &fn->blocks[ix]; | |
1573 unsigned *encoding; | |
1574 const source_t *src = NULL; | |
1575 unsigned jx; | |
1576 | |
1577 if (block->count && ix && ix + 1 != fn->num_blocks) | |
1578 fn->blocks_executed++; | |
1579 for (jx = 0, encoding = block->u.line.encoding; | |
1580 jx != block->u.line.num; jx++, encoding++) | |
1581 if (!*encoding) | |
1582 { | |
1583 unsigned src_n = *++encoding; | |
1584 | |
1585 for (src = sources; src->index != src_n; src = src->next) | |
1586 continue; | |
1587 jx++; | |
1588 } | |
1589 else | |
1590 { | |
1591 line = &src->lines[*encoding]; | |
1592 | |
1593 if (coverage) | |
1594 { | |
1595 if (!line->exists) | |
1596 coverage->lines++; | |
1597 if (!line->count && block->count) | |
1598 coverage->lines_executed++; | |
1599 } | |
1600 line->exists = 1; | |
1601 line->count += block->count; | |
1602 } | |
1603 free (block->u.line.encoding); | |
1604 block->u.cycle.arc = NULL; | |
1605 block->u.cycle.ident = ~0U; | |
1606 | |
1607 if (!ix || ix + 1 == fn->num_blocks) | |
1608 /* Entry or exit block */; | |
1609 else if (flag_all_blocks) | |
1610 { | |
1611 line_t *block_line = line ? line : &fn->src->lines[fn->line]; | |
1612 | |
1613 block->chain = block_line->u.blocks; | |
1614 block_line->u.blocks = block; | |
1615 } | |
1616 else if (flag_branches) | |
1617 { | |
1618 arc_t *arc; | |
1619 | |
1620 for (arc = block->succ; arc; arc = arc->succ_next) | |
1621 { | |
1622 arc->line_next = line->u.branches; | |
1623 line->u.branches = arc; | |
1624 if (coverage && !arc->is_unconditional) | |
1625 add_branch_counts (coverage, arc); | |
1626 } | |
1627 } | |
1628 } | |
1629 if (!line) | |
1630 fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name); | |
1631 } | |
1632 | |
1633 /* Accumulate the line counts of a file. */ | |
1634 | |
1635 static void | |
1636 accumulate_line_counts (source_t *src) | |
1637 { | |
1638 line_t *line; | |
1639 function_t *fn, *fn_p, *fn_n; | |
1640 unsigned ix; | |
1641 | |
1642 /* Reverse the function order. */ | |
1643 for (fn = src->functions, fn_p = NULL; fn; | |
1644 fn_p = fn, fn = fn_n) | |
1645 { | |
1646 fn_n = fn->line_next; | |
1647 fn->line_next = fn_p; | |
1648 } | |
1649 src->functions = fn_p; | |
1650 | |
1651 for (ix = src->num_lines, line = src->lines; ix--; line++) | |
1652 { | |
1653 if (!flag_all_blocks) | |
1654 { | |
1655 arc_t *arc, *arc_p, *arc_n; | |
1656 | |
1657 /* Total and reverse the branch information. */ | |
1658 for (arc = line->u.branches, arc_p = NULL; arc; | |
1659 arc_p = arc, arc = arc_n) | |
1660 { | |
1661 arc_n = arc->line_next; | |
1662 arc->line_next = arc_p; | |
1663 | |
1664 add_branch_counts (&src->coverage, arc); | |
1665 } | |
1666 line->u.branches = arc_p; | |
1667 } | |
1668 else if (line->u.blocks) | |
1669 { | |
1670 /* The user expects the line count to be the number of times | |
1671 a line has been executed. Simply summing the block count | |
1672 will give an artificially high number. The Right Thing | |
1673 is to sum the entry counts to the graph of blocks on this | |
1674 line, then find the elementary cycles of the local graph | |
1675 and add the transition counts of those cycles. */ | |
1676 block_t *block, *block_p, *block_n; | |
1677 gcov_type count = 0; | |
1678 | |
1679 /* Reverse the block information. */ | |
1680 for (block = line->u.blocks, block_p = NULL; block; | |
1681 block_p = block, block = block_n) | |
1682 { | |
1683 block_n = block->chain; | |
1684 block->chain = block_p; | |
1685 block->u.cycle.ident = ix; | |
1686 } | |
1687 line->u.blocks = block_p; | |
1688 | |
1689 /* Sum the entry arcs. */ | |
1690 for (block = line->u.blocks; block; block = block->chain) | |
1691 { | |
1692 arc_t *arc; | |
1693 | |
1694 for (arc = block->pred; arc; arc = arc->pred_next) | |
1695 { | |
1696 if (arc->src->u.cycle.ident != ix) | |
1697 count += arc->count; | |
1698 if (flag_branches) | |
1699 add_branch_counts (&src->coverage, arc); | |
1700 } | |
1701 | |
1702 /* Initialize the cs_count. */ | |
1703 for (arc = block->succ; arc; arc = arc->succ_next) | |
1704 arc->cs_count = arc->count; | |
1705 } | |
1706 | |
1707 /* Find the loops. This uses the algorithm described in | |
1708 Tiernan 'An Efficient Search Algorithm to Find the | |
1709 Elementary Circuits of a Graph', CACM Dec 1970. We hold | |
1710 the P array by having each block point to the arc that | |
1711 connects to the previous block. The H array is implicitly | |
1712 held because of the arc ordering, and the block's | |
1713 previous arc pointer. | |
1714 | |
1715 Although the algorithm is O(N^3) for highly connected | |
1716 graphs, at worst we'll have O(N^2), as most blocks have | |
1717 only one or two exits. Most graphs will be small. | |
1718 | |
1719 For each loop we find, locate the arc with the smallest | |
1720 transition count, and add that to the cumulative | |
1721 count. Decrease flow over the cycle and remove the arc | |
1722 from consideration. */ | |
1723 for (block = line->u.blocks; block; block = block->chain) | |
1724 { | |
1725 block_t *head = block; | |
1726 arc_t *arc; | |
1727 | |
1728 next_vertex:; | |
1729 arc = head->succ; | |
1730 current_vertex:; | |
1731 while (arc) | |
1732 { | |
1733 block_t *dst = arc->dst; | |
1734 if (/* Already used that arc. */ | |
1735 arc->cycle | |
1736 /* Not to same graph, or before first vertex. */ | |
1737 || dst->u.cycle.ident != ix | |
1738 /* Already in path. */ | |
1739 || dst->u.cycle.arc) | |
1740 { | |
1741 arc = arc->succ_next; | |
1742 continue; | |
1743 } | |
1744 | |
1745 if (dst == block) | |
1746 { | |
1747 /* Found a closing arc. */ | |
1748 gcov_type cycle_count = arc->cs_count; | |
1749 arc_t *cycle_arc = arc; | |
1750 arc_t *probe_arc; | |
1751 | |
1752 /* Locate the smallest arc count of the loop. */ | |
1753 for (dst = head; (probe_arc = dst->u.cycle.arc); | |
1754 dst = probe_arc->src) | |
1755 if (cycle_count > probe_arc->cs_count) | |
1756 { | |
1757 cycle_count = probe_arc->cs_count; | |
1758 cycle_arc = probe_arc; | |
1759 } | |
1760 | |
1761 count += cycle_count; | |
1762 cycle_arc->cycle = 1; | |
1763 | |
1764 /* Remove the flow from the cycle. */ | |
1765 arc->cs_count -= cycle_count; | |
1766 for (dst = head; (probe_arc = dst->u.cycle.arc); | |
1767 dst = probe_arc->src) | |
1768 probe_arc->cs_count -= cycle_count; | |
1769 | |
1770 /* Unwind to the cyclic arc. */ | |
1771 while (head != cycle_arc->src) | |
1772 { | |
1773 arc = head->u.cycle.arc; | |
1774 head->u.cycle.arc = NULL; | |
1775 head = arc->src; | |
1776 } | |
1777 /* Move on. */ | |
1778 arc = arc->succ_next; | |
1779 continue; | |
1780 } | |
1781 | |
1782 /* Add new block to chain. */ | |
1783 dst->u.cycle.arc = arc; | |
1784 head = dst; | |
1785 goto next_vertex; | |
1786 } | |
1787 /* We could not add another vertex to the path. Remove | |
1788 the last vertex from the list. */ | |
1789 arc = head->u.cycle.arc; | |
1790 if (arc) | |
1791 { | |
1792 /* It was not the first vertex. Move onto next arc. */ | |
1793 head->u.cycle.arc = NULL; | |
1794 head = arc->src; | |
1795 arc = arc->succ_next; | |
1796 goto current_vertex; | |
1797 } | |
1798 /* Mark this block as unusable. */ | |
1799 block->u.cycle.ident = ~0U; | |
1800 } | |
1801 | |
1802 line->count = count; | |
1803 } | |
1804 | |
1805 if (line->exists) | |
1806 { | |
1807 src->coverage.lines++; | |
1808 if (line->count) | |
1809 src->coverage.lines_executed++; | |
1810 } | |
1811 } | |
1812 } | |
1813 | |
1814 /* Output information about ARC number IX. Returns nonzero if | |
1815 anything is output. */ | |
1816 | |
1817 static int | |
1818 output_branch_count (FILE *gcov_file, int ix, const arc_t *arc) | |
1819 { | |
1820 | |
1821 if (arc->is_call_non_return) | |
1822 { | |
1823 if (arc->src->count) | |
1824 { | |
1825 fnotice (gcov_file, "call %2d returned %s\n", ix, | |
1826 format_gcov (arc->src->count - arc->count, | |
1827 arc->src->count, -flag_counts)); | |
1828 } | |
1829 else | |
1830 fnotice (gcov_file, "call %2d never executed\n", ix); | |
1831 } | |
1832 else if (!arc->is_unconditional) | |
1833 { | |
1834 if (arc->src->count) | |
1835 fnotice (gcov_file, "branch %2d taken %s%s\n", ix, | |
1836 format_gcov (arc->count, arc->src->count, -flag_counts), | |
1837 arc->fall_through ? " (fallthrough)" : ""); | |
1838 else | |
1839 fnotice (gcov_file, "branch %2d never executed\n", ix); | |
1840 } | |
1841 else if (flag_unconditional && !arc->dst->is_call_return) | |
1842 { | |
1843 if (arc->src->count) | |
1844 fnotice (gcov_file, "unconditional %2d taken %s\n", ix, | |
1845 format_gcov (arc->count, arc->src->count, -flag_counts)); | |
1846 else | |
1847 fnotice (gcov_file, "unconditional %2d never executed\n", ix); | |
1848 } | |
1849 else | |
1850 return 0; | |
1851 return 1; | |
1852 | |
1853 } | |
1854 | |
1855 /* Read in the source file one line at a time, and output that line to | |
1856 the gcov file preceded by its execution count and other | |
1857 information. */ | |
1858 | |
1859 static void | |
1860 output_lines (FILE *gcov_file, const source_t *src) | |
1861 { | |
1862 FILE *source_file; | |
1863 unsigned line_num; /* current line number. */ | |
1864 const line_t *line; /* current line info ptr. */ | |
1865 char string[STRING_SIZE]; /* line buffer. */ | |
1866 char const *retval = ""; /* status of source file reading. */ | |
1867 function_t *fn = NULL; | |
1868 | |
1869 fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, src->name); | |
1870 if (!multiple_files) | |
1871 { | |
1872 fprintf (gcov_file, "%9s:%5d:Graph:%s\n", "-", 0, bbg_file_name); | |
1873 fprintf (gcov_file, "%9s:%5d:Data:%s\n", "-", 0, | |
1874 no_data_file ? "-" : da_file_name); | |
1875 fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0, | |
1876 object_summary.ctrs[GCOV_COUNTER_ARCS].runs); | |
1877 } | |
1878 fprintf (gcov_file, "%9s:%5d:Programs:%u\n", "-", 0, program_count); | |
1879 | |
1880 source_file = fopen (src->name, "r"); | |
1881 if (!source_file) | |
1882 { | |
1883 fnotice (stderr, "%s:cannot open source file\n", src->name); | |
1884 retval = NULL; | |
1885 } | |
1886 else if (src->file_time == 0) | |
1887 fprintf (gcov_file, "%9s:%5d:Source is newer than graph\n", "-", 0); | |
1888 | |
1889 if (flag_branches) | |
1890 fn = src->functions; | |
1891 | |
1892 for (line_num = 1, line = &src->lines[line_num]; | |
1893 line_num < src->num_lines; line_num++, line++) | |
1894 { | |
1895 for (; fn && fn->line == line_num; fn = fn->line_next) | |
1896 { | |
1897 arc_t *arc = fn->blocks[fn->num_blocks - 1].pred; | |
1898 gcov_type return_count = fn->blocks[fn->num_blocks - 1].count; | |
55
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1899 |
0 | 1900 for (; arc; arc = arc->pred_next) |
1901 if (arc->fake) | |
1902 return_count -= arc->count; | |
55
77e2b8dfacca
update it from 4.4.3 to 4.5.0
ryoma <e075725@ie.u-ryukyu.ac.jp>
parents:
47
diff
changeset
|
1903 |
0 | 1904 fprintf (gcov_file, "function %s", fn->name); |
1905 fprintf (gcov_file, " called %s", | |
1906 format_gcov (fn->blocks[0].count, 0, -1)); | |
1907 fprintf (gcov_file, " returned %s", | |
1908 format_gcov (return_count, fn->blocks[0].count, 0)); | |
1909 fprintf (gcov_file, " blocks executed %s", | |
1910 format_gcov (fn->blocks_executed, fn->num_blocks - 2, 0)); | |
1911 fprintf (gcov_file, "\n"); | |
1912 } | |
1913 | |
1914 /* For lines which don't exist in the .bb file, print '-' before | |
1915 the source line. For lines which exist but were never | |
1916 executed, print '#####' before the source line. Otherwise, | |
1917 print the execution count before the source line. There are | |
1918 16 spaces of indentation added before the source line so that | |
1919 tabs won't be messed up. */ | |
1920 fprintf (gcov_file, "%9s:%5u:", | |
1921 !line->exists ? "-" : !line->count ? "#####" | |
1922 : format_gcov (line->count, 0, -1), line_num); | |
1923 | |
1924 if (retval) | |
1925 { | |
1926 /* Copy source line. */ | |
1927 do | |
1928 { | |
1929 retval = fgets (string, STRING_SIZE, source_file); | |
1930 if (!retval) | |
1931 break; | |
1932 fputs (retval, gcov_file); | |
1933 } | |
1934 while (!retval[0] || retval[strlen (retval) - 1] != '\n'); | |
1935 } | |
1936 if (!retval) | |
1937 fputs ("/*EOF*/\n", gcov_file); | |
1938 | |
1939 if (flag_all_blocks) | |
1940 { | |
1941 block_t *block; | |
1942 arc_t *arc; | |
1943 int ix, jx; | |
1944 | |
1945 for (ix = jx = 0, block = line->u.blocks; block; | |
1946 block = block->chain) | |
1947 { | |
1948 if (!block->is_call_return) | |
1949 fprintf (gcov_file, "%9s:%5u-block %2d\n", | |
1950 !line->exists ? "-" : !block->count ? "$$$$$" | |
1951 : format_gcov (block->count, 0, -1), | |
1952 line_num, ix++); | |
1953 if (flag_branches) | |
1954 for (arc = block->succ; arc; arc = arc->succ_next) | |
1955 jx += output_branch_count (gcov_file, jx, arc); | |
1956 } | |
1957 } | |
1958 else if (flag_branches) | |
1959 { | |
1960 int ix; | |
1961 arc_t *arc; | |
1962 | |
1963 for (ix = 0, arc = line->u.branches; arc; arc = arc->line_next) | |
1964 ix += output_branch_count (gcov_file, ix, arc); | |
1965 } | |
1966 } | |
1967 | |
1968 /* Handle all remaining source lines. There may be lines after the | |
1969 last line of code. */ | |
1970 if (retval) | |
1971 { | |
1972 for (; (retval = fgets (string, STRING_SIZE, source_file)); line_num++) | |
1973 { | |
1974 fprintf (gcov_file, "%9s:%5u:%s", "-", line_num, retval); | |
1975 | |
1976 while (!retval[0] || retval[strlen (retval) - 1] != '\n') | |
1977 { | |
1978 retval = fgets (string, STRING_SIZE, source_file); | |
1979 if (!retval) | |
1980 break; | |
1981 fputs (retval, gcov_file); | |
1982 } | |
1983 } | |
1984 } | |
1985 | |
1986 if (source_file) | |
1987 fclose (source_file); | |
1988 } |