gcov.c revision 52284
150397Sobrien/* Gcov.c: prepend line execution counts and branch probabilities to a 250397Sobrien source file. 352284Sobrien Copyright (C) 1990, 91-94, 96, 97, 98, 1999 Free Software Foundation, Inc. 450397Sobrien Contributed by James E. Wilson of Cygnus Support. 550397Sobrien Mangled by Bob Manson of Cygnus Support. 650397Sobrien 750397SobrienGcov is free software; you can redistribute it and/or modify 850397Sobrienit under the terms of the GNU General Public License as published by 950397Sobrienthe Free Software Foundation; either version 2, or (at your option) 1050397Sobrienany later version. 1150397Sobrien 1250397SobrienGcov is distributed in the hope that it will be useful, 1350397Sobrienbut WITHOUT ANY WARRANTY; without even the implied warranty of 1450397SobrienMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1550397SobrienGNU General Public License for more details. 1650397Sobrien 1750397SobrienYou should have received a copy of the GNU General Public License 1850397Sobrienalong with Gcov; see the file COPYING. If not, write to 1952284Sobrienthe Free Software Foundation, 59 Temple Place - Suite 330, 2052284SobrienBoston, MA 02111-1307, USA. */ 2150397Sobrien 2250397Sobrien/* ??? The code in final.c that produces the struct bb assumes that there is 2350397Sobrien no padding between the fields. This is not necessary true. The current 2450397Sobrien code can only be trusted if longs and pointers are the same size. */ 2550397Sobrien 2650397Sobrien/* ??? No need to print an execution count on every line, could just print 2750397Sobrien it on the first line of each block, and only print it on a subsequent 2850397Sobrien line in the same block if the count changes. */ 2950397Sobrien 3050397Sobrien/* ??? Print a list of the ten blocks with the highest execution counts, 3150397Sobrien and list the line numbers corresponding to those blocks. Also, perhaps 3250397Sobrien list the line numbers with the highest execution counts, only printing 3350397Sobrien the first if there are several which are all listed in the same block. */ 3450397Sobrien 3550397Sobrien/* ??? Should have an option to print the number of basic blocks, and the 3650397Sobrien percent of them that are covered. */ 3750397Sobrien 3850397Sobrien/* ??? Does not correctly handle the case where two .bb files refer to the 3950397Sobrien same included source file. For example, if one has a short file containing 4050397Sobrien only inline functions, which is then included in two other files, then 4150397Sobrien there will be two .bb files which refer to the include file, but there 4250397Sobrien is no way to get the total execution counts for the included file, can 4350397Sobrien only get execution counts for one or the other of the including files. */ 4450397Sobrien 4550397Sobrien#include "config.h" 4650397Sobrien#include "system.h" 4752284Sobrien#include "intl.h" 4852284Sobrien#undef abort 4950397Sobrien 5050397Sobrien#include "gcov-io.h" 5150397Sobrien 5250397Sobrien/* The .bb file format consists of several lists of 4-byte integers 5350397Sobrien which are the line numbers of each basic block in the file. Each 5450397Sobrien list is terminated by a zero. These lists correspond to the basic 5550397Sobrien blocks in the reconstructed program flow graph. 5650397Sobrien 5750397Sobrien A line number of -1 indicates that a source file name (padded to a 5850397Sobrien long boundary) follows. The padded file name is followed by 5950397Sobrien another -1 to make it easy to scan past file names. A -2 indicates 6050397Sobrien that a function name (padded to a long boundary) follows; the name 6150397Sobrien is followed by another -2 to make it easy to scan past the function 6250397Sobrien name. 6350397Sobrien 6450397Sobrien The .bbg file contains enough info to enable gcov to reconstruct the 6550397Sobrien program flow graph. The first word is the number of basic blocks, 6650397Sobrien the second word is the number of arcs, followed by the list of arcs 6750397Sobrien (source bb, dest bb pairs), then a -1, then the number of instrumented 6850397Sobrien arcs followed by the instrumented arcs, followed by another -1. This 6950397Sobrien is repeated for each function. 7050397Sobrien 7150397Sobrien The .da file contains the execution count for each instrumented branch. 7250397Sobrien 7350397Sobrien The .bb and .bbg files are created by giving GCC the -ftest-coverage option, 7450397Sobrien and the .da files are created when an executable compiled with 7550397Sobrien -fprofile-arcs is run. */ 7650397Sobrien 7750397Sobrien/* The functions in this file for creating and solution program flow graphs 7850397Sobrien are very similar to functions in the gcc source file profile.c. */ 7950397Sobrien 8050397Sobrienchar gcov_version_string[] = "GNU gcov version 1.5\n"; 8150397Sobrien 8250397Sobrien/* This is the size of the buffer used to read in source file lines. */ 8350397Sobrien 8450397Sobrien#define STRING_SIZE 200 8550397Sobrien 8650397Sobrien/* One copy of this structure is created for each source file mentioned in the 8750397Sobrien .bb file. */ 8850397Sobrien 8950397Sobrienstruct sourcefile 9050397Sobrien{ 9150397Sobrien char *name; 9250397Sobrien int maxlineno; 9350397Sobrien struct sourcefile *next; 9450397Sobrien}; 9550397Sobrien 9650397Sobrien/* This points to the head of the sourcefile structure list. */ 9750397Sobrien 9850397Sobrienstruct sourcefile *sources; 9950397Sobrien 10050397Sobrien/* One of these is dynamically created whenever we identify an arc in the 10150397Sobrien function. */ 10250397Sobrien 10350397Sobrienstruct adj_list { 10450397Sobrien int source; 10550397Sobrien int target; 10650397Sobrien int arc_count; 10750397Sobrien unsigned int count_valid : 1; 10850397Sobrien unsigned int on_tree : 1; 10950397Sobrien unsigned int fake : 1; 11050397Sobrien unsigned int fall_through : 1; 11150397Sobrien#if 0 11250397Sobrien /* Not needed for gcov, but defined in profile.c. */ 11350397Sobrien rtx branch_insn; 11450397Sobrien#endif 11550397Sobrien struct adj_list *pred_next; 11650397Sobrien struct adj_list *succ_next; 11750397Sobrien}; 11850397Sobrien 11950397Sobrien/* Count the number of basic blocks, and create an array of these structures, 12050397Sobrien one for each bb in the function. */ 12150397Sobrien 12250397Sobrienstruct bb_info { 12350397Sobrien struct adj_list *succ; 12450397Sobrien struct adj_list *pred; 12550397Sobrien int succ_count; 12650397Sobrien int pred_count; 12750397Sobrien int exec_count; 12850397Sobrien unsigned int count_valid : 1; 12950397Sobrien unsigned int on_tree : 1; 13050397Sobrien#if 0 13150397Sobrien /* Not needed for gcov, but defined in profile.c. */ 13250397Sobrien rtx first_insn; 13350397Sobrien#endif 13450397Sobrien}; 13550397Sobrien 13650397Sobrien/* When outputting branch probabilities, one of these structures is created 13750397Sobrien for each branch/call. */ 13850397Sobrien 13950397Sobrienstruct arcdata 14050397Sobrien{ 14150397Sobrien int prob; 14250397Sobrien int call_insn; 14350397Sobrien struct arcdata *next; 14450397Sobrien}; 14550397Sobrien 14650397Sobrien/* Used to save the list of bb_graphs, one per function. */ 14750397Sobrien 14850397Sobrienstruct bb_info_list { 14950397Sobrien /* Indexed by block number, holds the basic block graph for one function. */ 15050397Sobrien struct bb_info *bb_graph; 15150397Sobrien int num_blocks; 15250397Sobrien struct bb_info_list *next; 15350397Sobrien}; 15450397Sobrien 15550397Sobrien/* Holds a list of function basic block graphs. */ 15650397Sobrien 15750397Sobrienstatic struct bb_info_list *bb_graph_list = 0; 15850397Sobrien 15950397Sobrien/* Name and file pointer of the input file for the basic block graph. */ 16050397Sobrien 16150397Sobrienstatic char *bbg_file_name; 16250397Sobrienstatic FILE *bbg_file; 16350397Sobrien 16450397Sobrien/* Name and file pointer of the input file for the arc count data. */ 16550397Sobrien 16650397Sobrienstatic char *da_file_name; 16750397Sobrienstatic FILE *da_file; 16850397Sobrien 16950397Sobrien/* Name and file pointer of the input file for the basic block line counts. */ 17050397Sobrien 17150397Sobrienstatic char *bb_file_name; 17250397Sobrienstatic FILE *bb_file; 17350397Sobrien 17450397Sobrien/* Holds the entire contents of the bb_file read into memory. */ 17550397Sobrien 17650397Sobrienstatic char *bb_data; 17750397Sobrien 17850397Sobrien/* Size of bb_data array in longs. */ 17950397Sobrien 18050397Sobrienstatic long bb_data_size; 18150397Sobrien 18250397Sobrien/* Name and file pointer of the output file. */ 18350397Sobrien 18450397Sobrienstatic char *gcov_file_name; 18550397Sobrienstatic FILE *gcov_file; 18650397Sobrien 18750397Sobrien/* Name of the file mentioned on the command line. */ 18850397Sobrien 18950397Sobrienstatic char *input_file_name = 0; 19050397Sobrien 19150397Sobrien/* Output branch probabilities if true. */ 19250397Sobrien 19350397Sobrienstatic int output_branch_probs = 0; 19450397Sobrien 19550397Sobrien/* Output a gcov file if this is true. This is on by default, and can 19650397Sobrien be turned off by the -n option. */ 19750397Sobrien 19850397Sobrienstatic int output_gcov_file = 1; 19950397Sobrien 20050397Sobrien/* For included files, make the gcov output file name include the name of 20150397Sobrien the input source file. For example, if x.h is included in a.c, then the 20250397Sobrien output file name is a.c.x.h.gcov instead of x.h.gcov. This works only 20350397Sobrien when a single source file is specified. */ 20450397Sobrien 20550397Sobrienstatic int output_long_names = 0; 20650397Sobrien 20750397Sobrien/* Output summary info for each function. */ 20850397Sobrien 20950397Sobrienstatic int output_function_summary = 0; 21050397Sobrien 21150397Sobrien/* Object directory file prefix. This is the directory where .bb and .bbg 21250397Sobrien files are looked for, if non-zero. */ 21350397Sobrien 21450397Sobrienstatic char *object_directory = 0; 21550397Sobrien 21650397Sobrien/* Forward declarations. */ 21750397Sobrienstatic void process_args PROTO ((int, char **)); 21850397Sobrienstatic void open_files PROTO ((void)); 21950397Sobrienstatic void read_files PROTO ((void)); 22050397Sobrienstatic void scan_for_source_files PROTO ((void)); 22150397Sobrienstatic void output_data PROTO ((void)); 22252284Sobrienstatic void print_usage PROTO ((void)) ATTRIBUTE_NORETURN; 22350397Sobrien 22450397Sobrienint 22550397Sobrienmain (argc, argv) 22650397Sobrien int argc; 22750397Sobrien char **argv; 22850397Sobrien{ 22952284Sobrien#ifdef HAVE_LC_MESSAGES 23052284Sobrien setlocale (LC_MESSAGES, ""); 23152284Sobrien#endif 23252284Sobrien (void) bindtextdomain (PACKAGE, localedir); 23352284Sobrien (void) textdomain (PACKAGE); 23452284Sobrien 23550397Sobrien process_args (argc, argv); 23650397Sobrien 23750397Sobrien open_files (); 23850397Sobrien 23950397Sobrien read_files (); 24050397Sobrien 24150397Sobrien scan_for_source_files (); 24250397Sobrien 24350397Sobrien output_data (); 24450397Sobrien 24550397Sobrien return 0; 24650397Sobrien} 24750397Sobrien 24852284Sobrienstatic void fnotice PVPROTO ((FILE *, const char *, ...)) ATTRIBUTE_PRINTF_2; 24952284Sobrienstatic void 25052284Sobrienfnotice VPROTO ((FILE *file, const char *msgid, ...)) 25152284Sobrien{ 25252284Sobrien#ifndef ANSI_PROTOTYPES 25352284Sobrien FILE *file; 25452284Sobrien const char *msgid; 25552284Sobrien#endif 25652284Sobrien va_list ap; 25752284Sobrien 25852284Sobrien VA_START (ap, msgid); 25952284Sobrien 26052284Sobrien#ifndef ANSI_PROTOTYPES 26152284Sobrien file = va_arg (ap, FILE *); 26252284Sobrien msgid = va_arg (ap, const char *); 26352284Sobrien#endif 26452284Sobrien 26552284Sobrien vfprintf (file, _(msgid), ap); 26652284Sobrien va_end (ap); 26752284Sobrien} 26852284Sobrien 26952284Sobrien 27052284SobrienPTR 27150397Sobrienxmalloc (size) 27252284Sobrien size_t size; 27350397Sobrien{ 27452284Sobrien register PTR value = (PTR) malloc (size); 27550397Sobrien if (value == 0) 27650397Sobrien { 27752284Sobrien fnotice (stderr, "error: virtual memory exhausted"); 27850397Sobrien exit (FATAL_EXIT_CODE); 27950397Sobrien } 28050397Sobrien return value; 28150397Sobrien} 28250397Sobrien 28350397Sobrien/* More 'friendly' abort that prints the line and file. 28450397Sobrien config.h can #define abort fancy_abort if you like that sort of thing. */ 28550397Sobrien 28650397Sobrienvoid 28750397Sobrienfancy_abort () 28850397Sobrien{ 28952284Sobrien fnotice (stderr, "Internal gcc abort.\n"); 29050397Sobrien exit (FATAL_EXIT_CODE); 29150397Sobrien} 29250397Sobrien 29350397Sobrien/* Print a usage message and exit. */ 29450397Sobrien 29550397Sobrienstatic void 29650397Sobrienprint_usage () 29750397Sobrien{ 29852284Sobrien fnotice (stderr, "gcov [-b] [-v] [-n] [-l] [-f] [-o OBJDIR] file\n"); 29950397Sobrien exit (FATAL_EXIT_CODE); 30050397Sobrien} 30150397Sobrien 30250397Sobrien/* Parse the command line. */ 30350397Sobrien 30450397Sobrienstatic void 30550397Sobrienprocess_args (argc, argv) 30650397Sobrien int argc; 30750397Sobrien char **argv; 30850397Sobrien{ 30950397Sobrien int i; 31050397Sobrien 31150397Sobrien for (i = 1; i < argc; i++) 31250397Sobrien { 31350397Sobrien if (argv[i][0] == '-') 31450397Sobrien { 31550397Sobrien if (argv[i][1] == 'b') 31650397Sobrien output_branch_probs = 1; 31750397Sobrien else if (argv[i][1] == 'v') 31850397Sobrien fputs (gcov_version_string, stderr); 31950397Sobrien else if (argv[i][1] == 'n') 32050397Sobrien output_gcov_file = 0; 32150397Sobrien else if (argv[i][1] == 'l') 32250397Sobrien output_long_names = 1; 32350397Sobrien else if (argv[i][1] == 'f') 32450397Sobrien output_function_summary = 1; 32550397Sobrien else if (argv[i][1] == 'o' && argv[i][2] == '\0') 32650397Sobrien object_directory = argv[++i]; 32750397Sobrien else 32850397Sobrien print_usage (); 32950397Sobrien } 33050397Sobrien else if (! input_file_name) 33150397Sobrien input_file_name = argv[i]; 33250397Sobrien else 33350397Sobrien print_usage (); 33450397Sobrien } 33550397Sobrien 33650397Sobrien if (! input_file_name) 33750397Sobrien print_usage (); 33850397Sobrien} 33950397Sobrien 34050397Sobrien 34150397Sobrien/* Find and open the .bb, .da, and .bbg files. */ 34250397Sobrien 34350397Sobrienstatic void 34450397Sobrienopen_files () 34550397Sobrien{ 34650397Sobrien int count, objdir_count; 34750397Sobrien char *cptr; 34850397Sobrien 34950397Sobrien /* Determine the names of the .bb, .bbg, and .da files. Strip off the 35050397Sobrien extension, if any, and append the new extensions. */ 35150397Sobrien count = strlen (input_file_name); 35250397Sobrien if (object_directory) 35350397Sobrien objdir_count = strlen (object_directory); 35450397Sobrien else 35550397Sobrien objdir_count = 0; 35650397Sobrien 35750397Sobrien da_file_name = xmalloc (count + objdir_count + 4); 35850397Sobrien bb_file_name = xmalloc (count + objdir_count + 4); 35950397Sobrien bbg_file_name = xmalloc (count + objdir_count + 5); 36050397Sobrien 36150397Sobrien if (object_directory) 36250397Sobrien { 36350397Sobrien strcpy (da_file_name, object_directory); 36450397Sobrien strcpy (bb_file_name, object_directory); 36550397Sobrien strcpy (bbg_file_name, object_directory); 36650397Sobrien 36750397Sobrien if (object_directory[objdir_count - 1] != '/') 36850397Sobrien { 36950397Sobrien strcat (da_file_name, "/"); 37050397Sobrien strcat (bb_file_name, "/"); 37150397Sobrien strcat (bbg_file_name, "/"); 37250397Sobrien } 37350397Sobrien 37450397Sobrien cptr = rindex (input_file_name, '/'); 37550397Sobrien if (cptr) 37650397Sobrien { 37750397Sobrien strcat (da_file_name, cptr + 1); 37850397Sobrien strcat (bb_file_name, cptr + 1); 37950397Sobrien strcat (bbg_file_name, cptr + 1); 38050397Sobrien } 38150397Sobrien else 38250397Sobrien { 38350397Sobrien strcat (da_file_name, input_file_name); 38450397Sobrien strcat (bb_file_name, input_file_name); 38550397Sobrien strcat (bbg_file_name, input_file_name); 38650397Sobrien } 38750397Sobrien } 38850397Sobrien else 38950397Sobrien { 39050397Sobrien strcpy (da_file_name, input_file_name); 39150397Sobrien strcpy (bb_file_name, input_file_name); 39250397Sobrien strcpy (bbg_file_name, input_file_name); 39350397Sobrien } 39450397Sobrien 39550397Sobrien cptr = rindex (bb_file_name, '.'); 39650397Sobrien if (cptr) 39750397Sobrien strcpy (cptr, ".bb"); 39850397Sobrien else 39950397Sobrien strcat (bb_file_name, ".bb"); 40050397Sobrien 40150397Sobrien cptr = rindex (da_file_name, '.'); 40250397Sobrien if (cptr) 40350397Sobrien strcpy (cptr, ".da"); 40450397Sobrien else 40550397Sobrien strcat (da_file_name, ".da"); 40650397Sobrien 40750397Sobrien cptr = rindex (bbg_file_name, '.'); 40850397Sobrien if (cptr) 40950397Sobrien strcpy (cptr, ".bbg"); 41050397Sobrien else 41150397Sobrien strcat (bbg_file_name, ".bbg"); 41250397Sobrien 41350397Sobrien bb_file = fopen (bb_file_name, "r"); 41450397Sobrien if (bb_file == NULL) 41550397Sobrien { 41652284Sobrien fnotice (stderr, "Could not open basic block file %s.\n", bb_file_name); 41750397Sobrien exit (FATAL_EXIT_CODE); 41850397Sobrien } 41950397Sobrien 42050397Sobrien /* If none of the functions in the file were executed, then there won't 42150397Sobrien be a .da file. Just assume that all counts are zero in this case. */ 42250397Sobrien da_file = fopen (da_file_name, "r"); 42350397Sobrien if (da_file == NULL) 42450397Sobrien { 42552284Sobrien fnotice (stderr, "Could not open data file %s.\n", da_file_name); 42652284Sobrien fnotice (stderr, "Assuming that all execution counts are zero.\n"); 42750397Sobrien } 42850397Sobrien 42950397Sobrien bbg_file = fopen (bbg_file_name, "r"); 43050397Sobrien if (bbg_file == NULL) 43150397Sobrien { 43252284Sobrien fnotice (stderr, "Could not open program flow graph file %s.\n", 43350397Sobrien bbg_file_name); 43450397Sobrien exit (FATAL_EXIT_CODE); 43550397Sobrien } 43650397Sobrien 43750397Sobrien /* Check for empty .bbg file. This indicates that there is no executable 43850397Sobrien code in this source file. */ 43950397Sobrien /* Set the EOF condition if at the end of file. */ 44050397Sobrien ungetc (getc (bbg_file), bbg_file); 44150397Sobrien if (feof (bbg_file)) 44250397Sobrien { 44352284Sobrien fnotice (stderr, "No executable code associated with file %s.\n", 44450397Sobrien input_file_name); 44550397Sobrien exit (FATAL_EXIT_CODE); 44650397Sobrien } 44750397Sobrien} 44850397Sobrien 44950397Sobrien/* Initialize a new arc. */ 45050397Sobrien 45150397Sobrienstatic void 45250397Sobrieninit_arc (arcptr, source, target, bb_graph) 45350397Sobrien struct adj_list *arcptr; 45450397Sobrien int source, target; 45550397Sobrien struct bb_info *bb_graph; 45650397Sobrien{ 45750397Sobrien arcptr->target = target; 45850397Sobrien arcptr->source = source; 45950397Sobrien 46050397Sobrien arcptr->arc_count = 0; 46150397Sobrien arcptr->count_valid = 0; 46250397Sobrien arcptr->on_tree = 0; 46350397Sobrien arcptr->fake = 0; 46450397Sobrien arcptr->fall_through = 0; 46550397Sobrien 46650397Sobrien arcptr->succ_next = bb_graph[source].succ; 46750397Sobrien bb_graph[source].succ = arcptr; 46850397Sobrien bb_graph[source].succ_count++; 46950397Sobrien 47050397Sobrien arcptr->pred_next = bb_graph[target].pred; 47150397Sobrien bb_graph[target].pred = arcptr; 47250397Sobrien bb_graph[target].pred_count++; 47350397Sobrien} 47450397Sobrien 47550397Sobrien 47650397Sobrien/* Reverse the arcs on a arc list. */ 47750397Sobrien 47850397Sobrienstatic struct adj_list * 47950397Sobrienreverse_arcs (arcptr) 48050397Sobrien struct adj_list *arcptr; 48150397Sobrien{ 48250397Sobrien struct adj_list *prev = 0; 48350397Sobrien struct adj_list *next; 48450397Sobrien 48550397Sobrien for ( ; arcptr; arcptr = next) 48650397Sobrien { 48750397Sobrien next = arcptr->succ_next; 48850397Sobrien arcptr->succ_next = prev; 48950397Sobrien prev = arcptr; 49050397Sobrien } 49150397Sobrien 49250397Sobrien return prev; 49350397Sobrien} 49450397Sobrien 49550397Sobrien 49650397Sobrien/* Construct the program flow graph from the .bbg file, and read in the data 49750397Sobrien in the .da file. */ 49850397Sobrien 49950397Sobrienstatic void 50050397Sobriencreate_program_flow_graph (bptr) 50150397Sobrien struct bb_info_list *bptr; 50250397Sobrien{ 50350397Sobrien long num_blocks, number_arcs, src, dest, flag_bits, num_arcs_per_block; 50450397Sobrien int i; 50550397Sobrien struct adj_list *arcptr; 50650397Sobrien struct bb_info *bb_graph; 50750397Sobrien 50850397Sobrien /* Read the number of blocks. */ 50950397Sobrien __read_long (&num_blocks, bbg_file, 4); 51050397Sobrien 51150397Sobrien /* Create an array of size bb number of bb_info structs. Bzero it. */ 51250397Sobrien bb_graph = (struct bb_info *) xmalloc (num_blocks 51350397Sobrien * sizeof (struct bb_info)); 51450397Sobrien bzero ((char *) bb_graph, sizeof (struct bb_info) * num_blocks); 51550397Sobrien 51650397Sobrien bptr->bb_graph = bb_graph; 51750397Sobrien bptr->num_blocks = num_blocks; 51850397Sobrien 51950397Sobrien /* Read and create each arc from the .bbg file. */ 52050397Sobrien __read_long (&number_arcs, bbg_file, 4); 52150397Sobrien for (i = 0; i < num_blocks; i++) 52250397Sobrien { 52350397Sobrien int j; 52450397Sobrien 52550397Sobrien __read_long (&num_arcs_per_block, bbg_file, 4); 52650397Sobrien for (j = 0; j < num_arcs_per_block; j++) 52750397Sobrien { 52850397Sobrien if (number_arcs-- < 0) 52950397Sobrien abort (); 53050397Sobrien 53150397Sobrien src = i; 53250397Sobrien __read_long (&dest, bbg_file, 4); 53350397Sobrien 53450397Sobrien arcptr = (struct adj_list *) xmalloc (sizeof (struct adj_list)); 53550397Sobrien init_arc (arcptr, src, dest, bb_graph); 53650397Sobrien 53750397Sobrien __read_long (&flag_bits, bbg_file, 4); 53850397Sobrien arcptr->on_tree = flag_bits & 0x1; 53950397Sobrien arcptr->fake = !! (flag_bits & 0x2); 54050397Sobrien arcptr->fall_through = !! (flag_bits & 0x4); 54150397Sobrien } 54250397Sobrien } 54350397Sobrien 54450397Sobrien if (number_arcs) 54550397Sobrien abort (); 54650397Sobrien 54750397Sobrien /* Read and ignore the -1 separating the arc list from the arc list of the 54850397Sobrien next function. */ 54950397Sobrien __read_long (&src, bbg_file, 4); 55050397Sobrien if (src != -1) 55150397Sobrien abort (); 55250397Sobrien 55350397Sobrien /* Must reverse the order of all succ arcs, to ensure that they match 55450397Sobrien the order of the data in the .da file. */ 55550397Sobrien 55650397Sobrien for (i = 0; i < num_blocks; i++) 55750397Sobrien if (bb_graph[i].succ) 55850397Sobrien bb_graph[i].succ = reverse_arcs (bb_graph[i].succ); 55950397Sobrien 56050397Sobrien /* For each arc not on the spanning tree, set its execution count from 56150397Sobrien the .da file. */ 56250397Sobrien 56350397Sobrien /* The first count in the .da file is the number of times that the function 56450397Sobrien was entered. This is the exec_count for block zero. */ 56550397Sobrien 56650397Sobrien /* This duplicates code in branch_prob in profile.c. */ 56750397Sobrien 56850397Sobrien for (i = 0; i < num_blocks; i++) 56950397Sobrien for (arcptr = bb_graph[i].succ; arcptr; arcptr = arcptr->succ_next) 57050397Sobrien if (! arcptr->on_tree) 57150397Sobrien { 57250397Sobrien long tmp_count = 0;; 57350397Sobrien if (da_file && __read_long (&tmp_count, da_file, 8)) 57450397Sobrien abort(); 57550397Sobrien 57650397Sobrien arcptr->arc_count = tmp_count; 57750397Sobrien arcptr->count_valid = 1; 57850397Sobrien bb_graph[i].succ_count--; 57950397Sobrien bb_graph[arcptr->target].pred_count--; 58050397Sobrien } 58150397Sobrien} 58250397Sobrien 58350397Sobrienstatic void 58450397Sobriensolve_program_flow_graph (bptr) 58550397Sobrien struct bb_info_list *bptr; 58650397Sobrien{ 58750397Sobrien int passes, changes, total; 58850397Sobrien int i; 58950397Sobrien struct adj_list *arcptr; 59050397Sobrien struct bb_info *bb_graph; 59150397Sobrien int num_blocks; 59250397Sobrien 59350397Sobrien num_blocks = bptr->num_blocks; 59450397Sobrien bb_graph = bptr->bb_graph; 59550397Sobrien 59650397Sobrien /* For every block in the file, 59750397Sobrien - if every exit/entrance arc has a known count, then set the block count 59850397Sobrien - if the block count is known, and every exit/entrance arc but one has 59950397Sobrien a known execution count, then set the count of the remaining arc 60050397Sobrien 60150397Sobrien As arc counts are set, decrement the succ/pred count, but don't delete 60250397Sobrien the arc, that way we can easily tell when all arcs are known, or only 60350397Sobrien one arc is unknown. */ 60450397Sobrien 60550397Sobrien /* The order that the basic blocks are iterated through is important. 60650397Sobrien Since the code that finds spanning trees starts with block 0, low numbered 60750397Sobrien arcs are put on the spanning tree in preference to high numbered arcs. 60850397Sobrien Hence, most instrumented arcs are at the end. Graph solving works much 60950397Sobrien faster if we propagate numbers from the end to the start. 61050397Sobrien 61150397Sobrien This takes an average of slightly more than 3 passes. */ 61250397Sobrien 61350397Sobrien changes = 1; 61450397Sobrien passes = 0; 61550397Sobrien while (changes) 61650397Sobrien { 61750397Sobrien passes++; 61850397Sobrien changes = 0; 61950397Sobrien 62050397Sobrien for (i = num_blocks - 1; i >= 0; i--) 62150397Sobrien { 62250397Sobrien if (! bb_graph[i].count_valid) 62350397Sobrien { 62450397Sobrien if (bb_graph[i].succ_count == 0) 62550397Sobrien { 62650397Sobrien total = 0; 62750397Sobrien for (arcptr = bb_graph[i].succ; arcptr; 62850397Sobrien arcptr = arcptr->succ_next) 62950397Sobrien total += arcptr->arc_count; 63050397Sobrien bb_graph[i].exec_count = total; 63150397Sobrien bb_graph[i].count_valid = 1; 63250397Sobrien changes = 1; 63350397Sobrien } 63450397Sobrien else if (bb_graph[i].pred_count == 0) 63550397Sobrien { 63650397Sobrien total = 0; 63750397Sobrien for (arcptr = bb_graph[i].pred; arcptr; 63850397Sobrien arcptr = arcptr->pred_next) 63950397Sobrien total += arcptr->arc_count; 64050397Sobrien bb_graph[i].exec_count = total; 64150397Sobrien bb_graph[i].count_valid = 1; 64250397Sobrien changes = 1; 64350397Sobrien } 64450397Sobrien } 64550397Sobrien if (bb_graph[i].count_valid) 64650397Sobrien { 64750397Sobrien if (bb_graph[i].succ_count == 1) 64850397Sobrien { 64950397Sobrien total = 0; 65050397Sobrien /* One of the counts will be invalid, but it is zero, 65150397Sobrien so adding it in also doesn't hurt. */ 65250397Sobrien for (arcptr = bb_graph[i].succ; arcptr; 65350397Sobrien arcptr = arcptr->succ_next) 65450397Sobrien total += arcptr->arc_count; 65550397Sobrien /* Calculate count for remaining arc by conservation. */ 65650397Sobrien total = bb_graph[i].exec_count - total; 65750397Sobrien /* Search for the invalid arc, and set its count. */ 65850397Sobrien for (arcptr = bb_graph[i].succ; arcptr; 65950397Sobrien arcptr = arcptr->succ_next) 66050397Sobrien if (! arcptr->count_valid) 66150397Sobrien break; 66250397Sobrien if (! arcptr) 66350397Sobrien abort (); 66450397Sobrien arcptr->count_valid = 1; 66550397Sobrien arcptr->arc_count = total; 66650397Sobrien bb_graph[i].succ_count--; 66750397Sobrien 66850397Sobrien bb_graph[arcptr->target].pred_count--; 66950397Sobrien changes = 1; 67050397Sobrien } 67150397Sobrien if (bb_graph[i].pred_count == 1) 67250397Sobrien { 67350397Sobrien total = 0; 67450397Sobrien /* One of the counts will be invalid, but it is zero, 67550397Sobrien so adding it in also doesn't hurt. */ 67650397Sobrien for (arcptr = bb_graph[i].pred; arcptr; 67750397Sobrien arcptr = arcptr->pred_next) 67850397Sobrien total += arcptr->arc_count; 67950397Sobrien /* Calculate count for remaining arc by conservation. */ 68050397Sobrien total = bb_graph[i].exec_count - total; 68150397Sobrien /* Search for the invalid arc, and set its count. */ 68250397Sobrien for (arcptr = bb_graph[i].pred; arcptr; 68350397Sobrien arcptr = arcptr->pred_next) 68450397Sobrien if (! arcptr->count_valid) 68550397Sobrien break; 68650397Sobrien if (! arcptr) 68750397Sobrien abort (); 68850397Sobrien arcptr->count_valid = 1; 68950397Sobrien arcptr->arc_count = total; 69050397Sobrien bb_graph[i].pred_count--; 69150397Sobrien 69250397Sobrien bb_graph[arcptr->source].succ_count--; 69350397Sobrien changes = 1; 69450397Sobrien } 69550397Sobrien } 69650397Sobrien } 69750397Sobrien } 69850397Sobrien 69950397Sobrien /* If the graph has been correctly solved, every block will have a 70050397Sobrien succ and pred count of zero. */ 70150397Sobrien for (i = 0; i < num_blocks; i++) 70250397Sobrien if (bb_graph[i].succ_count || bb_graph[i].pred_count) 70350397Sobrien abort (); 70450397Sobrien} 70550397Sobrien 70650397Sobrien 70750397Sobrienstatic void 70850397Sobrienread_files () 70950397Sobrien{ 71050397Sobrien struct stat buf; 71150397Sobrien struct bb_info_list *list_end = 0; 71250397Sobrien struct bb_info_list *b_ptr; 71350397Sobrien long total; 71450397Sobrien 71550397Sobrien /* Read and ignore the first word of the .da file, which is the count of 71650397Sobrien how many numbers follow. */ 71750397Sobrien if (da_file && __read_long (&total, da_file, 8)) 71850397Sobrien abort(); 71950397Sobrien 72050397Sobrien while (! feof (bbg_file)) 72150397Sobrien { 72250397Sobrien b_ptr = (struct bb_info_list *) xmalloc (sizeof (struct bb_info_list)); 72350397Sobrien 72450397Sobrien b_ptr->next = 0; 72550397Sobrien if (list_end) 72650397Sobrien list_end->next = b_ptr; 72750397Sobrien else 72850397Sobrien bb_graph_list = b_ptr; 72950397Sobrien list_end = b_ptr; 73050397Sobrien 73150397Sobrien /* Read in the data in the .bbg file and reconstruct the program flow 73250397Sobrien graph for one function. */ 73350397Sobrien create_program_flow_graph (b_ptr); 73450397Sobrien 73550397Sobrien /* Set the EOF condition if at the end of file. */ 73650397Sobrien ungetc (getc (bbg_file), bbg_file); 73750397Sobrien } 73850397Sobrien 73950397Sobrien /* Check to make sure the .da file data is valid. */ 74050397Sobrien 74150397Sobrien if (da_file) 74250397Sobrien { 74350397Sobrien if (feof (da_file)) 74452284Sobrien fnotice (stderr, ".da file contents exhausted too early\n"); 74550397Sobrien /* Should be at end of file now. */ 74650397Sobrien if (__read_long (&total, da_file, 8) == 0) 74752284Sobrien fnotice (stderr, ".da file contents not exhausted\n"); 74850397Sobrien } 74950397Sobrien 75050397Sobrien /* Calculate all of the basic block execution counts and branch 75150397Sobrien taken probabilities. */ 75250397Sobrien 75350397Sobrien for (b_ptr = bb_graph_list; b_ptr; b_ptr = b_ptr->next) 75450397Sobrien solve_program_flow_graph (b_ptr); 75550397Sobrien 75650397Sobrien /* Read in all of the data from the .bb file. This info will be accessed 75750397Sobrien sequentially twice. */ 75850397Sobrien stat (bb_file_name, &buf); 75950397Sobrien bb_data_size = buf.st_size / 4; 76050397Sobrien 76150397Sobrien bb_data = (char *) xmalloc ((unsigned) buf.st_size); 76250397Sobrien fread (bb_data, sizeof (char), buf.st_size, bb_file); 76350397Sobrien 76450397Sobrien fclose (bb_file); 76550397Sobrien if (da_file) 76650397Sobrien fclose (da_file); 76750397Sobrien fclose (bbg_file); 76850397Sobrien} 76950397Sobrien 77050397Sobrien 77150397Sobrien/* Scan the data in the .bb file to find all source files referenced, 77250397Sobrien and the largest line number mentioned in each one. */ 77350397Sobrien 77450397Sobrienstatic void 77550397Sobrienscan_for_source_files () 77650397Sobrien{ 77750397Sobrien struct sourcefile *s_ptr = NULL; 77850397Sobrien char *ptr; 77950397Sobrien int count; 78050397Sobrien long line_num; 78150397Sobrien 78250397Sobrien /* Search the bb_data to find: 78350397Sobrien 1) The number of sources files contained herein, and 78450397Sobrien 2) The largest line number for each source file. */ 78550397Sobrien 78650397Sobrien ptr = bb_data; 78750397Sobrien sources = 0; 78850397Sobrien for (count = 0; count < bb_data_size; count++) 78950397Sobrien { 79050397Sobrien __fetch_long (&line_num, ptr, 4); 79150397Sobrien ptr += 4; 79250397Sobrien if (line_num == -1) 79350397Sobrien { 79450397Sobrien /* A source file name follows. Check to see if we already have 79550397Sobrien a sourcefile structure for this file. */ 79650397Sobrien s_ptr = sources; 79750397Sobrien while (s_ptr && strcmp (s_ptr->name, ptr)) 79850397Sobrien s_ptr = s_ptr->next; 79950397Sobrien 80050397Sobrien if (s_ptr == 0) 80150397Sobrien { 80250397Sobrien /* No sourcefile structure for this file name exists, create 80350397Sobrien a new one, and append it to the front of the sources list. */ 80450397Sobrien s_ptr = (struct sourcefile *) xmalloc (sizeof(struct sourcefile)); 80550397Sobrien s_ptr->name = xmalloc (strlen ((char *) ptr) + 1); 80650397Sobrien strcpy (s_ptr->name, (char *) ptr); 80750397Sobrien s_ptr->maxlineno = 0; 80850397Sobrien s_ptr->next = sources; 80950397Sobrien sources = s_ptr; 81050397Sobrien } 81150397Sobrien 81250397Sobrien /* Scan past the file name. */ 81350397Sobrien { 81450397Sobrien long delim; 81550397Sobrien do { 81650397Sobrien count++; 81750397Sobrien __fetch_long (&delim, ptr, 4); 81850397Sobrien ptr += 4; 81950397Sobrien } while (delim != line_num); 82050397Sobrien } 82150397Sobrien } 82250397Sobrien else if (line_num == -2) 82350397Sobrien { 82450397Sobrien long delim; 82550397Sobrien 82650397Sobrien /* A function name follows. Ignore it. */ 82750397Sobrien do { 82850397Sobrien count++; 82950397Sobrien __fetch_long (&delim, ptr, 4); 83050397Sobrien ptr += 4; 83150397Sobrien } while (delim != line_num); 83250397Sobrien } 83350397Sobrien /* There will be a zero before the first file name, in which case s_ptr 83450397Sobrien will still be uninitialized. So, only try to set the maxlineno 83550397Sobrien field if line_num is non-zero. */ 83650397Sobrien else if (line_num > 0) 83750397Sobrien { 83850397Sobrien if (s_ptr->maxlineno <= line_num) 83950397Sobrien s_ptr->maxlineno = line_num + 1; 84050397Sobrien } 84150397Sobrien else if (line_num < 0) 84250397Sobrien { 84350397Sobrien /* Don't know what this is, but it's garbage. */ 84450397Sobrien abort(); 84550397Sobrien } 84650397Sobrien } 84750397Sobrien} 84850397Sobrien 84950397Sobrien/* For calculating coverage at the function level. */ 85050397Sobrien 85150397Sobrienstatic int function_source_lines; 85250397Sobrienstatic int function_source_lines_executed; 85350397Sobrienstatic int function_branches; 85450397Sobrienstatic int function_branches_executed; 85550397Sobrienstatic int function_branches_taken; 85650397Sobrienstatic int function_calls; 85750397Sobrienstatic int function_calls_executed; 85850397Sobrienstatic char *function_name; 85950397Sobrien 86050397Sobrien/* Calculate the branch taken probabilities for all arcs branches at the 86150397Sobrien end of this block. */ 86250397Sobrien 86350397Sobrienstatic void 86450397Sobriencalculate_branch_probs (current_graph, block_num, branch_probs, last_line_num) 86550397Sobrien struct bb_info_list *current_graph; 86650397Sobrien int block_num; 86750397Sobrien struct arcdata **branch_probs; 86850397Sobrien int last_line_num; 86950397Sobrien{ 87050397Sobrien int total; 87150397Sobrien struct adj_list *arcptr; 87250397Sobrien struct arcdata *end_ptr, *a_ptr; 87350397Sobrien 87450397Sobrien total = current_graph->bb_graph[block_num].exec_count; 87550397Sobrien for (arcptr = current_graph->bb_graph[block_num].succ; arcptr; 87650397Sobrien arcptr = arcptr->succ_next) 87750397Sobrien { 87850397Sobrien /* Ignore fall through arcs as they aren't really branches. */ 87950397Sobrien 88050397Sobrien if (arcptr->fall_through) 88150397Sobrien continue; 88250397Sobrien 88350397Sobrien a_ptr = (struct arcdata *) xmalloc (sizeof (struct arcdata)); 88450397Sobrien if (total == 0) 88550397Sobrien a_ptr->prob = -1; 88650397Sobrien else 88750397Sobrien a_ptr->prob = ((arcptr->arc_count * 100) + (total >> 1)) / total; 88850397Sobrien a_ptr->call_insn = arcptr->fake; 88950397Sobrien 89050397Sobrien if (output_function_summary) 89150397Sobrien { 89250397Sobrien if (a_ptr->call_insn) 89350397Sobrien { 89450397Sobrien function_calls++; 89550397Sobrien if (a_ptr->prob != -1) 89650397Sobrien function_calls_executed++; 89750397Sobrien } 89850397Sobrien else 89950397Sobrien { 90050397Sobrien function_branches++; 90150397Sobrien if (a_ptr->prob != -1) 90250397Sobrien function_branches_executed++; 90350397Sobrien if (a_ptr->prob > 0) 90450397Sobrien function_branches_taken++; 90550397Sobrien } 90650397Sobrien } 90750397Sobrien 90850397Sobrien /* Append the new branch to the end of the list. */ 90950397Sobrien a_ptr->next = 0; 91050397Sobrien if (! branch_probs[last_line_num]) 91150397Sobrien branch_probs[last_line_num] = a_ptr; 91250397Sobrien else 91350397Sobrien { 91450397Sobrien end_ptr = branch_probs[last_line_num]; 91550397Sobrien while (end_ptr->next != 0) 91650397Sobrien end_ptr = end_ptr->next; 91750397Sobrien end_ptr->next = a_ptr; 91850397Sobrien } 91950397Sobrien } 92050397Sobrien} 92150397Sobrien 92250397Sobrien/* Output summary info for a function. */ 92350397Sobrien 92450397Sobrienstatic void 92550397Sobrienfunction_summary () 92650397Sobrien{ 92750397Sobrien if (function_source_lines) 92852284Sobrien fnotice (stdout, "%6.2f%% of %d source lines executed in function %s\n", 92950397Sobrien (((double) function_source_lines_executed / function_source_lines) 93050397Sobrien * 100), function_source_lines, function_name); 93150397Sobrien else 93252284Sobrien fnotice (stdout, "No executable source lines in function %s\n", 93350397Sobrien function_name); 93450397Sobrien 93550397Sobrien if (output_branch_probs) 93650397Sobrien { 93750397Sobrien if (function_branches) 93850397Sobrien { 93952284Sobrien fnotice (stdout, "%6.2f%% of %d branches executed in function %s\n", 94050397Sobrien (((double) function_branches_executed / function_branches) 94150397Sobrien * 100), function_branches, function_name); 94252284Sobrien fnotice (stdout, 94350397Sobrien "%6.2f%% of %d branches taken at least once in function %s\n", 94450397Sobrien (((double) function_branches_taken / function_branches) 94550397Sobrien * 100), function_branches, function_name); 94650397Sobrien } 94750397Sobrien else 94852284Sobrien fnotice (stdout, "No branches in function %s\n", function_name); 94950397Sobrien if (function_calls) 95052284Sobrien fnotice (stdout, "%6.2f%% of %d calls executed in function %s\n", 95150397Sobrien (((double) function_calls_executed / function_calls) 95250397Sobrien * 100), function_calls, function_name); 95350397Sobrien else 95452284Sobrien fnotice (stdout, "No calls in function %s\n", function_name); 95550397Sobrien } 95650397Sobrien} 95750397Sobrien 95850397Sobrien/* Calculate line execution counts, and output the data to a .tcov file. */ 95950397Sobrien 96050397Sobrienstatic void 96150397Sobrienoutput_data () 96250397Sobrien{ 96350397Sobrien /* When scanning data, this is true only if the data applies to the 96450397Sobrien current source file. */ 96550397Sobrien int this_file; 96650397Sobrien /* An array indexed by line number which indicates how many times that line 96750397Sobrien was executed. */ 96850397Sobrien long *line_counts; 96950397Sobrien /* An array indexed by line number which indicates whether the line was 97050397Sobrien present in the bb file (i.e. whether it had code associate with it). 97150397Sobrien Lines never executed are those which both exist, and have zero execution 97250397Sobrien counts. */ 97350397Sobrien char *line_exists; 97450397Sobrien /* An array indexed by line number, which contains a list of branch 97550397Sobrien probabilities, one for each branch on that line. */ 97650397Sobrien struct arcdata **branch_probs = NULL; 97750397Sobrien struct sourcefile *s_ptr; 97850397Sobrien char *source_file_name; 97950397Sobrien FILE *source_file; 98050397Sobrien struct bb_info_list *current_graph; 98150397Sobrien int count; 98250397Sobrien char *cptr; 98350397Sobrien long block_num; 98450397Sobrien long line_num; 98550397Sobrien long last_line_num = 0; 98650397Sobrien int i; 98750397Sobrien struct arcdata *a_ptr; 98850397Sobrien /* Buffer used for reading in lines from the source file. */ 98950397Sobrien char string[STRING_SIZE]; 99050397Sobrien /* For calculating coverage at the file level. */ 99150397Sobrien int total_source_lines; 99250397Sobrien int total_source_lines_executed; 99350397Sobrien int total_branches; 99450397Sobrien int total_branches_executed; 99550397Sobrien int total_branches_taken; 99650397Sobrien int total_calls; 99750397Sobrien int total_calls_executed; 99850397Sobrien 99950397Sobrien /* Now, for each source file, allocate an array big enough to hold a count 100050397Sobrien for each line. Scan through the bb_data, and when the file name matches 100150397Sobrien the current file name, then for each following line number, increment 100250397Sobrien the line number execution count indicated by the execution count of 100350397Sobrien the appropriate basic block. */ 100450397Sobrien 100550397Sobrien for (s_ptr = sources; s_ptr; s_ptr = s_ptr->next) 100650397Sobrien { 100750397Sobrien /* If this is a relative file name, and an object directory has been 100850397Sobrien specified, then make it relative to the object directory name. */ 100950397Sobrien if (*s_ptr->name != '/' && object_directory != 0 101050397Sobrien && *object_directory != '\0') 101150397Sobrien { 101250397Sobrien int objdir_count = strlen (object_directory); 101350397Sobrien source_file_name = xmalloc (objdir_count + strlen (s_ptr->name) + 2); 101450397Sobrien strcpy (source_file_name, object_directory); 101550397Sobrien if (object_directory[objdir_count - 1] != '/') 101650397Sobrien source_file_name[objdir_count++] = '/'; 101750397Sobrien strcpy (source_file_name + objdir_count, s_ptr->name); 101850397Sobrien } 101950397Sobrien else 102050397Sobrien source_file_name = s_ptr->name; 102150397Sobrien 102250397Sobrien line_counts = (long *) xmalloc (sizeof (long) * s_ptr->maxlineno); 102350397Sobrien bzero ((char *) line_counts, sizeof (long) * s_ptr->maxlineno); 102450397Sobrien line_exists = xmalloc (s_ptr->maxlineno); 102550397Sobrien bzero (line_exists, s_ptr->maxlineno); 102650397Sobrien if (output_branch_probs) 102750397Sobrien { 102852284Sobrien branch_probs = (struct arcdata **) xmalloc (sizeof (struct arcdata *) 102950397Sobrien * s_ptr->maxlineno); 103050397Sobrien bzero ((char *) branch_probs, 103152284Sobrien sizeof (struct arcdata *) * s_ptr->maxlineno); 103250397Sobrien } 103350397Sobrien 103450397Sobrien /* There will be a zero at the beginning of the bb info, before the 103550397Sobrien first list of line numbers, so must initialize block_num to 0. */ 103650397Sobrien block_num = 0; 103750397Sobrien this_file = 0; 103850397Sobrien current_graph = 0; 103950397Sobrien { 104050397Sobrien /* Pointer into the bb_data, incremented while scanning the data. */ 104150397Sobrien char *ptr = bb_data; 104250397Sobrien for (count = 0; count < bb_data_size; count++) 104350397Sobrien { 104450397Sobrien long delim; 104550397Sobrien 104650397Sobrien __fetch_long (&line_num, ptr, 4); 104750397Sobrien ptr += 4; 104850397Sobrien if (line_num == -1) 104950397Sobrien { 105050397Sobrien /* Marks the beginning of a file name. Check to see whether 105150397Sobrien this is the filename we are currently collecting data for. */ 105250397Sobrien 105350397Sobrien if (strcmp (s_ptr->name, ptr)) 105450397Sobrien this_file = 0; 105550397Sobrien else 105650397Sobrien this_file = 1; 105750397Sobrien 105850397Sobrien /* Scan past the file name. */ 105950397Sobrien do { 106050397Sobrien count++; 106150397Sobrien __fetch_long (&delim, ptr, 4); 106250397Sobrien ptr += 4; 106350397Sobrien } while (delim != line_num); 106450397Sobrien } 106550397Sobrien else if (line_num == -2) 106650397Sobrien { 106750397Sobrien /* Marks the start of a new function. Advance to the next 106850397Sobrien program flow graph. */ 106950397Sobrien 107050397Sobrien if (! current_graph) 107150397Sobrien current_graph = bb_graph_list; 107250397Sobrien else 107350397Sobrien { 107450397Sobrien if (block_num == current_graph->num_blocks - 1) 107550397Sobrien /* Last block falls through to exit. */ 107650397Sobrien ; 107750397Sobrien else if (block_num == current_graph->num_blocks - 2) 107850397Sobrien { 107950397Sobrien if (output_branch_probs && this_file) 108050397Sobrien calculate_branch_probs (current_graph, block_num, 108150397Sobrien branch_probs, last_line_num); 108250397Sobrien } 108350397Sobrien else 108450397Sobrien { 108552284Sobrien fnotice (stderr, 108650397Sobrien "didn't use all bb entries of graph, function %s\n", 108750397Sobrien function_name); 108852284Sobrien fnotice (stderr, "block_num = %ld, num_blocks = %d\n", 108950397Sobrien block_num, current_graph->num_blocks); 109050397Sobrien } 109150397Sobrien 109250397Sobrien current_graph = current_graph->next; 109350397Sobrien block_num = 0; 109450397Sobrien 109550397Sobrien if (output_function_summary && this_file) 109650397Sobrien function_summary (); 109750397Sobrien } 109850397Sobrien 109950397Sobrien if (output_function_summary) 110050397Sobrien { 110150397Sobrien function_source_lines = 0; 110250397Sobrien function_source_lines_executed = 0; 110350397Sobrien function_branches = 0; 110450397Sobrien function_branches_executed = 0; 110550397Sobrien function_branches_taken = 0; 110650397Sobrien function_calls = 0; 110750397Sobrien function_calls_executed = 0; 110850397Sobrien } 110950397Sobrien 111050397Sobrien /* Save the function name for later use. */ 111150397Sobrien function_name = ptr; 111250397Sobrien 111350397Sobrien /* Scan past the file name. */ 111450397Sobrien do { 111550397Sobrien count++; 111650397Sobrien __fetch_long (&delim, ptr, 4); 111750397Sobrien ptr += 4; 111850397Sobrien } while (delim != line_num); 111950397Sobrien } 112050397Sobrien else if (line_num == 0) 112150397Sobrien { 112250397Sobrien /* Marks the end of a block. */ 112350397Sobrien 112450397Sobrien if (block_num >= current_graph->num_blocks) 112550397Sobrien { 112652284Sobrien fnotice (stderr, "ERROR: too many basic blocks in .bb file %s\n", 112750397Sobrien function_name); 112850397Sobrien abort (); 112950397Sobrien } 113050397Sobrien 113150397Sobrien if (output_branch_probs && this_file) 113250397Sobrien calculate_branch_probs (current_graph, block_num, 113350397Sobrien branch_probs, last_line_num); 113450397Sobrien 113550397Sobrien block_num++; 113650397Sobrien } 113750397Sobrien else if (this_file) 113850397Sobrien { 113950397Sobrien if (output_function_summary) 114050397Sobrien { 114150397Sobrien if (line_exists[line_num] == 0) 114250397Sobrien function_source_lines++; 114350397Sobrien if (line_counts[line_num] == 0 114450397Sobrien && current_graph->bb_graph[block_num].exec_count != 0) 114550397Sobrien function_source_lines_executed++; 114650397Sobrien } 114750397Sobrien 114850397Sobrien /* Accumulate execution data for this line number. */ 114950397Sobrien 115050397Sobrien line_counts[line_num] 115150397Sobrien += current_graph->bb_graph[block_num].exec_count; 115250397Sobrien line_exists[line_num] = 1; 115350397Sobrien last_line_num = line_num; 115450397Sobrien } 115550397Sobrien } 115650397Sobrien } 115750397Sobrien 115850397Sobrien if (output_function_summary && this_file) 115950397Sobrien function_summary (); 116050397Sobrien 116150397Sobrien /* Calculate summary test coverage statistics. */ 116250397Sobrien 116350397Sobrien total_source_lines = 0; 116450397Sobrien total_source_lines_executed = 0; 116550397Sobrien total_branches = 0; 116650397Sobrien total_branches_executed = 0; 116750397Sobrien total_branches_taken = 0; 116850397Sobrien total_calls = 0; 116950397Sobrien total_calls_executed = 0; 117050397Sobrien 117150397Sobrien for (count = 1; count < s_ptr->maxlineno; count++) 117250397Sobrien { 117350397Sobrien if (line_exists[count]) 117450397Sobrien { 117550397Sobrien total_source_lines++; 117650397Sobrien if (line_counts[count]) 117750397Sobrien total_source_lines_executed++; 117850397Sobrien } 117950397Sobrien if (output_branch_probs) 118050397Sobrien { 118150397Sobrien for (a_ptr = branch_probs[count]; a_ptr; a_ptr = a_ptr->next) 118250397Sobrien { 118350397Sobrien if (a_ptr->call_insn) 118450397Sobrien { 118550397Sobrien total_calls++; 118650397Sobrien if (a_ptr->prob != -1) 118750397Sobrien total_calls_executed++; 118850397Sobrien } 118950397Sobrien else 119050397Sobrien { 119150397Sobrien total_branches++; 119250397Sobrien if (a_ptr->prob != -1) 119350397Sobrien total_branches_executed++; 119450397Sobrien if (a_ptr->prob > 0) 119550397Sobrien total_branches_taken++; 119650397Sobrien } 119750397Sobrien } 119850397Sobrien } 119950397Sobrien } 120050397Sobrien 120150397Sobrien if (total_source_lines) 120252284Sobrien fnotice (stdout, 120350397Sobrien "%6.2f%% of %d source lines executed in file %s\n", 120450397Sobrien (((double) total_source_lines_executed / total_source_lines) 120550397Sobrien * 100), total_source_lines, source_file_name); 120650397Sobrien else 120752284Sobrien fnotice (stdout, "No executable source lines in file %s\n", 120850397Sobrien source_file_name); 120950397Sobrien 121050397Sobrien if (output_branch_probs) 121150397Sobrien { 121250397Sobrien if (total_branches) 121350397Sobrien { 121452284Sobrien fnotice (stdout, "%6.2f%% of %d branches executed in file %s\n", 121550397Sobrien (((double) total_branches_executed / total_branches) 121650397Sobrien * 100), total_branches, source_file_name); 121752284Sobrien fnotice (stdout, 121850397Sobrien "%6.2f%% of %d branches taken at least once in file %s\n", 121950397Sobrien (((double) total_branches_taken / total_branches) 122050397Sobrien * 100), total_branches, source_file_name); 122150397Sobrien } 122250397Sobrien else 122352284Sobrien fnotice (stdout, "No branches in file %s\n", source_file_name); 122450397Sobrien if (total_calls) 122552284Sobrien fnotice (stdout, "%6.2f%% of %d calls executed in file %s\n", 122650397Sobrien (((double) total_calls_executed / total_calls) 122750397Sobrien * 100), total_calls, source_file_name); 122850397Sobrien else 122952284Sobrien fnotice (stdout, "No calls in file %s\n", source_file_name); 123050397Sobrien } 123150397Sobrien 123250397Sobrien if (output_gcov_file) 123350397Sobrien { 123450397Sobrien /* Now the statistics are ready. Read in the source file one line 123550397Sobrien at a time, and output that line to the gcov file preceded by 123650397Sobrien its execution count if non zero. */ 123750397Sobrien 123850397Sobrien source_file = fopen (source_file_name, "r"); 123950397Sobrien if (source_file == NULL) 124050397Sobrien { 124152284Sobrien fnotice (stderr, "Could not open source file %s.\n", 124250397Sobrien source_file_name); 124350397Sobrien free (line_counts); 124450397Sobrien free (line_exists); 124550397Sobrien continue; 124650397Sobrien } 124750397Sobrien 124850397Sobrien count = strlen (source_file_name); 124950397Sobrien cptr = rindex (s_ptr->name, '/'); 125050397Sobrien if (cptr) 125150397Sobrien cptr = cptr + 1; 125250397Sobrien else 125350397Sobrien cptr = s_ptr->name; 125450397Sobrien if (output_long_names && strcmp (cptr, input_file_name)) 125550397Sobrien { 125650397Sobrien gcov_file_name = xmalloc (count + 7 + strlen (input_file_name)); 125750397Sobrien 125850397Sobrien cptr = rindex (input_file_name, '/'); 125950397Sobrien if (cptr) 126050397Sobrien strcpy (gcov_file_name, cptr + 1); 126150397Sobrien else 126250397Sobrien strcpy (gcov_file_name, input_file_name); 126350397Sobrien 126450397Sobrien strcat (gcov_file_name, "."); 126550397Sobrien 126650397Sobrien cptr = rindex (source_file_name, '/'); 126750397Sobrien if (cptr) 126850397Sobrien strcat (gcov_file_name, cptr + 1); 126950397Sobrien else 127050397Sobrien strcat (gcov_file_name, source_file_name); 127150397Sobrien } 127250397Sobrien else 127350397Sobrien { 127450397Sobrien gcov_file_name = xmalloc (count + 6); 127550397Sobrien cptr = rindex (source_file_name, '/'); 127650397Sobrien if (cptr) 127750397Sobrien strcpy (gcov_file_name, cptr + 1); 127850397Sobrien else 127950397Sobrien strcpy (gcov_file_name, source_file_name); 128050397Sobrien } 128150397Sobrien 128250397Sobrien /* Don't strip off the ending for compatibility with tcov, since 128350397Sobrien this results in confusion if there is more than one file with 128450397Sobrien the same basename, e.g. tmp.c and tmp.h. */ 128550397Sobrien strcat (gcov_file_name, ".gcov"); 128650397Sobrien 128750397Sobrien gcov_file = fopen (gcov_file_name, "w"); 128850397Sobrien 128950397Sobrien if (gcov_file == NULL) 129050397Sobrien { 129152284Sobrien fnotice (stderr, "Could not open output file %s.\n", 129250397Sobrien gcov_file_name); 129350397Sobrien fclose (source_file); 129450397Sobrien free (line_counts); 129550397Sobrien free (line_exists); 129650397Sobrien continue; 129750397Sobrien } 129850397Sobrien 129952284Sobrien fnotice (stdout, "Creating %s.\n", gcov_file_name); 130050397Sobrien 130150397Sobrien for (count = 1; count < s_ptr->maxlineno; count++) 130250397Sobrien { 130350397Sobrien char *retval; 130450397Sobrien int len; 130550397Sobrien 130650397Sobrien retval = fgets (string, STRING_SIZE, source_file); 130750397Sobrien 130850397Sobrien /* For lines which don't exist in the .bb file, print nothing 130950397Sobrien before the source line. For lines which exist but were never 131050397Sobrien executed, print ###### before the source line. Otherwise, 131150397Sobrien print the execution count before the source line. */ 131250397Sobrien /* There are 16 spaces of indentation added before the source 131350397Sobrien line so that tabs won't be messed up. */ 131450397Sobrien if (line_exists[count]) 131550397Sobrien { 131650397Sobrien if (line_counts[count]) 131750397Sobrien fprintf (gcov_file, "%12ld %s", line_counts[count], 131850397Sobrien string); 131950397Sobrien else 132050397Sobrien fprintf (gcov_file, " ###### %s", string); 132150397Sobrien } 132250397Sobrien else 132350397Sobrien fprintf (gcov_file, "\t\t%s", string); 132450397Sobrien 132550397Sobrien /* In case the source file line is larger than our buffer, keep 132650397Sobrien reading and outputting lines until we get a newline. */ 132750397Sobrien len = strlen (string); 132850397Sobrien while ((len == 0 || string[strlen (string) - 1] != '\n') 132950397Sobrien && retval != NULL) 133050397Sobrien { 133150397Sobrien retval = fgets (string, STRING_SIZE, source_file); 133250397Sobrien fputs (string, gcov_file); 133350397Sobrien } 133450397Sobrien 133550397Sobrien if (output_branch_probs) 133650397Sobrien { 133750397Sobrien for (i = 0, a_ptr = branch_probs[count]; a_ptr; 133850397Sobrien a_ptr = a_ptr->next, i++) 133950397Sobrien { 134050397Sobrien if (a_ptr->call_insn) 134150397Sobrien { 134250397Sobrien if (a_ptr->prob == -1) 134352284Sobrien fnotice (gcov_file, "call %d never executed\n", i); 134450397Sobrien else 134552284Sobrien fnotice (gcov_file, 134650397Sobrien "call %d returns = %d%%\n", 134750397Sobrien i, 100 - a_ptr->prob); 134850397Sobrien } 134950397Sobrien else 135050397Sobrien { 135150397Sobrien if (a_ptr->prob == -1) 135252284Sobrien fnotice (gcov_file, "branch %d never executed\n", 135350397Sobrien i); 135450397Sobrien else 135552284Sobrien fnotice (gcov_file, "branch %d taken = %d%%\n", i, 135650397Sobrien a_ptr->prob); 135750397Sobrien } 135850397Sobrien } 135950397Sobrien } 136050397Sobrien 136150397Sobrien /* Gracefully handle errors while reading the source file. */ 136250397Sobrien if (retval == NULL) 136350397Sobrien { 136452284Sobrien fnotice (stderr, 136550397Sobrien "Unexpected EOF while reading source file %s.\n", 136650397Sobrien source_file_name); 136750397Sobrien break; 136850397Sobrien } 136950397Sobrien } 137050397Sobrien 137150397Sobrien /* Handle all remaining source lines. There may be lines 137250397Sobrien after the last line of code. */ 137350397Sobrien 137450397Sobrien { 137550397Sobrien char *retval = fgets (string, STRING_SIZE, source_file); 137650397Sobrien while (retval != NULL) 137750397Sobrien { 137850397Sobrien int len; 137950397Sobrien 138050397Sobrien fprintf (gcov_file, "\t\t%s", string); 138150397Sobrien 138250397Sobrien /* In case the source file line is larger than our buffer, keep 138350397Sobrien reading and outputting lines until we get a newline. */ 138450397Sobrien len = strlen (string); 138550397Sobrien while ((len == 0 || string[strlen (string) - 1] != '\n') 138650397Sobrien && retval != NULL) 138750397Sobrien { 138850397Sobrien retval = fgets (string, STRING_SIZE, source_file); 138950397Sobrien fputs (string, gcov_file); 139050397Sobrien } 139150397Sobrien 139250397Sobrien retval = fgets (string, STRING_SIZE, source_file); 139350397Sobrien } 139450397Sobrien } 139550397Sobrien 139650397Sobrien fclose (source_file); 139750397Sobrien fclose (gcov_file); 139850397Sobrien } 139950397Sobrien 140050397Sobrien free (line_counts); 140150397Sobrien free (line_exists); 140250397Sobrien } 140350397Sobrien} 1404