gcov.c revision 117395
150397Sobrien/* Gcov.c: prepend line execution counts and branch probabilities to a 250397Sobrien source file. 390075Sobrien Copyright (C) 1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 490075Sobrien 1999, 2000, 2001, 2002 Free Software Foundation, Inc. 550397Sobrien Contributed by James E. Wilson of Cygnus Support. 650397Sobrien Mangled by Bob Manson of Cygnus Support. 750397Sobrien 850397SobrienGcov is free software; you can redistribute it and/or modify 950397Sobrienit under the terms of the GNU General Public License as published by 1050397Sobrienthe Free Software Foundation; either version 2, or (at your option) 1150397Sobrienany later version. 1250397Sobrien 1350397SobrienGcov is distributed in the hope that it will be useful, 1450397Sobrienbut WITHOUT ANY WARRANTY; without even the implied warranty of 1550397SobrienMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1650397SobrienGNU General Public License for more details. 1750397Sobrien 1850397SobrienYou should have received a copy of the GNU General Public License 1950397Sobrienalong with Gcov; see the file COPYING. If not, write to 2052284Sobrienthe Free Software Foundation, 59 Temple Place - Suite 330, 2152284SobrienBoston, MA 02111-1307, USA. */ 2250397Sobrien 2350397Sobrien/* ??? The code in final.c that produces the struct bb assumes that there is 2450397Sobrien no padding between the fields. This is not necessary true. The current 2550397Sobrien code can only be trusted if longs and pointers are the same size. */ 2650397Sobrien 2750397Sobrien/* ??? No need to print an execution count on every line, could just print 2850397Sobrien it on the first line of each block, and only print it on a subsequent 2950397Sobrien line in the same block if the count changes. */ 3050397Sobrien 3150397Sobrien/* ??? Print a list of the ten blocks with the highest execution counts, 3250397Sobrien and list the line numbers corresponding to those blocks. Also, perhaps 3350397Sobrien list the line numbers with the highest execution counts, only printing 3450397Sobrien the first if there are several which are all listed in the same block. */ 3550397Sobrien 3650397Sobrien/* ??? Should have an option to print the number of basic blocks, and the 3750397Sobrien percent of them that are covered. */ 3850397Sobrien 3950397Sobrien/* ??? Does not correctly handle the case where two .bb files refer to the 4050397Sobrien same included source file. For example, if one has a short file containing 4150397Sobrien only inline functions, which is then included in two other files, then 4250397Sobrien there will be two .bb files which refer to the include file, but there 4350397Sobrien is no way to get the total execution counts for the included file, can 4450397Sobrien only get execution counts for one or the other of the including files. */ 4550397Sobrien 4650397Sobrien#include "config.h" 4750397Sobrien#include "system.h" 4852284Sobrien#include "intl.h" 4990075Sobrien#include "version.h" 5052284Sobrien#undef abort 5150397Sobrien 5290075Sobrien#include <getopt.h> 5390075Sobrien 5490075Sobrientypedef HOST_WIDEST_INT gcov_type; 5550397Sobrien#include "gcov-io.h" 5650397Sobrien 5750397Sobrien/* The .bb file format consists of several lists of 4-byte integers 5850397Sobrien which are the line numbers of each basic block in the file. Each 5950397Sobrien list is terminated by a zero. These lists correspond to the basic 6050397Sobrien blocks in the reconstructed program flow graph. 6150397Sobrien 6250397Sobrien A line number of -1 indicates that a source file name (padded to a 6350397Sobrien long boundary) follows. The padded file name is followed by 6450397Sobrien another -1 to make it easy to scan past file names. A -2 indicates 6550397Sobrien that a function name (padded to a long boundary) follows; the name 6650397Sobrien is followed by another -2 to make it easy to scan past the function 6750397Sobrien name. 6850397Sobrien 6950397Sobrien The .bbg file contains enough info to enable gcov to reconstruct the 7050397Sobrien program flow graph. The first word is the number of basic blocks, 7150397Sobrien the second word is the number of arcs, followed by the list of arcs 7250397Sobrien (source bb, dest bb pairs), then a -1, then the number of instrumented 7350397Sobrien arcs followed by the instrumented arcs, followed by another -1. This 7450397Sobrien is repeated for each function. 7550397Sobrien 7650397Sobrien The .da file contains the execution count for each instrumented branch. 7750397Sobrien 7850397Sobrien The .bb and .bbg files are created by giving GCC the -ftest-coverage option, 7950397Sobrien and the .da files are created when an executable compiled with 8050397Sobrien -fprofile-arcs is run. */ 8150397Sobrien 8250397Sobrien/* The functions in this file for creating and solution program flow graphs 8350397Sobrien are very similar to functions in the gcc source file profile.c. */ 8450397Sobrien 8550397Sobrien/* This is the size of the buffer used to read in source file lines. */ 8650397Sobrien 8750397Sobrien#define STRING_SIZE 200 8850397Sobrien 8950397Sobrien/* One copy of this structure is created for each source file mentioned in the 9050397Sobrien .bb file. */ 9150397Sobrien 9250397Sobrienstruct sourcefile 9350397Sobrien{ 9450397Sobrien char *name; 9550397Sobrien int maxlineno; 9650397Sobrien struct sourcefile *next; 9750397Sobrien}; 9850397Sobrien 9950397Sobrien/* This points to the head of the sourcefile structure list. */ 10050397Sobrien 10150397Sobrienstruct sourcefile *sources; 10250397Sobrien 10350397Sobrien/* One of these is dynamically created whenever we identify an arc in the 10450397Sobrien function. */ 10550397Sobrien 106117395Skanstruct adj_list 107117395Skan{ 10850397Sobrien int source; 10950397Sobrien int target; 11090075Sobrien gcov_type arc_count; 11150397Sobrien unsigned int count_valid : 1; 11250397Sobrien unsigned int on_tree : 1; 11350397Sobrien unsigned int fake : 1; 11450397Sobrien unsigned int fall_through : 1; 11550397Sobrien#if 0 11650397Sobrien /* Not needed for gcov, but defined in profile.c. */ 11750397Sobrien rtx branch_insn; 11850397Sobrien#endif 11950397Sobrien struct adj_list *pred_next; 12050397Sobrien struct adj_list *succ_next; 12150397Sobrien}; 12250397Sobrien 12350397Sobrien/* Count the number of basic blocks, and create an array of these structures, 12450397Sobrien one for each bb in the function. */ 12550397Sobrien 126117395Skanstruct bb_info 127117395Skan{ 12850397Sobrien struct adj_list *succ; 12950397Sobrien struct adj_list *pred; 13090075Sobrien gcov_type succ_count; 13190075Sobrien gcov_type pred_count; 13290075Sobrien gcov_type exec_count; 13350397Sobrien unsigned int count_valid : 1; 13450397Sobrien unsigned int on_tree : 1; 13550397Sobrien#if 0 13650397Sobrien /* Not needed for gcov, but defined in profile.c. */ 13750397Sobrien rtx first_insn; 13850397Sobrien#endif 13950397Sobrien}; 14050397Sobrien 14150397Sobrien/* When outputting branch probabilities, one of these structures is created 14250397Sobrien for each branch/call. */ 14350397Sobrien 14450397Sobrienstruct arcdata 14550397Sobrien{ 14690075Sobrien gcov_type hits; 14790075Sobrien gcov_type total; 14850397Sobrien int call_insn; 14950397Sobrien struct arcdata *next; 15050397Sobrien}; 15150397Sobrien 15250397Sobrien/* Used to save the list of bb_graphs, one per function. */ 15350397Sobrien 154117395Skanstruct bb_info_list 155117395Skan{ 15650397Sobrien /* Indexed by block number, holds the basic block graph for one function. */ 15750397Sobrien struct bb_info *bb_graph; 15850397Sobrien int num_blocks; 15950397Sobrien struct bb_info_list *next; 16050397Sobrien}; 16150397Sobrien 162117395Skan/* Used to hold information about each line. */ 163117395Skanstruct line_info 164117395Skan{ 165117395Skan gcov_type count; /* execution count */ 166117395Skan struct arcdata *branches; /* list of branch probabilities for line. */ 167117395Skan unsigned exists : 1; /* has code associated with it. */ 168117395Skan}; 169117395Skan 170117395Skanstruct coverage 171117395Skan{ 172117395Skan int lines; 173117395Skan int lines_executed; 174117395Skan 175117395Skan int branches; 176117395Skan int branches_executed; 177117395Skan int branches_taken; 178117395Skan 179117395Skan int calls; 180117395Skan int calls_executed; 181117395Skan 182117395Skan char *name; 183117395Skan}; 184117395Skan 18550397Sobrien/* Holds a list of function basic block graphs. */ 18650397Sobrien 18750397Sobrienstatic struct bb_info_list *bb_graph_list = 0; 18850397Sobrien 189117395Skan/* Modification time of data files. */ 190117395Skan 191117395Skanstatic time_t bb_file_time; 192117395Skan 19350397Sobrien/* Name and file pointer of the input file for the basic block graph. */ 19450397Sobrien 19550397Sobrienstatic char *bbg_file_name; 19650397Sobrienstatic FILE *bbg_file; 19750397Sobrien 19850397Sobrien/* Name and file pointer of the input file for the arc count data. */ 19950397Sobrien 20050397Sobrienstatic char *da_file_name; 20150397Sobrienstatic FILE *da_file; 20250397Sobrien 20350397Sobrien/* Name and file pointer of the input file for the basic block line counts. */ 20450397Sobrien 20550397Sobrienstatic char *bb_file_name; 20650397Sobrienstatic FILE *bb_file; 20750397Sobrien 20850397Sobrien/* Holds the entire contents of the bb_file read into memory. */ 20950397Sobrien 21050397Sobrienstatic char *bb_data; 21150397Sobrien 21250397Sobrien/* Size of bb_data array in longs. */ 21350397Sobrien 21450397Sobrienstatic long bb_data_size; 21550397Sobrien 21650397Sobrien/* Name of the file mentioned on the command line. */ 21750397Sobrien 21850397Sobrienstatic char *input_file_name = 0; 21950397Sobrien 22050397Sobrien/* Output branch probabilities if true. */ 22150397Sobrien 22250397Sobrienstatic int output_branch_probs = 0; 22350397Sobrien 22450397Sobrien/* Output a gcov file if this is true. This is on by default, and can 22550397Sobrien be turned off by the -n option. */ 22650397Sobrien 22750397Sobrienstatic int output_gcov_file = 1; 22850397Sobrien 22950397Sobrien/* For included files, make the gcov output file name include the name of 23050397Sobrien the input source file. For example, if x.h is included in a.c, then the 23150397Sobrien output file name is a.c.x.h.gcov instead of x.h.gcov. This works only 23250397Sobrien when a single source file is specified. */ 23350397Sobrien 23450397Sobrienstatic int output_long_names = 0; 23550397Sobrien 23650397Sobrien/* Output summary info for each function. */ 23750397Sobrien 23850397Sobrienstatic int output_function_summary = 0; 23950397Sobrien 240117395Skan/* Object directory file prefix. This is the directory/file 241117395Skan where .bb and .bbg files are looked for, if nonzero. */ 24250397Sobrien 24350397Sobrienstatic char *object_directory = 0; 24450397Sobrien 245117395Skan/* Preserve all pathname components. Needed when object files and 246117395Skan source files are in subdirectories. */ 247117395Skanstatic int preserve_paths = 0; 248117395Skan 24990075Sobrien/* Output the number of times a branch was taken as opposed to the percentage 25090075Sobrien of times it was taken. Turned on by the -c option */ 25190075Sobrien 25290075Sobrienstatic int output_branch_counts = 0; 25390075Sobrien 25450397Sobrien/* Forward declarations. */ 25590075Sobrienstatic void process_args PARAMS ((int, char **)); 25690075Sobrienstatic void open_files PARAMS ((void)); 25790075Sobrienstatic void read_files PARAMS ((void)); 25890075Sobrienstatic void scan_for_source_files PARAMS ((void)); 259117395Skanstatic void output_data PARAMS ((struct sourcefile *)); 26090075Sobrienstatic void print_usage PARAMS ((int)) ATTRIBUTE_NORETURN; 26190075Sobrienstatic void print_version PARAMS ((void)) ATTRIBUTE_NORETURN; 26290075Sobrienstatic void init_arc PARAMS ((struct adj_list *, int, int, struct bb_info *)); 26390075Sobrienstatic struct adj_list *reverse_arcs PARAMS ((struct adj_list *)); 264117395Skanstatic gcov_type *read_profile PARAMS ((char *, long, int)); 26590075Sobrienstatic void create_program_flow_graph PARAMS ((struct bb_info_list *)); 26690075Sobrienstatic void solve_program_flow_graph PARAMS ((struct bb_info_list *)); 267117395Skanstatic void accumulate_branch_counts PARAMS ((struct coverage *, 268117395Skan struct arcdata *)); 269117395Skanstatic void calculate_branch_probs PARAMS ((struct bb_info *, 270117395Skan struct line_info *, 271117395Skan struct coverage *)); 272117395Skanstatic void function_summary PARAMS ((struct coverage *, const char *)); 273117395Skanstatic void init_line_info PARAMS ((struct line_info *, 274117395Skan struct coverage *, long)); 275117395Skanstatic void output_line_info PARAMS ((FILE *, const struct line_info *, 276117395Skan const struct coverage *, long)); 277117395Skanstatic char *make_gcov_file_name PARAMS ((char *)); 278117395Skanstatic const char *format_hwint PARAMS ((HOST_WIDEST_INT, HOST_WIDEST_INT, 279117395Skan int)); 28050397Sobrien 28190075Sobrienextern int main PARAMS ((int, char **)); 28290075Sobrien 28350397Sobrienint 28450397Sobrienmain (argc, argv) 28550397Sobrien int argc; 28650397Sobrien char **argv; 28750397Sobrien{ 288117395Skan struct sourcefile *s_ptr; 289117395Skan 29090075Sobrien gcc_init_libintl (); 29152284Sobrien 29250397Sobrien process_args (argc, argv); 29350397Sobrien 29450397Sobrien open_files (); 29550397Sobrien 29650397Sobrien read_files (); 29750397Sobrien 29850397Sobrien scan_for_source_files (); 29950397Sobrien 300117395Skan for (s_ptr = sources; s_ptr; s_ptr = s_ptr->next) 301117395Skan output_data (s_ptr); 30250397Sobrien 30350397Sobrien return 0; 30450397Sobrien} 30550397Sobrien 30690075Sobrienstatic void fnotice PARAMS ((FILE *, const char *, ...)) ATTRIBUTE_PRINTF_2; 30752284Sobrienstatic void 30890075Sobrienfnotice VPARAMS ((FILE *file, const char *msgid, ...)) 30952284Sobrien{ 31090075Sobrien VA_OPEN (ap, msgid); 31190075Sobrien VA_FIXEDARG (ap, FILE *, file); 31290075Sobrien VA_FIXEDARG (ap, const char *, msgid); 31352284Sobrien 31452284Sobrien vfprintf (file, _(msgid), ap); 31590075Sobrien VA_CLOSE (ap); 31652284Sobrien} 31752284Sobrien 31850397Sobrien/* More 'friendly' abort that prints the line and file. 31950397Sobrien config.h can #define abort fancy_abort if you like that sort of thing. */ 32090075Sobrienextern void fancy_abort PARAMS ((void)) ATTRIBUTE_NORETURN; 32150397Sobrien 32250397Sobrienvoid 32350397Sobrienfancy_abort () 32450397Sobrien{ 32590075Sobrien fnotice (stderr, "Internal gcov abort.\n"); 32650397Sobrien exit (FATAL_EXIT_CODE); 32750397Sobrien} 32850397Sobrien 32990075Sobrien/* Print a usage message and exit. If ERROR_P is nonzero, this is an error, 33090075Sobrien otherwise the output of --help. */ 33150397Sobrien 33250397Sobrienstatic void 33390075Sobrienprint_usage (error_p) 33490075Sobrien int error_p; 33550397Sobrien{ 33690075Sobrien FILE *file = error_p ? stderr : stdout; 33790075Sobrien int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE; 33890075Sobrien fnotice (file, "Usage: gcov [OPTION]... SOURCEFILE\n\n"); 33990075Sobrien fnotice (file, "Print code coverage information.\n\n"); 34090075Sobrien fnotice (file, " -h, --help Print this help, then exit\n"); 34190075Sobrien fnotice (file, " -v, --version Print version number, then exit\n"); 34290075Sobrien fnotice (file, " -b, --branch-probabilities Include branch probabilities in output\n"); 34390075Sobrien fnotice (file, " -c, --branch-counts Given counts of branches taken\n\ 34490075Sobrien rather than percentages\n"); 34590075Sobrien fnotice (file, " -n, --no-output Do not create an output file\n"); 34690075Sobrien fnotice (file, " -l, --long-file-names Use long output file names for included\n\ 34790075Sobrien source files\n"); 34890075Sobrien fnotice (file, " -f, --function-summaries Output summaries for each function\n"); 349117395Skan fnotice (file, " -o, --object-directory DIR|FILE Search for object files in DIR or called FILE\n"); 350117395Skan fnotice (file, " -p, --preserve-paths Preserve all pathname components\n"); 35190075Sobrien fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n", 352117395Skan bug_report_url); 35390075Sobrien exit (status); 35450397Sobrien} 35550397Sobrien 35690075Sobrien/* Print version information and exit. */ 35790075Sobrien 35890075Sobrienstatic void 35990075Sobrienprint_version () 36090075Sobrien{ 36190075Sobrien fnotice (stdout, "gcov (GCC) %s\n", version_string); 36290075Sobrien fnotice (stdout, "Copyright (C) 2001 Free Software Foundation, Inc.\n"); 36390075Sobrien fnotice (stdout, 36490075Sobrien "This is free software; see the source for copying conditions. There is NO\n\ 36590075Sobrienwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"); 36690075Sobrien exit (SUCCESS_EXIT_CODE); 36790075Sobrien} 36890075Sobrien 36990075Sobrienstatic const struct option options[] = 37090075Sobrien{ 37190075Sobrien { "help", no_argument, NULL, 'h' }, 37290075Sobrien { "version", no_argument, NULL, 'v' }, 37390075Sobrien { "branch-probabilities", no_argument, NULL, 'b' }, 37490075Sobrien { "branch-counts", no_argument, NULL, 'c' }, 37590075Sobrien { "no-output", no_argument, NULL, 'n' }, 37690075Sobrien { "long-file-names", no_argument, NULL, 'l' }, 37790075Sobrien { "function-summaries", no_argument, NULL, 'f' }, 378117395Skan { "preserve-paths", no_argument, NULL, 'p' }, 379117395Skan { "object-directory", required_argument, NULL, 'o' }, 380117395Skan { "object-file", required_argument, NULL, 'o' }, 38190075Sobrien}; 38290075Sobrien 38350397Sobrien/* Parse the command line. */ 38450397Sobrien 38550397Sobrienstatic void 38650397Sobrienprocess_args (argc, argv) 38750397Sobrien int argc; 38850397Sobrien char **argv; 38950397Sobrien{ 39090075Sobrien int opt; 39150397Sobrien 392117395Skan while ((opt = getopt_long (argc, argv, "hvbclnfo:p", options, NULL)) != -1) 39350397Sobrien { 39490075Sobrien switch (opt) 39550397Sobrien { 39690075Sobrien case 'h': 39790075Sobrien print_usage (false); 39890075Sobrien /* print_usage will exit. */ 39990075Sobrien case 'v': 40090075Sobrien print_version (); 40190075Sobrien /* print_version will exit. */ 40290075Sobrien case 'b': 40390075Sobrien output_branch_probs = 1; 40490075Sobrien break; 40590075Sobrien case 'c': 40690075Sobrien output_branch_counts = 1; 40790075Sobrien break; 40890075Sobrien case 'n': 40990075Sobrien output_gcov_file = 0; 41090075Sobrien break; 41190075Sobrien case 'l': 41290075Sobrien output_long_names = 1; 41390075Sobrien break; 41490075Sobrien case 'f': 41590075Sobrien output_function_summary = 1; 41690075Sobrien break; 41790075Sobrien case 'o': 41890075Sobrien object_directory = optarg; 41990075Sobrien break; 420117395Skan case 'p': 421117395Skan preserve_paths = 1; 422117395Skan break; 42390075Sobrien default: 42490075Sobrien print_usage (true); 42590075Sobrien /* print_usage will exit. */ 42650397Sobrien } 42750397Sobrien } 42850397Sobrien 42990075Sobrien if (optind != argc - 1) 43090075Sobrien print_usage (true); 43190075Sobrien 43290075Sobrien input_file_name = argv[optind]; 43350397Sobrien} 43450397Sobrien 43550397Sobrien 436117395Skan/* Find and open the .bb, .da, and .bbg files. If OBJECT_DIRECTORY is 437117395Skan not specified, these are looked for in the current directory, and 438117395Skan named from the basename of the input_file_name sans extension. If 439117395Skan OBJECT_DIRECTORY is specified and is a directory, the files are in 440117395Skan that directory, but named from the basename of the input_file_name, 441117395Skan sans extension. Otherwise OBJECT_DIRECTORY is taken to be the name 442117395Skan of the object *file*, and the data files are named from that. */ 44350397Sobrien 44450397Sobrienstatic void 44550397Sobrienopen_files () 44650397Sobrien{ 44750397Sobrien char *cptr; 448117395Skan char *name; 449117395Skan int length = strlen (input_file_name); 450117395Skan int base; 451117395Skan 452117395Skan if (object_directory && object_directory[0]) 45350397Sobrien { 454117395Skan struct stat status; 45550397Sobrien 456117395Skan length += strlen (object_directory) + 2; 457117395Skan name = xmalloc (length); 458117395Skan name[0] = 0; 459117395Skan 460117395Skan base = !stat (object_directory, &status) && S_ISDIR (status.st_mode); 461117395Skan strcat (name, object_directory); 462117395Skan if (base && name[strlen (name) - 1] != '/') 463117395Skan strcat (name, "/"); 46450397Sobrien } 46550397Sobrien else 46650397Sobrien { 467117395Skan name = xmalloc (length + 1); 468117395Skan name[0] = 0; 469117395Skan base = 1; 47050397Sobrien } 471117395Skan 472117395Skan if (base) 473117395Skan { 474117395Skan /* Append source file name */ 475117395Skan cptr = strrchr (input_file_name, '/'); 476117395Skan cptr = cptr ? cptr + 1 : input_file_name; 47750397Sobrien 478117395Skan strcat (name, cptr); 479117395Skan } 480117395Skan /* Remove the extension. */ 481117395Skan cptr = strrchr (name, '.'); 48250397Sobrien if (cptr) 483117395Skan *cptr = 0; 484117395Skan 485117395Skan length = strlen (name); 486117395Skan da_file_name = xmalloc (length + 4); 487117395Skan bb_file_name = xmalloc (length + 4); 488117395Skan bbg_file_name = xmalloc (length + 5); 48950397Sobrien 490117395Skan strcpy (da_file_name, name); 491117395Skan strcpy (bb_file_name, name); 492117395Skan strcpy (bbg_file_name, name); 493117395Skan strcpy (da_file_name + length, ".da"); 494117395Skan strcpy (bb_file_name + length, ".bb"); 495117395Skan strcpy (bbg_file_name + length, ".bbg"); 49650397Sobrien 49790075Sobrien bb_file = fopen (bb_file_name, "rb"); 49850397Sobrien if (bb_file == NULL) 49950397Sobrien { 50052284Sobrien fnotice (stderr, "Could not open basic block file %s.\n", bb_file_name); 50150397Sobrien exit (FATAL_EXIT_CODE); 50250397Sobrien } 50350397Sobrien 504117395Skan bbg_file = fopen (bbg_file_name, "rb"); 505117395Skan if (bbg_file == NULL) 506117395Skan { 507117395Skan fnotice (stderr, "Could not open program flow graph file %s.\n", 508117395Skan bbg_file_name); 509117395Skan exit (FATAL_EXIT_CODE); 510117395Skan } 511117395Skan 512117395Skan { 513117395Skan struct stat status; 514117395Skan 515117395Skan if (!fstat (fileno (bb_file), &status)) 516117395Skan bb_file_time = status.st_mtime; 517117395Skan } 518117395Skan 51950397Sobrien /* If none of the functions in the file were executed, then there won't 52050397Sobrien be a .da file. Just assume that all counts are zero in this case. */ 52190075Sobrien da_file = fopen (da_file_name, "rb"); 52250397Sobrien if (da_file == NULL) 52350397Sobrien { 52452284Sobrien fnotice (stderr, "Could not open data file %s.\n", da_file_name); 52552284Sobrien fnotice (stderr, "Assuming that all execution counts are zero.\n"); 52650397Sobrien } 52790075Sobrien 52850397Sobrien /* Check for empty .bbg file. This indicates that there is no executable 52950397Sobrien code in this source file. */ 53050397Sobrien /* Set the EOF condition if at the end of file. */ 53150397Sobrien ungetc (getc (bbg_file), bbg_file); 53250397Sobrien if (feof (bbg_file)) 53350397Sobrien { 53452284Sobrien fnotice (stderr, "No executable code associated with file %s.\n", 53550397Sobrien input_file_name); 53650397Sobrien exit (FATAL_EXIT_CODE); 53750397Sobrien } 53850397Sobrien} 53950397Sobrien 54050397Sobrien/* Initialize a new arc. */ 54150397Sobrien 54250397Sobrienstatic void 54350397Sobrieninit_arc (arcptr, source, target, bb_graph) 54450397Sobrien struct adj_list *arcptr; 54550397Sobrien int source, target; 54650397Sobrien struct bb_info *bb_graph; 54750397Sobrien{ 54850397Sobrien arcptr->target = target; 54950397Sobrien arcptr->source = source; 55050397Sobrien 55150397Sobrien arcptr->arc_count = 0; 55250397Sobrien arcptr->count_valid = 0; 55350397Sobrien arcptr->on_tree = 0; 55450397Sobrien arcptr->fake = 0; 55550397Sobrien arcptr->fall_through = 0; 55650397Sobrien 55750397Sobrien arcptr->succ_next = bb_graph[source].succ; 55850397Sobrien bb_graph[source].succ = arcptr; 55950397Sobrien bb_graph[source].succ_count++; 56050397Sobrien 56150397Sobrien arcptr->pred_next = bb_graph[target].pred; 56250397Sobrien bb_graph[target].pred = arcptr; 56350397Sobrien bb_graph[target].pred_count++; 56450397Sobrien} 56550397Sobrien 56690075Sobrien/* Reverse the arcs on an arc list. */ 56750397Sobrien 56850397Sobrienstatic struct adj_list * 56950397Sobrienreverse_arcs (arcptr) 57050397Sobrien struct adj_list *arcptr; 57150397Sobrien{ 57250397Sobrien struct adj_list *prev = 0; 57350397Sobrien struct adj_list *next; 57450397Sobrien 57550397Sobrien for ( ; arcptr; arcptr = next) 57650397Sobrien { 57750397Sobrien next = arcptr->succ_next; 57850397Sobrien arcptr->succ_next = prev; 57950397Sobrien prev = arcptr; 58050397Sobrien } 58150397Sobrien 58250397Sobrien return prev; 58350397Sobrien} 58450397Sobrien 585117395Skan/* Reads profiles from the .da file and compute a hybrid profile. */ 58650397Sobrien 587117395Skanstatic gcov_type * 588117395Skanread_profile (function_name, cfg_checksum, instr_arcs) 589117395Skan char *function_name; 590117395Skan long cfg_checksum; 591117395Skan int instr_arcs; 592117395Skan{ 593117395Skan int i; 594117395Skan int okay = 1; 595117395Skan gcov_type *profile; 596117395Skan char *function_name_buffer; 597117395Skan int function_name_buffer_len; 598117395Skan 599117395Skan profile = xmalloc (sizeof (gcov_type) * instr_arcs); 600117395Skan function_name_buffer_len = strlen (function_name) + 1; 601117395Skan function_name_buffer = xmalloc (function_name_buffer_len + 1); 602117395Skan 603117395Skan for (i = 0; i < instr_arcs; i++) 604117395Skan profile[i] = 0; 605117395Skan 606117395Skan if (!da_file) 607117395Skan return profile; 608117395Skan 609117395Skan rewind (da_file); 610117395Skan while (1) 611117395Skan { 612117395Skan long magic, extra_bytes; 613117395Skan long func_count; 614117395Skan int i; 615117395Skan 616117395Skan if (__read_long (&magic, da_file, 4) != 0) 617117395Skan break; 618117395Skan 619117395Skan if (magic != -123) 620117395Skan { 621117395Skan okay = 0; 622117395Skan break; 623117395Skan } 624117395Skan 625117395Skan if (__read_long (&func_count, da_file, 4) != 0) 626117395Skan { 627117395Skan okay = 0; 628117395Skan break; 629117395Skan } 630117395Skan 631117395Skan if (__read_long (&extra_bytes, da_file, 4) != 0) 632117395Skan { 633117395Skan okay = 0; 634117395Skan break; 635117395Skan } 636117395Skan 637117395Skan /* skip extra data emited by __bb_exit_func. */ 638117395Skan fseek (da_file, extra_bytes, SEEK_CUR); 639117395Skan 640117395Skan for (i = 0; i < func_count; i++) 641117395Skan { 642117395Skan long arc_count; 643117395Skan long chksum; 644117395Skan int j; 645117395Skan 646117395Skan if (__read_gcov_string 647117395Skan (function_name_buffer, function_name_buffer_len, da_file, 648117395Skan -1) != 0) 649117395Skan { 650117395Skan okay = 0; 651117395Skan break; 652117395Skan } 653117395Skan 654117395Skan if (__read_long (&chksum, da_file, 4) != 0) 655117395Skan { 656117395Skan okay = 0; 657117395Skan break; 658117395Skan } 659117395Skan 660117395Skan if (__read_long (&arc_count, da_file, 4) != 0) 661117395Skan { 662117395Skan okay = 0; 663117395Skan break; 664117395Skan } 665117395Skan 666117395Skan if (strcmp (function_name_buffer, function_name) != 0 667117395Skan || arc_count != instr_arcs || chksum != cfg_checksum) 668117395Skan { 669117395Skan /* skip */ 670117395Skan if (fseek (da_file, arc_count * 8, SEEK_CUR) < 0) 671117395Skan { 672117395Skan okay = 0; 673117395Skan break; 674117395Skan } 675117395Skan } 676117395Skan else 677117395Skan { 678117395Skan gcov_type tmp; 679117395Skan 680117395Skan for (j = 0; j < arc_count; j++) 681117395Skan if (__read_gcov_type (&tmp, da_file, 8) != 0) 682117395Skan { 683117395Skan okay = 0; 684117395Skan break; 685117395Skan } 686117395Skan else 687117395Skan { 688117395Skan profile[j] += tmp; 689117395Skan } 690117395Skan } 691117395Skan } 692117395Skan 693117395Skan if (!okay) 694117395Skan break; 695117395Skan 696117395Skan } 697117395Skan 698117395Skan free (function_name_buffer); 699117395Skan 700117395Skan if (!okay) 701117395Skan { 702117395Skan fprintf (stderr, ".da file corrupted!\n"); 703117395Skan free (profile); 704117395Skan abort (); 705117395Skan } 706117395Skan 707117395Skan return profile; 708117395Skan} 709117395Skan 71050397Sobrien/* Construct the program flow graph from the .bbg file, and read in the data 71150397Sobrien in the .da file. */ 71250397Sobrien 71350397Sobrienstatic void 71450397Sobriencreate_program_flow_graph (bptr) 71550397Sobrien struct bb_info_list *bptr; 71650397Sobrien{ 71750397Sobrien long num_blocks, number_arcs, src, dest, flag_bits, num_arcs_per_block; 71850397Sobrien int i; 71950397Sobrien struct adj_list *arcptr; 72050397Sobrien struct bb_info *bb_graph; 721117395Skan long cfg_checksum; 722117395Skan long instr_arcs = 0; 723117395Skan gcov_type *profile; 724117395Skan int profile_pos = 0; 725117395Skan char *function_name; 726117395Skan long function_name_len, tmp; 72750397Sobrien 728117395Skan /* Read function name. */ 729117395Skan __read_long (&tmp, bbg_file, 4); /* ignore -1. */ 730117395Skan __read_long (&function_name_len, bbg_file, 4); 731117395Skan function_name = xmalloc (function_name_len + 1); 732117395Skan fread (function_name, 1, function_name_len + 1, bbg_file); 733117395Skan 734117395Skan /* Skip padding. */ 735117395Skan tmp = (function_name_len + 1) % 4; 736117395Skan 737117395Skan if (tmp) 738117395Skan fseek (bbg_file, 4 - tmp, SEEK_CUR); 739117395Skan 740117395Skan __read_long (&tmp, bbg_file, 4); /* ignore -1. */ 741117395Skan 742117395Skan /* Read the cfg checksum. */ 743117395Skan __read_long (&cfg_checksum, bbg_file, 4); 744117395Skan 74550397Sobrien /* Read the number of blocks. */ 74650397Sobrien __read_long (&num_blocks, bbg_file, 4); 74750397Sobrien 74890075Sobrien /* Create an array of size bb number of bb_info structs. */ 74990075Sobrien bb_graph = (struct bb_info *) xcalloc (num_blocks, sizeof (struct bb_info)); 75050397Sobrien 75150397Sobrien bptr->bb_graph = bb_graph; 75250397Sobrien bptr->num_blocks = num_blocks; 75350397Sobrien 75450397Sobrien /* Read and create each arc from the .bbg file. */ 75550397Sobrien __read_long (&number_arcs, bbg_file, 4); 75650397Sobrien for (i = 0; i < num_blocks; i++) 75750397Sobrien { 75850397Sobrien int j; 75950397Sobrien 76050397Sobrien __read_long (&num_arcs_per_block, bbg_file, 4); 76150397Sobrien for (j = 0; j < num_arcs_per_block; j++) 76250397Sobrien { 76350397Sobrien if (number_arcs-- < 0) 76450397Sobrien abort (); 76550397Sobrien 76650397Sobrien src = i; 76750397Sobrien __read_long (&dest, bbg_file, 4); 76850397Sobrien 76950397Sobrien arcptr = (struct adj_list *) xmalloc (sizeof (struct adj_list)); 77050397Sobrien init_arc (arcptr, src, dest, bb_graph); 77150397Sobrien 77250397Sobrien __read_long (&flag_bits, bbg_file, 4); 773117395Skan if (flag_bits & 0x1) 774117395Skan arcptr->on_tree++; 775117395Skan else 776117395Skan instr_arcs++; 77750397Sobrien arcptr->fake = !! (flag_bits & 0x2); 77850397Sobrien arcptr->fall_through = !! (flag_bits & 0x4); 77950397Sobrien } 78050397Sobrien } 78150397Sobrien 78250397Sobrien if (number_arcs) 78350397Sobrien abort (); 78450397Sobrien 78550397Sobrien /* Read and ignore the -1 separating the arc list from the arc list of the 78650397Sobrien next function. */ 78750397Sobrien __read_long (&src, bbg_file, 4); 78850397Sobrien if (src != -1) 78950397Sobrien abort (); 79050397Sobrien 79150397Sobrien /* Must reverse the order of all succ arcs, to ensure that they match 79250397Sobrien the order of the data in the .da file. */ 79350397Sobrien 79450397Sobrien for (i = 0; i < num_blocks; i++) 79550397Sobrien if (bb_graph[i].succ) 79650397Sobrien bb_graph[i].succ = reverse_arcs (bb_graph[i].succ); 79750397Sobrien 798117395Skan /* Read profile from the .da file. */ 799117395Skan 800117395Skan profile = read_profile (function_name, cfg_checksum, instr_arcs); 801117395Skan 80250397Sobrien /* For each arc not on the spanning tree, set its execution count from 80350397Sobrien the .da file. */ 80450397Sobrien 80550397Sobrien /* The first count in the .da file is the number of times that the function 80650397Sobrien was entered. This is the exec_count for block zero. */ 80750397Sobrien 80850397Sobrien /* This duplicates code in branch_prob in profile.c. */ 80950397Sobrien 81050397Sobrien for (i = 0; i < num_blocks; i++) 81150397Sobrien for (arcptr = bb_graph[i].succ; arcptr; arcptr = arcptr->succ_next) 81250397Sobrien if (! arcptr->on_tree) 81350397Sobrien { 814117395Skan arcptr->arc_count = profile[profile_pos++]; 81550397Sobrien arcptr->count_valid = 1; 81650397Sobrien bb_graph[i].succ_count--; 81750397Sobrien bb_graph[arcptr->target].pred_count--; 81850397Sobrien } 819117395Skan free (profile); 820117395Skan free (function_name); 82150397Sobrien} 82290075Sobrien 82350397Sobrienstatic void 82450397Sobriensolve_program_flow_graph (bptr) 82550397Sobrien struct bb_info_list *bptr; 82650397Sobrien{ 82790075Sobrien int passes, changes; 82890075Sobrien gcov_type total; 82950397Sobrien int i; 83050397Sobrien struct adj_list *arcptr; 83150397Sobrien struct bb_info *bb_graph; 83250397Sobrien int num_blocks; 83350397Sobrien 83450397Sobrien num_blocks = bptr->num_blocks; 83550397Sobrien bb_graph = bptr->bb_graph; 83650397Sobrien 83750397Sobrien /* For every block in the file, 83850397Sobrien - if every exit/entrance arc has a known count, then set the block count 83950397Sobrien - if the block count is known, and every exit/entrance arc but one has 84050397Sobrien a known execution count, then set the count of the remaining arc 84150397Sobrien 84250397Sobrien As arc counts are set, decrement the succ/pred count, but don't delete 84350397Sobrien the arc, that way we can easily tell when all arcs are known, or only 84450397Sobrien one arc is unknown. */ 84550397Sobrien 84650397Sobrien /* The order that the basic blocks are iterated through is important. 84750397Sobrien Since the code that finds spanning trees starts with block 0, low numbered 84850397Sobrien arcs are put on the spanning tree in preference to high numbered arcs. 84950397Sobrien Hence, most instrumented arcs are at the end. Graph solving works much 85050397Sobrien faster if we propagate numbers from the end to the start. 85150397Sobrien 85250397Sobrien This takes an average of slightly more than 3 passes. */ 85350397Sobrien 85450397Sobrien changes = 1; 85550397Sobrien passes = 0; 85650397Sobrien while (changes) 85750397Sobrien { 85850397Sobrien passes++; 85950397Sobrien changes = 0; 86050397Sobrien 86150397Sobrien for (i = num_blocks - 1; i >= 0; i--) 86250397Sobrien { 86350397Sobrien if (! bb_graph[i].count_valid) 86450397Sobrien { 86550397Sobrien if (bb_graph[i].succ_count == 0) 86650397Sobrien { 86750397Sobrien total = 0; 86850397Sobrien for (arcptr = bb_graph[i].succ; arcptr; 86950397Sobrien arcptr = arcptr->succ_next) 87050397Sobrien total += arcptr->arc_count; 87150397Sobrien bb_graph[i].exec_count = total; 87250397Sobrien bb_graph[i].count_valid = 1; 87350397Sobrien changes = 1; 87450397Sobrien } 87550397Sobrien else if (bb_graph[i].pred_count == 0) 87650397Sobrien { 87750397Sobrien total = 0; 87850397Sobrien for (arcptr = bb_graph[i].pred; arcptr; 87950397Sobrien arcptr = arcptr->pred_next) 88050397Sobrien total += arcptr->arc_count; 88150397Sobrien bb_graph[i].exec_count = total; 88250397Sobrien bb_graph[i].count_valid = 1; 88350397Sobrien changes = 1; 88450397Sobrien } 88550397Sobrien } 88650397Sobrien if (bb_graph[i].count_valid) 88750397Sobrien { 88850397Sobrien if (bb_graph[i].succ_count == 1) 88950397Sobrien { 89050397Sobrien total = 0; 89150397Sobrien /* One of the counts will be invalid, but it is zero, 89250397Sobrien so adding it in also doesn't hurt. */ 89350397Sobrien for (arcptr = bb_graph[i].succ; arcptr; 89450397Sobrien arcptr = arcptr->succ_next) 89550397Sobrien total += arcptr->arc_count; 89650397Sobrien /* Calculate count for remaining arc by conservation. */ 89750397Sobrien total = bb_graph[i].exec_count - total; 89850397Sobrien /* Search for the invalid arc, and set its count. */ 89950397Sobrien for (arcptr = bb_graph[i].succ; arcptr; 90050397Sobrien arcptr = arcptr->succ_next) 90150397Sobrien if (! arcptr->count_valid) 90250397Sobrien break; 90350397Sobrien if (! arcptr) 90450397Sobrien abort (); 90550397Sobrien arcptr->count_valid = 1; 90650397Sobrien arcptr->arc_count = total; 90750397Sobrien bb_graph[i].succ_count--; 90850397Sobrien 90950397Sobrien bb_graph[arcptr->target].pred_count--; 91050397Sobrien changes = 1; 91150397Sobrien } 91250397Sobrien if (bb_graph[i].pred_count == 1) 91350397Sobrien { 91450397Sobrien total = 0; 91550397Sobrien /* One of the counts will be invalid, but it is zero, 91650397Sobrien so adding it in also doesn't hurt. */ 91750397Sobrien for (arcptr = bb_graph[i].pred; arcptr; 91850397Sobrien arcptr = arcptr->pred_next) 91950397Sobrien total += arcptr->arc_count; 92050397Sobrien /* Calculate count for remaining arc by conservation. */ 92150397Sobrien total = bb_graph[i].exec_count - total; 92250397Sobrien /* Search for the invalid arc, and set its count. */ 92350397Sobrien for (arcptr = bb_graph[i].pred; arcptr; 92450397Sobrien arcptr = arcptr->pred_next) 92550397Sobrien if (! arcptr->count_valid) 92650397Sobrien break; 92750397Sobrien if (! arcptr) 92850397Sobrien abort (); 92950397Sobrien arcptr->count_valid = 1; 93050397Sobrien arcptr->arc_count = total; 93150397Sobrien bb_graph[i].pred_count--; 93250397Sobrien 93350397Sobrien bb_graph[arcptr->source].succ_count--; 93450397Sobrien changes = 1; 93550397Sobrien } 93650397Sobrien } 93750397Sobrien } 93850397Sobrien } 93990075Sobrien 94050397Sobrien /* If the graph has been correctly solved, every block will have a 94150397Sobrien succ and pred count of zero. */ 94250397Sobrien for (i = 0; i < num_blocks; i++) 94350397Sobrien if (bb_graph[i].succ_count || bb_graph[i].pred_count) 94450397Sobrien abort (); 94550397Sobrien} 94650397Sobrien 94750397Sobrien 94850397Sobrienstatic void 94950397Sobrienread_files () 95050397Sobrien{ 95150397Sobrien struct stat buf; 95250397Sobrien struct bb_info_list *list_end = 0; 95350397Sobrien struct bb_info_list *b_ptr; 95450397Sobrien 95550397Sobrien while (! feof (bbg_file)) 95650397Sobrien { 95750397Sobrien b_ptr = (struct bb_info_list *) xmalloc (sizeof (struct bb_info_list)); 95850397Sobrien 95950397Sobrien b_ptr->next = 0; 96050397Sobrien if (list_end) 96150397Sobrien list_end->next = b_ptr; 96250397Sobrien else 96350397Sobrien bb_graph_list = b_ptr; 96450397Sobrien list_end = b_ptr; 96550397Sobrien 96650397Sobrien /* Read in the data in the .bbg file and reconstruct the program flow 96750397Sobrien graph for one function. */ 96850397Sobrien create_program_flow_graph (b_ptr); 96950397Sobrien 97050397Sobrien /* Set the EOF condition if at the end of file. */ 97150397Sobrien ungetc (getc (bbg_file), bbg_file); 97250397Sobrien } 97350397Sobrien 97450397Sobrien /* Calculate all of the basic block execution counts and branch 97550397Sobrien taken probabilities. */ 97650397Sobrien 97750397Sobrien for (b_ptr = bb_graph_list; b_ptr; b_ptr = b_ptr->next) 97850397Sobrien solve_program_flow_graph (b_ptr); 97950397Sobrien 98050397Sobrien /* Read in all of the data from the .bb file. This info will be accessed 98150397Sobrien sequentially twice. */ 98250397Sobrien stat (bb_file_name, &buf); 98350397Sobrien bb_data_size = buf.st_size / 4; 98450397Sobrien 98550397Sobrien bb_data = (char *) xmalloc ((unsigned) buf.st_size); 98650397Sobrien fread (bb_data, sizeof (char), buf.st_size, bb_file); 98790075Sobrien 98850397Sobrien fclose (bb_file); 98950397Sobrien if (da_file) 99050397Sobrien fclose (da_file); 99150397Sobrien fclose (bbg_file); 99250397Sobrien} 99350397Sobrien 99450397Sobrien 99550397Sobrien/* Scan the data in the .bb file to find all source files referenced, 99650397Sobrien and the largest line number mentioned in each one. */ 99750397Sobrien 99850397Sobrienstatic void 99950397Sobrienscan_for_source_files () 100050397Sobrien{ 100150397Sobrien struct sourcefile *s_ptr = NULL; 100250397Sobrien char *ptr; 100390075Sobrien long count; 100450397Sobrien long line_num; 100550397Sobrien 100650397Sobrien /* Search the bb_data to find: 100750397Sobrien 1) The number of sources files contained herein, and 100850397Sobrien 2) The largest line number for each source file. */ 100950397Sobrien 101050397Sobrien ptr = bb_data; 101150397Sobrien sources = 0; 101250397Sobrien for (count = 0; count < bb_data_size; count++) 101350397Sobrien { 101450397Sobrien __fetch_long (&line_num, ptr, 4); 101550397Sobrien ptr += 4; 101650397Sobrien if (line_num == -1) 101750397Sobrien { 101850397Sobrien /* A source file name follows. Check to see if we already have 101950397Sobrien a sourcefile structure for this file. */ 102050397Sobrien s_ptr = sources; 102150397Sobrien while (s_ptr && strcmp (s_ptr->name, ptr)) 102250397Sobrien s_ptr = s_ptr->next; 102350397Sobrien 102450397Sobrien if (s_ptr == 0) 102550397Sobrien { 102650397Sobrien /* No sourcefile structure for this file name exists, create 102750397Sobrien a new one, and append it to the front of the sources list. */ 102850397Sobrien s_ptr = (struct sourcefile *) xmalloc (sizeof(struct sourcefile)); 102990075Sobrien s_ptr->name = xstrdup (ptr); 103050397Sobrien s_ptr->maxlineno = 0; 103150397Sobrien s_ptr->next = sources; 103250397Sobrien sources = s_ptr; 103350397Sobrien } 103450397Sobrien 103550397Sobrien /* Scan past the file name. */ 103650397Sobrien { 103750397Sobrien long delim; 103850397Sobrien do { 103950397Sobrien count++; 104050397Sobrien __fetch_long (&delim, ptr, 4); 104150397Sobrien ptr += 4; 104250397Sobrien } while (delim != line_num); 104350397Sobrien } 104450397Sobrien } 104550397Sobrien else if (line_num == -2) 104650397Sobrien { 104750397Sobrien long delim; 104850397Sobrien 104950397Sobrien /* A function name follows. Ignore it. */ 105050397Sobrien do { 105150397Sobrien count++; 105250397Sobrien __fetch_long (&delim, ptr, 4); 105350397Sobrien ptr += 4; 105450397Sobrien } while (delim != line_num); 105550397Sobrien } 105650397Sobrien /* There will be a zero before the first file name, in which case s_ptr 105750397Sobrien will still be uninitialized. So, only try to set the maxlineno 1058117395Skan field if line_num is nonzero. */ 105950397Sobrien else if (line_num > 0) 106050397Sobrien { 106150397Sobrien if (s_ptr->maxlineno <= line_num) 106250397Sobrien s_ptr->maxlineno = line_num + 1; 106350397Sobrien } 106450397Sobrien else if (line_num < 0) 106550397Sobrien { 106690075Sobrien /* Don't know what this is, but it's garbage. */ 106790075Sobrien abort (); 106850397Sobrien } 106950397Sobrien } 107050397Sobrien} 107150397Sobrien 107250397Sobrien 1073117395Skan/* Increment totals in FUNCTION according to arc A_PTR. */ 107450397Sobrien 1075117395Skanstatic void 1076117395Skanaccumulate_branch_counts (function, a_ptr) 1077117395Skan struct coverage *function; 1078117395Skan struct arcdata *a_ptr; 1079117395Skan{ 1080117395Skan if (a_ptr->call_insn) 1081117395Skan { 1082117395Skan function->calls++; 1083117395Skan if (a_ptr->total) 1084117395Skan function->calls_executed++; 1085117395Skan } 1086117395Skan else 1087117395Skan { 1088117395Skan function->branches++; 1089117395Skan if (a_ptr->total) 1090117395Skan function->branches_executed++; 1091117395Skan if (a_ptr->hits) 1092117395Skan function->branches_taken++; 1093117395Skan } 1094117395Skan} 1095117395Skan 109650397Sobrien/* Calculate the branch taken probabilities for all arcs branches at the 109750397Sobrien end of this block. */ 109850397Sobrien 109950397Sobrienstatic void 1100117395Skancalculate_branch_probs (block_ptr, line_info, function) 1101117395Skan struct bb_info *block_ptr; 1102117395Skan struct line_info *line_info; 1103117395Skan struct coverage *function; 110450397Sobrien{ 110590075Sobrien gcov_type total; 110650397Sobrien struct adj_list *arcptr; 110750397Sobrien 1108117395Skan total = block_ptr->exec_count; 1109117395Skan for (arcptr = block_ptr->succ; arcptr; arcptr = arcptr->succ_next) 111050397Sobrien { 1111117395Skan struct arcdata *a_ptr; 1112117395Skan 111350397Sobrien /* Ignore fall through arcs as they aren't really branches. */ 111450397Sobrien if (arcptr->fall_through) 111550397Sobrien continue; 111690075Sobrien 111750397Sobrien a_ptr = (struct arcdata *) xmalloc (sizeof (struct arcdata)); 111890075Sobrien a_ptr->total = total; 1119117395Skan a_ptr->hits = total ? arcptr->arc_count : 0; 112050397Sobrien a_ptr->call_insn = arcptr->fake; 112150397Sobrien 1122117395Skan if (function) 1123117395Skan accumulate_branch_counts (function, a_ptr); 1124117395Skan /* Prepend the new branch to the list. */ 1125117395Skan a_ptr->next = line_info->branches; 1126117395Skan line_info->branches = a_ptr; 1127117395Skan } 1128117395Skan} 1129117395Skan 1130117395Skan/* Format a HOST_WIDE_INT as either a percent ratio, or absolute 1131117395Skan count. If dp >= 0, format TOP/BOTTOM * 100 to DP decimal places. 1132117395Skan If DP is zero, no decimal point is printed. Only print 100% when 1133117395Skan TOP==BOTTOM and only print 0% when TOP=0. If dp < 0, then simply 1134117395Skan format TOP. Return pointer to a static string. */ 1135117395Skan 1136117395Skanstatic char const * 1137117395Skanformat_hwint (top, bottom, dp) 1138117395Skan HOST_WIDEST_INT top, bottom; 1139117395Skan int dp; 1140117395Skan{ 1141117395Skan static char buffer[20]; 1142117395Skan 1143117395Skan if (dp >= 0) 1144117395Skan { 1145117395Skan float ratio = bottom ? (float)top / bottom : 0; 1146117395Skan int ix; 1147117395Skan unsigned limit = 100; 1148117395Skan unsigned percent; 1149117395Skan 1150117395Skan for (ix = dp; ix--; ) 1151117395Skan limit *= 10; 1152117395Skan 1153117395Skan percent = (unsigned) (ratio * limit + (float)0.5); 1154117395Skan if (percent <= 0 && top) 1155117395Skan percent = 1; 1156117395Skan else if (percent >= limit && top != bottom) 1157117395Skan percent = limit - 1; 1158117395Skan ix = sprintf (buffer, "%.*u%%", dp + 1, percent); 1159117395Skan if (dp) 116050397Sobrien { 1161117395Skan dp++; 1162117395Skan do 116350397Sobrien { 1164117395Skan buffer[ix+1] = buffer[ix]; 1165117395Skan ix--; 116650397Sobrien } 1167117395Skan while (dp--); 1168117395Skan buffer[ix + 1] = '.'; 116950397Sobrien } 117050397Sobrien } 1171117395Skan else 1172117395Skan sprintf (buffer, HOST_WIDEST_INT_PRINT_DEC, top); 1173117395Skan 1174117395Skan return buffer; 117550397Sobrien} 117650397Sobrien 1177117395Skan 117850397Sobrien/* Output summary info for a function. */ 117950397Sobrien 118050397Sobrienstatic void 1181117395Skanfunction_summary (function, title) 1182117395Skan struct coverage *function; 1183117395Skan const char *title; 118450397Sobrien{ 1185117395Skan if (function->lines) 1186117395Skan fnotice (stdout, "%s of %d lines executed in %s %s\n", 1187117395Skan format_hwint (function->lines_executed, 1188117395Skan function->lines, 2), 1189117395Skan function->lines, title, function->name); 119050397Sobrien else 1191117395Skan fnotice (stdout, "No executable lines in %s %s\n", 1192117395Skan title, function->name); 119350397Sobrien 119450397Sobrien if (output_branch_probs) 119550397Sobrien { 1196117395Skan if (function->branches) 119750397Sobrien { 1198117395Skan fnotice (stdout, "%s of %d branches executed in %s %s\n", 1199117395Skan format_hwint (function->branches_executed, 1200117395Skan function->branches, 2), 1201117395Skan function->branches, title, function->name); 120252284Sobrien fnotice (stdout, 1203117395Skan "%s of %d branches taken at least once in %s %s\n", 1204117395Skan format_hwint (function->branches_taken, 1205117395Skan function->branches, 2), 1206117395Skan function->branches, title, function->name); 120750397Sobrien } 120850397Sobrien else 1209117395Skan fnotice (stdout, "No branches in %s %s\n", title, function->name); 1210117395Skan if (function->calls) 1211117395Skan fnotice (stdout, "%s of %d calls executed in %s %s\n", 1212117395Skan format_hwint (function->calls_executed, 1213117395Skan function->calls, 2), 1214117395Skan function->calls, title, function->name); 121550397Sobrien else 1216117395Skan fnotice (stdout, "No calls in %s %s\n", title, function->name); 121750397Sobrien } 121850397Sobrien} 121950397Sobrien 1220117395Skan/* Generate an output file name. LONG_OUTPUT_NAMES and PRESERVE_PATHS 1221117395Skan affect name generation. With preserve_paths we create a filename 1222117395Skan from all path components of the source file, replacing '/' with 1223117395Skan '#', without it we simply take the basename component. With 1224117395Skan long_output_names we prepend the processed name of the input file 1225117395Skan to each output name (except when the current source file is the 1226117395Skan input file, so you don't get a double concatenation). The two 1227117395Skan components are separated by '##'. Also '.' filename components are 1228117395Skan removed and '..' components are renamed to '^'. */ 122950397Sobrien 1230117395Skanstatic char * 1231117395Skanmake_gcov_file_name (src_name) 1232117395Skan char *src_name; 1233117395Skan{ 1234117395Skan char *cptr; 1235117395Skan char *name = xmalloc (strlen (src_name) + strlen (input_file_name) + 10); 1236117395Skan 1237117395Skan name[0] = 0; 1238117395Skan if (output_long_names && strcmp (src_name, input_file_name)) 1239117395Skan { 1240117395Skan /* Generate the input filename part. */ 1241117395Skan cptr = preserve_paths ? NULL : strrchr (input_file_name, '/'); 1242117395Skan cptr = cptr ? cptr + 1 : input_file_name; 1243117395Skan strcat (name, cptr); 1244117395Skan strcat (name, "##"); 1245117395Skan } 1246117395Skan 1247117395Skan /* Generate the source filename part. */ 1248117395Skan cptr = preserve_paths ? NULL : strrchr (src_name, '/'); 1249117395Skan cptr = cptr ? cptr + 1 : src_name; 1250117395Skan strcat (name, cptr); 1251117395Skan 1252117395Skan if (preserve_paths) 1253117395Skan { 1254117395Skan /* Convert '/' to '#', remove '/./', convert '/../' to '/^/' */ 1255117395Skan char *prev; 1256117395Skan 1257117395Skan for (cptr = name; (cptr = strchr ((prev = cptr), '/'));) 1258117395Skan { 1259117395Skan unsigned shift = 0; 1260117395Skan 1261117395Skan if (prev + 1 == cptr && prev[0] == '.') 1262117395Skan { 1263117395Skan /* Remove '.' */ 1264117395Skan shift = 2; 1265117395Skan } 1266117395Skan else if (prev + 2 == cptr && prev[0] == '.' && prev[1] == '.') 1267117395Skan { 1268117395Skan /* Convert '..' */ 1269117395Skan shift = 1; 1270117395Skan prev[1] = '^'; 1271117395Skan } 1272117395Skan else 1273117395Skan *cptr++ = '#'; 1274117395Skan if (shift) 1275117395Skan { 1276117395Skan cptr = prev; 1277117395Skan do 1278117395Skan prev[0] = prev[shift]; 1279117395Skan while (*prev++); 1280117395Skan } 1281117395Skan } 1282117395Skan } 1283117395Skan 1284117395Skan /* Don't strip off the ending for compatibility with tcov, since 1285117395Skan this results in confusion if there is more than one file with the 1286117395Skan same basename, e.g. tmp.c and tmp.h. */ 1287117395Skan strcat (name, ".gcov"); 1288117395Skan return name; 1289117395Skan} 1290117395Skan 1291117395Skan/* Scan through the bb_data, and when the file name matches the 1292117395Skan source file name, then for each following line number, increment 1293117395Skan the line number execution count indicated by the execution count of 1294117395Skan the appropriate basic block. */ 1295117395Skan 129650397Sobrienstatic void 1297117395Skaninit_line_info (line_info, total, maxlineno) 1298117395Skan struct line_info *line_info; 1299117395Skan struct coverage *total; 1300117395Skan long maxlineno; 130150397Sobrien{ 1302117395Skan long block_num = 0; /* current block number */ 1303117395Skan struct bb_info *block_ptr = NULL; /* current block ptr */ 1304117395Skan struct coverage function; 1305117395Skan struct coverage *func_ptr = NULL; 1306117395Skan struct bb_info_list *current_graph = NULL; /* Graph for current function. */ 1307117395Skan int is_this_file = 0; /* We're scanning a block from the desired file. */ 1308117395Skan char *ptr = bb_data; 130990075Sobrien long count; 131050397Sobrien long line_num; 1311117395Skan struct line_info *line_ptr = 0; /* line info ptr. */ 1312117395Skan 1313117395Skan memset (&function, 0, sizeof (function)); 1314117395Skan if (output_function_summary) 1315117395Skan func_ptr = &function; 1316117395Skan 1317117395Skan for (count = 0; count < bb_data_size; count++) 131850397Sobrien { 1319117395Skan __fetch_long (&line_num, ptr, 4); 1320117395Skan ptr += 4; 1321117395Skan if (line_num < 0) 132250397Sobrien { 1323117395Skan long delim; 1324117395Skan 1325117395Skan if (line_num == -1) 132650397Sobrien { 1327117395Skan /* Marks the beginning of a file name. Check to see 1328117395Skan whether this is the filename we are currently 1329117395Skan collecting data for. */ 1330117395Skan is_this_file = !strcmp (total->name, ptr); 133150397Sobrien } 1332117395Skan else if (line_num == -2) 133350397Sobrien { 1334117395Skan /* Marks the start of a new function. Advance to the 1335117395Skan next program flow graph. */ 1336117395Skan if (!current_graph) 1337117395Skan current_graph = bb_graph_list; 1338117395Skan else 133950397Sobrien { 1340117395Skan if (block_num == current_graph->num_blocks - 1) 1341117395Skan /* Last block falls through to exit. */ 1342117395Skan ; 1343117395Skan else if (block_num == current_graph->num_blocks - 2) 134450397Sobrien { 1345117395Skan if (output_branch_probs && is_this_file) 1346117395Skan calculate_branch_probs (block_ptr, line_ptr, func_ptr); 134750397Sobrien } 134850397Sobrien else 134950397Sobrien { 1350117395Skan fnotice (stderr, 1351117395Skan "didn't use all bb entries of graph, function %s\n", 1352117395Skan function.name); 1353117395Skan fnotice (stderr, "block_num = %ld, num_blocks = %d\n", 1354117395Skan block_num, current_graph->num_blocks); 135550397Sobrien } 1356117395Skan if (func_ptr && is_this_file) 1357117395Skan function_summary (func_ptr, "function"); 1358117395Skan current_graph = current_graph->next; 135950397Sobrien } 1360117395Skan block_num = 0; 1361117395Skan block_ptr = current_graph->bb_graph; 1362117395Skan memset (&function, 0, sizeof (function)); 1363117395Skan function.name = ptr; 136450397Sobrien } 1365117395Skan else 1366117395Skan { 1367117395Skan fnotice (stderr, "ERROR: unexpected line number %ld\n", line_num); 1368117395Skan abort (); 1369117395Skan } 1370117395Skan 1371117395Skan /* Scan past the string. */ 1372117395Skan for (delim = 0; delim != line_num; count++) 1373117395Skan { 1374117395Skan __fetch_long (&delim, ptr, 4); 1375117395Skan ptr += 4; 1376117395Skan } 137750397Sobrien } 1378117395Skan else if (!line_num) 137950397Sobrien { 1380117395Skan /* Marks the end of a block. */ 1381117395Skan if (block_num >= current_graph->num_blocks) 138250397Sobrien { 1383117395Skan fnotice (stderr, "ERROR: too many basic blocks in function %s\n", 1384117395Skan function.name); 1385117395Skan abort (); 138650397Sobrien } 1387117395Skan 1388117395Skan if (output_branch_probs && is_this_file) 1389117395Skan calculate_branch_probs (block_ptr, line_ptr, func_ptr); 1390117395Skan 1391117395Skan block_num++; 1392117395Skan block_ptr++; 139350397Sobrien } 1394117395Skan else if (is_this_file) 139550397Sobrien { 1396117395Skan if (line_num >= maxlineno) 139750397Sobrien { 1398117395Skan fnotice (stderr, "ERROR: out of range line number in function %s\n", 1399117395Skan function.name); 1400117395Skan abort (); 140150397Sobrien } 140250397Sobrien 1403117395Skan line_ptr = &line_info[line_num]; 1404117395Skan if (func_ptr) 140550397Sobrien { 1406117395Skan if (!line_ptr->exists) 1407117395Skan func_ptr->lines++; 1408117395Skan if (!line_ptr->count && block_ptr->exec_count) 1409117395Skan func_ptr->lines_executed++; 1410117395Skan } 1411117395Skan 1412117395Skan /* Accumulate execution data for this line number. */ 1413117395Skan line_ptr->count += block_ptr->exec_count; 1414117395Skan line_ptr->exists = 1; 1415117395Skan } 1416117395Skan } 1417117395Skan 1418117395Skan if (func_ptr && is_this_file) 1419117395Skan function_summary (func_ptr, "function"); 1420117395Skan 1421117395Skan /* Calculate summary test coverage statistics. */ 1422117395Skan for (line_num = 1, line_ptr = &line_info[line_num]; 1423117395Skan line_num < maxlineno; line_num++, line_ptr++) 1424117395Skan { 1425117395Skan struct arcdata *a_ptr, *prev, *next; 1426117395Skan 1427117395Skan if (line_ptr->exists) 1428117395Skan { 1429117395Skan total->lines++; 1430117395Skan if (line_ptr->count) 1431117395Skan total->lines_executed++; 1432117395Skan } 143390075Sobrien 1434117395Skan /* Total and reverse the branch information. */ 1435117395Skan for (a_ptr = line_ptr->branches, prev = NULL; a_ptr; a_ptr = next) 1436117395Skan { 1437117395Skan next = a_ptr->next; 1438117395Skan a_ptr->next = prev; 1439117395Skan prev = a_ptr; 144050397Sobrien 1441117395Skan accumulate_branch_counts (total, a_ptr); 1442117395Skan } 1443117395Skan line_ptr->branches = prev; 1444117395Skan } 1445117395Skan} 144650397Sobrien 1447117395Skan/* Read in the source file one line at a time, and output that line to 1448117395Skan the gcov file preceded by its execution count and other 1449117395Skan information. */ 145050397Sobrien 1451117395Skanstatic void 1452117395Skanoutput_line_info (gcov_file, line_info, total, maxlineno) 1453117395Skan FILE *gcov_file; 1454117395Skan const struct line_info *line_info; 1455117395Skan const struct coverage *total; 1456117395Skan long maxlineno; 1457117395Skan{ 1458117395Skan FILE *source_file; 1459117395Skan long line_num; /* current line number */ 1460117395Skan const struct line_info *line_ptr; /* current line info ptr. */ 1461117395Skan char string[STRING_SIZE]; /* line buffer. */ 1462117395Skan char const *retval = ""; /* status of source file reading. */ 146350397Sobrien 1464117395Skan fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, total->name); 1465117395Skan fprintf (gcov_file, "%9s:%5d:Object:%s\n", "-", 0, bb_file_name); 1466117395Skan 1467117395Skan source_file = fopen (total->name, "r"); 1468117395Skan if (!source_file) 1469117395Skan { 1470117395Skan fnotice (stderr, "Could not open source file %s.\n", total->name); 1471117395Skan retval = NULL; 1472117395Skan } 1473117395Skan else 1474117395Skan { 1475117395Skan struct stat status; 1476117395Skan 1477117395Skan if (!fstat (fileno (source_file), &status) 1478117395Skan && status.st_mtime > bb_file_time) 1479117395Skan { 1480117395Skan fnotice (stderr, "Warning: source file %s is newer than %s\n", 1481117395Skan total->name, bb_file_name); 1482117395Skan fprintf (gcov_file, "%9s:%5d:Source is newer than compiler output\n", 1483117395Skan "-", 0); 1484117395Skan } 1485117395Skan } 148650397Sobrien 1487117395Skan for (line_num = 1, line_ptr = &line_info[line_num]; 1488117395Skan line_num < maxlineno; line_num++, line_ptr++) 1489117395Skan { 1490117395Skan /* For lines which don't exist in the .bb file, print '-' before 1491117395Skan the source line. For lines which exist but were never 1492117395Skan executed, print '#####' before the source line. Otherwise, 1493117395Skan print the execution count before the source line. There are 1494117395Skan 16 spaces of indentation added before the source line so that 1495117395Skan tabs won't be messed up. */ 1496117395Skan fprintf (gcov_file, "%9s:%5ld:", 1497117395Skan !line_ptr->exists ? "-" 1498117395Skan : !line_ptr->count ? "#####" 1499117395Skan : format_hwint (line_ptr->count, 0, -1), line_num); 1500117395Skan 1501117395Skan if (retval) 1502117395Skan { 1503117395Skan /* Copy source line. */ 1504117395Skan do 150550397Sobrien { 1506117395Skan retval = fgets (string, STRING_SIZE, source_file); 1507117395Skan if (!retval) 1508117395Skan { 1509117395Skan fnotice (stderr, 1510117395Skan "Unexpected EOF while reading source file %s.\n", 1511117395Skan total->name); 1512117395Skan break; 1513117395Skan } 1514117395Skan fputs (retval, gcov_file); 151550397Sobrien } 1516117395Skan while (!retval[0] || retval[strlen (retval) - 1] != '\n'); 1517117395Skan } 1518117395Skan if (!retval) 1519117395Skan fputs ("??\n", gcov_file); 1520117395Skan 1521117395Skan if (output_branch_probs) 1522117395Skan { 1523117395Skan int i; 1524117395Skan struct arcdata *a_ptr; 1525117395Skan 1526117395Skan for (i = 0, a_ptr = line_ptr->branches; a_ptr; 1527117395Skan a_ptr = a_ptr->next, i++) 152850397Sobrien { 1529117395Skan if (a_ptr->call_insn) 153050397Sobrien { 1531117395Skan if (a_ptr->total == 0) 1532117395Skan fnotice (gcov_file, "call %2d never executed\n", i); 153350397Sobrien else 1534117395Skan fnotice 1535117395Skan (gcov_file, "call %2d returns %s\n", i, 1536117395Skan format_hwint (a_ptr->total - a_ptr->hits, 1537117395Skan a_ptr->total, 1538117395Skan -output_branch_counts)); 153950397Sobrien } 154050397Sobrien else 154150397Sobrien { 1542117395Skan if (a_ptr->total == 0) 1543117395Skan fnotice (gcov_file, "branch %2d never executed\n", i); 1544117395Skan else 1545117395Skan fnotice 1546117395Skan (gcov_file, "branch %2d taken %s\n", i, 1547117395Skan format_hwint (a_ptr->hits, a_ptr->total, 1548117395Skan -output_branch_counts)); 154950397Sobrien } 155050397Sobrien } 1551117395Skan } 1552117395Skan } 1553117395Skan 1554117395Skan /* Handle all remaining source lines. There may be lines after the 1555117395Skan last line of code. */ 1556117395Skan if (retval) 1557117395Skan { 1558117395Skan for (; (retval = fgets (string, STRING_SIZE, source_file)); line_num++) 1559117395Skan { 1560117395Skan fprintf (gcov_file, "%9s:%5ld:%s", "-", line_num, retval); 1561117395Skan 1562117395Skan while (!retval[0] || retval[strlen (retval) - 1] != '\n') 1563117395Skan { 1564117395Skan retval = fgets (string, STRING_SIZE, source_file); 1565117395Skan if (!retval) 1566117395Skan break; 1567117395Skan fputs (retval, gcov_file); 1568117395Skan } 1569117395Skan } 1570117395Skan } 1571117395Skan 1572117395Skan if (source_file) 1573117395Skan fclose (source_file); 1574117395Skan} 157550397Sobrien 1576117395Skan/* Calculate line execution counts, and output a .gcov file for source 1577117395Skan file S_PTR. Allocate an array big enough to hold a count for each 1578117395Skan line. Scan through the bb_data, and when the file name matches the 1579117395Skan current file name, then for each following line number, increment 1580117395Skan the line number execution count indicated by the execution count of 1581117395Skan the appropriate basic block. */ 158250397Sobrien 1583117395Skanstatic void 1584117395Skanoutput_data (s_ptr) 1585117395Skan struct sourcefile *s_ptr; 1586117395Skan{ 1587117395Skan struct line_info *line_info /* line info data */ 1588117395Skan = (struct line_info *) xcalloc (s_ptr->maxlineno, 1589117395Skan sizeof (struct line_info)); 1590117395Skan long line_num; 1591117395Skan struct coverage total; 1592117395Skan 1593117395Skan memset (&total, 0, sizeof (total)); 1594117395Skan total.name = s_ptr->name; 1595117395Skan 1596117395Skan init_line_info (line_info, &total, s_ptr->maxlineno); 1597117395Skan function_summary (&total, "file"); 159850397Sobrien 1599117395Skan if (output_gcov_file) 1600117395Skan { 1601117395Skan /* Now the statistics are ready. Read in the source file one 1602117395Skan line at a time, and output that line to the gcov file 1603117395Skan preceded by its execution information. */ 1604117395Skan 1605117395Skan char *gcov_file_name = make_gcov_file_name (total.name); 1606117395Skan FILE *gcov_file = fopen (gcov_file_name, "w"); 1607117395Skan 1608117395Skan if (gcov_file) 1609117395Skan { 1610117395Skan fnotice (stdout, "Creating %s.\n", gcov_file_name); 1611117395Skan output_line_info (gcov_file, line_info, &total, s_ptr->maxlineno); 1612117395Skan if (ferror (gcov_file)) 1613117395Skan fnotice (stderr, "Error writing output file %s.\n", 1614117395Skan gcov_file_name); 1615117395Skan fclose (gcov_file); 1616117395Skan } 1617117395Skan else 1618117395Skan fnotice (stderr, "Could not open output file %s.\n", gcov_file_name); 1619117395Skan free (gcov_file_name); 1620117395Skan } 162150397Sobrien 1622117395Skan /* Free data. */ 1623117395Skan for (line_num = 1; line_num != s_ptr->maxlineno; line_num++) 1624117395Skan { 1625117395Skan struct arcdata *branch, *next; 162650397Sobrien 1627117395Skan for (branch = line_info[line_num].branches; branch; branch = next) 1628117395Skan { 1629117395Skan next = branch->next; 1630117395Skan free (branch); 163150397Sobrien } 163250397Sobrien } 1633117395Skan free (line_info); 163450397Sobrien} 1635