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