gcov.c revision 260012
150397Sobrien/* Gcov.c: prepend line execution counts and branch probabilities to a 250397Sobrien source file. 3161651Skan Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 4161651Skan 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. 550397Sobrien Contributed by James E. Wilson of Cygnus Support. 650397Sobrien Mangled by Bob Manson of Cygnus Support. 7132718Skan Mangled further by Nathan Sidwell <nathan@codesourcery.com> 850397Sobrien 950397SobrienGcov is free software; you can redistribute it and/or modify 1050397Sobrienit under the terms of the GNU General Public License as published by 1150397Sobrienthe Free Software Foundation; either version 2, or (at your option) 1250397Sobrienany later version. 1350397Sobrien 1450397SobrienGcov is distributed in the hope that it will be useful, 1550397Sobrienbut WITHOUT ANY WARRANTY; without even the implied warranty of 1650397SobrienMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1750397SobrienGNU General Public License for more details. 1850397Sobrien 1950397SobrienYou should have received a copy of the GNU General Public License 2050397Sobrienalong with Gcov; see the file COPYING. If not, write to 21169689Skanthe Free Software Foundation, 51 Franklin Street, Fifth Floor, 22169689SkanBoston, MA 02110-1301, USA. */ 2350397Sobrien 2450397Sobrien/* ??? Print a list of the ten blocks with the highest execution counts, 2550397Sobrien and list the line numbers corresponding to those blocks. Also, perhaps 2650397Sobrien list the line numbers with the highest execution counts, only printing 2750397Sobrien the first if there are several which are all listed in the same block. */ 2850397Sobrien 2950397Sobrien/* ??? Should have an option to print the number of basic blocks, and the 3050397Sobrien percent of them that are covered. */ 3150397Sobrien 32132718Skan/* ??? Does not correctly handle the case where two .bb files refer to 33132718Skan the same included source file. For example, if one has a short 34132718Skan file containing only inline functions, which is then included in 35132718Skan two other files, then there will be two .bb files which refer to 36132718Skan the include file, but there is no way to get the total execution 37132718Skan counts for the included file, can only get execution counts for one 38132718Skan or the other of the including files. this can be fixed by --ratios 39132718Skan --long-file-names --preserve-paths and perl. */ 4050397Sobrien 41132718Skan/* Need an option to show individual block counts, and show 42132718Skan probabilities of fall through arcs. */ 43132718Skan 4450397Sobrien#include "config.h" 4550397Sobrien#include "system.h" 46132718Skan#include "coretypes.h" 47132718Skan#include "tm.h" 4852284Sobrien#include "intl.h" 4990075Sobrien#include "version.h" 5050397Sobrien 5190075Sobrien#include <getopt.h> 5290075Sobrien 53132718Skan#define IN_GCOV 1 5450397Sobrien#include "gcov-io.h" 55132718Skan#include "gcov-io.c" 5650397Sobrien 57132718Skan/* The bbg file is generated by -ftest-coverage option. The da file is 58132718Skan generated by a program compiled with -fprofile-arcs. Their formats 59132718Skan are documented in gcov-io.h. */ 6050397Sobrien 6150397Sobrien/* The functions in this file for creating and solution program flow graphs 62132718Skan are very similar to functions in the gcc source file profile.c. In 63132718Skan some places we make use of the knowledge of how profile.c works to 64132718Skan select particular algorithms here. */ 6550397Sobrien 6650397Sobrien/* This is the size of the buffer used to read in source file lines. */ 6750397Sobrien 6850397Sobrien#define STRING_SIZE 200 6950397Sobrien 70132718Skanstruct function_info; 71132718Skanstruct block_info; 72132718Skanstruct source_info; 7350397Sobrien 74132718Skan/* Describes an arc between two basic blocks. */ 75132718Skan 76132718Skantypedef struct arc_info 7750397Sobrien{ 78132718Skan /* source and destination blocks. */ 79132718Skan struct block_info *src; 80132718Skan struct block_info *dst; 8150397Sobrien 82132718Skan /* transition counts. */ 83132718Skan gcov_type count; 84132718Skan /* used in cycle search, so that we do not clobber original counts. */ 85132718Skan gcov_type cs_count; 8650397Sobrien 8750397Sobrien unsigned int count_valid : 1; 8850397Sobrien unsigned int on_tree : 1; 8950397Sobrien unsigned int fake : 1; 9050397Sobrien unsigned int fall_through : 1; 9150397Sobrien 92132718Skan /* Arc is for a function that abnormally returns. */ 93132718Skan unsigned int is_call_non_return : 1; 9450397Sobrien 95132718Skan /* Arc is for catch/setjump. */ 96132718Skan unsigned int is_nonlocal_return : 1; 9750397Sobrien 98132718Skan /* Is an unconditional branch. */ 99132718Skan unsigned int is_unconditional : 1; 10050397Sobrien 101132718Skan /* Loop making arc. */ 102132718Skan unsigned int cycle : 1; 103132718Skan 104132718Skan /* Next branch on line. */ 105132718Skan struct arc_info *line_next; 106132718Skan 107132718Skan /* Links to next arc on src and dst lists. */ 108132718Skan struct arc_info *succ_next; 109132718Skan struct arc_info *pred_next; 110132718Skan} arc_t; 111132718Skan 112132718Skan/* Describes a basic block. Contains lists of arcs to successor and 113132718Skan predecessor blocks. */ 114132718Skan 115132718Skantypedef struct block_info 11650397Sobrien{ 117132718Skan /* Chain of exit and entry arcs. */ 118132718Skan arc_t *succ; 119132718Skan arc_t *pred; 12050397Sobrien 121132718Skan /* Number of unprocessed exit and entry arcs. */ 122132718Skan gcov_type num_succ; 123132718Skan gcov_type num_pred; 12450397Sobrien 125132718Skan /* Block execution count. */ 126132718Skan gcov_type count; 127132718Skan unsigned flags : 13; 128132718Skan unsigned count_valid : 1; 129132718Skan unsigned valid_chain : 1; 130132718Skan unsigned invalid_chain : 1; 131132718Skan 132132718Skan /* Block is a call instrumenting site. */ 133132718Skan unsigned is_call_site : 1; /* Does the call. */ 134132718Skan unsigned is_call_return : 1; /* Is the return. */ 135132718Skan 136132718Skan /* Block is a landing pad for longjmp or throw. */ 137132718Skan unsigned is_nonlocal_return : 1; 138132718Skan 139132718Skan union 140132718Skan { 141132718Skan struct 142132718Skan { 143132718Skan /* Array of line numbers and source files. source files are 144132718Skan introduced by a linenumber of zero, the next 'line number' is 145132718Skan the number of the source file. Always starts with a source 146132718Skan file. */ 147132718Skan unsigned *encoding; 148132718Skan unsigned num; 149132718Skan } line; /* Valid until blocks are linked onto lines */ 150132718Skan struct 151132718Skan { 152132718Skan /* Single line graph cycle workspace. Used for all-blocks 153132718Skan mode. */ 154132718Skan arc_t *arc; 155132718Skan unsigned ident; 156132718Skan } cycle; /* Used in all-blocks mode, after blocks are linked onto 157132718Skan lines. */ 158132718Skan } u; 159132718Skan 160132718Skan /* Temporary chain for solving graph, and for chaining blocks on one 161132718Skan line. */ 162132718Skan struct block_info *chain; 163132718Skan 164132718Skan} block_t; 165132718Skan 166132718Skan/* Describes a single function. Contains an array of basic blocks. */ 167132718Skan 168132718Skantypedef struct function_info 169117395Skan{ 170132718Skan /* Name of function. */ 171132718Skan char *name; 172132718Skan unsigned ident; 173132718Skan unsigned checksum; 17450397Sobrien 175132718Skan /* Array of basic blocks. */ 176132718Skan block_t *blocks; 177132718Skan unsigned num_blocks; 178132718Skan unsigned blocks_executed; 179132718Skan 180132718Skan /* Raw arc coverage counts. */ 181132718Skan gcov_type *counts; 182132718Skan unsigned num_counts; 183132718Skan 184132718Skan /* First line number. */ 185132718Skan unsigned line; 186132718Skan struct source_info *src; 187132718Skan 188132718Skan /* Next function in same source file. */ 189132718Skan struct function_info *line_next; 190132718Skan 191132718Skan /* Next function. */ 192132718Skan struct function_info *next; 193132718Skan} function_t; 194132718Skan 195132718Skan/* Describes coverage of a file or function. */ 196132718Skan 197132718Skantypedef struct coverage_info 198117395Skan{ 199117395Skan int lines; 200117395Skan int lines_executed; 201132718Skan 202117395Skan int branches; 203117395Skan int branches_executed; 204117395Skan int branches_taken; 205132718Skan 206117395Skan int calls; 207117395Skan int calls_executed; 208132718Skan 209117395Skan char *name; 210132718Skan} coverage_t; 211117395Skan 212132718Skan/* Describes a single line of source. Contains a chain of basic blocks 213132718Skan with code on it. */ 21450397Sobrien 215132718Skantypedef struct line_info 216132718Skan{ 217132718Skan gcov_type count; /* execution count */ 218132718Skan union 219132718Skan { 220132718Skan arc_t *branches; /* branches from blocks that end on this 221132718Skan line. Used for branch-counts when not 222132718Skan all-blocks mode. */ 223132718Skan block_t *blocks; /* blocks which start on this line. Used 224132718Skan in all-blocks mode. */ 225132718Skan } u; 226132718Skan unsigned exists : 1; 227132718Skan} line_t; 22850397Sobrien 229132718Skan/* Describes a file mentioned in the block graph. Contains an array 230132718Skan of line info. */ 231117395Skan 232132718Skantypedef struct source_info 233132718Skan{ 234132718Skan /* Name of source file. */ 235132718Skan char *name; 236132718Skan unsigned index; 237117395Skan 238132718Skan /* Array of line information. */ 239132718Skan line_t *lines; 240132718Skan unsigned num_lines; 24150397Sobrien 242132718Skan coverage_t coverage; 24350397Sobrien 244132718Skan /* Functions in this source file. These are in ascending line 245132718Skan number order. */ 246132718Skan function_t *functions; 24750397Sobrien 248132718Skan /* Next source file. */ 249132718Skan struct source_info *next; 250132718Skan} source_t; 25150397Sobrien 252132718Skan/* Holds a list of function basic block graphs. */ 25350397Sobrien 254132718Skanstatic function_t *functions; 25550397Sobrien 256132718Skan/* This points to the head of the sourcefile structure list. */ 25750397Sobrien 258132718Skanstatic source_t *sources; 25950397Sobrien 260132718Skan/* This holds data summary information. */ 26150397Sobrien 262132718Skanstatic struct gcov_summary object_summary; 263132718Skanstatic unsigned program_count; 26450397Sobrien 265132718Skan/* Modification time of graph file. */ 26650397Sobrien 267132718Skanstatic time_t bbg_file_time; 26850397Sobrien 269132718Skan/* Name and file pointer of the input file for the basic block graph. */ 27050397Sobrien 271132718Skanstatic char *bbg_file_name; 27250397Sobrien 273132718Skan/* Stamp of the bbg file */ 274132718Skanstatic unsigned bbg_stamp; 275132718Skan 276132718Skan/* Name and file pointer of the input file for the arc count data. */ 277132718Skan 278132718Skanstatic char *da_file_name; 279132718Skan 280169689Skan/* Data file is missing. */ 281169689Skan 282169689Skanstatic int no_data_file; 283169689Skan 284132718Skan/* Output branch probabilities. */ 285132718Skan 286132718Skanstatic int flag_branches = 0; 287132718Skan 288132718Skan/* Show unconditional branches too. */ 289132718Skanstatic int flag_unconditional = 0; 290132718Skan 29150397Sobrien/* Output a gcov file if this is true. This is on by default, and can 29250397Sobrien be turned off by the -n option. */ 29350397Sobrien 294132718Skanstatic int flag_gcov_file = 1; 29550397Sobrien 296132718Skan/* For included files, make the gcov output file name include the name 297132718Skan of the input source file. For example, if x.h is included in a.c, 298132718Skan then the output file name is a.c##x.h.gcov instead of x.h.gcov. */ 29950397Sobrien 300132718Skanstatic int flag_long_names = 0; 30150397Sobrien 302132718Skan/* Output count information for every basic block, not merely those 303132718Skan that contain line number information. */ 304132718Skan 305132718Skanstatic int flag_all_blocks = 0; 306132718Skan 30750397Sobrien/* Output summary info for each function. */ 30850397Sobrien 309132718Skanstatic int flag_function_summary = 0; 31050397Sobrien 311132718Skan/* Object directory file prefix. This is the directory/file where the 312132718Skan graph and data files are looked for, if nonzero. */ 31350397Sobrien 31450397Sobrienstatic char *object_directory = 0; 31550397Sobrien 316117395Skan/* Preserve all pathname components. Needed when object files and 317132718Skan source files are in subdirectories. '/' is mangled as '#', '.' is 318132718Skan elided and '..' mangled to '^'. */ 319117395Skan 320132718Skanstatic int flag_preserve_paths = 0; 321132718Skan 32290075Sobrien/* Output the number of times a branch was taken as opposed to the percentage 323132718Skan of times it was taken. */ 32490075Sobrien 325132718Skanstatic int flag_counts = 0; 32690075Sobrien 32750397Sobrien/* Forward declarations. */ 328132718Skanstatic void fnotice (FILE *, const char *, ...) ATTRIBUTE_PRINTF_2; 329132718Skanstatic int process_args (int, char **); 330132718Skanstatic void print_usage (int) ATTRIBUTE_NORETURN; 331132718Skanstatic void print_version (void) ATTRIBUTE_NORETURN; 332132718Skanstatic void process_file (const char *); 333132718Skanstatic void create_file_names (const char *); 334132718Skanstatic source_t *find_source (const char *); 335132718Skanstatic int read_graph_file (void); 336132718Skanstatic int read_count_file (void); 337132718Skanstatic void solve_flow_graph (function_t *); 338132718Skanstatic void add_branch_counts (coverage_t *, const arc_t *); 339132718Skanstatic void add_line_counts (coverage_t *, function_t *); 340132718Skanstatic void function_summary (const coverage_t *, const char *); 341132718Skanstatic const char *format_gcov (gcov_type, gcov_type, int); 342132718Skanstatic void accumulate_line_counts (source_t *); 343132718Skanstatic int output_branch_count (FILE *, int, const arc_t *); 344132718Skanstatic void output_lines (FILE *, const source_t *); 345132718Skanstatic char *make_gcov_file_name (const char *, const char *); 346132718Skanstatic void release_structures (void); 347132718Skanextern int main (int, char **); 34850397Sobrien 34950397Sobrienint 350132718Skanmain (int argc, char **argv) 35150397Sobrien{ 352132718Skan int argno; 353132718Skan 354169689Skan /* Unlock the stdio streams. */ 355169689Skan unlock_std_streams (); 356169689Skan 35790075Sobrien gcc_init_libintl (); 35852284Sobrien 359132718Skan argno = process_args (argc, argv); 360132718Skan if (optind == argc) 361132718Skan print_usage (true); 36250397Sobrien 363132718Skan for (; argno != argc; argno++) 364132718Skan { 365132718Skan release_structures (); 36650397Sobrien 367132718Skan process_file (argv[argno]); 368132718Skan } 36950397Sobrien 37050397Sobrien return 0; 37150397Sobrien} 37250397Sobrien 37352284Sobrienstatic void 374169689Skanfnotice (FILE *file, const char *cmsgid, ...) 37552284Sobrien{ 376132718Skan va_list ap; 37752284Sobrien 378169689Skan va_start (ap, cmsgid); 379169689Skan vfprintf (file, _(cmsgid), ap); 380132718Skan va_end (ap); 38152284Sobrien} 38250397Sobrien 38390075Sobrien/* Print a usage message and exit. If ERROR_P is nonzero, this is an error, 38490075Sobrien otherwise the output of --help. */ 38550397Sobrien 38650397Sobrienstatic void 387132718Skanprint_usage (int error_p) 38850397Sobrien{ 38990075Sobrien FILE *file = error_p ? stderr : stdout; 39090075Sobrien int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE; 391132718Skan 39290075Sobrien fnotice (file, "Usage: gcov [OPTION]... SOURCEFILE\n\n"); 39390075Sobrien fnotice (file, "Print code coverage information.\n\n"); 39490075Sobrien fnotice (file, " -h, --help Print this help, then exit\n"); 39590075Sobrien fnotice (file, " -v, --version Print version number, then exit\n"); 396132718Skan fnotice (file, " -a, --all-blocks Show information for every basic block\n"); 39790075Sobrien fnotice (file, " -b, --branch-probabilities Include branch probabilities in output\n"); 39890075Sobrien fnotice (file, " -c, --branch-counts Given counts of branches taken\n\ 39990075Sobrien rather than percentages\n"); 40090075Sobrien fnotice (file, " -n, --no-output Do not create an output file\n"); 40190075Sobrien fnotice (file, " -l, --long-file-names Use long output file names for included\n\ 40290075Sobrien source files\n"); 40390075Sobrien fnotice (file, " -f, --function-summaries Output summaries for each function\n"); 404117395Skan fnotice (file, " -o, --object-directory DIR|FILE Search for object files in DIR or called FILE\n"); 405117395Skan fnotice (file, " -p, --preserve-paths Preserve all pathname components\n"); 406132718Skan fnotice (file, " -u, --unconditional-branches Show unconditional branch counts too\n"); 40790075Sobrien fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n", 408117395Skan bug_report_url); 40990075Sobrien exit (status); 41050397Sobrien} 41150397Sobrien 41290075Sobrien/* Print version information and exit. */ 41390075Sobrien 41490075Sobrienstatic void 415132718Skanprint_version (void) 41690075Sobrien{ 41790075Sobrien fnotice (stdout, "gcov (GCC) %s\n", version_string); 418161651Skan fprintf (stdout, "Copyright %s 2006 Free Software Foundation, Inc.\n", 419132718Skan _("(C)")); 420260012Spfg fnotice (stdout, "%s", 421132718Skan _("This is free software; see the source for copying conditions.\n" 422132718Skan "There is NO warranty; not even for MERCHANTABILITY or \n" 423132718Skan "FITNESS FOR A PARTICULAR PURPOSE.\n\n")); 42490075Sobrien exit (SUCCESS_EXIT_CODE); 42590075Sobrien} 42690075Sobrien 42790075Sobrienstatic const struct option options[] = 42890075Sobrien{ 42990075Sobrien { "help", no_argument, NULL, 'h' }, 43090075Sobrien { "version", no_argument, NULL, 'v' }, 431132718Skan { "all-blocks", no_argument, NULL, 'a' }, 43290075Sobrien { "branch-probabilities", no_argument, NULL, 'b' }, 43390075Sobrien { "branch-counts", no_argument, NULL, 'c' }, 43490075Sobrien { "no-output", no_argument, NULL, 'n' }, 43590075Sobrien { "long-file-names", no_argument, NULL, 'l' }, 43690075Sobrien { "function-summaries", no_argument, NULL, 'f' }, 437117395Skan { "preserve-paths", no_argument, NULL, 'p' }, 438117395Skan { "object-directory", required_argument, NULL, 'o' }, 439117395Skan { "object-file", required_argument, NULL, 'o' }, 440132718Skan { "unconditional-branches", no_argument, NULL, 'u' }, 441132718Skan { 0, 0, 0, 0 } 44290075Sobrien}; 44390075Sobrien 444132718Skan/* Process args, return index to first non-arg. */ 44550397Sobrien 446132718Skanstatic int 447132718Skanprocess_args (int argc, char **argv) 44850397Sobrien{ 44990075Sobrien int opt; 45050397Sobrien 451132718Skan while ((opt = getopt_long (argc, argv, "abcfhlno:puv", options, NULL)) != -1) 45250397Sobrien { 45390075Sobrien switch (opt) 45450397Sobrien { 455132718Skan case 'a': 456132718Skan flag_all_blocks = 1; 457132718Skan break; 45890075Sobrien case 'b': 459132718Skan flag_branches = 1; 46090075Sobrien break; 46190075Sobrien case 'c': 462132718Skan flag_counts = 1; 46390075Sobrien break; 464132718Skan case 'f': 465132718Skan flag_function_summary = 1; 46690075Sobrien break; 467132718Skan case 'h': 468132718Skan print_usage (false); 469132718Skan /* print_usage will exit. */ 47090075Sobrien case 'l': 471132718Skan flag_long_names = 1; 47290075Sobrien break; 473132718Skan case 'n': 474132718Skan flag_gcov_file = 0; 47590075Sobrien break; 47690075Sobrien case 'o': 47790075Sobrien object_directory = optarg; 47890075Sobrien break; 479117395Skan case 'p': 480132718Skan flag_preserve_paths = 1; 481117395Skan break; 482132718Skan case 'u': 483132718Skan flag_unconditional = 1; 484132718Skan break; 485132718Skan case 'v': 486132718Skan print_version (); 487132718Skan /* print_version will exit. */ 48890075Sobrien default: 48990075Sobrien print_usage (true); 49090075Sobrien /* print_usage will exit. */ 49150397Sobrien } 49250397Sobrien } 49350397Sobrien 494132718Skan return optind; 495132718Skan} 49690075Sobrien 497132718Skan/* Process a single source file. */ 498132718Skan 499132718Skanstatic void 500132718Skanprocess_file (const char *file_name) 501132718Skan{ 502132718Skan source_t *src; 503132718Skan function_t *fn; 504132718Skan 505132718Skan create_file_names (file_name); 506132718Skan if (read_graph_file ()) 507132718Skan return; 508132718Skan 509132718Skan if (!functions) 510132718Skan { 511132718Skan fnotice (stderr, "%s:no functions found\n", bbg_file_name); 512132718Skan return; 513132718Skan } 514132718Skan 515132718Skan if (read_count_file ()) 516132718Skan return; 517132718Skan 518132718Skan for (fn = functions; fn; fn = fn->next) 519132718Skan solve_flow_graph (fn); 520132718Skan for (src = sources; src; src = src->next) 521169689Skan src->lines = XCNEWVEC (line_t, src->num_lines); 522132718Skan for (fn = functions; fn; fn = fn->next) 523132718Skan { 524132718Skan coverage_t coverage; 525132718Skan 526132718Skan memset (&coverage, 0, sizeof (coverage)); 527132718Skan coverage.name = fn->name; 528132718Skan add_line_counts (flag_function_summary ? &coverage : NULL, fn); 529132718Skan if (flag_function_summary) 530132718Skan { 531132718Skan function_summary (&coverage, "Function"); 532132718Skan fnotice (stdout, "\n"); 533132718Skan } 534132718Skan } 535132718Skan 536132718Skan for (src = sources; src; src = src->next) 537132718Skan { 538132718Skan accumulate_line_counts (src); 539132718Skan function_summary (&src->coverage, "File"); 540132718Skan if (flag_gcov_file) 541132718Skan { 542132718Skan char *gcov_file_name = make_gcov_file_name (file_name, src->name); 543132718Skan FILE *gcov_file = fopen (gcov_file_name, "w"); 544132718Skan 545132718Skan if (gcov_file) 546132718Skan { 547169689Skan fnotice (stdout, "%s:creating '%s'\n", 548132718Skan src->name, gcov_file_name); 549132718Skan output_lines (gcov_file, src); 550132718Skan if (ferror (gcov_file)) 551169689Skan fnotice (stderr, "%s:error writing output file '%s'\n", 552132718Skan src->name, gcov_file_name); 553132718Skan fclose (gcov_file); 554132718Skan } 555132718Skan else 556169689Skan fnotice (stderr, "%s:could not open output file '%s'\n", 557132718Skan src->name, gcov_file_name); 558132718Skan free (gcov_file_name); 559132718Skan } 560132718Skan fnotice (stdout, "\n"); 561132718Skan } 56250397Sobrien} 56350397Sobrien 564132718Skan/* Release all memory used. */ 56550397Sobrien 566132718Skanstatic void 567132718Skanrelease_structures (void) 568132718Skan{ 569132718Skan function_t *fn; 570132718Skan source_t *src; 571132718Skan 572132718Skan free (bbg_file_name); 573132718Skan free (da_file_name); 574132718Skan da_file_name = bbg_file_name = NULL; 575132718Skan bbg_file_time = 0; 576132718Skan bbg_stamp = 0; 577132718Skan 578132718Skan while ((src = sources)) 579132718Skan { 580132718Skan sources = src->next; 581132718Skan 582132718Skan free (src->name); 583132718Skan free (src->lines); 584132718Skan } 585132718Skan 586132718Skan while ((fn = functions)) 587132718Skan { 588132718Skan unsigned ix; 589132718Skan block_t *block; 590132718Skan 591132718Skan functions = fn->next; 592132718Skan for (ix = fn->num_blocks, block = fn->blocks; ix--; block++) 593132718Skan { 594132718Skan arc_t *arc, *arc_n; 595132718Skan 596132718Skan for (arc = block->succ; arc; arc = arc_n) 597132718Skan { 598132718Skan arc_n = arc->succ_next; 599132718Skan free (arc); 600132718Skan } 601132718Skan } 602132718Skan free (fn->blocks); 603132718Skan free (fn->counts); 604132718Skan } 605132718Skan} 606132718Skan 607132718Skan/* Generate the names of the graph and data files. If OBJECT_DIRECTORY 608132718Skan is not specified, these are looked for in the current directory, 609132718Skan and named from the basename of the FILE_NAME sans extension. If 610117395Skan OBJECT_DIRECTORY is specified and is a directory, the files are in 611132718Skan that directory, but named from the basename of the FILE_NAME, sans 612132718Skan extension. Otherwise OBJECT_DIRECTORY is taken to be the name of 613132718Skan the object *file*, and the data files are named from that. */ 61450397Sobrien 61550397Sobrienstatic void 616132718Skancreate_file_names (const char *file_name) 61750397Sobrien{ 61850397Sobrien char *cptr; 619117395Skan char *name; 620132718Skan int length = strlen (file_name); 621117395Skan int base; 622132718Skan 623117395Skan if (object_directory && object_directory[0]) 62450397Sobrien { 625117395Skan struct stat status; 62650397Sobrien 627117395Skan length += strlen (object_directory) + 2; 628169689Skan name = XNEWVEC (char, length); 629117395Skan name[0] = 0; 630132718Skan 631117395Skan base = !stat (object_directory, &status) && S_ISDIR (status.st_mode); 632117395Skan strcat (name, object_directory); 633117395Skan if (base && name[strlen (name) - 1] != '/') 634117395Skan strcat (name, "/"); 63550397Sobrien } 63650397Sobrien else 63750397Sobrien { 638169689Skan name = XNEWVEC (char, length + 1); 639117395Skan name[0] = 0; 640117395Skan base = 1; 64150397Sobrien } 642132718Skan 643117395Skan if (base) 644117395Skan { 645132718Skan /* Append source file name. */ 646132718Skan cptr = strrchr (file_name, '/'); 647132718Skan strcat (name, cptr ? cptr + 1 : file_name); 648132718Skan } 64950397Sobrien 650117395Skan /* Remove the extension. */ 651117395Skan cptr = strrchr (name, '.'); 65250397Sobrien if (cptr) 653117395Skan *cptr = 0; 654132718Skan 655132718Skan length = strlen (name); 656117395Skan 657169689Skan bbg_file_name = XNEWVEC (char, length + strlen (GCOV_NOTE_SUFFIX) + 1); 658132718Skan strcpy (bbg_file_name, name); 659132718Skan strcpy (bbg_file_name + length, GCOV_NOTE_SUFFIX); 66050397Sobrien 661169689Skan da_file_name = XNEWVEC (char, length + strlen (GCOV_DATA_SUFFIX) + 1); 662117395Skan strcpy (da_file_name, name); 663132718Skan strcpy (da_file_name + length, GCOV_DATA_SUFFIX); 66450397Sobrien 665169689Skan free (name); 666132718Skan return; 667132718Skan} 66850397Sobrien 669132718Skan/* Find or create a source file structure for FILE_NAME. Copies 670132718Skan FILE_NAME on creation */ 671117395Skan 672132718Skanstatic source_t * 673132718Skanfind_source (const char *file_name) 674132718Skan{ 675132718Skan source_t *src; 67690075Sobrien 677132718Skan if (!file_name) 678132718Skan file_name = "<unknown>"; 67950397Sobrien 680132718Skan for (src = sources; src; src = src->next) 681132718Skan if (!strcmp (file_name, src->name)) 682132718Skan return src; 68350397Sobrien 684169689Skan src = XCNEW (source_t); 685132718Skan src->name = xstrdup (file_name); 686132718Skan src->coverage.name = src->name; 687132718Skan src->index = sources ? sources->index + 1 : 1; 688132718Skan src->next = sources; 689132718Skan sources = src; 69050397Sobrien 691132718Skan return src; 69250397Sobrien} 69350397Sobrien 694132718Skan/* Read the graph file. Return nonzero on fatal error. */ 69550397Sobrien 696132718Skanstatic int 697132718Skanread_graph_file (void) 69850397Sobrien{ 699132718Skan unsigned version; 700132718Skan unsigned current_tag = 0; 701132718Skan struct function_info *fn = NULL; 702132718Skan source_t *src = NULL; 703132718Skan unsigned ix; 704132718Skan unsigned tag; 70550397Sobrien 706132718Skan if (!gcov_open (bbg_file_name, 1)) 70750397Sobrien { 708132718Skan fnotice (stderr, "%s:cannot open graph file\n", bbg_file_name); 709132718Skan return 1; 71050397Sobrien } 711132718Skan bbg_file_time = gcov_time (); 712132718Skan if (!gcov_magic (gcov_read_unsigned (), GCOV_NOTE_MAGIC)) 713132718Skan { 714132718Skan fnotice (stderr, "%s:not a gcov graph file\n", bbg_file_name); 715132718Skan gcov_close (); 716132718Skan return 1; 717132718Skan } 71850397Sobrien 719132718Skan version = gcov_read_unsigned (); 720132718Skan if (version != GCOV_VERSION) 721132718Skan { 722132718Skan char v[4], e[4]; 72350397Sobrien 724132718Skan GCOV_UNSIGNED2STRING (v, version); 725132718Skan GCOV_UNSIGNED2STRING (e, GCOV_VERSION); 72650397Sobrien 727169689Skan fnotice (stderr, "%s:version '%.4s', prefer '%.4s'\n", 728132718Skan bbg_file_name, v, e); 729132718Skan } 730132718Skan bbg_stamp = gcov_read_unsigned (); 731117395Skan 732132718Skan while ((tag = gcov_read_unsigned ())) 733117395Skan { 734132718Skan unsigned length = gcov_read_unsigned (); 735132718Skan gcov_position_t base = gcov_position (); 736117395Skan 737132718Skan if (tag == GCOV_TAG_FUNCTION) 738117395Skan { 739132718Skan char *function_name; 740132718Skan unsigned ident, checksum, lineno; 741132718Skan source_t *src; 742132718Skan function_t *probe, *prev; 743117395Skan 744132718Skan ident = gcov_read_unsigned (); 745132718Skan checksum = gcov_read_unsigned (); 746132718Skan function_name = xstrdup (gcov_read_string ()); 747132718Skan src = find_source (gcov_read_string ()); 748132718Skan lineno = gcov_read_unsigned (); 749117395Skan 750169689Skan fn = XCNEW (function_t); 751132718Skan fn->name = function_name; 752132718Skan fn->ident = ident; 753132718Skan fn->checksum = checksum; 754132718Skan fn->src = src; 755132718Skan fn->line = lineno; 756117395Skan 757132718Skan fn->next = functions; 758132718Skan functions = fn; 759132718Skan current_tag = tag; 760117395Skan 761132718Skan if (lineno >= src->num_lines) 762132718Skan src->num_lines = lineno + 1; 763132718Skan /* Now insert it into the source file's list of 764132718Skan functions. Normally functions will be encountered in 765132718Skan ascending order, so a simple scan is quick. */ 766132718Skan for (probe = src->functions, prev = NULL; 767132718Skan probe && probe->line > lineno; 768132718Skan prev = probe, probe = probe->line_next) 769132718Skan continue; 770132718Skan fn->line_next = probe; 771132718Skan if (prev) 772132718Skan prev->line_next = fn; 773132718Skan else 774132718Skan src->functions = fn; 775132718Skan } 776132718Skan else if (fn && tag == GCOV_TAG_BLOCKS) 777117395Skan { 778132718Skan if (fn->blocks) 779169689Skan fnotice (stderr, "%s:already seen blocks for '%s'\n", 780132718Skan bbg_file_name, fn->name); 781117395Skan else 782117395Skan { 783132718Skan unsigned ix, num_blocks = GCOV_TAG_BLOCKS_NUM (length); 784132718Skan fn->num_blocks = num_blocks; 785117395Skan 786169689Skan fn->blocks = XCNEWVEC (block_t, fn->num_blocks); 787132718Skan for (ix = 0; ix != num_blocks; ix++) 788132718Skan fn->blocks[ix].flags = gcov_read_unsigned (); 789117395Skan } 790117395Skan } 791132718Skan else if (fn && tag == GCOV_TAG_ARCS) 792132718Skan { 793132718Skan unsigned src = gcov_read_unsigned (); 794132718Skan unsigned num_dests = GCOV_TAG_ARCS_NUM (length); 795117395Skan 796132718Skan if (src >= fn->num_blocks || fn->blocks[src].succ) 797132718Skan goto corrupt; 798117395Skan 799132718Skan while (num_dests--) 800132718Skan { 801132718Skan struct arc_info *arc; 802132718Skan unsigned dest = gcov_read_unsigned (); 803132718Skan unsigned flags = gcov_read_unsigned (); 804117395Skan 805132718Skan if (dest >= fn->num_blocks) 806132718Skan goto corrupt; 807169689Skan arc = XCNEW (arc_t); 808117395Skan 809132718Skan arc->dst = &fn->blocks[dest]; 810132718Skan arc->src = &fn->blocks[src]; 811117395Skan 812132718Skan arc->count = 0; 813132718Skan arc->count_valid = 0; 814132718Skan arc->on_tree = !!(flags & GCOV_ARC_ON_TREE); 815132718Skan arc->fake = !!(flags & GCOV_ARC_FAKE); 816132718Skan arc->fall_through = !!(flags & GCOV_ARC_FALLTHROUGH); 817117395Skan 818132718Skan arc->succ_next = fn->blocks[src].succ; 819132718Skan fn->blocks[src].succ = arc; 820132718Skan fn->blocks[src].num_succ++; 82150397Sobrien 822132718Skan arc->pred_next = fn->blocks[dest].pred; 823132718Skan fn->blocks[dest].pred = arc; 824132718Skan fn->blocks[dest].num_pred++; 82550397Sobrien 826132718Skan if (arc->fake) 827132718Skan { 828132718Skan if (src) 829132718Skan { 830132718Skan /* Exceptional exit from this function, the 831132718Skan source block must be a call. */ 832132718Skan fn->blocks[src].is_call_site = 1; 833132718Skan arc->is_call_non_return = 1; 834132718Skan } 835132718Skan else 836132718Skan { 837132718Skan /* Non-local return from a callee of this 838132718Skan function. The destination block is a catch or 839132718Skan setjmp. */ 840132718Skan arc->is_nonlocal_return = 1; 841132718Skan fn->blocks[dest].is_nonlocal_return = 1; 842132718Skan } 843132718Skan } 844117395Skan 845132718Skan if (!arc->on_tree) 846132718Skan fn->num_counts++; 847132718Skan } 848132718Skan } 849132718Skan else if (fn && tag == GCOV_TAG_LINES) 850132718Skan { 851132718Skan unsigned blockno = gcov_read_unsigned (); 852169689Skan unsigned *line_nos = XCNEWVEC (unsigned, length - 1); 853117395Skan 854132718Skan if (blockno >= fn->num_blocks || fn->blocks[blockno].u.line.encoding) 855132718Skan goto corrupt; 856117395Skan 857132718Skan for (ix = 0; ; ) 858132718Skan { 859132718Skan unsigned lineno = gcov_read_unsigned (); 860117395Skan 861132718Skan if (lineno) 862132718Skan { 863132718Skan if (!ix) 864132718Skan { 865132718Skan line_nos[ix++] = 0; 866132718Skan line_nos[ix++] = src->index; 867132718Skan } 868132718Skan line_nos[ix++] = lineno; 869132718Skan if (lineno >= src->num_lines) 870132718Skan src->num_lines = lineno + 1; 871132718Skan } 872132718Skan else 873132718Skan { 874132718Skan const char *file_name = gcov_read_string (); 875117395Skan 876132718Skan if (!file_name) 877132718Skan break; 878132718Skan src = find_source (file_name); 87950397Sobrien 880132718Skan line_nos[ix++] = 0; 881132718Skan line_nos[ix++] = src->index; 882132718Skan } 883132718Skan } 88450397Sobrien 885132718Skan fn->blocks[blockno].u.line.encoding = line_nos; 886132718Skan fn->blocks[blockno].u.line.num = ix; 887132718Skan } 888132718Skan else if (current_tag && !GCOV_TAG_IS_SUBTAG (current_tag, tag)) 88950397Sobrien { 890132718Skan fn = NULL; 891132718Skan current_tag = 0; 89250397Sobrien } 893132718Skan gcov_sync (base, length); 894132718Skan if (gcov_is_error ()) 895169689Skan { 896169689Skan corrupt:; 897169689Skan fnotice (stderr, "%s:corrupted\n", bbg_file_name); 898169689Skan gcov_close (); 899169689Skan return 1; 900169689Skan } 90150397Sobrien } 902132718Skan gcov_close (); 90350397Sobrien 904132718Skan /* We built everything backwards, so nreverse them all. */ 90550397Sobrien 906132718Skan /* Reverse sources. Not strictly necessary, but we'll then process 907132718Skan them in the 'expected' order. */ 908132718Skan { 909132718Skan source_t *src, *src_p, *src_n; 91050397Sobrien 911132718Skan for (src_p = NULL, src = sources; src; src_p = src, src = src_n) 912132718Skan { 913132718Skan src_n = src->next; 914132718Skan src->next = src_p; 915132718Skan } 916132718Skan sources = src_p; 917132718Skan } 91850397Sobrien 919132718Skan /* Reverse functions. */ 920132718Skan { 921132718Skan function_t *fn, *fn_p, *fn_n; 92250397Sobrien 923132718Skan for (fn_p = NULL, fn = functions; fn; fn_p = fn, fn = fn_n) 924132718Skan { 925132718Skan unsigned ix; 926117395Skan 927132718Skan fn_n = fn->next; 928132718Skan fn->next = fn_p; 929117395Skan 930132718Skan /* Reverse the arcs. */ 931132718Skan for (ix = fn->num_blocks; ix--;) 932132718Skan { 933132718Skan arc_t *arc, *arc_p, *arc_n; 93450397Sobrien 935132718Skan for (arc_p = NULL, arc = fn->blocks[ix].succ; arc; 936132718Skan arc_p = arc, arc = arc_n) 937132718Skan { 938132718Skan arc_n = arc->succ_next; 939132718Skan arc->succ_next = arc_p; 940132718Skan } 941132718Skan fn->blocks[ix].succ = arc_p; 94250397Sobrien 943132718Skan for (arc_p = NULL, arc = fn->blocks[ix].pred; arc; 944132718Skan arc_p = arc, arc = arc_n) 945132718Skan { 946132718Skan arc_n = arc->pred_next; 947132718Skan arc->pred_next = arc_p; 948132718Skan } 949132718Skan fn->blocks[ix].pred = arc_p; 950132718Skan } 951132718Skan } 952132718Skan functions = fn_p; 953132718Skan } 954132718Skan return 0; 95550397Sobrien} 95690075Sobrien 957132718Skan/* Reads profiles from the count file and attach to each 958132718Skan function. Return nonzero if fatal error. */ 959132718Skan 960132718Skanstatic int 961132718Skanread_count_file (void) 96250397Sobrien{ 963132718Skan unsigned ix; 964132718Skan unsigned version; 965132718Skan unsigned tag; 966132718Skan function_t *fn = NULL; 967132718Skan int error = 0; 96850397Sobrien 969132718Skan if (!gcov_open (da_file_name, 1)) 970132718Skan { 971169689Skan fnotice (stderr, "%s:cannot open data file, assuming not executed\n", 972169689Skan da_file_name); 973169689Skan no_data_file = 1; 974169689Skan return 0; 975132718Skan } 976132718Skan if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC)) 977132718Skan { 978132718Skan fnotice (stderr, "%s:not a gcov data file\n", da_file_name); 979132718Skan cleanup:; 980132718Skan gcov_close (); 981132718Skan return 1; 982132718Skan } 983132718Skan version = gcov_read_unsigned (); 984132718Skan if (version != GCOV_VERSION) 985132718Skan { 986132718Skan char v[4], e[4]; 98750397Sobrien 988132718Skan GCOV_UNSIGNED2STRING (v, version); 989132718Skan GCOV_UNSIGNED2STRING (e, GCOV_VERSION); 990132718Skan 991169689Skan fnotice (stderr, "%s:version '%.4s', prefer version '%.4s'\n", 992132718Skan da_file_name, v, e); 993132718Skan } 994132718Skan tag = gcov_read_unsigned (); 995132718Skan if (tag != bbg_stamp) 996132718Skan { 997132718Skan fnotice (stderr, "%s:stamp mismatch with graph file\n", da_file_name); 998132718Skan goto cleanup; 999132718Skan } 100050397Sobrien 1001132718Skan while ((tag = gcov_read_unsigned ())) 100250397Sobrien { 1003132718Skan unsigned length = gcov_read_unsigned (); 1004132718Skan unsigned long base = gcov_position (); 100550397Sobrien 1006132718Skan if (tag == GCOV_TAG_OBJECT_SUMMARY) 1007132718Skan gcov_read_summary (&object_summary); 1008132718Skan else if (tag == GCOV_TAG_PROGRAM_SUMMARY) 1009132718Skan program_count++; 1010132718Skan else if (tag == GCOV_TAG_FUNCTION) 101150397Sobrien { 1012132718Skan unsigned ident = gcov_read_unsigned (); 1013132718Skan struct function_info *fn_n = functions; 1014132718Skan 1015132718Skan for (fn = fn ? fn->next : NULL; ; fn = fn->next) 101650397Sobrien { 1017132718Skan if (fn) 1018132718Skan ; 1019132718Skan else if ((fn = fn_n)) 1020132718Skan fn_n = NULL; 1021132718Skan else 102250397Sobrien { 1023169689Skan fnotice (stderr, "%s:unknown function '%u'\n", 1024132718Skan da_file_name, ident); 1025132718Skan break; 102650397Sobrien } 1027132718Skan if (fn->ident == ident) 1028132718Skan break; 102950397Sobrien } 1030132718Skan 1031132718Skan if (!fn) 1032132718Skan ; 1033132718Skan else if (gcov_read_unsigned () != fn->checksum) 103450397Sobrien { 1035132718Skan mismatch:; 1036169689Skan fnotice (stderr, "%s:profile mismatch for '%s'\n", 1037132718Skan da_file_name, fn->name); 1038132718Skan goto cleanup; 1039132718Skan } 1040132718Skan } 1041132718Skan else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) && fn) 1042132718Skan { 1043132718Skan if (length != GCOV_TAG_COUNTER_LENGTH (fn->num_counts)) 1044132718Skan goto mismatch; 104550397Sobrien 1046132718Skan if (!fn->counts) 1047169689Skan fn->counts = XCNEWVEC (gcov_type, fn->num_counts); 104850397Sobrien 1049132718Skan for (ix = 0; ix != fn->num_counts; ix++) 1050132718Skan fn->counts[ix] += gcov_read_counter (); 105150397Sobrien } 1052132718Skan gcov_sync (base, length); 1053132718Skan if ((error = gcov_is_error ())) 1054169689Skan { 1055169689Skan fnotice (stderr, error < 0 ? "%s:overflowed\n" : "%s:corrupted\n", 1056169689Skan da_file_name); 1057169689Skan goto cleanup; 1058169689Skan } 105950397Sobrien } 106090075Sobrien 1061132718Skan gcov_close (); 1062132718Skan return 0; 106350397Sobrien} 106450397Sobrien 1065132718Skan/* Solve the flow graph. Propagate counts from the instrumented arcs 1066132718Skan to the blocks and the uninstrumented arcs. */ 106750397Sobrien 106850397Sobrienstatic void 1069132718Skansolve_flow_graph (function_t *fn) 107050397Sobrien{ 1071132718Skan unsigned ix; 1072132718Skan arc_t *arc; 1073132718Skan gcov_type *count_ptr = fn->counts; 1074132718Skan block_t *blk; 1075132718Skan block_t *valid_blocks = NULL; /* valid, but unpropagated blocks. */ 1076132718Skan block_t *invalid_blocks = NULL; /* invalid, but inferable blocks. */ 107750397Sobrien 1078132718Skan if (fn->num_blocks < 2) 1079169689Skan fnotice (stderr, "%s:'%s' lacks entry and/or exit blocks\n", 1080132718Skan bbg_file_name, fn->name); 1081132718Skan else 108250397Sobrien { 1083132718Skan if (fn->blocks[0].num_pred) 1084169689Skan fnotice (stderr, "%s:'%s' has arcs to entry block\n", 1085132718Skan bbg_file_name, fn->name); 1086132718Skan else 1087132718Skan /* We can't deduce the entry block counts from the lack of 1088132718Skan predecessors. */ 1089132718Skan fn->blocks[0].num_pred = ~(unsigned)0; 109050397Sobrien 1091132718Skan if (fn->blocks[fn->num_blocks - 1].num_succ) 1092169689Skan fnotice (stderr, "%s:'%s' has arcs from exit block\n", 1093132718Skan bbg_file_name, fn->name); 109450397Sobrien else 1095132718Skan /* Likewise, we can't deduce exit block counts from the lack 1096132718Skan of its successors. */ 1097132718Skan fn->blocks[fn->num_blocks - 1].num_succ = ~(unsigned)0; 109850397Sobrien } 109950397Sobrien 1100132718Skan /* Propagate the measured counts, this must be done in the same 1101132718Skan order as the code in profile.c */ 1102132718Skan for (ix = 0, blk = fn->blocks; ix != fn->num_blocks; ix++, blk++) 1103132718Skan { 1104132718Skan block_t const *prev_dst = NULL; 1105132718Skan int out_of_order = 0; 1106132718Skan int non_fake_succ = 0; 110750397Sobrien 1108132718Skan for (arc = blk->succ; arc; arc = arc->succ_next) 1109132718Skan { 1110132718Skan if (!arc->fake) 1111132718Skan non_fake_succ++; 111250397Sobrien 1113132718Skan if (!arc->on_tree) 1114132718Skan { 1115132718Skan if (count_ptr) 1116132718Skan arc->count = *count_ptr++; 1117132718Skan arc->count_valid = 1; 1118132718Skan blk->num_succ--; 1119132718Skan arc->dst->num_pred--; 1120132718Skan } 1121132718Skan if (prev_dst && prev_dst > arc->dst) 1122132718Skan out_of_order = 1; 1123132718Skan prev_dst = arc->dst; 1124132718Skan } 1125132718Skan if (non_fake_succ == 1) 1126132718Skan { 1127132718Skan /* If there is only one non-fake exit, it is an 1128132718Skan unconditional branch. */ 1129132718Skan for (arc = blk->succ; arc; arc = arc->succ_next) 1130132718Skan if (!arc->fake) 1131132718Skan { 1132132718Skan arc->is_unconditional = 1; 1133132718Skan /* If this block is instrumenting a call, it might be 1134132718Skan an artificial block. It is not artificial if it has 1135132718Skan a non-fallthrough exit, or the destination of this 1136132718Skan arc has more than one entry. Mark the destination 1137132718Skan block as a return site, if none of those conditions 1138132718Skan hold. */ 1139132718Skan if (blk->is_call_site && arc->fall_through 1140132718Skan && arc->dst->pred == arc && !arc->pred_next) 1141132718Skan arc->dst->is_call_return = 1; 1142132718Skan } 1143132718Skan } 114450397Sobrien 1145132718Skan /* Sort the successor arcs into ascending dst order. profile.c 1146132718Skan normally produces arcs in the right order, but sometimes with 1147132718Skan one or two out of order. We're not using a particularly 1148132718Skan smart sort. */ 1149132718Skan if (out_of_order) 1150132718Skan { 1151132718Skan arc_t *start = blk->succ; 1152132718Skan unsigned changes = 1; 115390075Sobrien 1154132718Skan while (changes) 1155132718Skan { 1156132718Skan arc_t *arc, *arc_p, *arc_n; 115750397Sobrien 1158132718Skan changes = 0; 1159132718Skan for (arc_p = NULL, arc = start; (arc_n = arc->succ_next);) 1160132718Skan { 1161132718Skan if (arc->dst > arc_n->dst) 1162132718Skan { 1163132718Skan changes = 1; 1164132718Skan if (arc_p) 1165132718Skan arc_p->succ_next = arc_n; 1166132718Skan else 1167132718Skan start = arc_n; 1168132718Skan arc->succ_next = arc_n->succ_next; 1169132718Skan arc_n->succ_next = arc; 1170132718Skan arc_p = arc_n; 1171132718Skan } 1172132718Skan else 1173132718Skan { 1174132718Skan arc_p = arc; 1175132718Skan arc = arc_n; 1176132718Skan } 1177132718Skan } 1178132718Skan } 1179132718Skan blk->succ = start; 1180132718Skan } 118150397Sobrien 1182132718Skan /* Place it on the invalid chain, it will be ignored if that's 1183132718Skan wrong. */ 1184132718Skan blk->invalid_chain = 1; 1185132718Skan blk->chain = invalid_blocks; 1186132718Skan invalid_blocks = blk; 1187132718Skan } 118850397Sobrien 1189132718Skan while (invalid_blocks || valid_blocks) 1190132718Skan { 1191132718Skan while ((blk = invalid_blocks)) 1192132718Skan { 1193132718Skan gcov_type total = 0; 1194132718Skan const arc_t *arc; 119550397Sobrien 1196132718Skan invalid_blocks = blk->chain; 1197132718Skan blk->invalid_chain = 0; 1198132718Skan if (!blk->num_succ) 1199132718Skan for (arc = blk->succ; arc; arc = arc->succ_next) 1200132718Skan total += arc->count; 1201132718Skan else if (!blk->num_pred) 1202132718Skan for (arc = blk->pred; arc; arc = arc->pred_next) 1203132718Skan total += arc->count; 1204132718Skan else 1205132718Skan continue; 120650397Sobrien 1207132718Skan blk->count = total; 1208132718Skan blk->count_valid = 1; 1209132718Skan blk->chain = valid_blocks; 1210132718Skan blk->valid_chain = 1; 1211132718Skan valid_blocks = blk; 1212132718Skan } 1213132718Skan while ((blk = valid_blocks)) 121450397Sobrien { 1215132718Skan gcov_type total; 1216132718Skan arc_t *arc, *inv_arc; 121750397Sobrien 1218132718Skan valid_blocks = blk->chain; 1219132718Skan blk->valid_chain = 0; 1220132718Skan if (blk->num_succ == 1) 122150397Sobrien { 1222132718Skan block_t *dst; 1223132718Skan 1224132718Skan total = blk->count; 1225132718Skan inv_arc = NULL; 1226132718Skan for (arc = blk->succ; arc; arc = arc->succ_next) 1227132718Skan { 1228132718Skan total -= arc->count; 1229132718Skan if (!arc->count_valid) 1230132718Skan inv_arc = arc; 1231132718Skan } 1232132718Skan dst = inv_arc->dst; 1233132718Skan inv_arc->count_valid = 1; 1234132718Skan inv_arc->count = total; 1235132718Skan blk->num_succ--; 1236132718Skan dst->num_pred--; 1237132718Skan if (dst->count_valid) 1238132718Skan { 1239132718Skan if (dst->num_pred == 1 && !dst->valid_chain) 1240132718Skan { 1241132718Skan dst->chain = valid_blocks; 1242132718Skan dst->valid_chain = 1; 1243132718Skan valid_blocks = dst; 1244132718Skan } 1245132718Skan } 1246132718Skan else 1247132718Skan { 1248132718Skan if (!dst->num_pred && !dst->invalid_chain) 1249132718Skan { 1250132718Skan dst->chain = invalid_blocks; 1251132718Skan dst->invalid_chain = 1; 1252132718Skan invalid_blocks = dst; 1253132718Skan } 1254132718Skan } 125550397Sobrien } 1256132718Skan if (blk->num_pred == 1) 1257132718Skan { 1258132718Skan block_t *src; 125950397Sobrien 1260132718Skan total = blk->count; 1261132718Skan inv_arc = NULL; 1262132718Skan for (arc = blk->pred; arc; arc = arc->pred_next) 1263132718Skan { 1264132718Skan total -= arc->count; 1265132718Skan if (!arc->count_valid) 1266132718Skan inv_arc = arc; 1267132718Skan } 1268132718Skan src = inv_arc->src; 1269132718Skan inv_arc->count_valid = 1; 1270132718Skan inv_arc->count = total; 1271132718Skan blk->num_pred--; 1272132718Skan src->num_succ--; 1273132718Skan if (src->count_valid) 1274132718Skan { 1275132718Skan if (src->num_succ == 1 && !src->valid_chain) 1276132718Skan { 1277132718Skan src->chain = valid_blocks; 1278132718Skan src->valid_chain = 1; 1279132718Skan valid_blocks = src; 1280132718Skan } 1281132718Skan } 1282132718Skan else 1283132718Skan { 1284132718Skan if (!src->num_succ && !src->invalid_chain) 1285132718Skan { 1286132718Skan src->chain = invalid_blocks; 1287132718Skan src->invalid_chain = 1; 1288132718Skan invalid_blocks = src; 1289132718Skan } 1290132718Skan } 1291132718Skan } 129250397Sobrien } 1293132718Skan } 129450397Sobrien 1295132718Skan /* If the graph has been correctly solved, every block will have a 1296132718Skan valid count. */ 1297132718Skan for (ix = 0; ix < fn->num_blocks; ix++) 1298132718Skan if (!fn->blocks[ix].count_valid) 1299132718Skan { 1300169689Skan fnotice (stderr, "%s:graph is unsolvable for '%s'\n", 1301132718Skan bbg_file_name, fn->name); 1302132718Skan break; 1303132718Skan } 130450397Sobrien} 1305132718Skan 130650397Sobrien 130750397Sobrien 1308132718Skan/* Increment totals in COVERAGE according to arc ARC. */ 130950397Sobrien 1310117395Skanstatic void 1311132718Skanadd_branch_counts (coverage_t *coverage, const arc_t *arc) 1312117395Skan{ 1313132718Skan if (arc->is_call_non_return) 1314117395Skan { 1315132718Skan coverage->calls++; 1316132718Skan if (arc->src->count) 1317132718Skan coverage->calls_executed++; 1318117395Skan } 1319132718Skan else if (!arc->is_unconditional) 1320117395Skan { 1321132718Skan coverage->branches++; 1322132718Skan if (arc->src->count) 1323132718Skan coverage->branches_executed++; 1324132718Skan if (arc->count) 1325132718Skan coverage->branches_taken++; 1326117395Skan } 1327117395Skan} 1328117395Skan 1329117395Skan/* Format a HOST_WIDE_INT as either a percent ratio, or absolute 1330117395Skan count. If dp >= 0, format TOP/BOTTOM * 100 to DP decimal places. 1331117395Skan If DP is zero, no decimal point is printed. Only print 100% when 1332117395Skan TOP==BOTTOM and only print 0% when TOP=0. If dp < 0, then simply 1333117395Skan format TOP. Return pointer to a static string. */ 1334117395Skan 1335117395Skanstatic char const * 1336132718Skanformat_gcov (gcov_type top, gcov_type bottom, int dp) 1337117395Skan{ 1338117395Skan static char buffer[20]; 1339132718Skan 1340117395Skan if (dp >= 0) 1341117395Skan { 1342117395Skan float ratio = bottom ? (float)top / bottom : 0; 1343117395Skan int ix; 1344117395Skan unsigned limit = 100; 1345117395Skan unsigned percent; 1346132718Skan 1347117395Skan for (ix = dp; ix--; ) 1348117395Skan limit *= 10; 1349132718Skan 1350117395Skan percent = (unsigned) (ratio * limit + (float)0.5); 1351117395Skan if (percent <= 0 && top) 1352117395Skan percent = 1; 1353117395Skan else if (percent >= limit && top != bottom) 1354117395Skan percent = limit - 1; 1355117395Skan ix = sprintf (buffer, "%.*u%%", dp + 1, percent); 1356117395Skan if (dp) 135750397Sobrien { 1358117395Skan dp++; 1359117395Skan do 136050397Sobrien { 1361117395Skan buffer[ix+1] = buffer[ix]; 1362117395Skan ix--; 136350397Sobrien } 1364117395Skan while (dp--); 1365117395Skan buffer[ix + 1] = '.'; 136650397Sobrien } 136750397Sobrien } 1368117395Skan else 1369132718Skan sprintf (buffer, HOST_WIDEST_INT_PRINT_DEC, (HOST_WIDEST_INT)top); 1370132718Skan 1371117395Skan return buffer; 137250397Sobrien} 137350397Sobrien 1374117395Skan 137550397Sobrien/* Output summary info for a function. */ 137650397Sobrien 137750397Sobrienstatic void 1378132718Skanfunction_summary (const coverage_t *coverage, const char *title) 137950397Sobrien{ 1380169689Skan fnotice (stdout, "%s '%s'\n", title, coverage->name); 1381132718Skan 1382132718Skan if (coverage->lines) 1383132718Skan fnotice (stdout, "Lines executed:%s of %d\n", 1384132718Skan format_gcov (coverage->lines_executed, coverage->lines, 2), 1385132718Skan coverage->lines); 138650397Sobrien else 1387169689Skan fnotice (stdout, "No executable lines\n"); 138850397Sobrien 1389132718Skan if (flag_branches) 139050397Sobrien { 1391132718Skan if (coverage->branches) 139250397Sobrien { 1393132718Skan fnotice (stdout, "Branches executed:%s of %d\n", 1394132718Skan format_gcov (coverage->branches_executed, 1395132718Skan coverage->branches, 2), 1396132718Skan coverage->branches); 1397132718Skan fnotice (stdout, "Taken at least once:%s of %d\n", 1398132718Skan format_gcov (coverage->branches_taken, 1399132718Skan coverage->branches, 2), 1400132718Skan coverage->branches); 140150397Sobrien } 140250397Sobrien else 1403132718Skan fnotice (stdout, "No branches\n"); 1404132718Skan if (coverage->calls) 1405132718Skan fnotice (stdout, "Calls executed:%s of %d\n", 1406132718Skan format_gcov (coverage->calls_executed, coverage->calls, 2), 1407132718Skan coverage->calls); 140850397Sobrien else 1409132718Skan fnotice (stdout, "No calls\n"); 141050397Sobrien } 141150397Sobrien} 141250397Sobrien 1413117395Skan/* Generate an output file name. LONG_OUTPUT_NAMES and PRESERVE_PATHS 1414117395Skan affect name generation. With preserve_paths we create a filename 1415117395Skan from all path components of the source file, replacing '/' with 1416117395Skan '#', without it we simply take the basename component. With 1417117395Skan long_output_names we prepend the processed name of the input file 1418117395Skan to each output name (except when the current source file is the 1419117395Skan input file, so you don't get a double concatenation). The two 1420117395Skan components are separated by '##'. Also '.' filename components are 1421117395Skan removed and '..' components are renamed to '^'. */ 142250397Sobrien 1423117395Skanstatic char * 1424132718Skanmake_gcov_file_name (const char *input_name, const char *src_name) 1425117395Skan{ 1426117395Skan char *cptr; 1427169689Skan char *name = XNEWVEC (char, strlen (src_name) + strlen (input_name) + 10); 1428132718Skan 1429117395Skan name[0] = 0; 1430132718Skan if (flag_long_names && strcmp (src_name, input_name)) 1431117395Skan { 1432117395Skan /* Generate the input filename part. */ 1433132718Skan cptr = flag_preserve_paths ? NULL : strrchr (input_name, '/'); 1434132718Skan strcat (name, cptr ? cptr + 1 : input_name); 1435117395Skan strcat (name, "##"); 1436117395Skan } 1437132718Skan 1438117395Skan /* Generate the source filename part. */ 1439132718Skan cptr = flag_preserve_paths ? NULL : strrchr (src_name, '/'); 1440132718Skan strcat (name, cptr ? cptr + 1 : src_name); 1441132718Skan 1442132718Skan if (flag_preserve_paths) 1443117395Skan { 1444117395Skan /* Convert '/' to '#', remove '/./', convert '/../' to '/^/' */ 1445117395Skan char *prev; 1446132718Skan 1447117395Skan for (cptr = name; (cptr = strchr ((prev = cptr), '/'));) 1448132718Skan { 1449132718Skan unsigned shift = 0; 1450132718Skan 1451132718Skan if (prev + 1 == cptr && prev[0] == '.') 1452132718Skan { 1453132718Skan /* Remove '.' */ 1454132718Skan shift = 2; 1455132718Skan } 1456132718Skan else if (prev + 2 == cptr && prev[0] == '.' && prev[1] == '.') 1457132718Skan { 1458132718Skan /* Convert '..' */ 1459132718Skan shift = 1; 1460132718Skan prev[1] = '^'; 1461132718Skan } 1462132718Skan else 1463132718Skan *cptr++ = '#'; 1464132718Skan if (shift) 1465132718Skan { 1466132718Skan cptr = prev; 1467132718Skan do 1468132718Skan prev[0] = prev[shift]; 1469117395Skan while (*prev++); 1470132718Skan } 1471132718Skan } 1472117395Skan } 1473132718Skan 1474117395Skan strcat (name, ".gcov"); 1475117395Skan return name; 1476117395Skan} 1477117395Skan 1478132718Skan/* Scan through the bb_data for each line in the block, increment 1479117395Skan the line number execution count indicated by the execution count of 1480117395Skan the appropriate basic block. */ 1481117395Skan 148250397Sobrienstatic void 1483132718Skanadd_line_counts (coverage_t *coverage, function_t *fn) 148450397Sobrien{ 1485132718Skan unsigned ix; 1486132718Skan line_t *line = NULL; /* This is propagated from one iteration to the 1487132718Skan next. */ 1488132718Skan 1489132718Skan /* Scan each basic block. */ 1490132718Skan for (ix = 0; ix != fn->num_blocks; ix++) 149150397Sobrien { 1492132718Skan block_t *block = &fn->blocks[ix]; 1493132718Skan unsigned *encoding; 1494132718Skan const source_t *src = NULL; 1495132718Skan unsigned jx; 1496132718Skan 1497132718Skan if (block->count && ix && ix + 1 != fn->num_blocks) 1498132718Skan fn->blocks_executed++; 1499132718Skan for (jx = 0, encoding = block->u.line.encoding; 1500132718Skan jx != block->u.line.num; jx++, encoding++) 1501132718Skan if (!*encoding) 1502132718Skan { 1503132718Skan unsigned src_n = *++encoding; 1504132718Skan 1505132718Skan for (src = sources; src->index != src_n; src = src->next) 1506132718Skan continue; 1507132718Skan jx++; 1508132718Skan } 1509132718Skan else 1510132718Skan { 1511132718Skan line = &src->lines[*encoding]; 1512132718Skan 1513132718Skan if (coverage) 1514132718Skan { 1515132718Skan if (!line->exists) 1516132718Skan coverage->lines++; 1517132718Skan if (!line->count && block->count) 1518132718Skan coverage->lines_executed++; 1519132718Skan } 1520132718Skan line->exists = 1; 1521132718Skan line->count += block->count; 1522132718Skan } 1523132718Skan free (block->u.line.encoding); 1524132718Skan block->u.cycle.arc = NULL; 1525132718Skan block->u.cycle.ident = ~0U; 1526132718Skan 1527132718Skan if (!ix || ix + 1 == fn->num_blocks) 1528132718Skan /* Entry or exit block */; 1529132718Skan else if (flag_all_blocks) 153050397Sobrien { 1531132718Skan line_t *block_line = line ? line : &fn->src->lines[fn->line]; 1532117395Skan 1533132718Skan block->chain = block_line->u.blocks; 1534132718Skan block_line->u.blocks = block; 1535132718Skan } 1536132718Skan else if (flag_branches) 1537132718Skan { 1538132718Skan arc_t *arc; 1539132718Skan 1540132718Skan for (arc = block->succ; arc; arc = arc->succ_next) 1541117395Skan { 1542132718Skan arc->line_next = line->u.branches; 1543132718Skan line->u.branches = arc; 1544132718Skan if (coverage && !arc->is_unconditional) 1545132718Skan add_branch_counts (coverage, arc); 1546117395Skan } 154750397Sobrien } 1548132718Skan } 1549132718Skan if (!line) 1550169689Skan fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name); 1551132718Skan} 1552132718Skan 1553132718Skan/* Accumulate the line counts of a file. */ 1554132718Skan 1555132718Skanstatic void 1556132718Skanaccumulate_line_counts (source_t *src) 1557132718Skan{ 1558132718Skan line_t *line; 1559132718Skan function_t *fn, *fn_p, *fn_n; 1560132718Skan unsigned ix; 1561132718Skan 1562132718Skan /* Reverse the function order. */ 1563132718Skan for (fn = src->functions, fn_p = NULL; fn; 1564132718Skan fn_p = fn, fn = fn_n) 1565132718Skan { 1566132718Skan fn_n = fn->line_next; 1567132718Skan fn->line_next = fn_p; 1568132718Skan } 1569132718Skan src->functions = fn_p; 1570132718Skan 1571132718Skan for (ix = src->num_lines, line = src->lines; ix--; line++) 1572132718Skan { 1573132718Skan if (!flag_all_blocks) 157450397Sobrien { 1575132718Skan arc_t *arc, *arc_p, *arc_n; 1576132718Skan 1577132718Skan /* Total and reverse the branch information. */ 1578132718Skan for (arc = line->u.branches, arc_p = NULL; arc; 1579132718Skan arc_p = arc, arc = arc_n) 158050397Sobrien { 1581132718Skan arc_n = arc->line_next; 1582132718Skan arc->line_next = arc_p; 1583132718Skan 1584132718Skan add_branch_counts (&src->coverage, arc); 158550397Sobrien } 1586132718Skan line->u.branches = arc_p; 158750397Sobrien } 1588132718Skan else if (line->u.blocks) 158950397Sobrien { 1590132718Skan /* The user expects the line count to be the number of times 1591132718Skan a line has been executed. Simply summing the block count 1592132718Skan will give an artificially high number. The Right Thing 1593132718Skan is to sum the entry counts to the graph of blocks on this 1594132718Skan line, then find the elementary cycles of the local graph 1595132718Skan and add the transition counts of those cycles. */ 1596132718Skan block_t *block, *block_p, *block_n; 1597132718Skan gcov_type count = 0; 1598132718Skan 1599132718Skan /* Reverse the block information. */ 1600132718Skan for (block = line->u.blocks, block_p = NULL; block; 1601132718Skan block_p = block, block = block_n) 160250397Sobrien { 1603132718Skan block_n = block->chain; 1604132718Skan block->chain = block_p; 1605132718Skan block->u.cycle.ident = ix; 160650397Sobrien } 1607132718Skan line->u.blocks = block_p; 160850397Sobrien 1609132718Skan /* Sum the entry arcs. */ 1610132718Skan for (block = line->u.blocks; block; block = block->chain) 161150397Sobrien { 1612132718Skan arc_t *arc; 1613132718Skan 1614132718Skan for (arc = block->pred; arc; arc = arc->pred_next) 1615132718Skan { 1616132718Skan if (arc->src->u.cycle.ident != ix) 1617132718Skan count += arc->count; 1618132718Skan if (flag_branches) 1619132718Skan add_branch_counts (&src->coverage, arc); 1620132718Skan } 1621132718Skan 1622132718Skan /* Initialize the cs_count. */ 1623132718Skan for (arc = block->succ; arc; arc = arc->succ_next) 1624132718Skan arc->cs_count = arc->count; 1625117395Skan } 1626132718Skan 1627132718Skan /* Find the loops. This uses the algorithm described in 1628132718Skan Tiernan 'An Efficient Search Algorithm to Find the 1629132718Skan Elementary Circuits of a Graph', CACM Dec 1970. We hold 1630132718Skan the P array by having each block point to the arc that 1631132718Skan connects to the previous block. The H array is implicitly 1632132718Skan held because of the arc ordering, and the block's 1633132718Skan previous arc pointer. 1634132718Skan 1635132718Skan Although the algorithm is O(N^3) for highly connected 1636132718Skan graphs, at worst we'll have O(N^2), as most blocks have 1637132718Skan only one or two exits. Most graphs will be small. 1638132718Skan 1639132718Skan For each loop we find, locate the arc with the smallest 1640132718Skan transition count, and add that to the cumulative 1641132718Skan count. Decrease flow over the cycle and remove the arc 1642132718Skan from consideration. */ 1643132718Skan for (block = line->u.blocks; block; block = block->chain) 1644132718Skan { 1645132718Skan block_t *head = block; 1646132718Skan arc_t *arc; 1647132718Skan 1648132718Skan next_vertex:; 1649132718Skan arc = head->succ; 1650132718Skan current_vertex:; 1651132718Skan while (arc) 1652132718Skan { 1653132718Skan block_t *dst = arc->dst; 1654132718Skan if (/* Already used that arc. */ 1655132718Skan arc->cycle 1656132718Skan /* Not to same graph, or before first vertex. */ 1657132718Skan || dst->u.cycle.ident != ix 1658132718Skan /* Already in path. */ 1659132718Skan || dst->u.cycle.arc) 1660132718Skan { 1661132718Skan arc = arc->succ_next; 1662132718Skan continue; 1663132718Skan } 1664132718Skan 1665132718Skan if (dst == block) 1666132718Skan { 1667132718Skan /* Found a closing arc. */ 1668132718Skan gcov_type cycle_count = arc->cs_count; 1669132718Skan arc_t *cycle_arc = arc; 1670132718Skan arc_t *probe_arc; 1671132718Skan 1672132718Skan /* Locate the smallest arc count of the loop. */ 1673132718Skan for (dst = head; (probe_arc = dst->u.cycle.arc); 1674132718Skan dst = probe_arc->src) 1675132718Skan if (cycle_count > probe_arc->cs_count) 1676132718Skan { 1677132718Skan cycle_count = probe_arc->cs_count; 1678132718Skan cycle_arc = probe_arc; 1679132718Skan } 1680132718Skan 1681132718Skan count += cycle_count; 1682132718Skan cycle_arc->cycle = 1; 1683132718Skan 1684132718Skan /* Remove the flow from the cycle. */ 1685132718Skan arc->cs_count -= cycle_count; 1686132718Skan for (dst = head; (probe_arc = dst->u.cycle.arc); 1687132718Skan dst = probe_arc->src) 1688132718Skan probe_arc->cs_count -= cycle_count; 1689132718Skan 1690132718Skan /* Unwind to the cyclic arc. */ 1691132718Skan while (head != cycle_arc->src) 1692132718Skan { 1693132718Skan arc = head->u.cycle.arc; 1694132718Skan head->u.cycle.arc = NULL; 1695132718Skan head = arc->src; 1696132718Skan } 1697132718Skan /* Move on. */ 1698132718Skan arc = arc->succ_next; 1699132718Skan continue; 1700132718Skan } 1701132718Skan 1702132718Skan /* Add new block to chain. */ 1703132718Skan dst->u.cycle.arc = arc; 1704132718Skan head = dst; 1705132718Skan goto next_vertex; 1706132718Skan } 1707132718Skan /* We could not add another vertex to the path. Remove 1708132718Skan the last vertex from the list. */ 1709132718Skan arc = head->u.cycle.arc; 1710132718Skan if (arc) 1711132718Skan { 1712132718Skan /* It was not the first vertex. Move onto next arc. */ 1713132718Skan head->u.cycle.arc = NULL; 1714132718Skan head = arc->src; 1715132718Skan arc = arc->succ_next; 1716132718Skan goto current_vertex; 1717132718Skan } 1718132718Skan /* Mark this block as unusable. */ 1719132718Skan block->u.cycle.ident = ~0U; 1720132718Skan } 1721132718Skan 1722132718Skan line->count = count; 1723117395Skan } 1724132718Skan 1725132718Skan if (line->exists) 1726117395Skan { 1727132718Skan src->coverage.lines++; 1728132718Skan if (line->count) 1729132718Skan src->coverage.lines_executed++; 1730117395Skan } 1731132718Skan } 1732132718Skan} 173390075Sobrien 1734169689Skan/* Output information about ARC number IX. Returns nonzero if 1735132718Skan anything is output. */ 1736132718Skan 1737132718Skanstatic int 1738132718Skanoutput_branch_count (FILE *gcov_file, int ix, const arc_t *arc) 1739132718Skan{ 1740132718Skan 1741132718Skan if (arc->is_call_non_return) 1742132718Skan { 1743132718Skan if (arc->src->count) 1744117395Skan { 1745132718Skan fnotice (gcov_file, "call %2d returned %s\n", ix, 1746132718Skan format_gcov (arc->src->count - arc->count, 1747132718Skan arc->src->count, -flag_counts)); 1748117395Skan } 1749132718Skan else 1750132718Skan fnotice (gcov_file, "call %2d never executed\n", ix); 1751117395Skan } 1752132718Skan else if (!arc->is_unconditional) 1753132718Skan { 1754132718Skan if (arc->src->count) 1755132718Skan fnotice (gcov_file, "branch %2d taken %s%s\n", ix, 1756132718Skan format_gcov (arc->count, arc->src->count, -flag_counts), 1757132718Skan arc->fall_through ? " (fallthrough)" : ""); 1758132718Skan else 1759132718Skan fnotice (gcov_file, "branch %2d never executed\n", ix); 1760132718Skan } 1761132718Skan else if (flag_unconditional && !arc->dst->is_call_return) 1762132718Skan { 1763132718Skan if (arc->src->count) 1764132718Skan fnotice (gcov_file, "unconditional %2d taken %s\n", ix, 1765132718Skan format_gcov (arc->count, arc->src->count, -flag_counts)); 1766132718Skan else 1767132718Skan fnotice (gcov_file, "unconditional %2d never executed\n", ix); 1768132718Skan } 1769132718Skan else 1770132718Skan return 0; 1771132718Skan return 1; 1772132718Skan 1773117395Skan} 177450397Sobrien 1775117395Skan/* Read in the source file one line at a time, and output that line to 1776117395Skan the gcov file preceded by its execution count and other 1777117395Skan information. */ 177850397Sobrien 1779117395Skanstatic void 1780132718Skanoutput_lines (FILE *gcov_file, const source_t *src) 1781117395Skan{ 1782117395Skan FILE *source_file; 1783132718Skan unsigned line_num; /* current line number. */ 1784132718Skan const line_t *line; /* current line info ptr. */ 1785132718Skan char string[STRING_SIZE]; /* line buffer. */ 1786132718Skan char const *retval = ""; /* status of source file reading. */ 1787169689Skan function_t *fn = NULL; 178850397Sobrien 1789132718Skan fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, src->name); 1790132718Skan fprintf (gcov_file, "%9s:%5d:Graph:%s\n", "-", 0, bbg_file_name); 1791169689Skan fprintf (gcov_file, "%9s:%5d:Data:%s\n", "-", 0, 1792169689Skan no_data_file ? "-" : da_file_name); 1793132718Skan fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0, 1794132718Skan object_summary.ctrs[GCOV_COUNTER_ARCS].runs); 1795132718Skan fprintf (gcov_file, "%9s:%5d:Programs:%u\n", "-", 0, program_count); 1796132718Skan 1797132718Skan source_file = fopen (src->name, "r"); 1798117395Skan if (!source_file) 1799117395Skan { 1800132718Skan fnotice (stderr, "%s:cannot open source file\n", src->name); 1801117395Skan retval = NULL; 1802117395Skan } 1803117395Skan else 1804117395Skan { 1805117395Skan struct stat status; 1806132718Skan 1807117395Skan if (!fstat (fileno (source_file), &status) 1808132718Skan && status.st_mtime > bbg_file_time) 1809117395Skan { 1810169689Skan fnotice (stderr, "%s:source file is newer than graph file '%s'\n", 1811132718Skan src->name, bbg_file_name); 1812132718Skan fprintf (gcov_file, "%9s:%5d:Source is newer than graph\n", 1813117395Skan "-", 0); 1814117395Skan } 1815117395Skan } 181650397Sobrien 1817169689Skan if (flag_branches) 1818169689Skan fn = src->functions; 1819169689Skan 1820132718Skan for (line_num = 1, line = &src->lines[line_num]; 1821132718Skan line_num < src->num_lines; line_num++, line++) 1822117395Skan { 1823132718Skan for (; fn && fn->line == line_num; fn = fn->line_next) 1824132718Skan { 1825132718Skan arc_t *arc = fn->blocks[fn->num_blocks - 1].pred; 1826132718Skan gcov_type return_count = fn->blocks[fn->num_blocks - 1].count; 1827169689Skan 1828132718Skan for (; arc; arc = arc->pred_next) 1829132718Skan if (arc->fake) 1830132718Skan return_count -= arc->count; 1831169689Skan 1832132718Skan fprintf (gcov_file, "function %s", fn->name); 1833132718Skan fprintf (gcov_file, " called %s", 1834132718Skan format_gcov (fn->blocks[0].count, 0, -1)); 1835132718Skan fprintf (gcov_file, " returned %s", 1836132718Skan format_gcov (return_count, fn->blocks[0].count, 0)); 1837132718Skan fprintf (gcov_file, " blocks executed %s", 1838132718Skan format_gcov (fn->blocks_executed, fn->num_blocks - 2, 0)); 1839132718Skan fprintf (gcov_file, "\n"); 1840132718Skan } 1841132718Skan 1842117395Skan /* For lines which don't exist in the .bb file, print '-' before 1843132718Skan the source line. For lines which exist but were never 1844132718Skan executed, print '#####' before the source line. Otherwise, 1845132718Skan print the execution count before the source line. There are 1846132718Skan 16 spaces of indentation added before the source line so that 1847132718Skan tabs won't be messed up. */ 1848132718Skan fprintf (gcov_file, "%9s:%5u:", 1849132718Skan !line->exists ? "-" : !line->count ? "#####" 1850132718Skan : format_gcov (line->count, 0, -1), line_num); 1851132718Skan 1852117395Skan if (retval) 1853117395Skan { 1854117395Skan /* Copy source line. */ 1855117395Skan do 185650397Sobrien { 1857117395Skan retval = fgets (string, STRING_SIZE, source_file); 1858117395Skan if (!retval) 1859132718Skan break; 1860117395Skan fputs (retval, gcov_file); 186150397Sobrien } 1862117395Skan while (!retval[0] || retval[strlen (retval) - 1] != '\n'); 1863117395Skan } 1864117395Skan if (!retval) 1865132718Skan fputs ("/*EOF*/\n", gcov_file); 1866132718Skan 1867132718Skan if (flag_all_blocks) 1868117395Skan { 1869132718Skan block_t *block; 1870132718Skan arc_t *arc; 1871132718Skan int ix, jx; 1872132718Skan 1873132718Skan for (ix = jx = 0, block = line->u.blocks; block; 1874132718Skan block = block->chain) 187550397Sobrien { 1876132718Skan if (!block->is_call_return) 1877132718Skan fprintf (gcov_file, "%9s:%5u-block %2d\n", 1878132718Skan !line->exists ? "-" : !block->count ? "$$$$$" 1879132718Skan : format_gcov (block->count, 0, -1), 1880132718Skan line_num, ix++); 1881132718Skan if (flag_branches) 1882132718Skan for (arc = block->succ; arc; arc = arc->succ_next) 1883132718Skan jx += output_branch_count (gcov_file, jx, arc); 188450397Sobrien } 1885117395Skan } 1886132718Skan else if (flag_branches) 1887132718Skan { 1888132718Skan int ix; 1889132718Skan arc_t *arc; 1890132718Skan 1891132718Skan for (ix = 0, arc = line->u.branches; arc; arc = arc->line_next) 1892132718Skan ix += output_branch_count (gcov_file, ix, arc); 1893132718Skan } 1894117395Skan } 1895132718Skan 1896117395Skan /* Handle all remaining source lines. There may be lines after the 1897117395Skan last line of code. */ 1898117395Skan if (retval) 1899117395Skan { 1900117395Skan for (; (retval = fgets (string, STRING_SIZE, source_file)); line_num++) 1901117395Skan { 1902132718Skan fprintf (gcov_file, "%9s:%5u:%s", "-", line_num, retval); 1903132718Skan 1904117395Skan while (!retval[0] || retval[strlen (retval) - 1] != '\n') 1905117395Skan { 1906117395Skan retval = fgets (string, STRING_SIZE, source_file); 1907117395Skan if (!retval) 1908117395Skan break; 1909117395Skan fputs (retval, gcov_file); 1910117395Skan } 1911117395Skan } 1912117395Skan } 1913132718Skan 1914117395Skan if (source_file) 1915117395Skan fclose (source_file); 1916117395Skan} 1917