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