gcov.c revision 161651
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 2152284Sobrienthe Free Software Foundation, 59 Temple Place - Suite 330, 2252284SobrienBoston, MA 02111-1307, 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" 5052284Sobrien#undef abort 5150397Sobrien 5290075Sobrien#include <getopt.h> 5390075Sobrien 54132718Skan#define IN_GCOV 1 5550397Sobrien#include "gcov-io.h" 56132718Skan#include "gcov-io.c" 5750397Sobrien 58132718Skan/* The bbg file is generated by -ftest-coverage option. The da file is 59132718Skan generated by a program compiled with -fprofile-arcs. Their formats 60132718Skan are documented in gcov-io.h. */ 6150397Sobrien 6250397Sobrien/* The functions in this file for creating and solution program flow graphs 63132718Skan are very similar to functions in the gcc source file profile.c. In 64132718Skan some places we make use of the knowledge of how profile.c works to 65132718Skan select particular algorithms here. */ 6650397Sobrien 6750397Sobrien/* This is the size of the buffer used to read in source file lines. */ 6850397Sobrien 6950397Sobrien#define STRING_SIZE 200 7050397Sobrien 71132718Skanstruct function_info; 72132718Skanstruct block_info; 73132718Skanstruct source_info; 7450397Sobrien 75132718Skan/* Describes an arc between two basic blocks. */ 76132718Skan 77132718Skantypedef struct arc_info 7850397Sobrien{ 79132718Skan /* source and destination blocks. */ 80132718Skan struct block_info *src; 81132718Skan struct block_info *dst; 8250397Sobrien 83132718Skan /* transition counts. */ 84132718Skan gcov_type count; 85132718Skan /* used in cycle search, so that we do not clobber original counts. */ 86132718Skan gcov_type cs_count; 8750397Sobrien 8850397Sobrien unsigned int count_valid : 1; 8950397Sobrien unsigned int on_tree : 1; 9050397Sobrien unsigned int fake : 1; 9150397Sobrien unsigned int fall_through : 1; 9250397Sobrien 93132718Skan /* Arc is for a function that abnormally returns. */ 94132718Skan unsigned int is_call_non_return : 1; 9550397Sobrien 96132718Skan /* Arc is for catch/setjump. */ 97132718Skan unsigned int is_nonlocal_return : 1; 9850397Sobrien 99132718Skan /* Is an unconditional branch. */ 100132718Skan unsigned int is_unconditional : 1; 10150397Sobrien 102132718Skan /* Loop making arc. */ 103132718Skan unsigned int cycle : 1; 104132718Skan 105132718Skan /* Next branch on line. */ 106132718Skan struct arc_info *line_next; 107132718Skan 108132718Skan /* Links to next arc on src and dst lists. */ 109132718Skan struct arc_info *succ_next; 110132718Skan struct arc_info *pred_next; 111132718Skan} arc_t; 112132718Skan 113132718Skan/* Describes a basic block. Contains lists of arcs to successor and 114132718Skan predecessor blocks. */ 115132718Skan 116132718Skantypedef struct block_info 11750397Sobrien{ 118132718Skan /* Chain of exit and entry arcs. */ 119132718Skan arc_t *succ; 120132718Skan arc_t *pred; 12150397Sobrien 122132718Skan /* Number of unprocessed exit and entry arcs. */ 123132718Skan gcov_type num_succ; 124132718Skan gcov_type num_pred; 12550397Sobrien 126132718Skan /* Block execution count. */ 127132718Skan gcov_type count; 128132718Skan unsigned flags : 13; 129132718Skan unsigned count_valid : 1; 130132718Skan unsigned valid_chain : 1; 131132718Skan unsigned invalid_chain : 1; 132132718Skan 133132718Skan /* Block is a call instrumenting site. */ 134132718Skan unsigned is_call_site : 1; /* Does the call. */ 135132718Skan unsigned is_call_return : 1; /* Is the return. */ 136132718Skan 137132718Skan /* Block is a landing pad for longjmp or throw. */ 138132718Skan unsigned is_nonlocal_return : 1; 139132718Skan 140132718Skan union 141132718Skan { 142132718Skan struct 143132718Skan { 144132718Skan /* Array of line numbers and source files. source files are 145132718Skan introduced by a linenumber of zero, the next 'line number' is 146132718Skan the number of the source file. Always starts with a source 147132718Skan file. */ 148132718Skan unsigned *encoding; 149132718Skan unsigned num; 150132718Skan } line; /* Valid until blocks are linked onto lines */ 151132718Skan struct 152132718Skan { 153132718Skan /* Single line graph cycle workspace. Used for all-blocks 154132718Skan mode. */ 155132718Skan arc_t *arc; 156132718Skan unsigned ident; 157132718Skan } cycle; /* Used in all-blocks mode, after blocks are linked onto 158132718Skan lines. */ 159132718Skan } u; 160132718Skan 161132718Skan /* Temporary chain for solving graph, and for chaining blocks on one 162132718Skan line. */ 163132718Skan struct block_info *chain; 164132718Skan 165132718Skan} block_t; 166132718Skan 167132718Skan/* Describes a single function. Contains an array of basic blocks. */ 168132718Skan 169132718Skantypedef struct function_info 170117395Skan{ 171132718Skan /* Name of function. */ 172132718Skan char *name; 173132718Skan unsigned ident; 174132718Skan unsigned checksum; 17550397Sobrien 176132718Skan /* Array of basic blocks. */ 177132718Skan block_t *blocks; 178132718Skan unsigned num_blocks; 179132718Skan unsigned blocks_executed; 180132718Skan 181132718Skan /* Raw arc coverage counts. */ 182132718Skan gcov_type *counts; 183132718Skan unsigned num_counts; 184132718Skan 185132718Skan /* First line number. */ 186132718Skan unsigned line; 187132718Skan struct source_info *src; 188132718Skan 189132718Skan /* Next function in same source file. */ 190132718Skan struct function_info *line_next; 191132718Skan 192132718Skan /* Next function. */ 193132718Skan struct function_info *next; 194132718Skan} function_t; 195132718Skan 196132718Skan/* Describes coverage of a file or function. */ 197132718Skan 198132718Skantypedef struct coverage_info 199117395Skan{ 200117395Skan int lines; 201117395Skan int lines_executed; 202132718Skan 203117395Skan int branches; 204117395Skan int branches_executed; 205117395Skan int branches_taken; 206132718Skan 207117395Skan int calls; 208117395Skan int calls_executed; 209132718Skan 210117395Skan char *name; 211132718Skan} coverage_t; 212117395Skan 213132718Skan/* Describes a single line of source. Contains a chain of basic blocks 214132718Skan with code on it. */ 21550397Sobrien 216132718Skantypedef struct line_info 217132718Skan{ 218132718Skan gcov_type count; /* execution count */ 219132718Skan union 220132718Skan { 221132718Skan arc_t *branches; /* branches from blocks that end on this 222132718Skan line. Used for branch-counts when not 223132718Skan all-blocks mode. */ 224132718Skan block_t *blocks; /* blocks which start on this line. Used 225132718Skan in all-blocks mode. */ 226132718Skan } u; 227132718Skan unsigned exists : 1; 228132718Skan} line_t; 22950397Sobrien 230132718Skan/* Describes a file mentioned in the block graph. Contains an array 231132718Skan of line info. */ 232117395Skan 233132718Skantypedef struct source_info 234132718Skan{ 235132718Skan /* Name of source file. */ 236132718Skan char *name; 237132718Skan unsigned index; 238117395Skan 239132718Skan /* Array of line information. */ 240132718Skan line_t *lines; 241132718Skan unsigned num_lines; 24250397Sobrien 243132718Skan coverage_t coverage; 24450397Sobrien 245132718Skan /* Functions in this source file. These are in ascending line 246132718Skan number order. */ 247132718Skan function_t *functions; 24850397Sobrien 249132718Skan /* Next source file. */ 250132718Skan struct source_info *next; 251132718Skan} source_t; 25250397Sobrien 253132718Skan/* Holds a list of function basic block graphs. */ 25450397Sobrien 255132718Skanstatic function_t *functions; 25650397Sobrien 257132718Skan/* This points to the head of the sourcefile structure list. */ 25850397Sobrien 259132718Skanstatic source_t *sources; 26050397Sobrien 261132718Skan/* This holds data summary information. */ 26250397Sobrien 263132718Skanstatic struct gcov_summary object_summary; 264132718Skanstatic unsigned program_count; 26550397Sobrien 266132718Skan/* Modification time of graph file. */ 26750397Sobrien 268132718Skanstatic time_t bbg_file_time; 26950397Sobrien 270132718Skan/* Name and file pointer of the input file for the basic block graph. */ 27150397Sobrien 272132718Skanstatic char *bbg_file_name; 27350397Sobrien 274132718Skan/* Stamp of the bbg file */ 275132718Skanstatic unsigned bbg_stamp; 276132718Skan 277132718Skan/* Name and file pointer of the input file for the arc count data. */ 278132718Skan 279132718Skanstatic char *da_file_name; 280132718Skan 281132718Skan/* Output branch probabilities. */ 282132718Skan 283132718Skanstatic int flag_branches = 0; 284132718Skan 285132718Skan/* Show unconditional branches too. */ 286132718Skanstatic int flag_unconditional = 0; 287132718Skan 28850397Sobrien/* Output a gcov file if this is true. This is on by default, and can 28950397Sobrien be turned off by the -n option. */ 29050397Sobrien 291132718Skanstatic int flag_gcov_file = 1; 29250397Sobrien 293132718Skan/* For included files, make the gcov output file name include the name 294132718Skan of the input source file. For example, if x.h is included in a.c, 295132718Skan then the output file name is a.c##x.h.gcov instead of x.h.gcov. */ 29650397Sobrien 297132718Skanstatic int flag_long_names = 0; 29850397Sobrien 299132718Skan/* Output count information for every basic block, not merely those 300132718Skan that contain line number information. */ 301132718Skan 302132718Skanstatic int flag_all_blocks = 0; 303132718Skan 30450397Sobrien/* Output summary info for each function. */ 30550397Sobrien 306132718Skanstatic int flag_function_summary = 0; 30750397Sobrien 308132718Skan/* Object directory file prefix. This is the directory/file where the 309132718Skan graph and data files are looked for, if nonzero. */ 31050397Sobrien 31150397Sobrienstatic char *object_directory = 0; 31250397Sobrien 313117395Skan/* Preserve all pathname components. Needed when object files and 314132718Skan source files are in subdirectories. '/' is mangled as '#', '.' is 315132718Skan elided and '..' mangled to '^'. */ 316117395Skan 317132718Skanstatic int flag_preserve_paths = 0; 318132718Skan 31990075Sobrien/* Output the number of times a branch was taken as opposed to the percentage 320132718Skan of times it was taken. */ 32190075Sobrien 322132718Skanstatic int flag_counts = 0; 32390075Sobrien 32450397Sobrien/* Forward declarations. */ 325132718Skanstatic void fnotice (FILE *, const char *, ...) ATTRIBUTE_PRINTF_2; 326132718Skanstatic int process_args (int, char **); 327132718Skanstatic void print_usage (int) ATTRIBUTE_NORETURN; 328132718Skanstatic void print_version (void) ATTRIBUTE_NORETURN; 329132718Skanstatic void process_file (const char *); 330132718Skanstatic void create_file_names (const char *); 331132718Skanstatic source_t *find_source (const char *); 332132718Skanstatic int read_graph_file (void); 333132718Skanstatic int read_count_file (void); 334132718Skanstatic void solve_flow_graph (function_t *); 335132718Skanstatic void add_branch_counts (coverage_t *, const arc_t *); 336132718Skanstatic void add_line_counts (coverage_t *, function_t *); 337132718Skanstatic void function_summary (const coverage_t *, const char *); 338132718Skanstatic const char *format_gcov (gcov_type, gcov_type, int); 339132718Skanstatic void accumulate_line_counts (source_t *); 340132718Skanstatic int output_branch_count (FILE *, int, const arc_t *); 341132718Skanstatic void output_lines (FILE *, const source_t *); 342132718Skanstatic char *make_gcov_file_name (const char *, const char *); 343132718Skanstatic void release_structures (void); 344132718Skanextern int main (int, char **); 34550397Sobrien 34650397Sobrienint 347132718Skanmain (int argc, char **argv) 34850397Sobrien{ 349132718Skan int argno; 350132718Skan 35190075Sobrien gcc_init_libintl (); 35252284Sobrien 353132718Skan argno = process_args (argc, argv); 354132718Skan if (optind == argc) 355132718Skan print_usage (true); 35650397Sobrien 357132718Skan for (; argno != argc; argno++) 358132718Skan { 359132718Skan release_structures (); 36050397Sobrien 361132718Skan process_file (argv[argno]); 362132718Skan } 36350397Sobrien 36450397Sobrien return 0; 36550397Sobrien} 36650397Sobrien 36752284Sobrienstatic void 368132718Skanfnotice (FILE *file, const char *msgid, ...) 36952284Sobrien{ 370132718Skan va_list ap; 37152284Sobrien 372132718Skan va_start (ap, msgid); 37352284Sobrien vfprintf (file, _(msgid), ap); 374132718Skan va_end (ap); 37552284Sobrien} 37652284Sobrien 37750397Sobrien/* More 'friendly' abort that prints the line and file. 37850397Sobrien config.h can #define abort fancy_abort if you like that sort of thing. */ 379132718Skanextern void fancy_abort (void) ATTRIBUTE_NORETURN; 38050397Sobrien 38150397Sobrienvoid 382132718Skanfancy_abort (void) 38350397Sobrien{ 38490075Sobrien fnotice (stderr, "Internal gcov abort.\n"); 38550397Sobrien exit (FATAL_EXIT_CODE); 38650397Sobrien} 38750397Sobrien 38890075Sobrien/* Print a usage message and exit. If ERROR_P is nonzero, this is an error, 38990075Sobrien otherwise the output of --help. */ 39050397Sobrien 39150397Sobrienstatic void 392132718Skanprint_usage (int error_p) 39350397Sobrien{ 39490075Sobrien FILE *file = error_p ? stderr : stdout; 39590075Sobrien int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE; 396132718Skan 39790075Sobrien fnotice (file, "Usage: gcov [OPTION]... SOURCEFILE\n\n"); 39890075Sobrien fnotice (file, "Print code coverage information.\n\n"); 39990075Sobrien fnotice (file, " -h, --help Print this help, then exit\n"); 40090075Sobrien fnotice (file, " -v, --version Print version number, then exit\n"); 401132718Skan fnotice (file, " -a, --all-blocks Show information for every basic block\n"); 40290075Sobrien fnotice (file, " -b, --branch-probabilities Include branch probabilities in output\n"); 40390075Sobrien fnotice (file, " -c, --branch-counts Given counts of branches taken\n\ 40490075Sobrien rather than percentages\n"); 40590075Sobrien fnotice (file, " -n, --no-output Do not create an output file\n"); 40690075Sobrien fnotice (file, " -l, --long-file-names Use long output file names for included\n\ 40790075Sobrien source files\n"); 40890075Sobrien fnotice (file, " -f, --function-summaries Output summaries for each function\n"); 409117395Skan fnotice (file, " -o, --object-directory DIR|FILE Search for object files in DIR or called FILE\n"); 410117395Skan fnotice (file, " -p, --preserve-paths Preserve all pathname components\n"); 411132718Skan fnotice (file, " -u, --unconditional-branches Show unconditional branch counts too\n"); 41290075Sobrien fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n", 413117395Skan bug_report_url); 41490075Sobrien exit (status); 41550397Sobrien} 41650397Sobrien 41790075Sobrien/* Print version information and exit. */ 41890075Sobrien 41990075Sobrienstatic void 420132718Skanprint_version (void) 42190075Sobrien{ 42290075Sobrien fnotice (stdout, "gcov (GCC) %s\n", version_string); 423161651Skan fprintf (stdout, "Copyright %s 2006 Free Software Foundation, Inc.\n", 424132718Skan _("(C)")); 42590075Sobrien fnotice (stdout, 426132718Skan _("This is free software; see the source for copying conditions.\n" 427132718Skan "There is NO warranty; not even for MERCHANTABILITY or \n" 428132718Skan "FITNESS FOR A PARTICULAR PURPOSE.\n\n")); 42990075Sobrien exit (SUCCESS_EXIT_CODE); 43090075Sobrien} 43190075Sobrien 43290075Sobrienstatic const struct option options[] = 43390075Sobrien{ 43490075Sobrien { "help", no_argument, NULL, 'h' }, 43590075Sobrien { "version", no_argument, NULL, 'v' }, 436132718Skan { "all-blocks", no_argument, NULL, 'a' }, 43790075Sobrien { "branch-probabilities", no_argument, NULL, 'b' }, 43890075Sobrien { "branch-counts", no_argument, NULL, 'c' }, 43990075Sobrien { "no-output", no_argument, NULL, 'n' }, 44090075Sobrien { "long-file-names", no_argument, NULL, 'l' }, 44190075Sobrien { "function-summaries", no_argument, NULL, 'f' }, 442117395Skan { "preserve-paths", no_argument, NULL, 'p' }, 443117395Skan { "object-directory", required_argument, NULL, 'o' }, 444117395Skan { "object-file", required_argument, NULL, 'o' }, 445132718Skan { "unconditional-branches", no_argument, NULL, 'u' }, 446132718Skan { 0, 0, 0, 0 } 44790075Sobrien}; 44890075Sobrien 449132718Skan/* Process args, return index to first non-arg. */ 45050397Sobrien 451132718Skanstatic int 452132718Skanprocess_args (int argc, char **argv) 45350397Sobrien{ 45490075Sobrien int opt; 45550397Sobrien 456132718Skan while ((opt = getopt_long (argc, argv, "abcfhlno:puv", options, NULL)) != -1) 45750397Sobrien { 45890075Sobrien switch (opt) 45950397Sobrien { 460132718Skan case 'a': 461132718Skan flag_all_blocks = 1; 462132718Skan break; 46390075Sobrien case 'b': 464132718Skan flag_branches = 1; 46590075Sobrien break; 46690075Sobrien case 'c': 467132718Skan flag_counts = 1; 46890075Sobrien break; 469132718Skan case 'f': 470132718Skan flag_function_summary = 1; 47190075Sobrien break; 472132718Skan case 'h': 473132718Skan print_usage (false); 474132718Skan /* print_usage will exit. */ 47590075Sobrien case 'l': 476132718Skan flag_long_names = 1; 47790075Sobrien break; 478132718Skan case 'n': 479132718Skan flag_gcov_file = 0; 48090075Sobrien break; 48190075Sobrien case 'o': 48290075Sobrien object_directory = optarg; 48390075Sobrien break; 484117395Skan case 'p': 485132718Skan flag_preserve_paths = 1; 486117395Skan break; 487132718Skan case 'u': 488132718Skan flag_unconditional = 1; 489132718Skan break; 490132718Skan case 'v': 491132718Skan print_version (); 492132718Skan /* print_version will exit. */ 49390075Sobrien default: 49490075Sobrien print_usage (true); 49590075Sobrien /* print_usage will exit. */ 49650397Sobrien } 49750397Sobrien } 49850397Sobrien 499132718Skan return optind; 500132718Skan} 50190075Sobrien 502132718Skan/* Process a single source file. */ 503132718Skan 504132718Skanstatic void 505132718Skanprocess_file (const char *file_name) 506132718Skan{ 507132718Skan source_t *src; 508132718Skan function_t *fn; 509132718Skan 510132718Skan create_file_names (file_name); 511132718Skan if (read_graph_file ()) 512132718Skan return; 513132718Skan 514132718Skan if (!functions) 515132718Skan { 516132718Skan fnotice (stderr, "%s:no functions found\n", bbg_file_name); 517132718Skan return; 518132718Skan } 519132718Skan 520132718Skan if (read_count_file ()) 521132718Skan return; 522132718Skan 523132718Skan for (fn = functions; fn; fn = fn->next) 524132718Skan solve_flow_graph (fn); 525132718Skan for (src = sources; src; src = src->next) 526132718Skan src->lines = xcalloc (src->num_lines, sizeof (line_t)); 527132718Skan for (fn = functions; fn; fn = fn->next) 528132718Skan { 529132718Skan coverage_t coverage; 530132718Skan 531132718Skan memset (&coverage, 0, sizeof (coverage)); 532132718Skan coverage.name = fn->name; 533132718Skan add_line_counts (flag_function_summary ? &coverage : NULL, fn); 534132718Skan if (flag_function_summary) 535132718Skan { 536132718Skan function_summary (&coverage, "Function"); 537132718Skan fnotice (stdout, "\n"); 538132718Skan } 539132718Skan } 540132718Skan 541132718Skan for (src = sources; src; src = src->next) 542132718Skan { 543132718Skan accumulate_line_counts (src); 544132718Skan function_summary (&src->coverage, "File"); 545132718Skan if (flag_gcov_file) 546132718Skan { 547132718Skan char *gcov_file_name = make_gcov_file_name (file_name, src->name); 548132718Skan FILE *gcov_file = fopen (gcov_file_name, "w"); 549132718Skan 550132718Skan if (gcov_file) 551132718Skan { 552132718Skan fnotice (stdout, "%s:creating `%s'\n", 553132718Skan src->name, gcov_file_name); 554132718Skan output_lines (gcov_file, src); 555132718Skan if (ferror (gcov_file)) 556132718Skan fnotice (stderr, "%s:error writing output file `%s'\n", 557132718Skan src->name, gcov_file_name); 558132718Skan fclose (gcov_file); 559132718Skan } 560132718Skan else 561132718Skan fnotice (stderr, "%s:could not open output file `%s'\n", 562132718Skan src->name, gcov_file_name); 563132718Skan free (gcov_file_name); 564132718Skan } 565132718Skan fnotice (stdout, "\n"); 566132718Skan } 56750397Sobrien} 56850397Sobrien 569132718Skan/* Release all memory used. */ 57050397Sobrien 571132718Skanstatic void 572132718Skanrelease_structures (void) 573132718Skan{ 574132718Skan function_t *fn; 575132718Skan source_t *src; 576132718Skan 577132718Skan free (bbg_file_name); 578132718Skan free (da_file_name); 579132718Skan da_file_name = bbg_file_name = NULL; 580132718Skan bbg_file_time = 0; 581132718Skan bbg_stamp = 0; 582132718Skan 583132718Skan while ((src = sources)) 584132718Skan { 585132718Skan sources = src->next; 586132718Skan 587132718Skan free (src->name); 588132718Skan free (src->lines); 589132718Skan } 590132718Skan 591132718Skan while ((fn = functions)) 592132718Skan { 593132718Skan unsigned ix; 594132718Skan block_t *block; 595132718Skan 596132718Skan functions = fn->next; 597132718Skan for (ix = fn->num_blocks, block = fn->blocks; ix--; block++) 598132718Skan { 599132718Skan arc_t *arc, *arc_n; 600132718Skan 601132718Skan for (arc = block->succ; arc; arc = arc_n) 602132718Skan { 603132718Skan arc_n = arc->succ_next; 604132718Skan free (arc); 605132718Skan } 606132718Skan } 607132718Skan free (fn->blocks); 608132718Skan free (fn->counts); 609132718Skan } 610132718Skan} 611132718Skan 612132718Skan/* Generate the names of the graph and data files. If OBJECT_DIRECTORY 613132718Skan is not specified, these are looked for in the current directory, 614132718Skan and named from the basename of the FILE_NAME sans extension. If 615117395Skan OBJECT_DIRECTORY is specified and is a directory, the files are in 616132718Skan that directory, but named from the basename of the FILE_NAME, sans 617132718Skan extension. Otherwise OBJECT_DIRECTORY is taken to be the name of 618132718Skan the object *file*, and the data files are named from that. */ 61950397Sobrien 62050397Sobrienstatic void 621132718Skancreate_file_names (const char *file_name) 62250397Sobrien{ 62350397Sobrien char *cptr; 624117395Skan char *name; 625132718Skan int length = strlen (file_name); 626117395Skan int base; 627132718Skan 628117395Skan if (object_directory && object_directory[0]) 62950397Sobrien { 630117395Skan struct stat status; 63150397Sobrien 632117395Skan length += strlen (object_directory) + 2; 633117395Skan name = xmalloc (length); 634117395Skan name[0] = 0; 635132718Skan 636117395Skan base = !stat (object_directory, &status) && S_ISDIR (status.st_mode); 637117395Skan strcat (name, object_directory); 638117395Skan if (base && name[strlen (name) - 1] != '/') 639117395Skan strcat (name, "/"); 64050397Sobrien } 64150397Sobrien else 64250397Sobrien { 643117395Skan name = xmalloc (length + 1); 644117395Skan name[0] = 0; 645117395Skan base = 1; 64650397Sobrien } 647132718Skan 648117395Skan if (base) 649117395Skan { 650132718Skan /* Append source file name. */ 651132718Skan cptr = strrchr (file_name, '/'); 652132718Skan strcat (name, cptr ? cptr + 1 : file_name); 653132718Skan } 65450397Sobrien 655117395Skan /* Remove the extension. */ 656117395Skan cptr = strrchr (name, '.'); 65750397Sobrien if (cptr) 658117395Skan *cptr = 0; 659132718Skan 660132718Skan length = strlen (name); 661117395Skan 662132718Skan bbg_file_name = xmalloc (length + strlen (GCOV_NOTE_SUFFIX) + 1); 663132718Skan strcpy (bbg_file_name, name); 664132718Skan strcpy (bbg_file_name + length, GCOV_NOTE_SUFFIX); 66550397Sobrien 666132718Skan da_file_name = xmalloc (length + strlen (GCOV_DATA_SUFFIX) + 1); 667117395Skan strcpy (da_file_name, name); 668132718Skan strcpy (da_file_name + length, GCOV_DATA_SUFFIX); 66950397Sobrien 670132718Skan return; 671132718Skan} 67250397Sobrien 673132718Skan/* Find or create a source file structure for FILE_NAME. Copies 674132718Skan FILE_NAME on creation */ 675117395Skan 676132718Skanstatic source_t * 677132718Skanfind_source (const char *file_name) 678132718Skan{ 679132718Skan source_t *src; 68090075Sobrien 681132718Skan if (!file_name) 682132718Skan file_name = "<unknown>"; 68350397Sobrien 684132718Skan for (src = sources; src; src = src->next) 685132718Skan if (!strcmp (file_name, src->name)) 686132718Skan return src; 68750397Sobrien 688132718Skan src = xcalloc (1, sizeof (source_t)); 689132718Skan src->name = xstrdup (file_name); 690132718Skan src->coverage.name = src->name; 691132718Skan src->index = sources ? sources->index + 1 : 1; 692132718Skan src->next = sources; 693132718Skan sources = src; 69450397Sobrien 695132718Skan return src; 69650397Sobrien} 69750397Sobrien 698132718Skan/* Read the graph file. Return nonzero on fatal error. */ 69950397Sobrien 700132718Skanstatic int 701132718Skanread_graph_file (void) 70250397Sobrien{ 703132718Skan unsigned version; 704132718Skan unsigned current_tag = 0; 705132718Skan struct function_info *fn = NULL; 706132718Skan source_t *src = NULL; 707132718Skan unsigned ix; 708132718Skan unsigned tag; 70950397Sobrien 710132718Skan if (!gcov_open (bbg_file_name, 1)) 71150397Sobrien { 712132718Skan fnotice (stderr, "%s:cannot open graph file\n", bbg_file_name); 713132718Skan return 1; 71450397Sobrien } 715132718Skan bbg_file_time = gcov_time (); 716132718Skan if (!gcov_magic (gcov_read_unsigned (), GCOV_NOTE_MAGIC)) 717132718Skan { 718132718Skan fnotice (stderr, "%s:not a gcov graph file\n", bbg_file_name); 719132718Skan gcov_close (); 720132718Skan return 1; 721132718Skan } 72250397Sobrien 723132718Skan version = gcov_read_unsigned (); 724132718Skan if (version != GCOV_VERSION) 725132718Skan { 726132718Skan char v[4], e[4]; 72750397Sobrien 728132718Skan GCOV_UNSIGNED2STRING (v, version); 729132718Skan GCOV_UNSIGNED2STRING (e, GCOV_VERSION); 73050397Sobrien 731132718Skan fnotice (stderr, "%s:version `%.4s', prefer `%.4s'\n", 732132718Skan bbg_file_name, v, e); 733132718Skan } 734132718Skan bbg_stamp = gcov_read_unsigned (); 735117395Skan 736132718Skan while ((tag = gcov_read_unsigned ())) 737117395Skan { 738132718Skan unsigned length = gcov_read_unsigned (); 739132718Skan gcov_position_t base = gcov_position (); 740117395Skan 741132718Skan if (tag == GCOV_TAG_FUNCTION) 742117395Skan { 743132718Skan char *function_name; 744132718Skan unsigned ident, checksum, lineno; 745132718Skan source_t *src; 746132718Skan function_t *probe, *prev; 747117395Skan 748132718Skan ident = gcov_read_unsigned (); 749132718Skan checksum = gcov_read_unsigned (); 750132718Skan function_name = xstrdup (gcov_read_string ()); 751132718Skan src = find_source (gcov_read_string ()); 752132718Skan lineno = gcov_read_unsigned (); 753117395Skan 754132718Skan fn = xcalloc (1, sizeof (function_t)); 755132718Skan fn->name = function_name; 756132718Skan fn->ident = ident; 757132718Skan fn->checksum = checksum; 758132718Skan fn->src = src; 759132718Skan fn->line = lineno; 760117395Skan 761132718Skan fn->next = functions; 762132718Skan functions = fn; 763132718Skan current_tag = tag; 764117395Skan 765132718Skan if (lineno >= src->num_lines) 766132718Skan src->num_lines = lineno + 1; 767132718Skan /* Now insert it into the source file's list of 768132718Skan functions. Normally functions will be encountered in 769132718Skan ascending order, so a simple scan is quick. */ 770132718Skan for (probe = src->functions, prev = NULL; 771132718Skan probe && probe->line > lineno; 772132718Skan prev = probe, probe = probe->line_next) 773132718Skan continue; 774132718Skan fn->line_next = probe; 775132718Skan if (prev) 776132718Skan prev->line_next = fn; 777132718Skan else 778132718Skan src->functions = fn; 779132718Skan } 780132718Skan else if (fn && tag == GCOV_TAG_BLOCKS) 781117395Skan { 782132718Skan if (fn->blocks) 783132718Skan fnotice (stderr, "%s:already seen blocks for `%s'\n", 784132718Skan bbg_file_name, fn->name); 785117395Skan else 786117395Skan { 787132718Skan unsigned ix, num_blocks = GCOV_TAG_BLOCKS_NUM (length); 788132718Skan fn->num_blocks = num_blocks; 789117395Skan 790132718Skan fn->blocks = xcalloc (fn->num_blocks, sizeof (block_t)); 791132718Skan for (ix = 0; ix != num_blocks; ix++) 792132718Skan fn->blocks[ix].flags = gcov_read_unsigned (); 793117395Skan } 794117395Skan } 795132718Skan else if (fn && tag == GCOV_TAG_ARCS) 796132718Skan { 797132718Skan unsigned src = gcov_read_unsigned (); 798132718Skan unsigned num_dests = GCOV_TAG_ARCS_NUM (length); 799117395Skan 800132718Skan if (src >= fn->num_blocks || fn->blocks[src].succ) 801132718Skan goto corrupt; 802117395Skan 803132718Skan while (num_dests--) 804132718Skan { 805132718Skan struct arc_info *arc; 806132718Skan unsigned dest = gcov_read_unsigned (); 807132718Skan unsigned flags = gcov_read_unsigned (); 808117395Skan 809132718Skan if (dest >= fn->num_blocks) 810132718Skan goto corrupt; 811132718Skan arc = xcalloc (1, sizeof (arc_t)); 812117395Skan 813132718Skan arc->dst = &fn->blocks[dest]; 814132718Skan arc->src = &fn->blocks[src]; 815117395Skan 816132718Skan arc->count = 0; 817132718Skan arc->count_valid = 0; 818132718Skan arc->on_tree = !!(flags & GCOV_ARC_ON_TREE); 819132718Skan arc->fake = !!(flags & GCOV_ARC_FAKE); 820132718Skan arc->fall_through = !!(flags & GCOV_ARC_FALLTHROUGH); 821117395Skan 822132718Skan arc->succ_next = fn->blocks[src].succ; 823132718Skan fn->blocks[src].succ = arc; 824132718Skan fn->blocks[src].num_succ++; 82550397Sobrien 826132718Skan arc->pred_next = fn->blocks[dest].pred; 827132718Skan fn->blocks[dest].pred = arc; 828132718Skan fn->blocks[dest].num_pred++; 82950397Sobrien 830132718Skan if (arc->fake) 831132718Skan { 832132718Skan if (src) 833132718Skan { 834132718Skan /* Exceptional exit from this function, the 835132718Skan source block must be a call. */ 836132718Skan fn->blocks[src].is_call_site = 1; 837132718Skan arc->is_call_non_return = 1; 838132718Skan } 839132718Skan else 840132718Skan { 841132718Skan /* Non-local return from a callee of this 842132718Skan function. The destination block is a catch or 843132718Skan setjmp. */ 844132718Skan arc->is_nonlocal_return = 1; 845132718Skan fn->blocks[dest].is_nonlocal_return = 1; 846132718Skan } 847132718Skan } 848117395Skan 849132718Skan if (!arc->on_tree) 850132718Skan fn->num_counts++; 851132718Skan } 852132718Skan } 853132718Skan else if (fn && tag == GCOV_TAG_LINES) 854132718Skan { 855132718Skan unsigned blockno = gcov_read_unsigned (); 856132718Skan unsigned *line_nos = xcalloc (length - 1, sizeof (unsigned)); 857117395Skan 858132718Skan if (blockno >= fn->num_blocks || fn->blocks[blockno].u.line.encoding) 859132718Skan goto corrupt; 860117395Skan 861132718Skan for (ix = 0; ; ) 862132718Skan { 863132718Skan unsigned lineno = gcov_read_unsigned (); 864117395Skan 865132718Skan if (lineno) 866132718Skan { 867132718Skan if (!ix) 868132718Skan { 869132718Skan line_nos[ix++] = 0; 870132718Skan line_nos[ix++] = src->index; 871132718Skan } 872132718Skan line_nos[ix++] = lineno; 873132718Skan if (lineno >= src->num_lines) 874132718Skan src->num_lines = lineno + 1; 875132718Skan } 876132718Skan else 877132718Skan { 878132718Skan const char *file_name = gcov_read_string (); 879117395Skan 880132718Skan if (!file_name) 881132718Skan break; 882132718Skan src = find_source (file_name); 88350397Sobrien 884132718Skan line_nos[ix++] = 0; 885132718Skan line_nos[ix++] = src->index; 886132718Skan } 887132718Skan } 88850397Sobrien 889132718Skan fn->blocks[blockno].u.line.encoding = line_nos; 890132718Skan fn->blocks[blockno].u.line.num = ix; 891132718Skan } 892132718Skan else if (current_tag && !GCOV_TAG_IS_SUBTAG (current_tag, tag)) 89350397Sobrien { 894132718Skan fn = NULL; 895132718Skan current_tag = 0; 89650397Sobrien } 897132718Skan gcov_sync (base, length); 898132718Skan if (gcov_is_error ()) 899132718Skan break; 90050397Sobrien } 901132718Skan if (!gcov_is_eof ()) 902132718Skan { 903132718Skan corrupt:; 904132718Skan fnotice (stderr, "%s:corrupted\n", bbg_file_name); 905132718Skan gcov_close (); 906132718Skan return 1; 907132718Skan } 908132718Skan gcov_close (); 90950397Sobrien 910132718Skan /* We built everything backwards, so nreverse them all. */ 91150397Sobrien 912132718Skan /* Reverse sources. Not strictly necessary, but we'll then process 913132718Skan them in the 'expected' order. */ 914132718Skan { 915132718Skan source_t *src, *src_p, *src_n; 91650397Sobrien 917132718Skan for (src_p = NULL, src = sources; src; src_p = src, src = src_n) 918132718Skan { 919132718Skan src_n = src->next; 920132718Skan src->next = src_p; 921132718Skan } 922132718Skan sources = src_p; 923132718Skan } 92450397Sobrien 925132718Skan /* Reverse functions. */ 926132718Skan { 927132718Skan function_t *fn, *fn_p, *fn_n; 92850397Sobrien 929132718Skan for (fn_p = NULL, fn = functions; fn; fn_p = fn, fn = fn_n) 930132718Skan { 931132718Skan unsigned ix; 932117395Skan 933132718Skan fn_n = fn->next; 934132718Skan fn->next = fn_p; 935117395Skan 936132718Skan /* Reverse the arcs. */ 937132718Skan for (ix = fn->num_blocks; ix--;) 938132718Skan { 939132718Skan arc_t *arc, *arc_p, *arc_n; 94050397Sobrien 941132718Skan for (arc_p = NULL, arc = fn->blocks[ix].succ; arc; 942132718Skan arc_p = arc, arc = arc_n) 943132718Skan { 944132718Skan arc_n = arc->succ_next; 945132718Skan arc->succ_next = arc_p; 946132718Skan } 947132718Skan fn->blocks[ix].succ = arc_p; 94850397Sobrien 949132718Skan for (arc_p = NULL, arc = fn->blocks[ix].pred; arc; 950132718Skan arc_p = arc, arc = arc_n) 951132718Skan { 952132718Skan arc_n = arc->pred_next; 953132718Skan arc->pred_next = arc_p; 954132718Skan } 955132718Skan fn->blocks[ix].pred = arc_p; 956132718Skan } 957132718Skan } 958132718Skan functions = fn_p; 959132718Skan } 960132718Skan return 0; 96150397Sobrien} 96290075Sobrien 963132718Skan/* Reads profiles from the count file and attach to each 964132718Skan function. Return nonzero if fatal error. */ 965132718Skan 966132718Skanstatic int 967132718Skanread_count_file (void) 96850397Sobrien{ 969132718Skan unsigned ix; 970132718Skan unsigned version; 971132718Skan unsigned tag; 972132718Skan function_t *fn = NULL; 973132718Skan int error = 0; 97450397Sobrien 975132718Skan if (!gcov_open (da_file_name, 1)) 976132718Skan { 977132718Skan fnotice (stderr, "%s:cannot open data file\n", da_file_name); 978132718Skan return 1; 979132718Skan } 980132718Skan if (!gcov_magic (gcov_read_unsigned (), GCOV_DATA_MAGIC)) 981132718Skan { 982132718Skan fnotice (stderr, "%s:not a gcov data file\n", da_file_name); 983132718Skan cleanup:; 984132718Skan gcov_close (); 985132718Skan return 1; 986132718Skan } 987132718Skan version = gcov_read_unsigned (); 988132718Skan if (version != GCOV_VERSION) 989132718Skan { 990132718Skan char v[4], e[4]; 99150397Sobrien 992132718Skan GCOV_UNSIGNED2STRING (v, version); 993132718Skan GCOV_UNSIGNED2STRING (e, GCOV_VERSION); 994132718Skan 995132718Skan fnotice (stderr, "%s:version `%.4s', prefer version `%.4s'\n", 996132718Skan da_file_name, v, e); 997132718Skan } 998132718Skan tag = gcov_read_unsigned (); 999132718Skan if (tag != bbg_stamp) 1000132718Skan { 1001132718Skan fnotice (stderr, "%s:stamp mismatch with graph file\n", da_file_name); 1002132718Skan goto cleanup; 1003132718Skan } 100450397Sobrien 1005132718Skan while ((tag = gcov_read_unsigned ())) 100650397Sobrien { 1007132718Skan unsigned length = gcov_read_unsigned (); 1008132718Skan unsigned long base = gcov_position (); 100950397Sobrien 1010132718Skan if (tag == GCOV_TAG_OBJECT_SUMMARY) 1011132718Skan gcov_read_summary (&object_summary); 1012132718Skan else if (tag == GCOV_TAG_PROGRAM_SUMMARY) 1013132718Skan program_count++; 1014132718Skan else if (tag == GCOV_TAG_FUNCTION) 101550397Sobrien { 1016132718Skan unsigned ident = gcov_read_unsigned (); 1017132718Skan struct function_info *fn_n = functions; 1018132718Skan 1019132718Skan for (fn = fn ? fn->next : NULL; ; fn = fn->next) 102050397Sobrien { 1021132718Skan if (fn) 1022132718Skan ; 1023132718Skan else if ((fn = fn_n)) 1024132718Skan fn_n = NULL; 1025132718Skan else 102650397Sobrien { 1027132718Skan fnotice (stderr, "%s:unknown function `%u'\n", 1028132718Skan da_file_name, ident); 1029132718Skan break; 103050397Sobrien } 1031132718Skan if (fn->ident == ident) 1032132718Skan break; 103350397Sobrien } 1034132718Skan 1035132718Skan if (!fn) 1036132718Skan ; 1037132718Skan else if (gcov_read_unsigned () != fn->checksum) 103850397Sobrien { 1039132718Skan mismatch:; 1040132718Skan fnotice (stderr, "%s:profile mismatch for `%s'\n", 1041132718Skan da_file_name, fn->name); 1042132718Skan goto cleanup; 1043132718Skan } 1044132718Skan } 1045132718Skan else if (tag == GCOV_TAG_FOR_COUNTER (GCOV_COUNTER_ARCS) && fn) 1046132718Skan { 1047132718Skan if (length != GCOV_TAG_COUNTER_LENGTH (fn->num_counts)) 1048132718Skan goto mismatch; 104950397Sobrien 1050132718Skan if (!fn->counts) 1051132718Skan fn->counts = xcalloc (fn->num_counts, sizeof (gcov_type)); 105250397Sobrien 1053132718Skan for (ix = 0; ix != fn->num_counts; ix++) 1054132718Skan fn->counts[ix] += gcov_read_counter (); 105550397Sobrien } 1056132718Skan gcov_sync (base, length); 1057132718Skan if ((error = gcov_is_error ())) 1058132718Skan break; 105950397Sobrien } 106090075Sobrien 1061132718Skan if (!gcov_is_eof ()) 1062132718Skan { 1063132718Skan fnotice (stderr, error < 0 ? "%s:overflowed\n" : "%s:corrupted\n", 1064132718Skan da_file_name); 1065132718Skan goto cleanup; 1066132718Skan } 1067132718Skan 1068132718Skan gcov_close (); 1069132718Skan return 0; 107050397Sobrien} 107150397Sobrien 1072132718Skan/* Solve the flow graph. Propagate counts from the instrumented arcs 1073132718Skan to the blocks and the uninstrumented arcs. */ 107450397Sobrien 107550397Sobrienstatic void 1076132718Skansolve_flow_graph (function_t *fn) 107750397Sobrien{ 1078132718Skan unsigned ix; 1079132718Skan arc_t *arc; 1080132718Skan gcov_type *count_ptr = fn->counts; 1081132718Skan block_t *blk; 1082132718Skan block_t *valid_blocks = NULL; /* valid, but unpropagated blocks. */ 1083132718Skan block_t *invalid_blocks = NULL; /* invalid, but inferable blocks. */ 108450397Sobrien 1085132718Skan if (fn->num_blocks < 2) 1086132718Skan fnotice (stderr, "%s:`%s' lacks entry and/or exit blocks\n", 1087132718Skan bbg_file_name, fn->name); 1088132718Skan else 108950397Sobrien { 1090132718Skan if (fn->blocks[0].num_pred) 1091132718Skan fnotice (stderr, "%s:`%s' has arcs to entry block\n", 1092132718Skan bbg_file_name, fn->name); 1093132718Skan else 1094132718Skan /* We can't deduce the entry block counts from the lack of 1095132718Skan predecessors. */ 1096132718Skan fn->blocks[0].num_pred = ~(unsigned)0; 109750397Sobrien 1098132718Skan if (fn->blocks[fn->num_blocks - 1].num_succ) 1099132718Skan fnotice (stderr, "%s:`%s' has arcs from exit block\n", 1100132718Skan bbg_file_name, fn->name); 110150397Sobrien else 1102132718Skan /* Likewise, we can't deduce exit block counts from the lack 1103132718Skan of its successors. */ 1104132718Skan fn->blocks[fn->num_blocks - 1].num_succ = ~(unsigned)0; 110550397Sobrien } 110650397Sobrien 1107132718Skan /* Propagate the measured counts, this must be done in the same 1108132718Skan order as the code in profile.c */ 1109132718Skan for (ix = 0, blk = fn->blocks; ix != fn->num_blocks; ix++, blk++) 1110132718Skan { 1111132718Skan block_t const *prev_dst = NULL; 1112132718Skan int out_of_order = 0; 1113132718Skan int non_fake_succ = 0; 111450397Sobrien 1115132718Skan for (arc = blk->succ; arc; arc = arc->succ_next) 1116132718Skan { 1117132718Skan if (!arc->fake) 1118132718Skan non_fake_succ++; 111950397Sobrien 1120132718Skan if (!arc->on_tree) 1121132718Skan { 1122132718Skan if (count_ptr) 1123132718Skan arc->count = *count_ptr++; 1124132718Skan arc->count_valid = 1; 1125132718Skan blk->num_succ--; 1126132718Skan arc->dst->num_pred--; 1127132718Skan } 1128132718Skan if (prev_dst && prev_dst > arc->dst) 1129132718Skan out_of_order = 1; 1130132718Skan prev_dst = arc->dst; 1131132718Skan } 1132132718Skan if (non_fake_succ == 1) 1133132718Skan { 1134132718Skan /* If there is only one non-fake exit, it is an 1135132718Skan unconditional branch. */ 1136132718Skan for (arc = blk->succ; arc; arc = arc->succ_next) 1137132718Skan if (!arc->fake) 1138132718Skan { 1139132718Skan arc->is_unconditional = 1; 1140132718Skan /* If this block is instrumenting a call, it might be 1141132718Skan an artificial block. It is not artificial if it has 1142132718Skan a non-fallthrough exit, or the destination of this 1143132718Skan arc has more than one entry. Mark the destination 1144132718Skan block as a return site, if none of those conditions 1145132718Skan hold. */ 1146132718Skan if (blk->is_call_site && arc->fall_through 1147132718Skan && arc->dst->pred == arc && !arc->pred_next) 1148132718Skan arc->dst->is_call_return = 1; 1149132718Skan } 1150132718Skan } 115150397Sobrien 1152132718Skan /* Sort the successor arcs into ascending dst order. profile.c 1153132718Skan normally produces arcs in the right order, but sometimes with 1154132718Skan one or two out of order. We're not using a particularly 1155132718Skan smart sort. */ 1156132718Skan if (out_of_order) 1157132718Skan { 1158132718Skan arc_t *start = blk->succ; 1159132718Skan unsigned changes = 1; 116090075Sobrien 1161132718Skan while (changes) 1162132718Skan { 1163132718Skan arc_t *arc, *arc_p, *arc_n; 116450397Sobrien 1165132718Skan changes = 0; 1166132718Skan for (arc_p = NULL, arc = start; (arc_n = arc->succ_next);) 1167132718Skan { 1168132718Skan if (arc->dst > arc_n->dst) 1169132718Skan { 1170132718Skan changes = 1; 1171132718Skan if (arc_p) 1172132718Skan arc_p->succ_next = arc_n; 1173132718Skan else 1174132718Skan start = arc_n; 1175132718Skan arc->succ_next = arc_n->succ_next; 1176132718Skan arc_n->succ_next = arc; 1177132718Skan arc_p = arc_n; 1178132718Skan } 1179132718Skan else 1180132718Skan { 1181132718Skan arc_p = arc; 1182132718Skan arc = arc_n; 1183132718Skan } 1184132718Skan } 1185132718Skan } 1186132718Skan blk->succ = start; 1187132718Skan } 118850397Sobrien 1189132718Skan /* Place it on the invalid chain, it will be ignored if that's 1190132718Skan wrong. */ 1191132718Skan blk->invalid_chain = 1; 1192132718Skan blk->chain = invalid_blocks; 1193132718Skan invalid_blocks = blk; 1194132718Skan } 119550397Sobrien 1196132718Skan while (invalid_blocks || valid_blocks) 1197132718Skan { 1198132718Skan while ((blk = invalid_blocks)) 1199132718Skan { 1200132718Skan gcov_type total = 0; 1201132718Skan const arc_t *arc; 120250397Sobrien 1203132718Skan invalid_blocks = blk->chain; 1204132718Skan blk->invalid_chain = 0; 1205132718Skan if (!blk->num_succ) 1206132718Skan for (arc = blk->succ; arc; arc = arc->succ_next) 1207132718Skan total += arc->count; 1208132718Skan else if (!blk->num_pred) 1209132718Skan for (arc = blk->pred; arc; arc = arc->pred_next) 1210132718Skan total += arc->count; 1211132718Skan else 1212132718Skan continue; 121350397Sobrien 1214132718Skan blk->count = total; 1215132718Skan blk->count_valid = 1; 1216132718Skan blk->chain = valid_blocks; 1217132718Skan blk->valid_chain = 1; 1218132718Skan valid_blocks = blk; 1219132718Skan } 1220132718Skan while ((blk = valid_blocks)) 122150397Sobrien { 1222132718Skan gcov_type total; 1223132718Skan arc_t *arc, *inv_arc; 122450397Sobrien 1225132718Skan valid_blocks = blk->chain; 1226132718Skan blk->valid_chain = 0; 1227132718Skan if (blk->num_succ == 1) 122850397Sobrien { 1229132718Skan block_t *dst; 1230132718Skan 1231132718Skan total = blk->count; 1232132718Skan inv_arc = NULL; 1233132718Skan for (arc = blk->succ; arc; arc = arc->succ_next) 1234132718Skan { 1235132718Skan total -= arc->count; 1236132718Skan if (!arc->count_valid) 1237132718Skan inv_arc = arc; 1238132718Skan } 1239132718Skan dst = inv_arc->dst; 1240132718Skan inv_arc->count_valid = 1; 1241132718Skan inv_arc->count = total; 1242132718Skan blk->num_succ--; 1243132718Skan dst->num_pred--; 1244132718Skan if (dst->count_valid) 1245132718Skan { 1246132718Skan if (dst->num_pred == 1 && !dst->valid_chain) 1247132718Skan { 1248132718Skan dst->chain = valid_blocks; 1249132718Skan dst->valid_chain = 1; 1250132718Skan valid_blocks = dst; 1251132718Skan } 1252132718Skan } 1253132718Skan else 1254132718Skan { 1255132718Skan if (!dst->num_pred && !dst->invalid_chain) 1256132718Skan { 1257132718Skan dst->chain = invalid_blocks; 1258132718Skan dst->invalid_chain = 1; 1259132718Skan invalid_blocks = dst; 1260132718Skan } 1261132718Skan } 126250397Sobrien } 1263132718Skan if (blk->num_pred == 1) 1264132718Skan { 1265132718Skan block_t *src; 126650397Sobrien 1267132718Skan total = blk->count; 1268132718Skan inv_arc = NULL; 1269132718Skan for (arc = blk->pred; arc; arc = arc->pred_next) 1270132718Skan { 1271132718Skan total -= arc->count; 1272132718Skan if (!arc->count_valid) 1273132718Skan inv_arc = arc; 1274132718Skan } 1275132718Skan src = inv_arc->src; 1276132718Skan inv_arc->count_valid = 1; 1277132718Skan inv_arc->count = total; 1278132718Skan blk->num_pred--; 1279132718Skan src->num_succ--; 1280132718Skan if (src->count_valid) 1281132718Skan { 1282132718Skan if (src->num_succ == 1 && !src->valid_chain) 1283132718Skan { 1284132718Skan src->chain = valid_blocks; 1285132718Skan src->valid_chain = 1; 1286132718Skan valid_blocks = src; 1287132718Skan } 1288132718Skan } 1289132718Skan else 1290132718Skan { 1291132718Skan if (!src->num_succ && !src->invalid_chain) 1292132718Skan { 1293132718Skan src->chain = invalid_blocks; 1294132718Skan src->invalid_chain = 1; 1295132718Skan invalid_blocks = src; 1296132718Skan } 1297132718Skan } 1298132718Skan } 129950397Sobrien } 1300132718Skan } 130150397Sobrien 1302132718Skan /* If the graph has been correctly solved, every block will have a 1303132718Skan valid count. */ 1304132718Skan for (ix = 0; ix < fn->num_blocks; ix++) 1305132718Skan if (!fn->blocks[ix].count_valid) 1306132718Skan { 1307132718Skan fnotice (stderr, "%s:graph is unsolvable for `%s'\n", 1308132718Skan bbg_file_name, fn->name); 1309132718Skan break; 1310132718Skan } 131150397Sobrien} 1312132718Skan 131350397Sobrien 131450397Sobrien 1315132718Skan/* Increment totals in COVERAGE according to arc ARC. */ 131650397Sobrien 1317117395Skanstatic void 1318132718Skanadd_branch_counts (coverage_t *coverage, const arc_t *arc) 1319117395Skan{ 1320132718Skan if (arc->is_call_non_return) 1321117395Skan { 1322132718Skan coverage->calls++; 1323132718Skan if (arc->src->count) 1324132718Skan coverage->calls_executed++; 1325117395Skan } 1326132718Skan else if (!arc->is_unconditional) 1327117395Skan { 1328132718Skan coverage->branches++; 1329132718Skan if (arc->src->count) 1330132718Skan coverage->branches_executed++; 1331132718Skan if (arc->count) 1332132718Skan coverage->branches_taken++; 1333117395Skan } 1334117395Skan} 1335117395Skan 1336117395Skan/* Format a HOST_WIDE_INT as either a percent ratio, or absolute 1337117395Skan count. If dp >= 0, format TOP/BOTTOM * 100 to DP decimal places. 1338117395Skan If DP is zero, no decimal point is printed. Only print 100% when 1339117395Skan TOP==BOTTOM and only print 0% when TOP=0. If dp < 0, then simply 1340117395Skan format TOP. Return pointer to a static string. */ 1341117395Skan 1342117395Skanstatic char const * 1343132718Skanformat_gcov (gcov_type top, gcov_type bottom, int dp) 1344117395Skan{ 1345117395Skan static char buffer[20]; 1346132718Skan 1347117395Skan if (dp >= 0) 1348117395Skan { 1349117395Skan float ratio = bottom ? (float)top / bottom : 0; 1350117395Skan int ix; 1351117395Skan unsigned limit = 100; 1352117395Skan unsigned percent; 1353132718Skan 1354117395Skan for (ix = dp; ix--; ) 1355117395Skan limit *= 10; 1356132718Skan 1357117395Skan percent = (unsigned) (ratio * limit + (float)0.5); 1358117395Skan if (percent <= 0 && top) 1359117395Skan percent = 1; 1360117395Skan else if (percent >= limit && top != bottom) 1361117395Skan percent = limit - 1; 1362117395Skan ix = sprintf (buffer, "%.*u%%", dp + 1, percent); 1363117395Skan if (dp) 136450397Sobrien { 1365117395Skan dp++; 1366117395Skan do 136750397Sobrien { 1368117395Skan buffer[ix+1] = buffer[ix]; 1369117395Skan ix--; 137050397Sobrien } 1371117395Skan while (dp--); 1372117395Skan buffer[ix + 1] = '.'; 137350397Sobrien } 137450397Sobrien } 1375117395Skan else 1376132718Skan sprintf (buffer, HOST_WIDEST_INT_PRINT_DEC, (HOST_WIDEST_INT)top); 1377132718Skan 1378117395Skan return buffer; 137950397Sobrien} 138050397Sobrien 1381117395Skan 138250397Sobrien/* Output summary info for a function. */ 138350397Sobrien 138450397Sobrienstatic void 1385132718Skanfunction_summary (const coverage_t *coverage, const char *title) 138650397Sobrien{ 1387132718Skan fnotice (stdout, "%s `%s'\n", title, coverage->name); 1388132718Skan 1389132718Skan if (coverage->lines) 1390132718Skan fnotice (stdout, "Lines executed:%s of %d\n", 1391132718Skan format_gcov (coverage->lines_executed, coverage->lines, 2), 1392132718Skan coverage->lines); 139350397Sobrien else 1394132718Skan fnotice (stdout, "No executable lines"); 139550397Sobrien 1396132718Skan if (flag_branches) 139750397Sobrien { 1398132718Skan if (coverage->branches) 139950397Sobrien { 1400132718Skan fnotice (stdout, "Branches executed:%s of %d\n", 1401132718Skan format_gcov (coverage->branches_executed, 1402132718Skan coverage->branches, 2), 1403132718Skan coverage->branches); 1404132718Skan fnotice (stdout, "Taken at least once:%s of %d\n", 1405132718Skan format_gcov (coverage->branches_taken, 1406132718Skan coverage->branches, 2), 1407132718Skan coverage->branches); 140850397Sobrien } 140950397Sobrien else 1410132718Skan fnotice (stdout, "No branches\n"); 1411132718Skan if (coverage->calls) 1412132718Skan fnotice (stdout, "Calls executed:%s of %d\n", 1413132718Skan format_gcov (coverage->calls_executed, coverage->calls, 2), 1414132718Skan coverage->calls); 141550397Sobrien else 1416132718Skan fnotice (stdout, "No calls\n"); 141750397Sobrien } 141850397Sobrien} 141950397Sobrien 1420117395Skan/* Generate an output file name. LONG_OUTPUT_NAMES and PRESERVE_PATHS 1421117395Skan affect name generation. With preserve_paths we create a filename 1422117395Skan from all path components of the source file, replacing '/' with 1423117395Skan '#', without it we simply take the basename component. With 1424117395Skan long_output_names we prepend the processed name of the input file 1425117395Skan to each output name (except when the current source file is the 1426117395Skan input file, so you don't get a double concatenation). The two 1427117395Skan components are separated by '##'. Also '.' filename components are 1428117395Skan removed and '..' components are renamed to '^'. */ 142950397Sobrien 1430117395Skanstatic char * 1431132718Skanmake_gcov_file_name (const char *input_name, const char *src_name) 1432117395Skan{ 1433117395Skan char *cptr; 1434132718Skan char *name = xmalloc (strlen (src_name) + strlen (input_name) + 10); 1435132718Skan 1436117395Skan name[0] = 0; 1437132718Skan if (flag_long_names && strcmp (src_name, input_name)) 1438117395Skan { 1439117395Skan /* Generate the input filename part. */ 1440132718Skan cptr = flag_preserve_paths ? NULL : strrchr (input_name, '/'); 1441132718Skan strcat (name, cptr ? cptr + 1 : input_name); 1442117395Skan strcat (name, "##"); 1443117395Skan } 1444132718Skan 1445117395Skan /* Generate the source filename part. */ 1446132718Skan cptr = flag_preserve_paths ? NULL : strrchr (src_name, '/'); 1447132718Skan strcat (name, cptr ? cptr + 1 : src_name); 1448132718Skan 1449132718Skan if (flag_preserve_paths) 1450117395Skan { 1451117395Skan /* Convert '/' to '#', remove '/./', convert '/../' to '/^/' */ 1452117395Skan char *prev; 1453132718Skan 1454117395Skan for (cptr = name; (cptr = strchr ((prev = cptr), '/'));) 1455132718Skan { 1456132718Skan unsigned shift = 0; 1457132718Skan 1458132718Skan if (prev + 1 == cptr && prev[0] == '.') 1459132718Skan { 1460132718Skan /* Remove '.' */ 1461132718Skan shift = 2; 1462132718Skan } 1463132718Skan else if (prev + 2 == cptr && prev[0] == '.' && prev[1] == '.') 1464132718Skan { 1465132718Skan /* Convert '..' */ 1466132718Skan shift = 1; 1467132718Skan prev[1] = '^'; 1468132718Skan } 1469132718Skan else 1470132718Skan *cptr++ = '#'; 1471132718Skan if (shift) 1472132718Skan { 1473132718Skan cptr = prev; 1474132718Skan do 1475132718Skan prev[0] = prev[shift]; 1476117395Skan while (*prev++); 1477132718Skan } 1478132718Skan } 1479117395Skan } 1480132718Skan 1481117395Skan strcat (name, ".gcov"); 1482117395Skan return name; 1483117395Skan} 1484117395Skan 1485132718Skan/* Scan through the bb_data for each line in the block, increment 1486117395Skan the line number execution count indicated by the execution count of 1487117395Skan the appropriate basic block. */ 1488117395Skan 148950397Sobrienstatic void 1490132718Skanadd_line_counts (coverage_t *coverage, function_t *fn) 149150397Sobrien{ 1492132718Skan unsigned ix; 1493132718Skan line_t *line = NULL; /* This is propagated from one iteration to the 1494132718Skan next. */ 1495132718Skan 1496132718Skan /* Scan each basic block. */ 1497132718Skan for (ix = 0; ix != fn->num_blocks; ix++) 149850397Sobrien { 1499132718Skan block_t *block = &fn->blocks[ix]; 1500132718Skan unsigned *encoding; 1501132718Skan const source_t *src = NULL; 1502132718Skan unsigned jx; 1503132718Skan 1504132718Skan if (block->count && ix && ix + 1 != fn->num_blocks) 1505132718Skan fn->blocks_executed++; 1506132718Skan for (jx = 0, encoding = block->u.line.encoding; 1507132718Skan jx != block->u.line.num; jx++, encoding++) 1508132718Skan if (!*encoding) 1509132718Skan { 1510132718Skan unsigned src_n = *++encoding; 1511132718Skan 1512132718Skan for (src = sources; src->index != src_n; src = src->next) 1513132718Skan continue; 1514132718Skan jx++; 1515132718Skan } 1516132718Skan else 1517132718Skan { 1518132718Skan line = &src->lines[*encoding]; 1519132718Skan 1520132718Skan if (coverage) 1521132718Skan { 1522132718Skan if (!line->exists) 1523132718Skan coverage->lines++; 1524132718Skan if (!line->count && block->count) 1525132718Skan coverage->lines_executed++; 1526132718Skan } 1527132718Skan line->exists = 1; 1528132718Skan line->count += block->count; 1529132718Skan } 1530132718Skan free (block->u.line.encoding); 1531132718Skan block->u.cycle.arc = NULL; 1532132718Skan block->u.cycle.ident = ~0U; 1533132718Skan 1534132718Skan if (!ix || ix + 1 == fn->num_blocks) 1535132718Skan /* Entry or exit block */; 1536132718Skan else if (flag_all_blocks) 153750397Sobrien { 1538132718Skan line_t *block_line = line ? line : &fn->src->lines[fn->line]; 1539117395Skan 1540132718Skan block->chain = block_line->u.blocks; 1541132718Skan block_line->u.blocks = block; 1542132718Skan } 1543132718Skan else if (flag_branches) 1544132718Skan { 1545132718Skan arc_t *arc; 1546132718Skan 1547132718Skan for (arc = block->succ; arc; arc = arc->succ_next) 1548117395Skan { 1549132718Skan arc->line_next = line->u.branches; 1550132718Skan line->u.branches = arc; 1551132718Skan if (coverage && !arc->is_unconditional) 1552132718Skan add_branch_counts (coverage, arc); 1553117395Skan } 155450397Sobrien } 1555132718Skan } 1556132718Skan if (!line) 1557132718Skan fnotice (stderr, "%s:no lines for `%s'\n", bbg_file_name, fn->name); 1558132718Skan} 1559132718Skan 1560132718Skan/* Accumulate the line counts of a file. */ 1561132718Skan 1562132718Skanstatic void 1563132718Skanaccumulate_line_counts (source_t *src) 1564132718Skan{ 1565132718Skan line_t *line; 1566132718Skan function_t *fn, *fn_p, *fn_n; 1567132718Skan unsigned ix; 1568132718Skan 1569132718Skan /* Reverse the function order. */ 1570132718Skan for (fn = src->functions, fn_p = NULL; fn; 1571132718Skan fn_p = fn, fn = fn_n) 1572132718Skan { 1573132718Skan fn_n = fn->line_next; 1574132718Skan fn->line_next = fn_p; 1575132718Skan } 1576132718Skan src->functions = fn_p; 1577132718Skan 1578132718Skan for (ix = src->num_lines, line = src->lines; ix--; line++) 1579132718Skan { 1580132718Skan if (!flag_all_blocks) 158150397Sobrien { 1582132718Skan arc_t *arc, *arc_p, *arc_n; 1583132718Skan 1584132718Skan /* Total and reverse the branch information. */ 1585132718Skan for (arc = line->u.branches, arc_p = NULL; arc; 1586132718Skan arc_p = arc, arc = arc_n) 158750397Sobrien { 1588132718Skan arc_n = arc->line_next; 1589132718Skan arc->line_next = arc_p; 1590132718Skan 1591132718Skan add_branch_counts (&src->coverage, arc); 159250397Sobrien } 1593132718Skan line->u.branches = arc_p; 159450397Sobrien } 1595132718Skan else if (line->u.blocks) 159650397Sobrien { 1597132718Skan /* The user expects the line count to be the number of times 1598132718Skan a line has been executed. Simply summing the block count 1599132718Skan will give an artificially high number. The Right Thing 1600132718Skan is to sum the entry counts to the graph of blocks on this 1601132718Skan line, then find the elementary cycles of the local graph 1602132718Skan and add the transition counts of those cycles. */ 1603132718Skan block_t *block, *block_p, *block_n; 1604132718Skan gcov_type count = 0; 1605132718Skan 1606132718Skan /* Reverse the block information. */ 1607132718Skan for (block = line->u.blocks, block_p = NULL; block; 1608132718Skan block_p = block, block = block_n) 160950397Sobrien { 1610132718Skan block_n = block->chain; 1611132718Skan block->chain = block_p; 1612132718Skan block->u.cycle.ident = ix; 161350397Sobrien } 1614132718Skan line->u.blocks = block_p; 161550397Sobrien 1616132718Skan /* Sum the entry arcs. */ 1617132718Skan for (block = line->u.blocks; block; block = block->chain) 161850397Sobrien { 1619132718Skan arc_t *arc; 1620132718Skan 1621132718Skan for (arc = block->pred; arc; arc = arc->pred_next) 1622132718Skan { 1623132718Skan if (arc->src->u.cycle.ident != ix) 1624132718Skan count += arc->count; 1625132718Skan if (flag_branches) 1626132718Skan add_branch_counts (&src->coverage, arc); 1627132718Skan } 1628132718Skan 1629132718Skan /* Initialize the cs_count. */ 1630132718Skan for (arc = block->succ; arc; arc = arc->succ_next) 1631132718Skan arc->cs_count = arc->count; 1632117395Skan } 1633132718Skan 1634132718Skan /* Find the loops. This uses the algorithm described in 1635132718Skan Tiernan 'An Efficient Search Algorithm to Find the 1636132718Skan Elementary Circuits of a Graph', CACM Dec 1970. We hold 1637132718Skan the P array by having each block point to the arc that 1638132718Skan connects to the previous block. The H array is implicitly 1639132718Skan held because of the arc ordering, and the block's 1640132718Skan previous arc pointer. 1641132718Skan 1642132718Skan Although the algorithm is O(N^3) for highly connected 1643132718Skan graphs, at worst we'll have O(N^2), as most blocks have 1644132718Skan only one or two exits. Most graphs will be small. 1645132718Skan 1646132718Skan For each loop we find, locate the arc with the smallest 1647132718Skan transition count, and add that to the cumulative 1648132718Skan count. Decrease flow over the cycle and remove the arc 1649132718Skan from consideration. */ 1650132718Skan for (block = line->u.blocks; block; block = block->chain) 1651132718Skan { 1652132718Skan block_t *head = block; 1653132718Skan arc_t *arc; 1654132718Skan 1655132718Skan next_vertex:; 1656132718Skan arc = head->succ; 1657132718Skan current_vertex:; 1658132718Skan while (arc) 1659132718Skan { 1660132718Skan block_t *dst = arc->dst; 1661132718Skan if (/* Already used that arc. */ 1662132718Skan arc->cycle 1663132718Skan /* Not to same graph, or before first vertex. */ 1664132718Skan || dst->u.cycle.ident != ix 1665132718Skan /* Already in path. */ 1666132718Skan || dst->u.cycle.arc) 1667132718Skan { 1668132718Skan arc = arc->succ_next; 1669132718Skan continue; 1670132718Skan } 1671132718Skan 1672132718Skan if (dst == block) 1673132718Skan { 1674132718Skan /* Found a closing arc. */ 1675132718Skan gcov_type cycle_count = arc->cs_count; 1676132718Skan arc_t *cycle_arc = arc; 1677132718Skan arc_t *probe_arc; 1678132718Skan 1679132718Skan /* Locate the smallest arc count of the loop. */ 1680132718Skan for (dst = head; (probe_arc = dst->u.cycle.arc); 1681132718Skan dst = probe_arc->src) 1682132718Skan if (cycle_count > probe_arc->cs_count) 1683132718Skan { 1684132718Skan cycle_count = probe_arc->cs_count; 1685132718Skan cycle_arc = probe_arc; 1686132718Skan } 1687132718Skan 1688132718Skan count += cycle_count; 1689132718Skan cycle_arc->cycle = 1; 1690132718Skan 1691132718Skan /* Remove the flow from the cycle. */ 1692132718Skan arc->cs_count -= cycle_count; 1693132718Skan for (dst = head; (probe_arc = dst->u.cycle.arc); 1694132718Skan dst = probe_arc->src) 1695132718Skan probe_arc->cs_count -= cycle_count; 1696132718Skan 1697132718Skan /* Unwind to the cyclic arc. */ 1698132718Skan while (head != cycle_arc->src) 1699132718Skan { 1700132718Skan arc = head->u.cycle.arc; 1701132718Skan head->u.cycle.arc = NULL; 1702132718Skan head = arc->src; 1703132718Skan } 1704132718Skan /* Move on. */ 1705132718Skan arc = arc->succ_next; 1706132718Skan continue; 1707132718Skan } 1708132718Skan 1709132718Skan /* Add new block to chain. */ 1710132718Skan dst->u.cycle.arc = arc; 1711132718Skan head = dst; 1712132718Skan goto next_vertex; 1713132718Skan } 1714132718Skan /* We could not add another vertex to the path. Remove 1715132718Skan the last vertex from the list. */ 1716132718Skan arc = head->u.cycle.arc; 1717132718Skan if (arc) 1718132718Skan { 1719132718Skan /* It was not the first vertex. Move onto next arc. */ 1720132718Skan head->u.cycle.arc = NULL; 1721132718Skan head = arc->src; 1722132718Skan arc = arc->succ_next; 1723132718Skan goto current_vertex; 1724132718Skan } 1725132718Skan /* Mark this block as unusable. */ 1726132718Skan block->u.cycle.ident = ~0U; 1727132718Skan } 1728132718Skan 1729132718Skan line->count = count; 1730117395Skan } 1731132718Skan 1732132718Skan if (line->exists) 1733117395Skan { 1734132718Skan src->coverage.lines++; 1735132718Skan if (line->count) 1736132718Skan src->coverage.lines_executed++; 1737117395Skan } 1738132718Skan } 1739132718Skan} 174090075Sobrien 1741132718Skan/* Ouput information about ARC number IX. Returns nonzero if 1742132718Skan anything is output. */ 1743132718Skan 1744132718Skanstatic int 1745132718Skanoutput_branch_count (FILE *gcov_file, int ix, const arc_t *arc) 1746132718Skan{ 1747132718Skan 1748132718Skan if (arc->is_call_non_return) 1749132718Skan { 1750132718Skan if (arc->src->count) 1751117395Skan { 1752132718Skan fnotice (gcov_file, "call %2d returned %s\n", ix, 1753132718Skan format_gcov (arc->src->count - arc->count, 1754132718Skan arc->src->count, -flag_counts)); 1755117395Skan } 1756132718Skan else 1757132718Skan fnotice (gcov_file, "call %2d never executed\n", ix); 1758117395Skan } 1759132718Skan else if (!arc->is_unconditional) 1760132718Skan { 1761132718Skan if (arc->src->count) 1762132718Skan fnotice (gcov_file, "branch %2d taken %s%s\n", ix, 1763132718Skan format_gcov (arc->count, arc->src->count, -flag_counts), 1764132718Skan arc->fall_through ? " (fallthrough)" : ""); 1765132718Skan else 1766132718Skan fnotice (gcov_file, "branch %2d never executed\n", ix); 1767132718Skan } 1768132718Skan else if (flag_unconditional && !arc->dst->is_call_return) 1769132718Skan { 1770132718Skan if (arc->src->count) 1771132718Skan fnotice (gcov_file, "unconditional %2d taken %s\n", ix, 1772132718Skan format_gcov (arc->count, arc->src->count, -flag_counts)); 1773132718Skan else 1774132718Skan fnotice (gcov_file, "unconditional %2d never executed\n", ix); 1775132718Skan } 1776132718Skan else 1777132718Skan return 0; 1778132718Skan return 1; 1779132718Skan 1780117395Skan} 178150397Sobrien 1782117395Skan/* Read in the source file one line at a time, and output that line to 1783117395Skan the gcov file preceded by its execution count and other 1784117395Skan information. */ 178550397Sobrien 1786117395Skanstatic void 1787132718Skanoutput_lines (FILE *gcov_file, const source_t *src) 1788117395Skan{ 1789117395Skan FILE *source_file; 1790132718Skan unsigned line_num; /* current line number. */ 1791132718Skan const line_t *line; /* current line info ptr. */ 1792132718Skan char string[STRING_SIZE]; /* line buffer. */ 1793132718Skan char const *retval = ""; /* status of source file reading. */ 1794132718Skan function_t *fn = src->functions; 179550397Sobrien 1796132718Skan fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, src->name); 1797132718Skan fprintf (gcov_file, "%9s:%5d:Graph:%s\n", "-", 0, bbg_file_name); 1798132718Skan fprintf (gcov_file, "%9s:%5d:Data:%s\n", "-", 0, da_file_name); 1799132718Skan fprintf (gcov_file, "%9s:%5d:Runs:%u\n", "-", 0, 1800132718Skan object_summary.ctrs[GCOV_COUNTER_ARCS].runs); 1801132718Skan fprintf (gcov_file, "%9s:%5d:Programs:%u\n", "-", 0, program_count); 1802132718Skan 1803132718Skan source_file = fopen (src->name, "r"); 1804117395Skan if (!source_file) 1805117395Skan { 1806132718Skan fnotice (stderr, "%s:cannot open source file\n", src->name); 1807117395Skan retval = NULL; 1808117395Skan } 1809117395Skan else 1810117395Skan { 1811117395Skan struct stat status; 1812132718Skan 1813117395Skan if (!fstat (fileno (source_file), &status) 1814132718Skan && status.st_mtime > bbg_file_time) 1815117395Skan { 1816132718Skan fnotice (stderr, "%s:source file is newer than graph file `%s'\n", 1817132718Skan src->name, bbg_file_name); 1818132718Skan fprintf (gcov_file, "%9s:%5d:Source is newer than graph\n", 1819117395Skan "-", 0); 1820117395Skan } 1821117395Skan } 182250397Sobrien 1823132718Skan for (line_num = 1, line = &src->lines[line_num]; 1824132718Skan line_num < src->num_lines; line_num++, line++) 1825117395Skan { 1826132718Skan for (; fn && fn->line == line_num; fn = fn->line_next) 1827132718Skan { 1828132718Skan arc_t *arc = fn->blocks[fn->num_blocks - 1].pred; 1829132718Skan gcov_type return_count = fn->blocks[fn->num_blocks - 1].count; 1830132718Skan 1831132718Skan for (; arc; arc = arc->pred_next) 1832132718Skan if (arc->fake) 1833132718Skan return_count -= arc->count; 1834132718Skan 1835132718Skan fprintf (gcov_file, "function %s", fn->name); 1836132718Skan fprintf (gcov_file, " called %s", 1837132718Skan format_gcov (fn->blocks[0].count, 0, -1)); 1838132718Skan fprintf (gcov_file, " returned %s", 1839132718Skan format_gcov (return_count, fn->blocks[0].count, 0)); 1840132718Skan fprintf (gcov_file, " blocks executed %s", 1841132718Skan format_gcov (fn->blocks_executed, fn->num_blocks - 2, 0)); 1842132718Skan fprintf (gcov_file, "\n"); 1843132718Skan } 1844132718Skan 1845117395Skan /* For lines which don't exist in the .bb file, print '-' before 1846132718Skan the source line. For lines which exist but were never 1847132718Skan executed, print '#####' before the source line. Otherwise, 1848132718Skan print the execution count before the source line. There are 1849132718Skan 16 spaces of indentation added before the source line so that 1850132718Skan tabs won't be messed up. */ 1851132718Skan fprintf (gcov_file, "%9s:%5u:", 1852132718Skan !line->exists ? "-" : !line->count ? "#####" 1853132718Skan : format_gcov (line->count, 0, -1), line_num); 1854132718Skan 1855117395Skan if (retval) 1856117395Skan { 1857117395Skan /* Copy source line. */ 1858117395Skan do 185950397Sobrien { 1860117395Skan retval = fgets (string, STRING_SIZE, source_file); 1861117395Skan if (!retval) 1862132718Skan break; 1863117395Skan fputs (retval, gcov_file); 186450397Sobrien } 1865117395Skan while (!retval[0] || retval[strlen (retval) - 1] != '\n'); 1866117395Skan } 1867117395Skan if (!retval) 1868132718Skan fputs ("/*EOF*/\n", gcov_file); 1869132718Skan 1870132718Skan if (flag_all_blocks) 1871117395Skan { 1872132718Skan block_t *block; 1873132718Skan arc_t *arc; 1874132718Skan int ix, jx; 1875132718Skan 1876132718Skan for (ix = jx = 0, block = line->u.blocks; block; 1877132718Skan block = block->chain) 187850397Sobrien { 1879132718Skan if (!block->is_call_return) 1880132718Skan fprintf (gcov_file, "%9s:%5u-block %2d\n", 1881132718Skan !line->exists ? "-" : !block->count ? "$$$$$" 1882132718Skan : format_gcov (block->count, 0, -1), 1883132718Skan line_num, ix++); 1884132718Skan if (flag_branches) 1885132718Skan for (arc = block->succ; arc; arc = arc->succ_next) 1886132718Skan jx += output_branch_count (gcov_file, jx, arc); 188750397Sobrien } 1888117395Skan } 1889132718Skan else if (flag_branches) 1890132718Skan { 1891132718Skan int ix; 1892132718Skan arc_t *arc; 1893132718Skan 1894132718Skan for (ix = 0, arc = line->u.branches; arc; arc = arc->line_next) 1895132718Skan ix += output_branch_count (gcov_file, ix, arc); 1896132718Skan } 1897117395Skan } 1898132718Skan 1899117395Skan /* Handle all remaining source lines. There may be lines after the 1900117395Skan last line of code. */ 1901117395Skan if (retval) 1902117395Skan { 1903117395Skan for (; (retval = fgets (string, STRING_SIZE, source_file)); line_num++) 1904117395Skan { 1905132718Skan fprintf (gcov_file, "%9s:%5u:%s", "-", line_num, retval); 1906132718Skan 1907117395Skan while (!retval[0] || retval[strlen (retval) - 1] != '\n') 1908117395Skan { 1909117395Skan retval = fgets (string, STRING_SIZE, source_file); 1910117395Skan if (!retval) 1911117395Skan break; 1912117395Skan fputs (retval, gcov_file); 1913117395Skan } 1914117395Skan } 1915117395Skan } 1916132718Skan 1917117395Skan if (source_file) 1918117395Skan fclose (source_file); 1919117395Skan} 1920