]>
Commit | Line | Data |
---|---|---|
10f2b886 | 1 | /* Gcov.c: prepend line execution counts and branch probabilities to a |
2 | source file. | |
c773fc10 | 3 | Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, |
dfe09cce | 4 | 1999, 2000, 2001, 2002 Free Software Foundation, Inc. |
10f2b886 | 5 | Contributed by James E. Wilson of Cygnus Support. |
99c14947 | 6 | Mangled by Bob Manson of Cygnus Support. |
10f2b886 | 7 | |
8 | Gcov is free software; you can redistribute it and/or modify | |
9 | it under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 2, or (at your option) | |
11 | any later version. | |
12 | ||
13 | Gcov is distributed in the hope that it will be useful, | |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with Gcov; see the file COPYING. If not, write to | |
7f19c2d0 | 20 | the Free Software Foundation, 59 Temple Place - Suite 330, |
21 | Boston, MA 02111-1307, USA. */ | |
10f2b886 | 22 | |
23 | /* ??? The code in final.c that produces the struct bb assumes that there is | |
24 | no padding between the fields. This is not necessary true. The current | |
25 | code can only be trusted if longs and pointers are the same size. */ | |
26 | ||
27 | /* ??? No need to print an execution count on every line, could just print | |
28 | it on the first line of each block, and only print it on a subsequent | |
29 | line in the same block if the count changes. */ | |
30 | ||
31 | /* ??? Print a list of the ten blocks with the highest execution counts, | |
32 | and list the line numbers corresponding to those blocks. Also, perhaps | |
33 | list the line numbers with the highest execution counts, only printing | |
34 | the first if there are several which are all listed in the same block. */ | |
35 | ||
36 | /* ??? Should have an option to print the number of basic blocks, and the | |
37 | percent of them that are covered. */ | |
38 | ||
39 | /* ??? Does not correctly handle the case where two .bb files refer to the | |
40 | same included source file. For example, if one has a short file containing | |
41 | only inline functions, which is then included in two other files, then | |
42 | there will be two .bb files which refer to the include file, but there | |
43 | is no way to get the total execution counts for the included file, can | |
44 | only get execution counts for one or the other of the including files. */ | |
45 | ||
99c14947 | 46 | #include "config.h" |
1486870d | 47 | #include "system.h" |
be2828ce | 48 | #include "intl.h" |
f5fff740 | 49 | #include "version.h" |
ef5ed00c | 50 | #undef abort |
10f2b886 | 51 | |
f5fff740 | 52 | #include <getopt.h> |
53 | ||
63f23608 | 54 | typedef HOST_WIDEST_INT gcov_type; |
10f2b886 | 55 | #include "gcov-io.h" |
56 | ||
10f2b886 | 57 | /* The .bb file format consists of several lists of 4-byte integers |
58 | which are the line numbers of each basic block in the file. Each | |
59 | list is terminated by a zero. These lists correspond to the basic | |
60 | blocks in the reconstructed program flow graph. | |
61 | ||
62 | A line number of -1 indicates that a source file name (padded to a | |
63 | long boundary) follows. The padded file name is followed by | |
64 | another -1 to make it easy to scan past file names. A -2 indicates | |
65 | that a function name (padded to a long boundary) follows; the name | |
66 | is followed by another -2 to make it easy to scan past the function | |
67 | name. | |
68 | ||
69 | The .bbg file contains enough info to enable gcov to reconstruct the | |
70 | program flow graph. The first word is the number of basic blocks, | |
71 | the second word is the number of arcs, followed by the list of arcs | |
72 | (source bb, dest bb pairs), then a -1, then the number of instrumented | |
73 | arcs followed by the instrumented arcs, followed by another -1. This | |
74 | is repeated for each function. | |
75 | ||
76 | The .da file contains the execution count for each instrumented branch. | |
77 | ||
78 | The .bb and .bbg files are created by giving GCC the -ftest-coverage option, | |
79 | and the .da files are created when an executable compiled with | |
80 | -fprofile-arcs is run. */ | |
81 | ||
82 | /* The functions in this file for creating and solution program flow graphs | |
83 | are very similar to functions in the gcc source file profile.c. */ | |
84 | ||
10f2b886 | 85 | /* This is the size of the buffer used to read in source file lines. */ |
86 | ||
87 | #define STRING_SIZE 200 | |
88 | ||
89 | /* One copy of this structure is created for each source file mentioned in the | |
90 | .bb file. */ | |
91 | ||
92 | struct sourcefile | |
93 | { | |
94 | char *name; | |
95 | int maxlineno; | |
96 | struct sourcefile *next; | |
97 | }; | |
98 | ||
99 | /* This points to the head of the sourcefile structure list. */ | |
100 | ||
101 | struct sourcefile *sources; | |
102 | ||
103 | /* One of these is dynamically created whenever we identify an arc in the | |
104 | function. */ | |
105 | ||
106 | struct adj_list { | |
107 | int source; | |
108 | int target; | |
63f23608 | 109 | gcov_type arc_count; |
10f2b886 | 110 | unsigned int count_valid : 1; |
111 | unsigned int on_tree : 1; | |
112 | unsigned int fake : 1; | |
113 | unsigned int fall_through : 1; | |
114 | #if 0 | |
115 | /* Not needed for gcov, but defined in profile.c. */ | |
116 | rtx branch_insn; | |
117 | #endif | |
118 | struct adj_list *pred_next; | |
119 | struct adj_list *succ_next; | |
120 | }; | |
121 | ||
122 | /* Count the number of basic blocks, and create an array of these structures, | |
123 | one for each bb in the function. */ | |
124 | ||
125 | struct bb_info { | |
126 | struct adj_list *succ; | |
127 | struct adj_list *pred; | |
63f23608 | 128 | gcov_type succ_count; |
129 | gcov_type pred_count; | |
130 | gcov_type exec_count; | |
10f2b886 | 131 | unsigned int count_valid : 1; |
132 | unsigned int on_tree : 1; | |
133 | #if 0 | |
134 | /* Not needed for gcov, but defined in profile.c. */ | |
135 | rtx first_insn; | |
136 | #endif | |
137 | }; | |
138 | ||
139 | /* When outputting branch probabilities, one of these structures is created | |
140 | for each branch/call. */ | |
141 | ||
142 | struct arcdata | |
143 | { | |
3f83cb00 | 144 | gcov_type hits; |
145 | gcov_type total; | |
10f2b886 | 146 | int call_insn; |
147 | struct arcdata *next; | |
148 | }; | |
149 | ||
150 | /* Used to save the list of bb_graphs, one per function. */ | |
151 | ||
152 | struct bb_info_list { | |
153 | /* Indexed by block number, holds the basic block graph for one function. */ | |
154 | struct bb_info *bb_graph; | |
155 | int num_blocks; | |
156 | struct bb_info_list *next; | |
157 | }; | |
158 | ||
159 | /* Holds a list of function basic block graphs. */ | |
160 | ||
161 | static struct bb_info_list *bb_graph_list = 0; | |
162 | ||
163 | /* Name and file pointer of the input file for the basic block graph. */ | |
164 | ||
165 | static char *bbg_file_name; | |
166 | static FILE *bbg_file; | |
167 | ||
168 | /* Name and file pointer of the input file for the arc count data. */ | |
169 | ||
170 | static char *da_file_name; | |
171 | static FILE *da_file; | |
172 | ||
173 | /* Name and file pointer of the input file for the basic block line counts. */ | |
174 | ||
175 | static char *bb_file_name; | |
176 | static FILE *bb_file; | |
177 | ||
178 | /* Holds the entire contents of the bb_file read into memory. */ | |
179 | ||
180 | static char *bb_data; | |
181 | ||
182 | /* Size of bb_data array in longs. */ | |
183 | ||
184 | static long bb_data_size; | |
185 | ||
186 | /* Name and file pointer of the output file. */ | |
187 | ||
188 | static char *gcov_file_name; | |
189 | static FILE *gcov_file; | |
190 | ||
191 | /* Name of the file mentioned on the command line. */ | |
192 | ||
193 | static char *input_file_name = 0; | |
194 | ||
195 | /* Output branch probabilities if true. */ | |
196 | ||
197 | static int output_branch_probs = 0; | |
198 | ||
199 | /* Output a gcov file if this is true. This is on by default, and can | |
200 | be turned off by the -n option. */ | |
201 | ||
202 | static int output_gcov_file = 1; | |
203 | ||
204 | /* For included files, make the gcov output file name include the name of | |
205 | the input source file. For example, if x.h is included in a.c, then the | |
206 | output file name is a.c.x.h.gcov instead of x.h.gcov. This works only | |
207 | when a single source file is specified. */ | |
208 | ||
209 | static int output_long_names = 0; | |
210 | ||
211 | /* Output summary info for each function. */ | |
212 | ||
213 | static int output_function_summary = 0; | |
214 | ||
215 | /* Object directory file prefix. This is the directory where .bb and .bbg | |
216 | files are looked for, if non-zero. */ | |
217 | ||
218 | static char *object_directory = 0; | |
219 | ||
887f59f0 | 220 | /* Output the number of times a branch was taken as opposed to the percentage |
221 | of times it was taken. Turned on by the -c option */ | |
87e97de6 | 222 | |
887f59f0 | 223 | static int output_branch_counts = 0; |
224 | ||
10f2b886 | 225 | /* Forward declarations. */ |
621f6678 | 226 | static void process_args PARAMS ((int, char **)); |
227 | static void open_files PARAMS ((void)); | |
228 | static void read_files PARAMS ((void)); | |
229 | static void scan_for_source_files PARAMS ((void)); | |
230 | static void output_data PARAMS ((void)); | |
f5fff740 | 231 | static void print_usage PARAMS ((int)) ATTRIBUTE_NORETURN; |
232 | static void print_version PARAMS ((void)) ATTRIBUTE_NORETURN; | |
621f6678 | 233 | static void init_arc PARAMS ((struct adj_list *, int, int, struct bb_info *)); |
234 | static struct adj_list *reverse_arcs PARAMS ((struct adj_list *)); | |
235 | static void create_program_flow_graph PARAMS ((struct bb_info_list *)); | |
236 | static void solve_program_flow_graph PARAMS ((struct bb_info_list *)); | |
237 | static void calculate_branch_probs PARAMS ((struct bb_info_list *, int, | |
238 | struct arcdata **, int)); | |
239 | static void function_summary PARAMS ((void)); | |
240 | ||
241 | extern int main PARAMS ((int, char **)); | |
10f2b886 | 242 | |
243 | int | |
244 | main (argc, argv) | |
245 | int argc; | |
246 | char **argv; | |
247 | { | |
eb718689 | 248 | gcc_init_libintl (); |
be2828ce | 249 | |
10f2b886 | 250 | process_args (argc, argv); |
251 | ||
252 | open_files (); | |
253 | ||
254 | read_files (); | |
255 | ||
256 | scan_for_source_files (); | |
257 | ||
258 | output_data (); | |
259 | ||
260 | return 0; | |
261 | } | |
262 | ||
621f6678 | 263 | static void fnotice PARAMS ((FILE *, const char *, ...)) ATTRIBUTE_PRINTF_2; |
be2828ce | 264 | static void |
621f6678 | 265 | fnotice VPARAMS ((FILE *file, const char *msgid, ...)) |
be2828ce | 266 | { |
0903457a | 267 | VA_OPEN (ap, msgid); |
268 | VA_FIXEDARG (ap, FILE *, file); | |
269 | VA_FIXEDARG (ap, const char *, msgid); | |
be2828ce | 270 | |
0cc5917f | 271 | vfprintf (file, _(msgid), ap); |
0903457a | 272 | VA_CLOSE (ap); |
be2828ce | 273 | } |
274 | ||
10f2b886 | 275 | /* More 'friendly' abort that prints the line and file. |
276 | config.h can #define abort fancy_abort if you like that sort of thing. */ | |
621f6678 | 277 | extern void fancy_abort PARAMS ((void)) ATTRIBUTE_NORETURN; |
10f2b886 | 278 | |
279 | void | |
280 | fancy_abort () | |
281 | { | |
155b05dc | 282 | fnotice (stderr, "Internal gcov abort.\n"); |
10f2b886 | 283 | exit (FATAL_EXIT_CODE); |
284 | } | |
285 | \f | |
f5fff740 | 286 | /* Print a usage message and exit. If ERROR_P is nonzero, this is an error, |
287 | otherwise the output of --help. */ | |
10f2b886 | 288 | |
289 | static void | |
f5fff740 | 290 | print_usage (error_p) |
291 | int error_p; | |
10f2b886 | 292 | { |
f5fff740 | 293 | FILE *file = error_p ? stderr : stdout; |
294 | int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE; | |
295 | fnotice (file, "Usage: gcov [OPTION]... SOURCEFILE\n\n"); | |
296 | fnotice (file, "Print code coverage information.\n\n"); | |
297 | fnotice (file, " -h, --help Print this help, then exit\n"); | |
298 | fnotice (file, " -v, --version Print version number, then exit\n"); | |
299 | fnotice (file, " -b, --branch-probabilities Include branch probabilities in output\n"); | |
300 | fnotice (file, " -c, --branch-counts Given counts of branches taken\n\ | |
301 | rather than percentages\n"); | |
302 | fnotice (file, " -n, --no-output Do not create an output file\n"); | |
303 | fnotice (file, " -l, --long-file-names Use long output file names for included\n\ | |
304 | source files\n"); | |
305 | fnotice (file, " -f, --function-summaries Output summaries for each function\n"); | |
306 | fnotice (file, " -o, --object-directory OBJDIR Search for object files in OBJDIR\n"); | |
307 | fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n", | |
308 | GCCBUGURL); | |
309 | exit (status); | |
310 | } | |
311 | ||
312 | /* Print version information and exit. */ | |
313 | ||
314 | static void | |
315 | print_version () | |
316 | { | |
317 | fnotice (stdout, "gcov (GCC) %s\n", version_string); | |
318 | fnotice (stdout, "Copyright (C) 2001 Free Software Foundation, Inc.\n"); | |
319 | fnotice (stdout, | |
320 | "This is free software; see the source for copying conditions. There is NO\n\ | |
321 | warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"); | |
322 | exit (SUCCESS_EXIT_CODE); | |
10f2b886 | 323 | } |
324 | ||
f5fff740 | 325 | static const struct option options[] = |
326 | { | |
327 | { "help", no_argument, NULL, 'h' }, | |
328 | { "version", no_argument, NULL, 'v' }, | |
329 | { "branch-probabilities", no_argument, NULL, 'b' }, | |
330 | { "branch-counts", no_argument, NULL, 'c' }, | |
331 | { "no-output", no_argument, NULL, 'n' }, | |
332 | { "long-file-names", no_argument, NULL, 'l' }, | |
333 | { "function-summaries", no_argument, NULL, 'f' }, | |
334 | { "object-directory", required_argument, NULL, 'o' } | |
335 | }; | |
336 | ||
10f2b886 | 337 | /* Parse the command line. */ |
338 | ||
339 | static void | |
340 | process_args (argc, argv) | |
341 | int argc; | |
342 | char **argv; | |
343 | { | |
f5fff740 | 344 | int opt; |
10f2b886 | 345 | |
f5fff740 | 346 | while ((opt = getopt_long (argc, argv, "hvbclnfo:", options, NULL)) != -1) |
10f2b886 | 347 | { |
f5fff740 | 348 | switch (opt) |
10f2b886 | 349 | { |
f5fff740 | 350 | case 'h': |
351 | print_usage (false); | |
352 | /* print_usage will exit. */ | |
353 | case 'v': | |
354 | print_version (); | |
355 | /* print_version will exit. */ | |
356 | case 'b': | |
357 | output_branch_probs = 1; | |
358 | break; | |
359 | case 'c': | |
360 | output_branch_counts = 1; | |
361 | break; | |
362 | case 'n': | |
363 | output_gcov_file = 0; | |
364 | break; | |
365 | case 'l': | |
366 | output_long_names = 1; | |
367 | break; | |
368 | case 'f': | |
369 | output_function_summary = 1; | |
370 | break; | |
371 | case 'o': | |
372 | object_directory = optarg; | |
373 | break; | |
374 | default: | |
375 | print_usage (true); | |
376 | /* print_usage will exit. */ | |
10f2b886 | 377 | } |
10f2b886 | 378 | } |
379 | ||
f5fff740 | 380 | if (optind != argc - 1) |
381 | print_usage (true); | |
382 | ||
383 | input_file_name = argv[optind]; | |
10f2b886 | 384 | } |
385 | ||
386 | ||
387 | /* Find and open the .bb, .da, and .bbg files. */ | |
388 | ||
389 | static void | |
390 | open_files () | |
391 | { | |
392 | int count, objdir_count; | |
393 | char *cptr; | |
394 | ||
395 | /* Determine the names of the .bb, .bbg, and .da files. Strip off the | |
396 | extension, if any, and append the new extensions. */ | |
397 | count = strlen (input_file_name); | |
398 | if (object_directory) | |
399 | objdir_count = strlen (object_directory); | |
400 | else | |
401 | objdir_count = 0; | |
402 | ||
403 | da_file_name = xmalloc (count + objdir_count + 4); | |
404 | bb_file_name = xmalloc (count + objdir_count + 4); | |
405 | bbg_file_name = xmalloc (count + objdir_count + 5); | |
406 | ||
407 | if (object_directory) | |
408 | { | |
409 | strcpy (da_file_name, object_directory); | |
410 | strcpy (bb_file_name, object_directory); | |
411 | strcpy (bbg_file_name, object_directory); | |
412 | ||
413 | if (object_directory[objdir_count - 1] != '/') | |
414 | { | |
415 | strcat (da_file_name, "/"); | |
416 | strcat (bb_file_name, "/"); | |
417 | strcat (bbg_file_name, "/"); | |
418 | } | |
419 | ||
78dbff7c | 420 | cptr = strrchr (input_file_name, '/'); |
10f2b886 | 421 | if (cptr) |
422 | { | |
423 | strcat (da_file_name, cptr + 1); | |
424 | strcat (bb_file_name, cptr + 1); | |
425 | strcat (bbg_file_name, cptr + 1); | |
426 | } | |
427 | else | |
428 | { | |
429 | strcat (da_file_name, input_file_name); | |
430 | strcat (bb_file_name, input_file_name); | |
431 | strcat (bbg_file_name, input_file_name); | |
432 | } | |
433 | } | |
434 | else | |
435 | { | |
436 | strcpy (da_file_name, input_file_name); | |
437 | strcpy (bb_file_name, input_file_name); | |
438 | strcpy (bbg_file_name, input_file_name); | |
439 | } | |
440 | ||
78dbff7c | 441 | cptr = strrchr (bb_file_name, '.'); |
10f2b886 | 442 | if (cptr) |
443 | strcpy (cptr, ".bb"); | |
444 | else | |
445 | strcat (bb_file_name, ".bb"); | |
446 | ||
78dbff7c | 447 | cptr = strrchr (da_file_name, '.'); |
10f2b886 | 448 | if (cptr) |
449 | strcpy (cptr, ".da"); | |
450 | else | |
451 | strcat (da_file_name, ".da"); | |
452 | ||
78dbff7c | 453 | cptr = strrchr (bbg_file_name, '.'); |
10f2b886 | 454 | if (cptr) |
455 | strcpy (cptr, ".bbg"); | |
456 | else | |
457 | strcat (bbg_file_name, ".bbg"); | |
458 | ||
155b05dc | 459 | bb_file = fopen (bb_file_name, "rb"); |
10f2b886 | 460 | if (bb_file == NULL) |
461 | { | |
be2828ce | 462 | fnotice (stderr, "Could not open basic block file %s.\n", bb_file_name); |
d587587a | 463 | exit (FATAL_EXIT_CODE); |
10f2b886 | 464 | } |
465 | ||
466 | /* If none of the functions in the file were executed, then there won't | |
467 | be a .da file. Just assume that all counts are zero in this case. */ | |
155b05dc | 468 | da_file = fopen (da_file_name, "rb"); |
10f2b886 | 469 | if (da_file == NULL) |
470 | { | |
be2828ce | 471 | fnotice (stderr, "Could not open data file %s.\n", da_file_name); |
472 | fnotice (stderr, "Assuming that all execution counts are zero.\n"); | |
10f2b886 | 473 | } |
87e97de6 | 474 | |
155b05dc | 475 | bbg_file = fopen (bbg_file_name, "rb"); |
10f2b886 | 476 | if (bbg_file == NULL) |
477 | { | |
be2828ce | 478 | fnotice (stderr, "Could not open program flow graph file %s.\n", |
10f2b886 | 479 | bbg_file_name); |
d587587a | 480 | exit (FATAL_EXIT_CODE); |
10f2b886 | 481 | } |
482 | ||
483 | /* Check for empty .bbg file. This indicates that there is no executable | |
484 | code in this source file. */ | |
485 | /* Set the EOF condition if at the end of file. */ | |
486 | ungetc (getc (bbg_file), bbg_file); | |
487 | if (feof (bbg_file)) | |
488 | { | |
be2828ce | 489 | fnotice (stderr, "No executable code associated with file %s.\n", |
10f2b886 | 490 | input_file_name); |
d587587a | 491 | exit (FATAL_EXIT_CODE); |
10f2b886 | 492 | } |
493 | } | |
494 | \f | |
495 | /* Initialize a new arc. */ | |
496 | ||
497 | static void | |
498 | init_arc (arcptr, source, target, bb_graph) | |
499 | struct adj_list *arcptr; | |
500 | int source, target; | |
501 | struct bb_info *bb_graph; | |
502 | { | |
503 | arcptr->target = target; | |
504 | arcptr->source = source; | |
505 | ||
506 | arcptr->arc_count = 0; | |
507 | arcptr->count_valid = 0; | |
508 | arcptr->on_tree = 0; | |
509 | arcptr->fake = 0; | |
510 | arcptr->fall_through = 0; | |
511 | ||
512 | arcptr->succ_next = bb_graph[source].succ; | |
513 | bb_graph[source].succ = arcptr; | |
514 | bb_graph[source].succ_count++; | |
515 | ||
516 | arcptr->pred_next = bb_graph[target].pred; | |
517 | bb_graph[target].pred = arcptr; | |
518 | bb_graph[target].pred_count++; | |
519 | } | |
520 | ||
521 | ||
20dd417a | 522 | /* Reverse the arcs on an arc list. */ |
10f2b886 | 523 | |
524 | static struct adj_list * | |
525 | reverse_arcs (arcptr) | |
526 | struct adj_list *arcptr; | |
527 | { | |
528 | struct adj_list *prev = 0; | |
529 | struct adj_list *next; | |
530 | ||
531 | for ( ; arcptr; arcptr = next) | |
532 | { | |
533 | next = arcptr->succ_next; | |
534 | arcptr->succ_next = prev; | |
535 | prev = arcptr; | |
536 | } | |
537 | ||
538 | return prev; | |
539 | } | |
540 | ||
541 | ||
542 | /* Construct the program flow graph from the .bbg file, and read in the data | |
543 | in the .da file. */ | |
544 | ||
545 | static void | |
546 | create_program_flow_graph (bptr) | |
547 | struct bb_info_list *bptr; | |
548 | { | |
549 | long num_blocks, number_arcs, src, dest, flag_bits, num_arcs_per_block; | |
550 | int i; | |
551 | struct adj_list *arcptr; | |
552 | struct bb_info *bb_graph; | |
553 | ||
554 | /* Read the number of blocks. */ | |
555 | __read_long (&num_blocks, bbg_file, 4); | |
556 | ||
713829e9 | 557 | /* Create an array of size bb number of bb_info structs. */ |
558 | bb_graph = (struct bb_info *) xcalloc (num_blocks, sizeof (struct bb_info)); | |
10f2b886 | 559 | |
560 | bptr->bb_graph = bb_graph; | |
561 | bptr->num_blocks = num_blocks; | |
562 | ||
563 | /* Read and create each arc from the .bbg file. */ | |
564 | __read_long (&number_arcs, bbg_file, 4); | |
565 | for (i = 0; i < num_blocks; i++) | |
566 | { | |
567 | int j; | |
568 | ||
569 | __read_long (&num_arcs_per_block, bbg_file, 4); | |
570 | for (j = 0; j < num_arcs_per_block; j++) | |
571 | { | |
572 | if (number_arcs-- < 0) | |
573 | abort (); | |
574 | ||
575 | src = i; | |
576 | __read_long (&dest, bbg_file, 4); | |
577 | ||
578 | arcptr = (struct adj_list *) xmalloc (sizeof (struct adj_list)); | |
579 | init_arc (arcptr, src, dest, bb_graph); | |
580 | ||
581 | __read_long (&flag_bits, bbg_file, 4); | |
582 | arcptr->on_tree = flag_bits & 0x1; | |
583 | arcptr->fake = !! (flag_bits & 0x2); | |
584 | arcptr->fall_through = !! (flag_bits & 0x4); | |
585 | } | |
586 | } | |
587 | ||
588 | if (number_arcs) | |
589 | abort (); | |
590 | ||
591 | /* Read and ignore the -1 separating the arc list from the arc list of the | |
592 | next function. */ | |
593 | __read_long (&src, bbg_file, 4); | |
594 | if (src != -1) | |
595 | abort (); | |
596 | ||
597 | /* Must reverse the order of all succ arcs, to ensure that they match | |
598 | the order of the data in the .da file. */ | |
599 | ||
600 | for (i = 0; i < num_blocks; i++) | |
601 | if (bb_graph[i].succ) | |
602 | bb_graph[i].succ = reverse_arcs (bb_graph[i].succ); | |
603 | ||
604 | /* For each arc not on the spanning tree, set its execution count from | |
605 | the .da file. */ | |
606 | ||
607 | /* The first count in the .da file is the number of times that the function | |
608 | was entered. This is the exec_count for block zero. */ | |
609 | ||
610 | /* This duplicates code in branch_prob in profile.c. */ | |
611 | ||
612 | for (i = 0; i < num_blocks; i++) | |
613 | for (arcptr = bb_graph[i].succ; arcptr; arcptr = arcptr->succ_next) | |
614 | if (! arcptr->on_tree) | |
615 | { | |
63f23608 | 616 | gcov_type tmp_count = 0; |
617 | if (da_file && __read_gcov_type (&tmp_count, da_file, 8)) | |
dfe09cce | 618 | abort (); |
10f2b886 | 619 | |
620 | arcptr->arc_count = tmp_count; | |
621 | arcptr->count_valid = 1; | |
622 | bb_graph[i].succ_count--; | |
623 | bb_graph[arcptr->target].pred_count--; | |
624 | } | |
625 | } | |
87e97de6 | 626 | |
10f2b886 | 627 | static void |
628 | solve_program_flow_graph (bptr) | |
629 | struct bb_info_list *bptr; | |
630 | { | |
63f23608 | 631 | int passes, changes; |
632 | gcov_type total; | |
10f2b886 | 633 | int i; |
634 | struct adj_list *arcptr; | |
635 | struct bb_info *bb_graph; | |
636 | int num_blocks; | |
637 | ||
638 | num_blocks = bptr->num_blocks; | |
639 | bb_graph = bptr->bb_graph; | |
640 | ||
641 | /* For every block in the file, | |
642 | - if every exit/entrance arc has a known count, then set the block count | |
643 | - if the block count is known, and every exit/entrance arc but one has | |
644 | a known execution count, then set the count of the remaining arc | |
645 | ||
646 | As arc counts are set, decrement the succ/pred count, but don't delete | |
647 | the arc, that way we can easily tell when all arcs are known, or only | |
648 | one arc is unknown. */ | |
649 | ||
650 | /* The order that the basic blocks are iterated through is important. | |
651 | Since the code that finds spanning trees starts with block 0, low numbered | |
652 | arcs are put on the spanning tree in preference to high numbered arcs. | |
653 | Hence, most instrumented arcs are at the end. Graph solving works much | |
654 | faster if we propagate numbers from the end to the start. | |
655 | ||
656 | This takes an average of slightly more than 3 passes. */ | |
657 | ||
658 | changes = 1; | |
659 | passes = 0; | |
660 | while (changes) | |
661 | { | |
662 | passes++; | |
663 | changes = 0; | |
664 | ||
665 | for (i = num_blocks - 1; i >= 0; i--) | |
666 | { | |
667 | if (! bb_graph[i].count_valid) | |
668 | { | |
669 | if (bb_graph[i].succ_count == 0) | |
670 | { | |
671 | total = 0; | |
672 | for (arcptr = bb_graph[i].succ; arcptr; | |
673 | arcptr = arcptr->succ_next) | |
674 | total += arcptr->arc_count; | |
675 | bb_graph[i].exec_count = total; | |
676 | bb_graph[i].count_valid = 1; | |
677 | changes = 1; | |
678 | } | |
679 | else if (bb_graph[i].pred_count == 0) | |
680 | { | |
681 | total = 0; | |
682 | for (arcptr = bb_graph[i].pred; arcptr; | |
683 | arcptr = arcptr->pred_next) | |
684 | total += arcptr->arc_count; | |
685 | bb_graph[i].exec_count = total; | |
686 | bb_graph[i].count_valid = 1; | |
687 | changes = 1; | |
688 | } | |
689 | } | |
690 | if (bb_graph[i].count_valid) | |
691 | { | |
692 | if (bb_graph[i].succ_count == 1) | |
693 | { | |
694 | total = 0; | |
695 | /* One of the counts will be invalid, but it is zero, | |
696 | so adding it in also doesn't hurt. */ | |
697 | for (arcptr = bb_graph[i].succ; arcptr; | |
698 | arcptr = arcptr->succ_next) | |
699 | total += arcptr->arc_count; | |
700 | /* Calculate count for remaining arc by conservation. */ | |
701 | total = bb_graph[i].exec_count - total; | |
702 | /* Search for the invalid arc, and set its count. */ | |
703 | for (arcptr = bb_graph[i].succ; arcptr; | |
704 | arcptr = arcptr->succ_next) | |
705 | if (! arcptr->count_valid) | |
706 | break; | |
707 | if (! arcptr) | |
708 | abort (); | |
709 | arcptr->count_valid = 1; | |
710 | arcptr->arc_count = total; | |
711 | bb_graph[i].succ_count--; | |
712 | ||
713 | bb_graph[arcptr->target].pred_count--; | |
714 | changes = 1; | |
715 | } | |
716 | if (bb_graph[i].pred_count == 1) | |
717 | { | |
718 | total = 0; | |
719 | /* One of the counts will be invalid, but it is zero, | |
720 | so adding it in also doesn't hurt. */ | |
721 | for (arcptr = bb_graph[i].pred; arcptr; | |
722 | arcptr = arcptr->pred_next) | |
723 | total += arcptr->arc_count; | |
724 | /* Calculate count for remaining arc by conservation. */ | |
725 | total = bb_graph[i].exec_count - total; | |
726 | /* Search for the invalid arc, and set its count. */ | |
727 | for (arcptr = bb_graph[i].pred; arcptr; | |
728 | arcptr = arcptr->pred_next) | |
729 | if (! arcptr->count_valid) | |
730 | break; | |
731 | if (! arcptr) | |
732 | abort (); | |
733 | arcptr->count_valid = 1; | |
734 | arcptr->arc_count = total; | |
735 | bb_graph[i].pred_count--; | |
736 | ||
737 | bb_graph[arcptr->source].succ_count--; | |
738 | changes = 1; | |
739 | } | |
740 | } | |
741 | } | |
742 | } | |
87e97de6 | 743 | |
10f2b886 | 744 | /* If the graph has been correctly solved, every block will have a |
745 | succ and pred count of zero. */ | |
746 | for (i = 0; i < num_blocks; i++) | |
747 | if (bb_graph[i].succ_count || bb_graph[i].pred_count) | |
748 | abort (); | |
749 | } | |
750 | ||
751 | ||
752 | static void | |
753 | read_files () | |
754 | { | |
755 | struct stat buf; | |
756 | struct bb_info_list *list_end = 0; | |
757 | struct bb_info_list *b_ptr; | |
99c14947 | 758 | long total; |
10f2b886 | 759 | |
760 | /* Read and ignore the first word of the .da file, which is the count of | |
761 | how many numbers follow. */ | |
762 | if (da_file && __read_long (&total, da_file, 8)) | |
dfe09cce | 763 | abort (); |
10f2b886 | 764 | |
765 | while (! feof (bbg_file)) | |
766 | { | |
767 | b_ptr = (struct bb_info_list *) xmalloc (sizeof (struct bb_info_list)); | |
768 | ||
769 | b_ptr->next = 0; | |
770 | if (list_end) | |
771 | list_end->next = b_ptr; | |
772 | else | |
773 | bb_graph_list = b_ptr; | |
774 | list_end = b_ptr; | |
775 | ||
776 | /* Read in the data in the .bbg file and reconstruct the program flow | |
777 | graph for one function. */ | |
99c14947 | 778 | create_program_flow_graph (b_ptr); |
10f2b886 | 779 | |
780 | /* Set the EOF condition if at the end of file. */ | |
781 | ungetc (getc (bbg_file), bbg_file); | |
782 | } | |
783 | ||
784 | /* Check to make sure the .da file data is valid. */ | |
785 | ||
786 | if (da_file) | |
787 | { | |
788 | if (feof (da_file)) | |
be2828ce | 789 | fnotice (stderr, ".da file contents exhausted too early\n"); |
10f2b886 | 790 | /* Should be at end of file now. */ |
791 | if (__read_long (&total, da_file, 8) == 0) | |
be2828ce | 792 | fnotice (stderr, ".da file contents not exhausted\n"); |
10f2b886 | 793 | } |
794 | ||
795 | /* Calculate all of the basic block execution counts and branch | |
796 | taken probabilities. */ | |
797 | ||
798 | for (b_ptr = bb_graph_list; b_ptr; b_ptr = b_ptr->next) | |
799 | solve_program_flow_graph (b_ptr); | |
800 | ||
801 | /* Read in all of the data from the .bb file. This info will be accessed | |
802 | sequentially twice. */ | |
803 | stat (bb_file_name, &buf); | |
804 | bb_data_size = buf.st_size / 4; | |
805 | ||
99c14947 | 806 | bb_data = (char *) xmalloc ((unsigned) buf.st_size); |
10f2b886 | 807 | fread (bb_data, sizeof (char), buf.st_size, bb_file); |
87e97de6 | 808 | |
10f2b886 | 809 | fclose (bb_file); |
810 | if (da_file) | |
811 | fclose (da_file); | |
812 | fclose (bbg_file); | |
813 | } | |
814 | ||
815 | ||
816 | /* Scan the data in the .bb file to find all source files referenced, | |
817 | and the largest line number mentioned in each one. */ | |
818 | ||
819 | static void | |
820 | scan_for_source_files () | |
821 | { | |
99c14947 | 822 | struct sourcefile *s_ptr = NULL; |
10f2b886 | 823 | char *ptr; |
e725f898 | 824 | long count; |
10f2b886 | 825 | long line_num; |
826 | ||
827 | /* Search the bb_data to find: | |
828 | 1) The number of sources files contained herein, and | |
829 | 2) The largest line number for each source file. */ | |
830 | ||
831 | ptr = bb_data; | |
832 | sources = 0; | |
833 | for (count = 0; count < bb_data_size; count++) | |
834 | { | |
835 | __fetch_long (&line_num, ptr, 4); | |
836 | ptr += 4; | |
837 | if (line_num == -1) | |
838 | { | |
839 | /* A source file name follows. Check to see if we already have | |
840 | a sourcefile structure for this file. */ | |
841 | s_ptr = sources; | |
842 | while (s_ptr && strcmp (s_ptr->name, ptr)) | |
843 | s_ptr = s_ptr->next; | |
844 | ||
845 | if (s_ptr == 0) | |
846 | { | |
847 | /* No sourcefile structure for this file name exists, create | |
848 | a new one, and append it to the front of the sources list. */ | |
849 | s_ptr = (struct sourcefile *) xmalloc (sizeof(struct sourcefile)); | |
713829e9 | 850 | s_ptr->name = xstrdup (ptr); |
10f2b886 | 851 | s_ptr->maxlineno = 0; |
852 | s_ptr->next = sources; | |
853 | sources = s_ptr; | |
854 | } | |
855 | ||
856 | /* Scan past the file name. */ | |
857 | { | |
858 | long delim; | |
859 | do { | |
860 | count++; | |
861 | __fetch_long (&delim, ptr, 4); | |
862 | ptr += 4; | |
863 | } while (delim != line_num); | |
864 | } | |
865 | } | |
866 | else if (line_num == -2) | |
867 | { | |
868 | long delim; | |
869 | ||
870 | /* A function name follows. Ignore it. */ | |
871 | do { | |
872 | count++; | |
873 | __fetch_long (&delim, ptr, 4); | |
874 | ptr += 4; | |
875 | } while (delim != line_num); | |
876 | } | |
877 | /* There will be a zero before the first file name, in which case s_ptr | |
878 | will still be uninitialized. So, only try to set the maxlineno | |
879 | field if line_num is non-zero. */ | |
880 | else if (line_num > 0) | |
881 | { | |
882 | if (s_ptr->maxlineno <= line_num) | |
883 | s_ptr->maxlineno = line_num + 1; | |
884 | } | |
885 | else if (line_num < 0) | |
886 | { | |
aa40f561 | 887 | /* Don't know what this is, but it's garbage. */ |
dfe09cce | 888 | abort (); |
10f2b886 | 889 | } |
890 | } | |
891 | } | |
892 | \f | |
893 | /* For calculating coverage at the function level. */ | |
894 | ||
895 | static int function_source_lines; | |
896 | static int function_source_lines_executed; | |
897 | static int function_branches; | |
898 | static int function_branches_executed; | |
899 | static int function_branches_taken; | |
900 | static int function_calls; | |
901 | static int function_calls_executed; | |
902 | static char *function_name; | |
903 | ||
904 | /* Calculate the branch taken probabilities for all arcs branches at the | |
905 | end of this block. */ | |
906 | ||
907 | static void | |
908 | calculate_branch_probs (current_graph, block_num, branch_probs, last_line_num) | |
909 | struct bb_info_list *current_graph; | |
910 | int block_num; | |
911 | struct arcdata **branch_probs; | |
912 | int last_line_num; | |
913 | { | |
e725f898 | 914 | gcov_type total; |
10f2b886 | 915 | struct adj_list *arcptr; |
916 | struct arcdata *end_ptr, *a_ptr; | |
917 | ||
918 | total = current_graph->bb_graph[block_num].exec_count; | |
919 | for (arcptr = current_graph->bb_graph[block_num].succ; arcptr; | |
920 | arcptr = arcptr->succ_next) | |
921 | { | |
922 | /* Ignore fall through arcs as they aren't really branches. */ | |
923 | ||
924 | if (arcptr->fall_through) | |
925 | continue; | |
87e97de6 | 926 | |
10f2b886 | 927 | a_ptr = (struct arcdata *) xmalloc (sizeof (struct arcdata)); |
887f59f0 | 928 | a_ptr->total = total; |
10f2b886 | 929 | if (total == 0) |
887f59f0 | 930 | a_ptr->hits = 0; |
10f2b886 | 931 | else |
887f59f0 | 932 | a_ptr->hits = arcptr->arc_count; |
10f2b886 | 933 | a_ptr->call_insn = arcptr->fake; |
934 | ||
935 | if (output_function_summary) | |
936 | { | |
937 | if (a_ptr->call_insn) | |
938 | { | |
939 | function_calls++; | |
887f59f0 | 940 | if (a_ptr->total != 0) |
10f2b886 | 941 | function_calls_executed++; |
942 | } | |
943 | else | |
944 | { | |
945 | function_branches++; | |
887f59f0 | 946 | if (a_ptr->total != 0) |
10f2b886 | 947 | function_branches_executed++; |
887f59f0 | 948 | if (a_ptr->hits > 0) |
10f2b886 | 949 | function_branches_taken++; |
950 | } | |
951 | } | |
952 | ||
953 | /* Append the new branch to the end of the list. */ | |
954 | a_ptr->next = 0; | |
955 | if (! branch_probs[last_line_num]) | |
956 | branch_probs[last_line_num] = a_ptr; | |
957 | else | |
958 | { | |
959 | end_ptr = branch_probs[last_line_num]; | |
960 | while (end_ptr->next != 0) | |
961 | end_ptr = end_ptr->next; | |
962 | end_ptr->next = a_ptr; | |
963 | } | |
964 | } | |
965 | } | |
966 | ||
967 | /* Output summary info for a function. */ | |
968 | ||
969 | static void | |
970 | function_summary () | |
971 | { | |
972 | if (function_source_lines) | |
0cc5917f | 973 | fnotice (stdout, "%6.2f%% of %d source lines executed in function %s\n", |
10f2b886 | 974 | (((double) function_source_lines_executed / function_source_lines) |
975 | * 100), function_source_lines, function_name); | |
976 | else | |
be2828ce | 977 | fnotice (stdout, "No executable source lines in function %s\n", |
10f2b886 | 978 | function_name); |
979 | ||
980 | if (output_branch_probs) | |
981 | { | |
982 | if (function_branches) | |
983 | { | |
0cc5917f | 984 | fnotice (stdout, "%6.2f%% of %d branches executed in function %s\n", |
10f2b886 | 985 | (((double) function_branches_executed / function_branches) |
986 | * 100), function_branches, function_name); | |
be2828ce | 987 | fnotice (stdout, |
0cc5917f | 988 | "%6.2f%% of %d branches taken at least once in function %s\n", |
10f2b886 | 989 | (((double) function_branches_taken / function_branches) |
990 | * 100), function_branches, function_name); | |
991 | } | |
992 | else | |
be2828ce | 993 | fnotice (stdout, "No branches in function %s\n", function_name); |
10f2b886 | 994 | if (function_calls) |
0cc5917f | 995 | fnotice (stdout, "%6.2f%% of %d calls executed in function %s\n", |
10f2b886 | 996 | (((double) function_calls_executed / function_calls) |
997 | * 100), function_calls, function_name); | |
998 | else | |
be2828ce | 999 | fnotice (stdout, "No calls in function %s\n", function_name); |
10f2b886 | 1000 | } |
1001 | } | |
1002 | ||
1003 | /* Calculate line execution counts, and output the data to a .tcov file. */ | |
1004 | ||
1005 | static void | |
1006 | output_data () | |
1007 | { | |
1008 | /* When scanning data, this is true only if the data applies to the | |
1009 | current source file. */ | |
1010 | int this_file; | |
1011 | /* An array indexed by line number which indicates how many times that line | |
1012 | was executed. */ | |
63f23608 | 1013 | gcov_type *line_counts; |
10f2b886 | 1014 | /* An array indexed by line number which indicates whether the line was |
1015 | present in the bb file (i.e. whether it had code associate with it). | |
1016 | Lines never executed are those which both exist, and have zero execution | |
1017 | counts. */ | |
1018 | char *line_exists; | |
1019 | /* An array indexed by line number, which contains a list of branch | |
1020 | probabilities, one for each branch on that line. */ | |
99c14947 | 1021 | struct arcdata **branch_probs = NULL; |
10f2b886 | 1022 | struct sourcefile *s_ptr; |
1023 | char *source_file_name; | |
1024 | FILE *source_file; | |
1025 | struct bb_info_list *current_graph; | |
e725f898 | 1026 | long count; |
10f2b886 | 1027 | char *cptr; |
1028 | long block_num; | |
1029 | long line_num; | |
99c14947 | 1030 | long last_line_num = 0; |
10f2b886 | 1031 | int i; |
1032 | struct arcdata *a_ptr; | |
1033 | /* Buffer used for reading in lines from the source file. */ | |
1034 | char string[STRING_SIZE]; | |
1035 | /* For calculating coverage at the file level. */ | |
1036 | int total_source_lines; | |
1037 | int total_source_lines_executed; | |
1038 | int total_branches; | |
1039 | int total_branches_executed; | |
1040 | int total_branches_taken; | |
1041 | int total_calls; | |
1042 | int total_calls_executed; | |
1043 | ||
1044 | /* Now, for each source file, allocate an array big enough to hold a count | |
1045 | for each line. Scan through the bb_data, and when the file name matches | |
1046 | the current file name, then for each following line number, increment | |
1047 | the line number execution count indicated by the execution count of | |
1048 | the appropriate basic block. */ | |
1049 | ||
1050 | for (s_ptr = sources; s_ptr; s_ptr = s_ptr->next) | |
1051 | { | |
1052 | /* If this is a relative file name, and an object directory has been | |
1053 | specified, then make it relative to the object directory name. */ | |
c026482a | 1054 | if (! IS_ABSOLUTE_PATHNAME (s_ptr->name) |
155b05dc | 1055 | && object_directory != 0 |
10f2b886 | 1056 | && *object_directory != '\0') |
1057 | { | |
1058 | int objdir_count = strlen (object_directory); | |
1059 | source_file_name = xmalloc (objdir_count + strlen (s_ptr->name) + 2); | |
1060 | strcpy (source_file_name, object_directory); | |
1061 | if (object_directory[objdir_count - 1] != '/') | |
1062 | source_file_name[objdir_count++] = '/'; | |
1063 | strcpy (source_file_name + objdir_count, s_ptr->name); | |
1064 | } | |
1065 | else | |
1066 | source_file_name = s_ptr->name; | |
1067 | ||
63f23608 | 1068 | line_counts = (gcov_type *) xcalloc (sizeof (gcov_type), s_ptr->maxlineno); |
713829e9 | 1069 | line_exists = xcalloc (1, s_ptr->maxlineno); |
10f2b886 | 1070 | if (output_branch_probs) |
713829e9 | 1071 | branch_probs = (struct arcdata **) |
1072 | xcalloc (sizeof (struct arcdata *), s_ptr->maxlineno); | |
87e97de6 | 1073 | |
10f2b886 | 1074 | /* There will be a zero at the beginning of the bb info, before the |
1075 | first list of line numbers, so must initialize block_num to 0. */ | |
1076 | block_num = 0; | |
1077 | this_file = 0; | |
1078 | current_graph = 0; | |
1079 | { | |
1080 | /* Pointer into the bb_data, incremented while scanning the data. */ | |
1081 | char *ptr = bb_data; | |
1082 | for (count = 0; count < bb_data_size; count++) | |
1083 | { | |
1084 | long delim; | |
1085 | ||
1086 | __fetch_long (&line_num, ptr, 4); | |
1087 | ptr += 4; | |
1088 | if (line_num == -1) | |
1089 | { | |
1090 | /* Marks the beginning of a file name. Check to see whether | |
1091 | this is the filename we are currently collecting data for. */ | |
1092 | ||
1093 | if (strcmp (s_ptr->name, ptr)) | |
1094 | this_file = 0; | |
1095 | else | |
1096 | this_file = 1; | |
87e97de6 | 1097 | |
10f2b886 | 1098 | /* Scan past the file name. */ |
1099 | do { | |
1100 | count++; | |
1101 | __fetch_long (&delim, ptr, 4); | |
1102 | ptr += 4; | |
1103 | } while (delim != line_num); | |
1104 | } | |
1105 | else if (line_num == -2) | |
1106 | { | |
1107 | /* Marks the start of a new function. Advance to the next | |
1108 | program flow graph. */ | |
1109 | ||
1110 | if (! current_graph) | |
1111 | current_graph = bb_graph_list; | |
1112 | else | |
1113 | { | |
1114 | if (block_num == current_graph->num_blocks - 1) | |
1115 | /* Last block falls through to exit. */ | |
1116 | ; | |
1117 | else if (block_num == current_graph->num_blocks - 2) | |
1118 | { | |
1119 | if (output_branch_probs && this_file) | |
1120 | calculate_branch_probs (current_graph, block_num, | |
1121 | branch_probs, last_line_num); | |
1122 | } | |
1123 | else | |
1124 | { | |
be2828ce | 1125 | fnotice (stderr, |
10f2b886 | 1126 | "didn't use all bb entries of graph, function %s\n", |
1127 | function_name); | |
0cc5917f | 1128 | fnotice (stderr, "block_num = %ld, num_blocks = %d\n", |
10f2b886 | 1129 | block_num, current_graph->num_blocks); |
1130 | } | |
1131 | ||
1132 | current_graph = current_graph->next; | |
1133 | block_num = 0; | |
1134 | ||
1135 | if (output_function_summary && this_file) | |
1136 | function_summary (); | |
1137 | } | |
1138 | ||
1139 | if (output_function_summary) | |
1140 | { | |
1141 | function_source_lines = 0; | |
1142 | function_source_lines_executed = 0; | |
1143 | function_branches = 0; | |
1144 | function_branches_executed = 0; | |
1145 | function_branches_taken = 0; | |
1146 | function_calls = 0; | |
1147 | function_calls_executed = 0; | |
1148 | } | |
1149 | ||
1150 | /* Save the function name for later use. */ | |
1151 | function_name = ptr; | |
1152 | ||
1153 | /* Scan past the file name. */ | |
1154 | do { | |
1155 | count++; | |
1156 | __fetch_long (&delim, ptr, 4); | |
1157 | ptr += 4; | |
1158 | } while (delim != line_num); | |
1159 | } | |
1160 | else if (line_num == 0) | |
1161 | { | |
1162 | /* Marks the end of a block. */ | |
1163 | ||
1164 | if (block_num >= current_graph->num_blocks) | |
1165 | { | |
be2828ce | 1166 | fnotice (stderr, "ERROR: too many basic blocks in .bb file %s\n", |
10f2b886 | 1167 | function_name); |
1168 | abort (); | |
1169 | } | |
87e97de6 | 1170 | |
10f2b886 | 1171 | if (output_branch_probs && this_file) |
1172 | calculate_branch_probs (current_graph, block_num, | |
1173 | branch_probs, last_line_num); | |
1174 | ||
1175 | block_num++; | |
1176 | } | |
1177 | else if (this_file) | |
1178 | { | |
1179 | if (output_function_summary) | |
1180 | { | |
1181 | if (line_exists[line_num] == 0) | |
1182 | function_source_lines++; | |
1183 | if (line_counts[line_num] == 0 | |
1184 | && current_graph->bb_graph[block_num].exec_count != 0) | |
1185 | function_source_lines_executed++; | |
1186 | } | |
1187 | ||
1188 | /* Accumulate execution data for this line number. */ | |
1189 | ||
1190 | line_counts[line_num] | |
1191 | += current_graph->bb_graph[block_num].exec_count; | |
1192 | line_exists[line_num] = 1; | |
1193 | last_line_num = line_num; | |
1194 | } | |
1195 | } | |
1196 | } | |
1197 | ||
1198 | if (output_function_summary && this_file) | |
1199 | function_summary (); | |
1200 | ||
1201 | /* Calculate summary test coverage statistics. */ | |
1202 | ||
1203 | total_source_lines = 0; | |
1204 | total_source_lines_executed = 0; | |
1205 | total_branches = 0; | |
1206 | total_branches_executed = 0; | |
1207 | total_branches_taken = 0; | |
1208 | total_calls = 0; | |
1209 | total_calls_executed = 0; | |
1210 | ||
1211 | for (count = 1; count < s_ptr->maxlineno; count++) | |
1212 | { | |
1213 | if (line_exists[count]) | |
1214 | { | |
1215 | total_source_lines++; | |
1216 | if (line_counts[count]) | |
1217 | total_source_lines_executed++; | |
1218 | } | |
1219 | if (output_branch_probs) | |
1220 | { | |
1221 | for (a_ptr = branch_probs[count]; a_ptr; a_ptr = a_ptr->next) | |
1222 | { | |
1223 | if (a_ptr->call_insn) | |
1224 | { | |
1225 | total_calls++; | |
887f59f0 | 1226 | if (a_ptr->total != 0) |
10f2b886 | 1227 | total_calls_executed++; |
1228 | } | |
1229 | else | |
1230 | { | |
1231 | total_branches++; | |
887f59f0 | 1232 | if (a_ptr->total != 0) |
10f2b886 | 1233 | total_branches_executed++; |
887f59f0 | 1234 | if (a_ptr->hits > 0) |
10f2b886 | 1235 | total_branches_taken++; |
1236 | } | |
1237 | } | |
1238 | } | |
1239 | } | |
1240 | ||
1241 | if (total_source_lines) | |
be2828ce | 1242 | fnotice (stdout, |
0cc5917f | 1243 | "%6.2f%% of %d source lines executed in file %s\n", |
10f2b886 | 1244 | (((double) total_source_lines_executed / total_source_lines) |
1245 | * 100), total_source_lines, source_file_name); | |
1246 | else | |
be2828ce | 1247 | fnotice (stdout, "No executable source lines in file %s\n", |
10f2b886 | 1248 | source_file_name); |
1249 | ||
1250 | if (output_branch_probs) | |
1251 | { | |
1252 | if (total_branches) | |
1253 | { | |
0cc5917f | 1254 | fnotice (stdout, "%6.2f%% of %d branches executed in file %s\n", |
10f2b886 | 1255 | (((double) total_branches_executed / total_branches) |
1256 | * 100), total_branches, source_file_name); | |
be2828ce | 1257 | fnotice (stdout, |
0cc5917f | 1258 | "%6.2f%% of %d branches taken at least once in file %s\n", |
10f2b886 | 1259 | (((double) total_branches_taken / total_branches) |
1260 | * 100), total_branches, source_file_name); | |
1261 | } | |
1262 | else | |
be2828ce | 1263 | fnotice (stdout, "No branches in file %s\n", source_file_name); |
10f2b886 | 1264 | if (total_calls) |
0cc5917f | 1265 | fnotice (stdout, "%6.2f%% of %d calls executed in file %s\n", |
10f2b886 | 1266 | (((double) total_calls_executed / total_calls) |
1267 | * 100), total_calls, source_file_name); | |
1268 | else | |
be2828ce | 1269 | fnotice (stdout, "No calls in file %s\n", source_file_name); |
10f2b886 | 1270 | } |
1271 | ||
1272 | if (output_gcov_file) | |
1273 | { | |
1274 | /* Now the statistics are ready. Read in the source file one line | |
99c14947 | 1275 | at a time, and output that line to the gcov file preceded by |
10f2b886 | 1276 | its execution count if non zero. */ |
87e97de6 | 1277 | |
10f2b886 | 1278 | source_file = fopen (source_file_name, "r"); |
1279 | if (source_file == NULL) | |
1280 | { | |
be2828ce | 1281 | fnotice (stderr, "Could not open source file %s.\n", |
10f2b886 | 1282 | source_file_name); |
1283 | free (line_counts); | |
1284 | free (line_exists); | |
1285 | continue; | |
1286 | } | |
1287 | ||
1288 | count = strlen (source_file_name); | |
78dbff7c | 1289 | cptr = strrchr (s_ptr->name, '/'); |
10f2b886 | 1290 | if (cptr) |
1291 | cptr = cptr + 1; | |
1292 | else | |
1293 | cptr = s_ptr->name; | |
1294 | if (output_long_names && strcmp (cptr, input_file_name)) | |
1295 | { | |
1296 | gcov_file_name = xmalloc (count + 7 + strlen (input_file_name)); | |
87e97de6 | 1297 | |
78dbff7c | 1298 | cptr = strrchr (input_file_name, '/'); |
10f2b886 | 1299 | if (cptr) |
1300 | strcpy (gcov_file_name, cptr + 1); | |
1301 | else | |
1302 | strcpy (gcov_file_name, input_file_name); | |
1303 | ||
1304 | strcat (gcov_file_name, "."); | |
1305 | ||
78dbff7c | 1306 | cptr = strrchr (source_file_name, '/'); |
10f2b886 | 1307 | if (cptr) |
1308 | strcat (gcov_file_name, cptr + 1); | |
1309 | else | |
1310 | strcat (gcov_file_name, source_file_name); | |
1311 | } | |
1312 | else | |
1313 | { | |
1314 | gcov_file_name = xmalloc (count + 6); | |
78dbff7c | 1315 | cptr = strrchr (source_file_name, '/'); |
10f2b886 | 1316 | if (cptr) |
1317 | strcpy (gcov_file_name, cptr + 1); | |
1318 | else | |
1319 | strcpy (gcov_file_name, source_file_name); | |
1320 | } | |
1321 | ||
1322 | /* Don't strip off the ending for compatibility with tcov, since | |
1323 | this results in confusion if there is more than one file with | |
1324 | the same basename, e.g. tmp.c and tmp.h. */ | |
1325 | strcat (gcov_file_name, ".gcov"); | |
1326 | ||
1327 | gcov_file = fopen (gcov_file_name, "w"); | |
1328 | ||
1329 | if (gcov_file == NULL) | |
1330 | { | |
be2828ce | 1331 | fnotice (stderr, "Could not open output file %s.\n", |
10f2b886 | 1332 | gcov_file_name); |
1333 | fclose (source_file); | |
1334 | free (line_counts); | |
1335 | free (line_exists); | |
1336 | continue; | |
1337 | } | |
1338 | ||
be2828ce | 1339 | fnotice (stdout, "Creating %s.\n", gcov_file_name); |
10f2b886 | 1340 | |
1341 | for (count = 1; count < s_ptr->maxlineno; count++) | |
1342 | { | |
1343 | char *retval; | |
1344 | int len; | |
1345 | ||
1346 | retval = fgets (string, STRING_SIZE, source_file); | |
1347 | ||
1348 | /* For lines which don't exist in the .bb file, print nothing | |
1349 | before the source line. For lines which exist but were never | |
1350 | executed, print ###### before the source line. Otherwise, | |
1351 | print the execution count before the source line. */ | |
99c14947 | 1352 | /* There are 16 spaces of indentation added before the source |
1353 | line so that tabs won't be messed up. */ | |
10f2b886 | 1354 | if (line_exists[count]) |
1355 | { | |
1356 | if (line_counts[count]) | |
63f23608 | 1357 | { |
1358 | char c[20]; | |
1359 | sprintf (c, HOST_WIDEST_INT_PRINT_DEC, (HOST_WIDEST_INT)line_counts[count]); | |
1360 | fprintf (gcov_file, "%12s %s", c, | |
1361 | string); | |
1362 | } | |
10f2b886 | 1363 | else |
1364 | fprintf (gcov_file, " ###### %s", string); | |
1365 | } | |
1366 | else | |
1367 | fprintf (gcov_file, "\t\t%s", string); | |
1368 | ||
1369 | /* In case the source file line is larger than our buffer, keep | |
99c14947 | 1370 | reading and outputting lines until we get a newline. */ |
10f2b886 | 1371 | len = strlen (string); |
0bc644e0 | 1372 | while ((len == 0 || string[strlen (string) - 1] != '\n') |
1373 | && retval != NULL) | |
10f2b886 | 1374 | { |
1375 | retval = fgets (string, STRING_SIZE, source_file); | |
1376 | fputs (string, gcov_file); | |
1377 | } | |
1378 | ||
1379 | if (output_branch_probs) | |
1380 | { | |
1381 | for (i = 0, a_ptr = branch_probs[count]; a_ptr; | |
1382 | a_ptr = a_ptr->next, i++) | |
1383 | { | |
1384 | if (a_ptr->call_insn) | |
1385 | { | |
887f59f0 | 1386 | if (a_ptr->total == 0) |
be2828ce | 1387 | fnotice (gcov_file, "call %d never executed\n", i); |
887f59f0 | 1388 | else |
1389 | { | |
1390 | if (output_branch_counts) | |
e725f898 | 1391 | { |
1392 | char c[20]; | |
1393 | sprintf (c, HOST_WIDEST_INT_PRINT_DEC, | |
1394 | a_ptr->total - a_ptr->hits); | |
1395 | fnotice (gcov_file, | |
1396 | "call %d returns = %s\n", i, c); | |
1397 | } | |
887f59f0 | 1398 | else |
e725f898 | 1399 | { |
1400 | char c[20]; | |
1401 | sprintf (c, HOST_WIDEST_INT_PRINT_DEC, | |
1402 | 100 - ((a_ptr->hits * 100) | |
1403 | + (a_ptr->total >> 1)) | |
1404 | / a_ptr->total); | |
1405 | fnotice (gcov_file, | |
1406 | "call %d returns = %s%%\n", i, c); | |
1407 | } | |
887f59f0 | 1408 | } |
10f2b886 | 1409 | } |
1410 | else | |
1411 | { | |
887f59f0 | 1412 | if (a_ptr->total == 0) |
be2828ce | 1413 | fnotice (gcov_file, "branch %d never executed\n", |
10f2b886 | 1414 | i); |
1415 | else | |
887f59f0 | 1416 | { |
1417 | if (output_branch_counts) | |
e725f898 | 1418 | { |
1419 | char c[20]; | |
1420 | sprintf (c, HOST_WIDEST_INT_PRINT_DEC, | |
1421 | a_ptr->hits); | |
1422 | fnotice (gcov_file, | |
1423 | "branch %d taken = %s\n", i, c); | |
1424 | } | |
887f59f0 | 1425 | else |
e725f898 | 1426 | { |
1427 | char c[20]; | |
1428 | sprintf (c, HOST_WIDEST_INT_PRINT_DEC, | |
1429 | ((a_ptr->hits * 100) | |
1430 | + (a_ptr->total >> 1)) | |
1431 | / a_ptr->total); | |
887f59f0 | 1432 | fnotice (gcov_file, |
e725f898 | 1433 | "branch %d taken = %s%%\n", i, c); |
1434 | } | |
887f59f0 | 1435 | } |
10f2b886 | 1436 | } |
887f59f0 | 1437 | } |
1438 | } | |
10f2b886 | 1439 | |
1440 | /* Gracefully handle errors while reading the source file. */ | |
1441 | if (retval == NULL) | |
1442 | { | |
be2828ce | 1443 | fnotice (stderr, |
10f2b886 | 1444 | "Unexpected EOF while reading source file %s.\n", |
1445 | source_file_name); | |
1446 | break; | |
1447 | } | |
1448 | } | |
1449 | ||
1450 | /* Handle all remaining source lines. There may be lines | |
1451 | after the last line of code. */ | |
1452 | ||
1453 | { | |
1454 | char *retval = fgets (string, STRING_SIZE, source_file); | |
1455 | while (retval != NULL) | |
1456 | { | |
1457 | int len; | |
1458 | ||
1459 | fprintf (gcov_file, "\t\t%s", string); | |
1460 | ||
1461 | /* In case the source file line is larger than our buffer, keep | |
99c14947 | 1462 | reading and outputting lines until we get a newline. */ |
10f2b886 | 1463 | len = strlen (string); |
0bc644e0 | 1464 | while ((len == 0 || string[strlen (string) - 1] != '\n') |
1465 | && retval != NULL) | |
10f2b886 | 1466 | { |
1467 | retval = fgets (string, STRING_SIZE, source_file); | |
1468 | fputs (string, gcov_file); | |
1469 | } | |
1470 | ||
1471 | retval = fgets (string, STRING_SIZE, source_file); | |
1472 | } | |
1473 | } | |
1474 | ||
1475 | fclose (source_file); | |
1476 | fclose (gcov_file); | |
1477 | } | |
1478 | ||
1479 | free (line_counts); | |
1480 | free (line_exists); | |
1481 | } | |
1482 | } |