gcov.c revision 90075
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 10650397Sobrienstruct adj_list { 10750397Sobrien int source; 10850397Sobrien int target; 10990075Sobrien gcov_type arc_count; 11050397Sobrien unsigned int count_valid : 1; 11150397Sobrien unsigned int on_tree : 1; 11250397Sobrien unsigned int fake : 1; 11350397Sobrien unsigned int fall_through : 1; 11450397Sobrien#if 0 11550397Sobrien /* Not needed for gcov, but defined in profile.c. */ 11650397Sobrien rtx branch_insn; 11750397Sobrien#endif 11850397Sobrien struct adj_list *pred_next; 11950397Sobrien struct adj_list *succ_next; 12050397Sobrien}; 12150397Sobrien 12250397Sobrien/* Count the number of basic blocks, and create an array of these structures, 12350397Sobrien one for each bb in the function. */ 12450397Sobrien 12550397Sobrienstruct bb_info { 12650397Sobrien struct adj_list *succ; 12750397Sobrien struct adj_list *pred; 12890075Sobrien gcov_type succ_count; 12990075Sobrien gcov_type pred_count; 13090075Sobrien gcov_type exec_count; 13150397Sobrien unsigned int count_valid : 1; 13250397Sobrien unsigned int on_tree : 1; 13350397Sobrien#if 0 13450397Sobrien /* Not needed for gcov, but defined in profile.c. */ 13550397Sobrien rtx first_insn; 13650397Sobrien#endif 13750397Sobrien}; 13850397Sobrien 13950397Sobrien/* When outputting branch probabilities, one of these structures is created 14050397Sobrien for each branch/call. */ 14150397Sobrien 14250397Sobrienstruct arcdata 14350397Sobrien{ 14490075Sobrien gcov_type hits; 14590075Sobrien gcov_type total; 14650397Sobrien int call_insn; 14750397Sobrien struct arcdata *next; 14850397Sobrien}; 14950397Sobrien 15050397Sobrien/* Used to save the list of bb_graphs, one per function. */ 15150397Sobrien 15250397Sobrienstruct bb_info_list { 15350397Sobrien /* Indexed by block number, holds the basic block graph for one function. */ 15450397Sobrien struct bb_info *bb_graph; 15550397Sobrien int num_blocks; 15650397Sobrien struct bb_info_list *next; 15750397Sobrien}; 15850397Sobrien 15950397Sobrien/* Holds a list of function basic block graphs. */ 16050397Sobrien 16150397Sobrienstatic struct bb_info_list *bb_graph_list = 0; 16250397Sobrien 16350397Sobrien/* Name and file pointer of the input file for the basic block graph. */ 16450397Sobrien 16550397Sobrienstatic char *bbg_file_name; 16650397Sobrienstatic FILE *bbg_file; 16750397Sobrien 16850397Sobrien/* Name and file pointer of the input file for the arc count data. */ 16950397Sobrien 17050397Sobrienstatic char *da_file_name; 17150397Sobrienstatic FILE *da_file; 17250397Sobrien 17350397Sobrien/* Name and file pointer of the input file for the basic block line counts. */ 17450397Sobrien 17550397Sobrienstatic char *bb_file_name; 17650397Sobrienstatic FILE *bb_file; 17750397Sobrien 17850397Sobrien/* Holds the entire contents of the bb_file read into memory. */ 17950397Sobrien 18050397Sobrienstatic char *bb_data; 18150397Sobrien 18250397Sobrien/* Size of bb_data array in longs. */ 18350397Sobrien 18450397Sobrienstatic long bb_data_size; 18550397Sobrien 18650397Sobrien/* Name and file pointer of the output file. */ 18750397Sobrien 18850397Sobrienstatic char *gcov_file_name; 18950397Sobrienstatic FILE *gcov_file; 19050397Sobrien 19150397Sobrien/* Name of the file mentioned on the command line. */ 19250397Sobrien 19350397Sobrienstatic char *input_file_name = 0; 19450397Sobrien 19550397Sobrien/* Output branch probabilities if true. */ 19650397Sobrien 19750397Sobrienstatic int output_branch_probs = 0; 19850397Sobrien 19950397Sobrien/* Output a gcov file if this is true. This is on by default, and can 20050397Sobrien be turned off by the -n option. */ 20150397Sobrien 20250397Sobrienstatic int output_gcov_file = 1; 20350397Sobrien 20450397Sobrien/* For included files, make the gcov output file name include the name of 20550397Sobrien the input source file. For example, if x.h is included in a.c, then the 20650397Sobrien output file name is a.c.x.h.gcov instead of x.h.gcov. This works only 20750397Sobrien when a single source file is specified. */ 20850397Sobrien 20950397Sobrienstatic int output_long_names = 0; 21050397Sobrien 21150397Sobrien/* Output summary info for each function. */ 21250397Sobrien 21350397Sobrienstatic int output_function_summary = 0; 21450397Sobrien 21550397Sobrien/* Object directory file prefix. This is the directory where .bb and .bbg 21650397Sobrien files are looked for, if non-zero. */ 21750397Sobrien 21850397Sobrienstatic char *object_directory = 0; 21950397Sobrien 22090075Sobrien/* Output the number of times a branch was taken as opposed to the percentage 22190075Sobrien of times it was taken. Turned on by the -c option */ 22290075Sobrien 22390075Sobrienstatic int output_branch_counts = 0; 22490075Sobrien 22550397Sobrien/* Forward declarations. */ 22690075Sobrienstatic void process_args PARAMS ((int, char **)); 22790075Sobrienstatic void open_files PARAMS ((void)); 22890075Sobrienstatic void read_files PARAMS ((void)); 22990075Sobrienstatic void scan_for_source_files PARAMS ((void)); 23090075Sobrienstatic void output_data PARAMS ((void)); 23190075Sobrienstatic void print_usage PARAMS ((int)) ATTRIBUTE_NORETURN; 23290075Sobrienstatic void print_version PARAMS ((void)) ATTRIBUTE_NORETURN; 23390075Sobrienstatic void init_arc PARAMS ((struct adj_list *, int, int, struct bb_info *)); 23490075Sobrienstatic struct adj_list *reverse_arcs PARAMS ((struct adj_list *)); 23590075Sobrienstatic void create_program_flow_graph PARAMS ((struct bb_info_list *)); 23690075Sobrienstatic void solve_program_flow_graph PARAMS ((struct bb_info_list *)); 23790075Sobrienstatic void calculate_branch_probs PARAMS ((struct bb_info_list *, int, 23890075Sobrien struct arcdata **, int)); 23990075Sobrienstatic void function_summary PARAMS ((void)); 24050397Sobrien 24190075Sobrienextern int main PARAMS ((int, char **)); 24290075Sobrien 24350397Sobrienint 24450397Sobrienmain (argc, argv) 24550397Sobrien int argc; 24650397Sobrien char **argv; 24750397Sobrien{ 24890075Sobrien gcc_init_libintl (); 24952284Sobrien 25050397Sobrien process_args (argc, argv); 25150397Sobrien 25250397Sobrien open_files (); 25350397Sobrien 25450397Sobrien read_files (); 25550397Sobrien 25650397Sobrien scan_for_source_files (); 25750397Sobrien 25850397Sobrien output_data (); 25950397Sobrien 26050397Sobrien return 0; 26150397Sobrien} 26250397Sobrien 26390075Sobrienstatic void fnotice PARAMS ((FILE *, const char *, ...)) ATTRIBUTE_PRINTF_2; 26452284Sobrienstatic void 26590075Sobrienfnotice VPARAMS ((FILE *file, const char *msgid, ...)) 26652284Sobrien{ 26790075Sobrien VA_OPEN (ap, msgid); 26890075Sobrien VA_FIXEDARG (ap, FILE *, file); 26990075Sobrien VA_FIXEDARG (ap, const char *, msgid); 27052284Sobrien 27152284Sobrien vfprintf (file, _(msgid), ap); 27290075Sobrien VA_CLOSE (ap); 27352284Sobrien} 27452284Sobrien 27550397Sobrien/* More 'friendly' abort that prints the line and file. 27650397Sobrien config.h can #define abort fancy_abort if you like that sort of thing. */ 27790075Sobrienextern void fancy_abort PARAMS ((void)) ATTRIBUTE_NORETURN; 27850397Sobrien 27950397Sobrienvoid 28050397Sobrienfancy_abort () 28150397Sobrien{ 28290075Sobrien fnotice (stderr, "Internal gcov abort.\n"); 28350397Sobrien exit (FATAL_EXIT_CODE); 28450397Sobrien} 28550397Sobrien 28690075Sobrien/* Print a usage message and exit. If ERROR_P is nonzero, this is an error, 28790075Sobrien otherwise the output of --help. */ 28850397Sobrien 28950397Sobrienstatic void 29090075Sobrienprint_usage (error_p) 29190075Sobrien int error_p; 29250397Sobrien{ 29390075Sobrien FILE *file = error_p ? stderr : stdout; 29490075Sobrien int status = error_p ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE; 29590075Sobrien fnotice (file, "Usage: gcov [OPTION]... SOURCEFILE\n\n"); 29690075Sobrien fnotice (file, "Print code coverage information.\n\n"); 29790075Sobrien fnotice (file, " -h, --help Print this help, then exit\n"); 29890075Sobrien fnotice (file, " -v, --version Print version number, then exit\n"); 29990075Sobrien fnotice (file, " -b, --branch-probabilities Include branch probabilities in output\n"); 30090075Sobrien fnotice (file, " -c, --branch-counts Given counts of branches taken\n\ 30190075Sobrien rather than percentages\n"); 30290075Sobrien fnotice (file, " -n, --no-output Do not create an output file\n"); 30390075Sobrien fnotice (file, " -l, --long-file-names Use long output file names for included\n\ 30490075Sobrien source files\n"); 30590075Sobrien fnotice (file, " -f, --function-summaries Output summaries for each function\n"); 30690075Sobrien fnotice (file, " -o, --object-directory OBJDIR Search for object files in OBJDIR\n"); 30790075Sobrien fnotice (file, "\nFor bug reporting instructions, please see:\n%s.\n", 30890075Sobrien GCCBUGURL); 30990075Sobrien exit (status); 31050397Sobrien} 31150397Sobrien 31290075Sobrien/* Print version information and exit. */ 31390075Sobrien 31490075Sobrienstatic void 31590075Sobrienprint_version () 31690075Sobrien{ 31790075Sobrien fnotice (stdout, "gcov (GCC) %s\n", version_string); 31890075Sobrien fnotice (stdout, "Copyright (C) 2001 Free Software Foundation, Inc.\n"); 31990075Sobrien fnotice (stdout, 32090075Sobrien "This is free software; see the source for copying conditions. There is NO\n\ 32190075Sobrienwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"); 32290075Sobrien exit (SUCCESS_EXIT_CODE); 32390075Sobrien} 32490075Sobrien 32590075Sobrienstatic const struct option options[] = 32690075Sobrien{ 32790075Sobrien { "help", no_argument, NULL, 'h' }, 32890075Sobrien { "version", no_argument, NULL, 'v' }, 32990075Sobrien { "branch-probabilities", no_argument, NULL, 'b' }, 33090075Sobrien { "branch-counts", no_argument, NULL, 'c' }, 33190075Sobrien { "no-output", no_argument, NULL, 'n' }, 33290075Sobrien { "long-file-names", no_argument, NULL, 'l' }, 33390075Sobrien { "function-summaries", no_argument, NULL, 'f' }, 33490075Sobrien { "object-directory", required_argument, NULL, 'o' } 33590075Sobrien}; 33690075Sobrien 33750397Sobrien/* Parse the command line. */ 33850397Sobrien 33950397Sobrienstatic void 34050397Sobrienprocess_args (argc, argv) 34150397Sobrien int argc; 34250397Sobrien char **argv; 34350397Sobrien{ 34490075Sobrien int opt; 34550397Sobrien 34690075Sobrien while ((opt = getopt_long (argc, argv, "hvbclnfo:", options, NULL)) != -1) 34750397Sobrien { 34890075Sobrien switch (opt) 34950397Sobrien { 35090075Sobrien case 'h': 35190075Sobrien print_usage (false); 35290075Sobrien /* print_usage will exit. */ 35390075Sobrien case 'v': 35490075Sobrien print_version (); 35590075Sobrien /* print_version will exit. */ 35690075Sobrien case 'b': 35790075Sobrien output_branch_probs = 1; 35890075Sobrien break; 35990075Sobrien case 'c': 36090075Sobrien output_branch_counts = 1; 36190075Sobrien break; 36290075Sobrien case 'n': 36390075Sobrien output_gcov_file = 0; 36490075Sobrien break; 36590075Sobrien case 'l': 36690075Sobrien output_long_names = 1; 36790075Sobrien break; 36890075Sobrien case 'f': 36990075Sobrien output_function_summary = 1; 37090075Sobrien break; 37190075Sobrien case 'o': 37290075Sobrien object_directory = optarg; 37390075Sobrien break; 37490075Sobrien default: 37590075Sobrien print_usage (true); 37690075Sobrien /* print_usage will exit. */ 37750397Sobrien } 37850397Sobrien } 37950397Sobrien 38090075Sobrien if (optind != argc - 1) 38190075Sobrien print_usage (true); 38290075Sobrien 38390075Sobrien input_file_name = argv[optind]; 38450397Sobrien} 38550397Sobrien 38650397Sobrien 38750397Sobrien/* Find and open the .bb, .da, and .bbg files. */ 38850397Sobrien 38950397Sobrienstatic void 39050397Sobrienopen_files () 39150397Sobrien{ 39250397Sobrien int count, objdir_count; 39350397Sobrien char *cptr; 39450397Sobrien 39550397Sobrien /* Determine the names of the .bb, .bbg, and .da files. Strip off the 39650397Sobrien extension, if any, and append the new extensions. */ 39750397Sobrien count = strlen (input_file_name); 39850397Sobrien if (object_directory) 39950397Sobrien objdir_count = strlen (object_directory); 40050397Sobrien else 40150397Sobrien objdir_count = 0; 40250397Sobrien 40350397Sobrien da_file_name = xmalloc (count + objdir_count + 4); 40450397Sobrien bb_file_name = xmalloc (count + objdir_count + 4); 40550397Sobrien bbg_file_name = xmalloc (count + objdir_count + 5); 40650397Sobrien 40750397Sobrien if (object_directory) 40850397Sobrien { 40950397Sobrien strcpy (da_file_name, object_directory); 41050397Sobrien strcpy (bb_file_name, object_directory); 41150397Sobrien strcpy (bbg_file_name, object_directory); 41250397Sobrien 41350397Sobrien if (object_directory[objdir_count - 1] != '/') 41450397Sobrien { 41550397Sobrien strcat (da_file_name, "/"); 41650397Sobrien strcat (bb_file_name, "/"); 41750397Sobrien strcat (bbg_file_name, "/"); 41850397Sobrien } 41950397Sobrien 42090075Sobrien cptr = strrchr (input_file_name, '/'); 42150397Sobrien if (cptr) 42250397Sobrien { 42350397Sobrien strcat (da_file_name, cptr + 1); 42450397Sobrien strcat (bb_file_name, cptr + 1); 42550397Sobrien strcat (bbg_file_name, cptr + 1); 42650397Sobrien } 42750397Sobrien else 42850397Sobrien { 42950397Sobrien strcat (da_file_name, input_file_name); 43050397Sobrien strcat (bb_file_name, input_file_name); 43150397Sobrien strcat (bbg_file_name, input_file_name); 43250397Sobrien } 43350397Sobrien } 43450397Sobrien else 43550397Sobrien { 43650397Sobrien strcpy (da_file_name, input_file_name); 43750397Sobrien strcpy (bb_file_name, input_file_name); 43850397Sobrien strcpy (bbg_file_name, input_file_name); 43950397Sobrien } 44050397Sobrien 44190075Sobrien cptr = strrchr (bb_file_name, '.'); 44250397Sobrien if (cptr) 44350397Sobrien strcpy (cptr, ".bb"); 44450397Sobrien else 44550397Sobrien strcat (bb_file_name, ".bb"); 44650397Sobrien 44790075Sobrien cptr = strrchr (da_file_name, '.'); 44850397Sobrien if (cptr) 44950397Sobrien strcpy (cptr, ".da"); 45050397Sobrien else 45150397Sobrien strcat (da_file_name, ".da"); 45250397Sobrien 45390075Sobrien cptr = strrchr (bbg_file_name, '.'); 45450397Sobrien if (cptr) 45550397Sobrien strcpy (cptr, ".bbg"); 45650397Sobrien else 45750397Sobrien strcat (bbg_file_name, ".bbg"); 45850397Sobrien 45990075Sobrien bb_file = fopen (bb_file_name, "rb"); 46050397Sobrien if (bb_file == NULL) 46150397Sobrien { 46252284Sobrien fnotice (stderr, "Could not open basic block file %s.\n", bb_file_name); 46350397Sobrien exit (FATAL_EXIT_CODE); 46450397Sobrien } 46550397Sobrien 46650397Sobrien /* If none of the functions in the file were executed, then there won't 46750397Sobrien be a .da file. Just assume that all counts are zero in this case. */ 46890075Sobrien da_file = fopen (da_file_name, "rb"); 46950397Sobrien if (da_file == NULL) 47050397Sobrien { 47152284Sobrien fnotice (stderr, "Could not open data file %s.\n", da_file_name); 47252284Sobrien fnotice (stderr, "Assuming that all execution counts are zero.\n"); 47350397Sobrien } 47490075Sobrien 47590075Sobrien bbg_file = fopen (bbg_file_name, "rb"); 47650397Sobrien if (bbg_file == NULL) 47750397Sobrien { 47852284Sobrien fnotice (stderr, "Could not open program flow graph file %s.\n", 47950397Sobrien bbg_file_name); 48050397Sobrien exit (FATAL_EXIT_CODE); 48150397Sobrien } 48250397Sobrien 48350397Sobrien /* Check for empty .bbg file. This indicates that there is no executable 48450397Sobrien code in this source file. */ 48550397Sobrien /* Set the EOF condition if at the end of file. */ 48650397Sobrien ungetc (getc (bbg_file), bbg_file); 48750397Sobrien if (feof (bbg_file)) 48850397Sobrien { 48952284Sobrien fnotice (stderr, "No executable code associated with file %s.\n", 49050397Sobrien input_file_name); 49150397Sobrien exit (FATAL_EXIT_CODE); 49250397Sobrien } 49350397Sobrien} 49450397Sobrien 49550397Sobrien/* Initialize a new arc. */ 49650397Sobrien 49750397Sobrienstatic void 49850397Sobrieninit_arc (arcptr, source, target, bb_graph) 49950397Sobrien struct adj_list *arcptr; 50050397Sobrien int source, target; 50150397Sobrien struct bb_info *bb_graph; 50250397Sobrien{ 50350397Sobrien arcptr->target = target; 50450397Sobrien arcptr->source = source; 50550397Sobrien 50650397Sobrien arcptr->arc_count = 0; 50750397Sobrien arcptr->count_valid = 0; 50850397Sobrien arcptr->on_tree = 0; 50950397Sobrien arcptr->fake = 0; 51050397Sobrien arcptr->fall_through = 0; 51150397Sobrien 51250397Sobrien arcptr->succ_next = bb_graph[source].succ; 51350397Sobrien bb_graph[source].succ = arcptr; 51450397Sobrien bb_graph[source].succ_count++; 51550397Sobrien 51650397Sobrien arcptr->pred_next = bb_graph[target].pred; 51750397Sobrien bb_graph[target].pred = arcptr; 51850397Sobrien bb_graph[target].pred_count++; 51950397Sobrien} 52050397Sobrien 52150397Sobrien 52290075Sobrien/* Reverse the arcs on an arc list. */ 52350397Sobrien 52450397Sobrienstatic struct adj_list * 52550397Sobrienreverse_arcs (arcptr) 52650397Sobrien struct adj_list *arcptr; 52750397Sobrien{ 52850397Sobrien struct adj_list *prev = 0; 52950397Sobrien struct adj_list *next; 53050397Sobrien 53150397Sobrien for ( ; arcptr; arcptr = next) 53250397Sobrien { 53350397Sobrien next = arcptr->succ_next; 53450397Sobrien arcptr->succ_next = prev; 53550397Sobrien prev = arcptr; 53650397Sobrien } 53750397Sobrien 53850397Sobrien return prev; 53950397Sobrien} 54050397Sobrien 54150397Sobrien 54250397Sobrien/* Construct the program flow graph from the .bbg file, and read in the data 54350397Sobrien in the .da file. */ 54450397Sobrien 54550397Sobrienstatic void 54650397Sobriencreate_program_flow_graph (bptr) 54750397Sobrien struct bb_info_list *bptr; 54850397Sobrien{ 54950397Sobrien long num_blocks, number_arcs, src, dest, flag_bits, num_arcs_per_block; 55050397Sobrien int i; 55150397Sobrien struct adj_list *arcptr; 55250397Sobrien struct bb_info *bb_graph; 55350397Sobrien 55450397Sobrien /* Read the number of blocks. */ 55550397Sobrien __read_long (&num_blocks, bbg_file, 4); 55650397Sobrien 55790075Sobrien /* Create an array of size bb number of bb_info structs. */ 55890075Sobrien bb_graph = (struct bb_info *) xcalloc (num_blocks, sizeof (struct bb_info)); 55950397Sobrien 56050397Sobrien bptr->bb_graph = bb_graph; 56150397Sobrien bptr->num_blocks = num_blocks; 56250397Sobrien 56350397Sobrien /* Read and create each arc from the .bbg file. */ 56450397Sobrien __read_long (&number_arcs, bbg_file, 4); 56550397Sobrien for (i = 0; i < num_blocks; i++) 56650397Sobrien { 56750397Sobrien int j; 56850397Sobrien 56950397Sobrien __read_long (&num_arcs_per_block, bbg_file, 4); 57050397Sobrien for (j = 0; j < num_arcs_per_block; j++) 57150397Sobrien { 57250397Sobrien if (number_arcs-- < 0) 57350397Sobrien abort (); 57450397Sobrien 57550397Sobrien src = i; 57650397Sobrien __read_long (&dest, bbg_file, 4); 57750397Sobrien 57850397Sobrien arcptr = (struct adj_list *) xmalloc (sizeof (struct adj_list)); 57950397Sobrien init_arc (arcptr, src, dest, bb_graph); 58050397Sobrien 58150397Sobrien __read_long (&flag_bits, bbg_file, 4); 58250397Sobrien arcptr->on_tree = flag_bits & 0x1; 58350397Sobrien arcptr->fake = !! (flag_bits & 0x2); 58450397Sobrien arcptr->fall_through = !! (flag_bits & 0x4); 58550397Sobrien } 58650397Sobrien } 58750397Sobrien 58850397Sobrien if (number_arcs) 58950397Sobrien abort (); 59050397Sobrien 59150397Sobrien /* Read and ignore the -1 separating the arc list from the arc list of the 59250397Sobrien next function. */ 59350397Sobrien __read_long (&src, bbg_file, 4); 59450397Sobrien if (src != -1) 59550397Sobrien abort (); 59650397Sobrien 59750397Sobrien /* Must reverse the order of all succ arcs, to ensure that they match 59850397Sobrien the order of the data in the .da file. */ 59950397Sobrien 60050397Sobrien for (i = 0; i < num_blocks; i++) 60150397Sobrien if (bb_graph[i].succ) 60250397Sobrien bb_graph[i].succ = reverse_arcs (bb_graph[i].succ); 60350397Sobrien 60450397Sobrien /* For each arc not on the spanning tree, set its execution count from 60550397Sobrien the .da file. */ 60650397Sobrien 60750397Sobrien /* The first count in the .da file is the number of times that the function 60850397Sobrien was entered. This is the exec_count for block zero. */ 60950397Sobrien 61050397Sobrien /* This duplicates code in branch_prob in profile.c. */ 61150397Sobrien 61250397Sobrien for (i = 0; i < num_blocks; i++) 61350397Sobrien for (arcptr = bb_graph[i].succ; arcptr; arcptr = arcptr->succ_next) 61450397Sobrien if (! arcptr->on_tree) 61550397Sobrien { 61690075Sobrien gcov_type tmp_count = 0; 61790075Sobrien if (da_file && __read_gcov_type (&tmp_count, da_file, 8)) 61890075Sobrien abort (); 61950397Sobrien 62050397Sobrien arcptr->arc_count = tmp_count; 62150397Sobrien arcptr->count_valid = 1; 62250397Sobrien bb_graph[i].succ_count--; 62350397Sobrien bb_graph[arcptr->target].pred_count--; 62450397Sobrien } 62550397Sobrien} 62690075Sobrien 62750397Sobrienstatic void 62850397Sobriensolve_program_flow_graph (bptr) 62950397Sobrien struct bb_info_list *bptr; 63050397Sobrien{ 63190075Sobrien int passes, changes; 63290075Sobrien gcov_type total; 63350397Sobrien int i; 63450397Sobrien struct adj_list *arcptr; 63550397Sobrien struct bb_info *bb_graph; 63650397Sobrien int num_blocks; 63750397Sobrien 63850397Sobrien num_blocks = bptr->num_blocks; 63950397Sobrien bb_graph = bptr->bb_graph; 64050397Sobrien 64150397Sobrien /* For every block in the file, 64250397Sobrien - if every exit/entrance arc has a known count, then set the block count 64350397Sobrien - if the block count is known, and every exit/entrance arc but one has 64450397Sobrien a known execution count, then set the count of the remaining arc 64550397Sobrien 64650397Sobrien As arc counts are set, decrement the succ/pred count, but don't delete 64750397Sobrien the arc, that way we can easily tell when all arcs are known, or only 64850397Sobrien one arc is unknown. */ 64950397Sobrien 65050397Sobrien /* The order that the basic blocks are iterated through is important. 65150397Sobrien Since the code that finds spanning trees starts with block 0, low numbered 65250397Sobrien arcs are put on the spanning tree in preference to high numbered arcs. 65350397Sobrien Hence, most instrumented arcs are at the end. Graph solving works much 65450397Sobrien faster if we propagate numbers from the end to the start. 65550397Sobrien 65650397Sobrien This takes an average of slightly more than 3 passes. */ 65750397Sobrien 65850397Sobrien changes = 1; 65950397Sobrien passes = 0; 66050397Sobrien while (changes) 66150397Sobrien { 66250397Sobrien passes++; 66350397Sobrien changes = 0; 66450397Sobrien 66550397Sobrien for (i = num_blocks - 1; i >= 0; i--) 66650397Sobrien { 66750397Sobrien if (! bb_graph[i].count_valid) 66850397Sobrien { 66950397Sobrien if (bb_graph[i].succ_count == 0) 67050397Sobrien { 67150397Sobrien total = 0; 67250397Sobrien for (arcptr = bb_graph[i].succ; arcptr; 67350397Sobrien arcptr = arcptr->succ_next) 67450397Sobrien total += arcptr->arc_count; 67550397Sobrien bb_graph[i].exec_count = total; 67650397Sobrien bb_graph[i].count_valid = 1; 67750397Sobrien changes = 1; 67850397Sobrien } 67950397Sobrien else if (bb_graph[i].pred_count == 0) 68050397Sobrien { 68150397Sobrien total = 0; 68250397Sobrien for (arcptr = bb_graph[i].pred; arcptr; 68350397Sobrien arcptr = arcptr->pred_next) 68450397Sobrien total += arcptr->arc_count; 68550397Sobrien bb_graph[i].exec_count = total; 68650397Sobrien bb_graph[i].count_valid = 1; 68750397Sobrien changes = 1; 68850397Sobrien } 68950397Sobrien } 69050397Sobrien if (bb_graph[i].count_valid) 69150397Sobrien { 69250397Sobrien if (bb_graph[i].succ_count == 1) 69350397Sobrien { 69450397Sobrien total = 0; 69550397Sobrien /* One of the counts will be invalid, but it is zero, 69650397Sobrien so adding it in also doesn't hurt. */ 69750397Sobrien for (arcptr = bb_graph[i].succ; arcptr; 69850397Sobrien arcptr = arcptr->succ_next) 69950397Sobrien total += arcptr->arc_count; 70050397Sobrien /* Calculate count for remaining arc by conservation. */ 70150397Sobrien total = bb_graph[i].exec_count - total; 70250397Sobrien /* Search for the invalid arc, and set its count. */ 70350397Sobrien for (arcptr = bb_graph[i].succ; arcptr; 70450397Sobrien arcptr = arcptr->succ_next) 70550397Sobrien if (! arcptr->count_valid) 70650397Sobrien break; 70750397Sobrien if (! arcptr) 70850397Sobrien abort (); 70950397Sobrien arcptr->count_valid = 1; 71050397Sobrien arcptr->arc_count = total; 71150397Sobrien bb_graph[i].succ_count--; 71250397Sobrien 71350397Sobrien bb_graph[arcptr->target].pred_count--; 71450397Sobrien changes = 1; 71550397Sobrien } 71650397Sobrien if (bb_graph[i].pred_count == 1) 71750397Sobrien { 71850397Sobrien total = 0; 71950397Sobrien /* One of the counts will be invalid, but it is zero, 72050397Sobrien so adding it in also doesn't hurt. */ 72150397Sobrien for (arcptr = bb_graph[i].pred; arcptr; 72250397Sobrien arcptr = arcptr->pred_next) 72350397Sobrien total += arcptr->arc_count; 72450397Sobrien /* Calculate count for remaining arc by conservation. */ 72550397Sobrien total = bb_graph[i].exec_count - total; 72650397Sobrien /* Search for the invalid arc, and set its count. */ 72750397Sobrien for (arcptr = bb_graph[i].pred; arcptr; 72850397Sobrien arcptr = arcptr->pred_next) 72950397Sobrien if (! arcptr->count_valid) 73050397Sobrien break; 73150397Sobrien if (! arcptr) 73250397Sobrien abort (); 73350397Sobrien arcptr->count_valid = 1; 73450397Sobrien arcptr->arc_count = total; 73550397Sobrien bb_graph[i].pred_count--; 73650397Sobrien 73750397Sobrien bb_graph[arcptr->source].succ_count--; 73850397Sobrien changes = 1; 73950397Sobrien } 74050397Sobrien } 74150397Sobrien } 74250397Sobrien } 74390075Sobrien 74450397Sobrien /* If the graph has been correctly solved, every block will have a 74550397Sobrien succ and pred count of zero. */ 74650397Sobrien for (i = 0; i < num_blocks; i++) 74750397Sobrien if (bb_graph[i].succ_count || bb_graph[i].pred_count) 74850397Sobrien abort (); 74950397Sobrien} 75050397Sobrien 75150397Sobrien 75250397Sobrienstatic void 75350397Sobrienread_files () 75450397Sobrien{ 75550397Sobrien struct stat buf; 75650397Sobrien struct bb_info_list *list_end = 0; 75750397Sobrien struct bb_info_list *b_ptr; 75850397Sobrien long total; 75950397Sobrien 76050397Sobrien /* Read and ignore the first word of the .da file, which is the count of 76150397Sobrien how many numbers follow. */ 76250397Sobrien if (da_file && __read_long (&total, da_file, 8)) 76390075Sobrien abort (); 76450397Sobrien 76550397Sobrien while (! feof (bbg_file)) 76650397Sobrien { 76750397Sobrien b_ptr = (struct bb_info_list *) xmalloc (sizeof (struct bb_info_list)); 76850397Sobrien 76950397Sobrien b_ptr->next = 0; 77050397Sobrien if (list_end) 77150397Sobrien list_end->next = b_ptr; 77250397Sobrien else 77350397Sobrien bb_graph_list = b_ptr; 77450397Sobrien list_end = b_ptr; 77550397Sobrien 77650397Sobrien /* Read in the data in the .bbg file and reconstruct the program flow 77750397Sobrien graph for one function. */ 77850397Sobrien create_program_flow_graph (b_ptr); 77950397Sobrien 78050397Sobrien /* Set the EOF condition if at the end of file. */ 78150397Sobrien ungetc (getc (bbg_file), bbg_file); 78250397Sobrien } 78350397Sobrien 78450397Sobrien /* Check to make sure the .da file data is valid. */ 78550397Sobrien 78650397Sobrien if (da_file) 78750397Sobrien { 78850397Sobrien if (feof (da_file)) 78952284Sobrien fnotice (stderr, ".da file contents exhausted too early\n"); 79050397Sobrien /* Should be at end of file now. */ 79150397Sobrien if (__read_long (&total, da_file, 8) == 0) 79252284Sobrien fnotice (stderr, ".da file contents not exhausted\n"); 79350397Sobrien } 79450397Sobrien 79550397Sobrien /* Calculate all of the basic block execution counts and branch 79650397Sobrien taken probabilities. */ 79750397Sobrien 79850397Sobrien for (b_ptr = bb_graph_list; b_ptr; b_ptr = b_ptr->next) 79950397Sobrien solve_program_flow_graph (b_ptr); 80050397Sobrien 80150397Sobrien /* Read in all of the data from the .bb file. This info will be accessed 80250397Sobrien sequentially twice. */ 80350397Sobrien stat (bb_file_name, &buf); 80450397Sobrien bb_data_size = buf.st_size / 4; 80550397Sobrien 80650397Sobrien bb_data = (char *) xmalloc ((unsigned) buf.st_size); 80750397Sobrien fread (bb_data, sizeof (char), buf.st_size, bb_file); 80890075Sobrien 80950397Sobrien fclose (bb_file); 81050397Sobrien if (da_file) 81150397Sobrien fclose (da_file); 81250397Sobrien fclose (bbg_file); 81350397Sobrien} 81450397Sobrien 81550397Sobrien 81650397Sobrien/* Scan the data in the .bb file to find all source files referenced, 81750397Sobrien and the largest line number mentioned in each one. */ 81850397Sobrien 81950397Sobrienstatic void 82050397Sobrienscan_for_source_files () 82150397Sobrien{ 82250397Sobrien struct sourcefile *s_ptr = NULL; 82350397Sobrien char *ptr; 82490075Sobrien long count; 82550397Sobrien long line_num; 82650397Sobrien 82750397Sobrien /* Search the bb_data to find: 82850397Sobrien 1) The number of sources files contained herein, and 82950397Sobrien 2) The largest line number for each source file. */ 83050397Sobrien 83150397Sobrien ptr = bb_data; 83250397Sobrien sources = 0; 83350397Sobrien for (count = 0; count < bb_data_size; count++) 83450397Sobrien { 83550397Sobrien __fetch_long (&line_num, ptr, 4); 83650397Sobrien ptr += 4; 83750397Sobrien if (line_num == -1) 83850397Sobrien { 83950397Sobrien /* A source file name follows. Check to see if we already have 84050397Sobrien a sourcefile structure for this file. */ 84150397Sobrien s_ptr = sources; 84250397Sobrien while (s_ptr && strcmp (s_ptr->name, ptr)) 84350397Sobrien s_ptr = s_ptr->next; 84450397Sobrien 84550397Sobrien if (s_ptr == 0) 84650397Sobrien { 84750397Sobrien /* No sourcefile structure for this file name exists, create 84850397Sobrien a new one, and append it to the front of the sources list. */ 84950397Sobrien s_ptr = (struct sourcefile *) xmalloc (sizeof(struct sourcefile)); 85090075Sobrien s_ptr->name = xstrdup (ptr); 85150397Sobrien s_ptr->maxlineno = 0; 85250397Sobrien s_ptr->next = sources; 85350397Sobrien sources = s_ptr; 85450397Sobrien } 85550397Sobrien 85650397Sobrien /* Scan past the file name. */ 85750397Sobrien { 85850397Sobrien long delim; 85950397Sobrien do { 86050397Sobrien count++; 86150397Sobrien __fetch_long (&delim, ptr, 4); 86250397Sobrien ptr += 4; 86350397Sobrien } while (delim != line_num); 86450397Sobrien } 86550397Sobrien } 86650397Sobrien else if (line_num == -2) 86750397Sobrien { 86850397Sobrien long delim; 86950397Sobrien 87050397Sobrien /* A function name follows. Ignore it. */ 87150397Sobrien do { 87250397Sobrien count++; 87350397Sobrien __fetch_long (&delim, ptr, 4); 87450397Sobrien ptr += 4; 87550397Sobrien } while (delim != line_num); 87650397Sobrien } 87750397Sobrien /* There will be a zero before the first file name, in which case s_ptr 87850397Sobrien will still be uninitialized. So, only try to set the maxlineno 87950397Sobrien field if line_num is non-zero. */ 88050397Sobrien else if (line_num > 0) 88150397Sobrien { 88250397Sobrien if (s_ptr->maxlineno <= line_num) 88350397Sobrien s_ptr->maxlineno = line_num + 1; 88450397Sobrien } 88550397Sobrien else if (line_num < 0) 88650397Sobrien { 88790075Sobrien /* Don't know what this is, but it's garbage. */ 88890075Sobrien abort (); 88950397Sobrien } 89050397Sobrien } 89150397Sobrien} 89250397Sobrien 89350397Sobrien/* For calculating coverage at the function level. */ 89450397Sobrien 89550397Sobrienstatic int function_source_lines; 89650397Sobrienstatic int function_source_lines_executed; 89750397Sobrienstatic int function_branches; 89850397Sobrienstatic int function_branches_executed; 89950397Sobrienstatic int function_branches_taken; 90050397Sobrienstatic int function_calls; 90150397Sobrienstatic int function_calls_executed; 90250397Sobrienstatic char *function_name; 90350397Sobrien 90450397Sobrien/* Calculate the branch taken probabilities for all arcs branches at the 90550397Sobrien end of this block. */ 90650397Sobrien 90750397Sobrienstatic void 90850397Sobriencalculate_branch_probs (current_graph, block_num, branch_probs, last_line_num) 90950397Sobrien struct bb_info_list *current_graph; 91050397Sobrien int block_num; 91150397Sobrien struct arcdata **branch_probs; 91250397Sobrien int last_line_num; 91350397Sobrien{ 91490075Sobrien gcov_type total; 91550397Sobrien struct adj_list *arcptr; 91650397Sobrien struct arcdata *end_ptr, *a_ptr; 91750397Sobrien 91850397Sobrien total = current_graph->bb_graph[block_num].exec_count; 91950397Sobrien for (arcptr = current_graph->bb_graph[block_num].succ; arcptr; 92050397Sobrien arcptr = arcptr->succ_next) 92150397Sobrien { 92250397Sobrien /* Ignore fall through arcs as they aren't really branches. */ 92350397Sobrien 92450397Sobrien if (arcptr->fall_through) 92550397Sobrien continue; 92690075Sobrien 92750397Sobrien a_ptr = (struct arcdata *) xmalloc (sizeof (struct arcdata)); 92890075Sobrien a_ptr->total = total; 92950397Sobrien if (total == 0) 93090075Sobrien a_ptr->hits = 0; 93150397Sobrien else 93290075Sobrien a_ptr->hits = arcptr->arc_count; 93350397Sobrien a_ptr->call_insn = arcptr->fake; 93450397Sobrien 93550397Sobrien if (output_function_summary) 93650397Sobrien { 93750397Sobrien if (a_ptr->call_insn) 93850397Sobrien { 93950397Sobrien function_calls++; 94090075Sobrien if (a_ptr->total != 0) 94150397Sobrien function_calls_executed++; 94250397Sobrien } 94350397Sobrien else 94450397Sobrien { 94550397Sobrien function_branches++; 94690075Sobrien if (a_ptr->total != 0) 94750397Sobrien function_branches_executed++; 94890075Sobrien if (a_ptr->hits > 0) 94950397Sobrien function_branches_taken++; 95050397Sobrien } 95150397Sobrien } 95250397Sobrien 95350397Sobrien /* Append the new branch to the end of the list. */ 95450397Sobrien a_ptr->next = 0; 95550397Sobrien if (! branch_probs[last_line_num]) 95650397Sobrien branch_probs[last_line_num] = a_ptr; 95750397Sobrien else 95850397Sobrien { 95950397Sobrien end_ptr = branch_probs[last_line_num]; 96050397Sobrien while (end_ptr->next != 0) 96150397Sobrien end_ptr = end_ptr->next; 96250397Sobrien end_ptr->next = a_ptr; 96350397Sobrien } 96450397Sobrien } 96550397Sobrien} 96650397Sobrien 96750397Sobrien/* Output summary info for a function. */ 96850397Sobrien 96950397Sobrienstatic void 97050397Sobrienfunction_summary () 97150397Sobrien{ 97250397Sobrien if (function_source_lines) 97352284Sobrien fnotice (stdout, "%6.2f%% of %d source lines executed in function %s\n", 97450397Sobrien (((double) function_source_lines_executed / function_source_lines) 97550397Sobrien * 100), function_source_lines, function_name); 97650397Sobrien else 97752284Sobrien fnotice (stdout, "No executable source lines in function %s\n", 97850397Sobrien function_name); 97950397Sobrien 98050397Sobrien if (output_branch_probs) 98150397Sobrien { 98250397Sobrien if (function_branches) 98350397Sobrien { 98452284Sobrien fnotice (stdout, "%6.2f%% of %d branches executed in function %s\n", 98550397Sobrien (((double) function_branches_executed / function_branches) 98650397Sobrien * 100), function_branches, function_name); 98752284Sobrien fnotice (stdout, 98850397Sobrien "%6.2f%% of %d branches taken at least once in function %s\n", 98950397Sobrien (((double) function_branches_taken / function_branches) 99050397Sobrien * 100), function_branches, function_name); 99150397Sobrien } 99250397Sobrien else 99352284Sobrien fnotice (stdout, "No branches in function %s\n", function_name); 99450397Sobrien if (function_calls) 99552284Sobrien fnotice (stdout, "%6.2f%% of %d calls executed in function %s\n", 99650397Sobrien (((double) function_calls_executed / function_calls) 99750397Sobrien * 100), function_calls, function_name); 99850397Sobrien else 99952284Sobrien fnotice (stdout, "No calls in function %s\n", function_name); 100050397Sobrien } 100150397Sobrien} 100250397Sobrien 100350397Sobrien/* Calculate line execution counts, and output the data to a .tcov file. */ 100450397Sobrien 100550397Sobrienstatic void 100650397Sobrienoutput_data () 100750397Sobrien{ 100850397Sobrien /* When scanning data, this is true only if the data applies to the 100950397Sobrien current source file. */ 101050397Sobrien int this_file; 101150397Sobrien /* An array indexed by line number which indicates how many times that line 101250397Sobrien was executed. */ 101390075Sobrien gcov_type *line_counts; 101450397Sobrien /* An array indexed by line number which indicates whether the line was 101550397Sobrien present in the bb file (i.e. whether it had code associate with it). 101650397Sobrien Lines never executed are those which both exist, and have zero execution 101750397Sobrien counts. */ 101850397Sobrien char *line_exists; 101950397Sobrien /* An array indexed by line number, which contains a list of branch 102050397Sobrien probabilities, one for each branch on that line. */ 102150397Sobrien struct arcdata **branch_probs = NULL; 102250397Sobrien struct sourcefile *s_ptr; 102350397Sobrien char *source_file_name; 102450397Sobrien FILE *source_file; 102550397Sobrien struct bb_info_list *current_graph; 102690075Sobrien long count; 102750397Sobrien char *cptr; 102850397Sobrien long block_num; 102950397Sobrien long line_num; 103050397Sobrien long last_line_num = 0; 103150397Sobrien int i; 103250397Sobrien struct arcdata *a_ptr; 103350397Sobrien /* Buffer used for reading in lines from the source file. */ 103450397Sobrien char string[STRING_SIZE]; 103550397Sobrien /* For calculating coverage at the file level. */ 103650397Sobrien int total_source_lines; 103750397Sobrien int total_source_lines_executed; 103850397Sobrien int total_branches; 103950397Sobrien int total_branches_executed; 104050397Sobrien int total_branches_taken; 104150397Sobrien int total_calls; 104250397Sobrien int total_calls_executed; 104350397Sobrien 104450397Sobrien /* Now, for each source file, allocate an array big enough to hold a count 104550397Sobrien for each line. Scan through the bb_data, and when the file name matches 104650397Sobrien the current file name, then for each following line number, increment 104750397Sobrien the line number execution count indicated by the execution count of 104850397Sobrien the appropriate basic block. */ 104950397Sobrien 105050397Sobrien for (s_ptr = sources; s_ptr; s_ptr = s_ptr->next) 105150397Sobrien { 105250397Sobrien /* If this is a relative file name, and an object directory has been 105350397Sobrien specified, then make it relative to the object directory name. */ 105490075Sobrien if (! IS_ABSOLUTE_PATHNAME (s_ptr->name) 105590075Sobrien && object_directory != 0 105650397Sobrien && *object_directory != '\0') 105750397Sobrien { 105850397Sobrien int objdir_count = strlen (object_directory); 105950397Sobrien source_file_name = xmalloc (objdir_count + strlen (s_ptr->name) + 2); 106050397Sobrien strcpy (source_file_name, object_directory); 106150397Sobrien if (object_directory[objdir_count - 1] != '/') 106250397Sobrien source_file_name[objdir_count++] = '/'; 106350397Sobrien strcpy (source_file_name + objdir_count, s_ptr->name); 106450397Sobrien } 106550397Sobrien else 106650397Sobrien source_file_name = s_ptr->name; 106750397Sobrien 106890075Sobrien line_counts = (gcov_type *) xcalloc (sizeof (gcov_type), s_ptr->maxlineno); 106990075Sobrien line_exists = xcalloc (1, s_ptr->maxlineno); 107050397Sobrien if (output_branch_probs) 107190075Sobrien branch_probs = (struct arcdata **) 107290075Sobrien xcalloc (sizeof (struct arcdata *), s_ptr->maxlineno); 107390075Sobrien 107450397Sobrien /* There will be a zero at the beginning of the bb info, before the 107550397Sobrien first list of line numbers, so must initialize block_num to 0. */ 107650397Sobrien block_num = 0; 107750397Sobrien this_file = 0; 107850397Sobrien current_graph = 0; 107950397Sobrien { 108050397Sobrien /* Pointer into the bb_data, incremented while scanning the data. */ 108150397Sobrien char *ptr = bb_data; 108250397Sobrien for (count = 0; count < bb_data_size; count++) 108350397Sobrien { 108450397Sobrien long delim; 108550397Sobrien 108650397Sobrien __fetch_long (&line_num, ptr, 4); 108750397Sobrien ptr += 4; 108850397Sobrien if (line_num == -1) 108950397Sobrien { 109050397Sobrien /* Marks the beginning of a file name. Check to see whether 109150397Sobrien this is the filename we are currently collecting data for. */ 109250397Sobrien 109350397Sobrien if (strcmp (s_ptr->name, ptr)) 109450397Sobrien this_file = 0; 109550397Sobrien else 109650397Sobrien this_file = 1; 109790075Sobrien 109850397Sobrien /* Scan past the file name. */ 109950397Sobrien do { 110050397Sobrien count++; 110150397Sobrien __fetch_long (&delim, ptr, 4); 110250397Sobrien ptr += 4; 110350397Sobrien } while (delim != line_num); 110450397Sobrien } 110550397Sobrien else if (line_num == -2) 110650397Sobrien { 110750397Sobrien /* Marks the start of a new function. Advance to the next 110850397Sobrien program flow graph. */ 110950397Sobrien 111050397Sobrien if (! current_graph) 111150397Sobrien current_graph = bb_graph_list; 111250397Sobrien else 111350397Sobrien { 111450397Sobrien if (block_num == current_graph->num_blocks - 1) 111550397Sobrien /* Last block falls through to exit. */ 111650397Sobrien ; 111750397Sobrien else if (block_num == current_graph->num_blocks - 2) 111850397Sobrien { 111950397Sobrien if (output_branch_probs && this_file) 112050397Sobrien calculate_branch_probs (current_graph, block_num, 112150397Sobrien branch_probs, last_line_num); 112250397Sobrien } 112350397Sobrien else 112450397Sobrien { 112552284Sobrien fnotice (stderr, 112650397Sobrien "didn't use all bb entries of graph, function %s\n", 112750397Sobrien function_name); 112852284Sobrien fnotice (stderr, "block_num = %ld, num_blocks = %d\n", 112950397Sobrien block_num, current_graph->num_blocks); 113050397Sobrien } 113150397Sobrien 113250397Sobrien current_graph = current_graph->next; 113350397Sobrien block_num = 0; 113450397Sobrien 113550397Sobrien if (output_function_summary && this_file) 113650397Sobrien function_summary (); 113750397Sobrien } 113850397Sobrien 113950397Sobrien if (output_function_summary) 114050397Sobrien { 114150397Sobrien function_source_lines = 0; 114250397Sobrien function_source_lines_executed = 0; 114350397Sobrien function_branches = 0; 114450397Sobrien function_branches_executed = 0; 114550397Sobrien function_branches_taken = 0; 114650397Sobrien function_calls = 0; 114750397Sobrien function_calls_executed = 0; 114850397Sobrien } 114950397Sobrien 115050397Sobrien /* Save the function name for later use. */ 115150397Sobrien function_name = ptr; 115250397Sobrien 115350397Sobrien /* Scan past the file name. */ 115450397Sobrien do { 115550397Sobrien count++; 115650397Sobrien __fetch_long (&delim, ptr, 4); 115750397Sobrien ptr += 4; 115850397Sobrien } while (delim != line_num); 115950397Sobrien } 116050397Sobrien else if (line_num == 0) 116150397Sobrien { 116250397Sobrien /* Marks the end of a block. */ 116350397Sobrien 116450397Sobrien if (block_num >= current_graph->num_blocks) 116550397Sobrien { 116652284Sobrien fnotice (stderr, "ERROR: too many basic blocks in .bb file %s\n", 116750397Sobrien function_name); 116850397Sobrien abort (); 116950397Sobrien } 117090075Sobrien 117150397Sobrien if (output_branch_probs && this_file) 117250397Sobrien calculate_branch_probs (current_graph, block_num, 117350397Sobrien branch_probs, last_line_num); 117450397Sobrien 117550397Sobrien block_num++; 117650397Sobrien } 117750397Sobrien else if (this_file) 117850397Sobrien { 117950397Sobrien if (output_function_summary) 118050397Sobrien { 118150397Sobrien if (line_exists[line_num] == 0) 118250397Sobrien function_source_lines++; 118350397Sobrien if (line_counts[line_num] == 0 118450397Sobrien && current_graph->bb_graph[block_num].exec_count != 0) 118550397Sobrien function_source_lines_executed++; 118650397Sobrien } 118750397Sobrien 118850397Sobrien /* Accumulate execution data for this line number. */ 118950397Sobrien 119050397Sobrien line_counts[line_num] 119150397Sobrien += current_graph->bb_graph[block_num].exec_count; 119250397Sobrien line_exists[line_num] = 1; 119350397Sobrien last_line_num = line_num; 119450397Sobrien } 119550397Sobrien } 119650397Sobrien } 119750397Sobrien 119850397Sobrien if (output_function_summary && this_file) 119950397Sobrien function_summary (); 120050397Sobrien 120150397Sobrien /* Calculate summary test coverage statistics. */ 120250397Sobrien 120350397Sobrien total_source_lines = 0; 120450397Sobrien total_source_lines_executed = 0; 120550397Sobrien total_branches = 0; 120650397Sobrien total_branches_executed = 0; 120750397Sobrien total_branches_taken = 0; 120850397Sobrien total_calls = 0; 120950397Sobrien total_calls_executed = 0; 121050397Sobrien 121150397Sobrien for (count = 1; count < s_ptr->maxlineno; count++) 121250397Sobrien { 121350397Sobrien if (line_exists[count]) 121450397Sobrien { 121550397Sobrien total_source_lines++; 121650397Sobrien if (line_counts[count]) 121750397Sobrien total_source_lines_executed++; 121850397Sobrien } 121950397Sobrien if (output_branch_probs) 122050397Sobrien { 122150397Sobrien for (a_ptr = branch_probs[count]; a_ptr; a_ptr = a_ptr->next) 122250397Sobrien { 122350397Sobrien if (a_ptr->call_insn) 122450397Sobrien { 122550397Sobrien total_calls++; 122690075Sobrien if (a_ptr->total != 0) 122750397Sobrien total_calls_executed++; 122850397Sobrien } 122950397Sobrien else 123050397Sobrien { 123150397Sobrien total_branches++; 123290075Sobrien if (a_ptr->total != 0) 123350397Sobrien total_branches_executed++; 123490075Sobrien if (a_ptr->hits > 0) 123550397Sobrien total_branches_taken++; 123650397Sobrien } 123750397Sobrien } 123850397Sobrien } 123950397Sobrien } 124050397Sobrien 124150397Sobrien if (total_source_lines) 124252284Sobrien fnotice (stdout, 124350397Sobrien "%6.2f%% of %d source lines executed in file %s\n", 124450397Sobrien (((double) total_source_lines_executed / total_source_lines) 124550397Sobrien * 100), total_source_lines, source_file_name); 124650397Sobrien else 124752284Sobrien fnotice (stdout, "No executable source lines in file %s\n", 124850397Sobrien source_file_name); 124950397Sobrien 125050397Sobrien if (output_branch_probs) 125150397Sobrien { 125250397Sobrien if (total_branches) 125350397Sobrien { 125452284Sobrien fnotice (stdout, "%6.2f%% of %d branches executed in file %s\n", 125550397Sobrien (((double) total_branches_executed / total_branches) 125650397Sobrien * 100), total_branches, source_file_name); 125752284Sobrien fnotice (stdout, 125850397Sobrien "%6.2f%% of %d branches taken at least once in file %s\n", 125950397Sobrien (((double) total_branches_taken / total_branches) 126050397Sobrien * 100), total_branches, source_file_name); 126150397Sobrien } 126250397Sobrien else 126352284Sobrien fnotice (stdout, "No branches in file %s\n", source_file_name); 126450397Sobrien if (total_calls) 126552284Sobrien fnotice (stdout, "%6.2f%% of %d calls executed in file %s\n", 126650397Sobrien (((double) total_calls_executed / total_calls) 126750397Sobrien * 100), total_calls, source_file_name); 126850397Sobrien else 126952284Sobrien fnotice (stdout, "No calls in file %s\n", source_file_name); 127050397Sobrien } 127150397Sobrien 127250397Sobrien if (output_gcov_file) 127350397Sobrien { 127450397Sobrien /* Now the statistics are ready. Read in the source file one line 127550397Sobrien at a time, and output that line to the gcov file preceded by 127650397Sobrien its execution count if non zero. */ 127790075Sobrien 127850397Sobrien source_file = fopen (source_file_name, "r"); 127950397Sobrien if (source_file == NULL) 128050397Sobrien { 128152284Sobrien fnotice (stderr, "Could not open source file %s.\n", 128250397Sobrien source_file_name); 128350397Sobrien free (line_counts); 128450397Sobrien free (line_exists); 128550397Sobrien continue; 128650397Sobrien } 128750397Sobrien 128850397Sobrien count = strlen (source_file_name); 128990075Sobrien cptr = strrchr (s_ptr->name, '/'); 129050397Sobrien if (cptr) 129150397Sobrien cptr = cptr + 1; 129250397Sobrien else 129350397Sobrien cptr = s_ptr->name; 129450397Sobrien if (output_long_names && strcmp (cptr, input_file_name)) 129550397Sobrien { 129650397Sobrien gcov_file_name = xmalloc (count + 7 + strlen (input_file_name)); 129790075Sobrien 129890075Sobrien cptr = strrchr (input_file_name, '/'); 129950397Sobrien if (cptr) 130050397Sobrien strcpy (gcov_file_name, cptr + 1); 130150397Sobrien else 130250397Sobrien strcpy (gcov_file_name, input_file_name); 130350397Sobrien 130450397Sobrien strcat (gcov_file_name, "."); 130550397Sobrien 130690075Sobrien cptr = strrchr (source_file_name, '/'); 130750397Sobrien if (cptr) 130850397Sobrien strcat (gcov_file_name, cptr + 1); 130950397Sobrien else 131050397Sobrien strcat (gcov_file_name, source_file_name); 131150397Sobrien } 131250397Sobrien else 131350397Sobrien { 131450397Sobrien gcov_file_name = xmalloc (count + 6); 131590075Sobrien cptr = strrchr (source_file_name, '/'); 131650397Sobrien if (cptr) 131750397Sobrien strcpy (gcov_file_name, cptr + 1); 131850397Sobrien else 131950397Sobrien strcpy (gcov_file_name, source_file_name); 132050397Sobrien } 132150397Sobrien 132250397Sobrien /* Don't strip off the ending for compatibility with tcov, since 132350397Sobrien this results in confusion if there is more than one file with 132450397Sobrien the same basename, e.g. tmp.c and tmp.h. */ 132550397Sobrien strcat (gcov_file_name, ".gcov"); 132650397Sobrien 132750397Sobrien gcov_file = fopen (gcov_file_name, "w"); 132850397Sobrien 132950397Sobrien if (gcov_file == NULL) 133050397Sobrien { 133152284Sobrien fnotice (stderr, "Could not open output file %s.\n", 133250397Sobrien gcov_file_name); 133350397Sobrien fclose (source_file); 133450397Sobrien free (line_counts); 133550397Sobrien free (line_exists); 133650397Sobrien continue; 133750397Sobrien } 133850397Sobrien 133952284Sobrien fnotice (stdout, "Creating %s.\n", gcov_file_name); 134050397Sobrien 134150397Sobrien for (count = 1; count < s_ptr->maxlineno; count++) 134250397Sobrien { 134350397Sobrien char *retval; 134450397Sobrien int len; 134550397Sobrien 134650397Sobrien retval = fgets (string, STRING_SIZE, source_file); 134750397Sobrien 134850397Sobrien /* For lines which don't exist in the .bb file, print nothing 134950397Sobrien before the source line. For lines which exist but were never 135050397Sobrien executed, print ###### before the source line. Otherwise, 135150397Sobrien print the execution count before the source line. */ 135250397Sobrien /* There are 16 spaces of indentation added before the source 135350397Sobrien line so that tabs won't be messed up. */ 135450397Sobrien if (line_exists[count]) 135550397Sobrien { 135650397Sobrien if (line_counts[count]) 135790075Sobrien { 135890075Sobrien char c[20]; 135990075Sobrien sprintf (c, HOST_WIDEST_INT_PRINT_DEC, (HOST_WIDEST_INT)line_counts[count]); 136090075Sobrien fprintf (gcov_file, "%12s %s", c, 136190075Sobrien string); 136290075Sobrien } 136350397Sobrien else 136450397Sobrien fprintf (gcov_file, " ###### %s", string); 136550397Sobrien } 136650397Sobrien else 136750397Sobrien fprintf (gcov_file, "\t\t%s", string); 136850397Sobrien 136950397Sobrien /* In case the source file line is larger than our buffer, keep 137050397Sobrien reading and outputting lines until we get a newline. */ 137150397Sobrien len = strlen (string); 137250397Sobrien while ((len == 0 || string[strlen (string) - 1] != '\n') 137350397Sobrien && retval != NULL) 137450397Sobrien { 137550397Sobrien retval = fgets (string, STRING_SIZE, source_file); 137650397Sobrien fputs (string, gcov_file); 137750397Sobrien } 137850397Sobrien 137950397Sobrien if (output_branch_probs) 138050397Sobrien { 138150397Sobrien for (i = 0, a_ptr = branch_probs[count]; a_ptr; 138250397Sobrien a_ptr = a_ptr->next, i++) 138350397Sobrien { 138450397Sobrien if (a_ptr->call_insn) 138550397Sobrien { 138690075Sobrien if (a_ptr->total == 0) 138752284Sobrien fnotice (gcov_file, "call %d never executed\n", i); 138890075Sobrien else 138990075Sobrien { 139090075Sobrien if (output_branch_counts) 139190075Sobrien { 139290075Sobrien char c[20]; 139390075Sobrien sprintf (c, HOST_WIDEST_INT_PRINT_DEC, 139490075Sobrien a_ptr->total - a_ptr->hits); 139590075Sobrien fnotice (gcov_file, 139690075Sobrien "call %d returns = %s\n", i, c); 139790075Sobrien } 139890075Sobrien else 139990075Sobrien { 140090075Sobrien char c[20]; 140190075Sobrien sprintf (c, HOST_WIDEST_INT_PRINT_DEC, 140290075Sobrien 100 - ((a_ptr->hits * 100) 140390075Sobrien + (a_ptr->total >> 1)) 140490075Sobrien / a_ptr->total); 140590075Sobrien fnotice (gcov_file, 140690075Sobrien "call %d returns = %s%%\n", i, c); 140790075Sobrien } 140890075Sobrien } 140950397Sobrien } 141050397Sobrien else 141150397Sobrien { 141290075Sobrien if (a_ptr->total == 0) 141352284Sobrien fnotice (gcov_file, "branch %d never executed\n", 141450397Sobrien i); 141550397Sobrien else 141690075Sobrien { 141790075Sobrien if (output_branch_counts) 141890075Sobrien { 141990075Sobrien char c[20]; 142090075Sobrien sprintf (c, HOST_WIDEST_INT_PRINT_DEC, 142190075Sobrien a_ptr->hits); 142290075Sobrien fnotice (gcov_file, 142390075Sobrien "branch %d taken = %s\n", i, c); 142490075Sobrien } 142590075Sobrien else 142690075Sobrien { 142790075Sobrien char c[20]; 142890075Sobrien sprintf (c, HOST_WIDEST_INT_PRINT_DEC, 142990075Sobrien ((a_ptr->hits * 100) 143090075Sobrien + (a_ptr->total >> 1)) 143190075Sobrien / a_ptr->total); 143290075Sobrien fnotice (gcov_file, 143390075Sobrien "branch %d taken = %s%%\n", i, c); 143490075Sobrien } 143590075Sobrien } 143650397Sobrien } 143790075Sobrien } 143890075Sobrien } 143950397Sobrien 144050397Sobrien /* Gracefully handle errors while reading the source file. */ 144150397Sobrien if (retval == NULL) 144250397Sobrien { 144352284Sobrien fnotice (stderr, 144450397Sobrien "Unexpected EOF while reading source file %s.\n", 144550397Sobrien source_file_name); 144650397Sobrien break; 144750397Sobrien } 144850397Sobrien } 144950397Sobrien 145050397Sobrien /* Handle all remaining source lines. There may be lines 145150397Sobrien after the last line of code. */ 145250397Sobrien 145350397Sobrien { 145450397Sobrien char *retval = fgets (string, STRING_SIZE, source_file); 145550397Sobrien while (retval != NULL) 145650397Sobrien { 145750397Sobrien int len; 145850397Sobrien 145950397Sobrien fprintf (gcov_file, "\t\t%s", string); 146050397Sobrien 146150397Sobrien /* In case the source file line is larger than our buffer, keep 146250397Sobrien reading and outputting lines until we get a newline. */ 146350397Sobrien len = strlen (string); 146450397Sobrien while ((len == 0 || string[strlen (string) - 1] != '\n') 146550397Sobrien && retval != NULL) 146650397Sobrien { 146750397Sobrien retval = fgets (string, STRING_SIZE, source_file); 146850397Sobrien fputs (string, gcov_file); 146950397Sobrien } 147050397Sobrien 147150397Sobrien retval = fgets (string, STRING_SIZE, source_file); 147250397Sobrien } 147350397Sobrien } 147450397Sobrien 147550397Sobrien fclose (source_file); 147650397Sobrien fclose (gcov_file); 147750397Sobrien } 147850397Sobrien 147950397Sobrien free (line_counts); 148050397Sobrien free (line_exists); 148150397Sobrien } 148250397Sobrien} 1483