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