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