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