summaryrefslogtreecommitdiff
path: root/contrib/gcc/gcov.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/gcc/gcov.c')
-rw-r--r--contrib/gcc/gcov.c1403
1 files changed, 0 insertions, 1403 deletions
diff --git a/contrib/gcc/gcov.c b/contrib/gcc/gcov.c
deleted file mode 100644
index 629aa0239853..000000000000
--- a/contrib/gcc/gcov.c
+++ /dev/null
@@ -1,1403 +0,0 @@
-/* Gcov.c: prepend line execution counts and branch probabilities to a
- source file.
- Copyright (C) 1990, 91-94, 96, 97, 98, 1999 Free Software Foundation, Inc.
- Contributed by James E. Wilson of Cygnus Support.
- Mangled by Bob Manson of Cygnus Support.
-
-Gcov is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
-
-Gcov is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with Gcov; see the file COPYING. If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
-
-/* ??? The code in final.c that produces the struct bb assumes that there is
- no padding between the fields. This is not necessary true. The current
- code can only be trusted if longs and pointers are the same size. */
-
-/* ??? No need to print an execution count on every line, could just print
- it on the first line of each block, and only print it on a subsequent
- line in the same block if the count changes. */
-
-/* ??? Print a list of the ten blocks with the highest execution counts,
- and list the line numbers corresponding to those blocks. Also, perhaps
- list the line numbers with the highest execution counts, only printing
- the first if there are several which are all listed in the same block. */
-
-/* ??? Should have an option to print the number of basic blocks, and the
- percent of them that are covered. */
-
-/* ??? Does not correctly handle the case where two .bb files refer to the
- same included source file. For example, if one has a short file containing
- only inline functions, which is then included in two other files, then
- there will be two .bb files which refer to the include file, but there
- is no way to get the total execution counts for the included file, can
- only get execution counts for one or the other of the including files. */
-
-#include "config.h"
-#include "system.h"
-#include "intl.h"
-#undef abort
-
-#include "gcov-io.h"
-
-/* The .bb file format consists of several lists of 4-byte integers
- which are the line numbers of each basic block in the file. Each
- list is terminated by a zero. These lists correspond to the basic
- blocks in the reconstructed program flow graph.
-
- A line number of -1 indicates that a source file name (padded to a
- long boundary) follows. The padded file name is followed by
- another -1 to make it easy to scan past file names. A -2 indicates
- that a function name (padded to a long boundary) follows; the name
- is followed by another -2 to make it easy to scan past the function
- name.
-
- The .bbg file contains enough info to enable gcov to reconstruct the
- program flow graph. The first word is the number of basic blocks,
- the second word is the number of arcs, followed by the list of arcs
- (source bb, dest bb pairs), then a -1, then the number of instrumented
- arcs followed by the instrumented arcs, followed by another -1. This
- is repeated for each function.
-
- The .da file contains the execution count for each instrumented branch.
-
- The .bb and .bbg files are created by giving GCC the -ftest-coverage option,
- and the .da files are created when an executable compiled with
- -fprofile-arcs is run. */
-
-/* The functions in this file for creating and solution program flow graphs
- are very similar to functions in the gcc source file profile.c. */
-
-char gcov_version_string[] = "GNU gcov version 1.5\n";
-
-/* This is the size of the buffer used to read in source file lines. */
-
-#define STRING_SIZE 200
-
-/* One copy of this structure is created for each source file mentioned in the
- .bb file. */
-
-struct sourcefile
-{
- char *name;
- int maxlineno;
- struct sourcefile *next;
-};
-
-/* This points to the head of the sourcefile structure list. */
-
-struct sourcefile *sources;
-
-/* One of these is dynamically created whenever we identify an arc in the
- function. */
-
-struct adj_list {
- int source;
- int target;
- int arc_count;
- unsigned int count_valid : 1;
- unsigned int on_tree : 1;
- unsigned int fake : 1;
- unsigned int fall_through : 1;
-#if 0
- /* Not needed for gcov, but defined in profile.c. */
- rtx branch_insn;
-#endif
- struct adj_list *pred_next;
- struct adj_list *succ_next;
-};
-
-/* Count the number of basic blocks, and create an array of these structures,
- one for each bb in the function. */
-
-struct bb_info {
- struct adj_list *succ;
- struct adj_list *pred;
- int succ_count;
- int pred_count;
- int exec_count;
- unsigned int count_valid : 1;
- unsigned int on_tree : 1;
-#if 0
- /* Not needed for gcov, but defined in profile.c. */
- rtx first_insn;
-#endif
-};
-
-/* When outputting branch probabilities, one of these structures is created
- for each branch/call. */
-
-struct arcdata
-{
- int prob;
- int call_insn;
- struct arcdata *next;
-};
-
-/* Used to save the list of bb_graphs, one per function. */
-
-struct bb_info_list {
- /* Indexed by block number, holds the basic block graph for one function. */
- struct bb_info *bb_graph;
- int num_blocks;
- struct bb_info_list *next;
-};
-
-/* Holds a list of function basic block graphs. */
-
-static struct bb_info_list *bb_graph_list = 0;
-
-/* Name and file pointer of the input file for the basic block graph. */
-
-static char *bbg_file_name;
-static FILE *bbg_file;
-
-/* Name and file pointer of the input file for the arc count data. */
-
-static char *da_file_name;
-static FILE *da_file;
-
-/* Name and file pointer of the input file for the basic block line counts. */
-
-static char *bb_file_name;
-static FILE *bb_file;
-
-/* Holds the entire contents of the bb_file read into memory. */
-
-static char *bb_data;
-
-/* Size of bb_data array in longs. */
-
-static long bb_data_size;
-
-/* Name and file pointer of the output file. */
-
-static char *gcov_file_name;
-static FILE *gcov_file;
-
-/* Name of the file mentioned on the command line. */
-
-static char *input_file_name = 0;
-
-/* Output branch probabilities if true. */
-
-static int output_branch_probs = 0;
-
-/* Output a gcov file if this is true. This is on by default, and can
- be turned off by the -n option. */
-
-static int output_gcov_file = 1;
-
-/* For included files, make the gcov output file name include the name of
- the input source file. For example, if x.h is included in a.c, then the
- output file name is a.c.x.h.gcov instead of x.h.gcov. This works only
- when a single source file is specified. */
-
-static int output_long_names = 0;
-
-/* Output summary info for each function. */
-
-static int output_function_summary = 0;
-
-/* Object directory file prefix. This is the directory where .bb and .bbg
- files are looked for, if non-zero. */
-
-static char *object_directory = 0;
-
-/* Forward declarations. */
-static void process_args PROTO ((int, char **));
-static void open_files PROTO ((void));
-static void read_files PROTO ((void));
-static void scan_for_source_files PROTO ((void));
-static void output_data PROTO ((void));
-static void print_usage PROTO ((void)) ATTRIBUTE_NORETURN;
-
-int
-main (argc, argv)
- int argc;
- char **argv;
-{
-#ifdef HAVE_LC_MESSAGES
- setlocale (LC_MESSAGES, "");
-#endif
- (void) bindtextdomain (PACKAGE, localedir);
- (void) textdomain (PACKAGE);
-
- process_args (argc, argv);
-
- open_files ();
-
- read_files ();
-
- scan_for_source_files ();
-
- output_data ();
-
- return 0;
-}
-
-static void fnotice PVPROTO ((FILE *, const char *, ...)) ATTRIBUTE_PRINTF_2;
-static void
-fnotice VPROTO ((FILE *file, const char *msgid, ...))
-{
-#ifndef ANSI_PROTOTYPES
- FILE *file;
- const char *msgid;
-#endif
- va_list ap;
-
- VA_START (ap, msgid);
-
-#ifndef ANSI_PROTOTYPES
- file = va_arg (ap, FILE *);
- msgid = va_arg (ap, const char *);
-#endif
-
- vfprintf (file, _(msgid), ap);
- va_end (ap);
-}
-
-
-PTR
-xmalloc (size)
- size_t size;
-{
- register PTR value = (PTR) malloc (size);
- if (value == 0)
- {
- fnotice (stderr, "error: virtual memory exhausted");
- exit (FATAL_EXIT_CODE);
- }
- return value;
-}
-
-/* More 'friendly' abort that prints the line and file.
- config.h can #define abort fancy_abort if you like that sort of thing. */
-
-void
-fancy_abort ()
-{
- fnotice (stderr, "Internal gcc abort.\n");
- exit (FATAL_EXIT_CODE);
-}
-
-/* Print a usage message and exit. */
-
-static void
-print_usage ()
-{
- fnotice (stderr, "gcov [-b] [-v] [-n] [-l] [-f] [-o OBJDIR] file\n");
- exit (FATAL_EXIT_CODE);
-}
-
-/* Parse the command line. */
-
-static void
-process_args (argc, argv)
- int argc;
- char **argv;
-{
- int i;
-
- for (i = 1; i < argc; i++)
- {
- if (argv[i][0] == '-')
- {
- if (argv[i][1] == 'b')
- output_branch_probs = 1;
- else if (argv[i][1] == 'v')
- fputs (gcov_version_string, stderr);
- else if (argv[i][1] == 'n')
- output_gcov_file = 0;
- else if (argv[i][1] == 'l')
- output_long_names = 1;
- else if (argv[i][1] == 'f')
- output_function_summary = 1;
- else if (argv[i][1] == 'o' && argv[i][2] == '\0')
- object_directory = argv[++i];
- else
- print_usage ();
- }
- else if (! input_file_name)
- input_file_name = argv[i];
- else
- print_usage ();
- }
-
- if (! input_file_name)
- print_usage ();
-}
-
-
-/* Find and open the .bb, .da, and .bbg files. */
-
-static void
-open_files ()
-{
- int count, objdir_count;
- char *cptr;
-
- /* Determine the names of the .bb, .bbg, and .da files. Strip off the
- extension, if any, and append the new extensions. */
- count = strlen (input_file_name);
- if (object_directory)
- objdir_count = strlen (object_directory);
- else
- objdir_count = 0;
-
- da_file_name = xmalloc (count + objdir_count + 4);
- bb_file_name = xmalloc (count + objdir_count + 4);
- bbg_file_name = xmalloc (count + objdir_count + 5);
-
- if (object_directory)
- {
- strcpy (da_file_name, object_directory);
- strcpy (bb_file_name, object_directory);
- strcpy (bbg_file_name, object_directory);
-
- if (object_directory[objdir_count - 1] != '/')
- {
- strcat (da_file_name, "/");
- strcat (bb_file_name, "/");
- strcat (bbg_file_name, "/");
- }
-
- cptr = rindex (input_file_name, '/');
- if (cptr)
- {
- strcat (da_file_name, cptr + 1);
- strcat (bb_file_name, cptr + 1);
- strcat (bbg_file_name, cptr + 1);
- }
- else
- {
- strcat (da_file_name, input_file_name);
- strcat (bb_file_name, input_file_name);
- strcat (bbg_file_name, input_file_name);
- }
- }
- else
- {
- strcpy (da_file_name, input_file_name);
- strcpy (bb_file_name, input_file_name);
- strcpy (bbg_file_name, input_file_name);
- }
-
- cptr = rindex (bb_file_name, '.');
- if (cptr)
- strcpy (cptr, ".bb");
- else
- strcat (bb_file_name, ".bb");
-
- cptr = rindex (da_file_name, '.');
- if (cptr)
- strcpy (cptr, ".da");
- else
- strcat (da_file_name, ".da");
-
- cptr = rindex (bbg_file_name, '.');
- if (cptr)
- strcpy (cptr, ".bbg");
- else
- strcat (bbg_file_name, ".bbg");
-
- bb_file = fopen (bb_file_name, "r");
- if (bb_file == NULL)
- {
- fnotice (stderr, "Could not open basic block file %s.\n", bb_file_name);
- exit (FATAL_EXIT_CODE);
- }
-
- /* If none of the functions in the file were executed, then there won't
- be a .da file. Just assume that all counts are zero in this case. */
- da_file = fopen (da_file_name, "r");
- if (da_file == NULL)
- {
- fnotice (stderr, "Could not open data file %s.\n", da_file_name);
- fnotice (stderr, "Assuming that all execution counts are zero.\n");
- }
-
- bbg_file = fopen (bbg_file_name, "r");
- if (bbg_file == NULL)
- {
- fnotice (stderr, "Could not open program flow graph file %s.\n",
- bbg_file_name);
- exit (FATAL_EXIT_CODE);
- }
-
- /* Check for empty .bbg file. This indicates that there is no executable
- code in this source file. */
- /* Set the EOF condition if at the end of file. */
- ungetc (getc (bbg_file), bbg_file);
- if (feof (bbg_file))
- {
- fnotice (stderr, "No executable code associated with file %s.\n",
- input_file_name);
- exit (FATAL_EXIT_CODE);
- }
-}
-
-/* Initialize a new arc. */
-
-static void
-init_arc (arcptr, source, target, bb_graph)
- struct adj_list *arcptr;
- int source, target;
- struct bb_info *bb_graph;
-{
- arcptr->target = target;
- arcptr->source = source;
-
- arcptr->arc_count = 0;
- arcptr->count_valid = 0;
- arcptr->on_tree = 0;
- arcptr->fake = 0;
- arcptr->fall_through = 0;
-
- arcptr->succ_next = bb_graph[source].succ;
- bb_graph[source].succ = arcptr;
- bb_graph[source].succ_count++;
-
- arcptr->pred_next = bb_graph[target].pred;
- bb_graph[target].pred = arcptr;
- bb_graph[target].pred_count++;
-}
-
-
-/* Reverse the arcs on a arc list. */
-
-static struct adj_list *
-reverse_arcs (arcptr)
- struct adj_list *arcptr;
-{
- struct adj_list *prev = 0;
- struct adj_list *next;
-
- for ( ; arcptr; arcptr = next)
- {
- next = arcptr->succ_next;
- arcptr->succ_next = prev;
- prev = arcptr;
- }
-
- return prev;
-}
-
-
-/* Construct the program flow graph from the .bbg file, and read in the data
- in the .da file. */
-
-static void
-create_program_flow_graph (bptr)
- struct bb_info_list *bptr;
-{
- long num_blocks, number_arcs, src, dest, flag_bits, num_arcs_per_block;
- int i;
- struct adj_list *arcptr;
- struct bb_info *bb_graph;
-
- /* Read the number of blocks. */
- __read_long (&num_blocks, bbg_file, 4);
-
- /* Create an array of size bb number of bb_info structs. Bzero it. */
- bb_graph = (struct bb_info *) xmalloc (num_blocks
- * sizeof (struct bb_info));
- bzero ((char *) bb_graph, sizeof (struct bb_info) * num_blocks);
-
- bptr->bb_graph = bb_graph;
- bptr->num_blocks = num_blocks;
-
- /* Read and create each arc from the .bbg file. */
- __read_long (&number_arcs, bbg_file, 4);
- for (i = 0; i < num_blocks; i++)
- {
- int j;
-
- __read_long (&num_arcs_per_block, bbg_file, 4);
- for (j = 0; j < num_arcs_per_block; j++)
- {
- if (number_arcs-- < 0)
- abort ();
-
- src = i;
- __read_long (&dest, bbg_file, 4);
-
- arcptr = (struct adj_list *) xmalloc (sizeof (struct adj_list));
- init_arc (arcptr, src, dest, bb_graph);
-
- __read_long (&flag_bits, bbg_file, 4);
- arcptr->on_tree = flag_bits & 0x1;
- arcptr->fake = !! (flag_bits & 0x2);
- arcptr->fall_through = !! (flag_bits & 0x4);
- }
- }
-
- if (number_arcs)
- abort ();
-
- /* Read and ignore the -1 separating the arc list from the arc list of the
- next function. */
- __read_long (&src, bbg_file, 4);
- if (src != -1)
- abort ();
-
- /* Must reverse the order of all succ arcs, to ensure that they match
- the order of the data in the .da file. */
-
- for (i = 0; i < num_blocks; i++)
- if (bb_graph[i].succ)
- bb_graph[i].succ = reverse_arcs (bb_graph[i].succ);
-
- /* For each arc not on the spanning tree, set its execution count from
- the .da file. */
-
- /* The first count in the .da file is the number of times that the function
- was entered. This is the exec_count for block zero. */
-
- /* This duplicates code in branch_prob in profile.c. */
-
- for (i = 0; i < num_blocks; i++)
- for (arcptr = bb_graph[i].succ; arcptr; arcptr = arcptr->succ_next)
- if (! arcptr->on_tree)
- {
- long tmp_count = 0;;
- if (da_file && __read_long (&tmp_count, da_file, 8))
- abort();
-
- arcptr->arc_count = tmp_count;
- arcptr->count_valid = 1;
- bb_graph[i].succ_count--;
- bb_graph[arcptr->target].pred_count--;
- }
-}
-
-static void
-solve_program_flow_graph (bptr)
- struct bb_info_list *bptr;
-{
- int passes, changes, total;
- int i;
- struct adj_list *arcptr;
- struct bb_info *bb_graph;
- int num_blocks;
-
- num_blocks = bptr->num_blocks;
- bb_graph = bptr->bb_graph;
-
- /* For every block in the file,
- - if every exit/entrance arc has a known count, then set the block count
- - if the block count is known, and every exit/entrance arc but one has
- a known execution count, then set the count of the remaining arc
-
- As arc counts are set, decrement the succ/pred count, but don't delete
- the arc, that way we can easily tell when all arcs are known, or only
- one arc is unknown. */
-
- /* The order that the basic blocks are iterated through is important.
- Since the code that finds spanning trees starts with block 0, low numbered
- arcs are put on the spanning tree in preference to high numbered arcs.
- Hence, most instrumented arcs are at the end. Graph solving works much
- faster if we propagate numbers from the end to the start.
-
- This takes an average of slightly more than 3 passes. */
-
- changes = 1;
- passes = 0;
- while (changes)
- {
- passes++;
- changes = 0;
-
- for (i = num_blocks - 1; i >= 0; i--)
- {
- if (! bb_graph[i].count_valid)
- {
- if (bb_graph[i].succ_count == 0)
- {
- total = 0;
- for (arcptr = bb_graph[i].succ; arcptr;
- arcptr = arcptr->succ_next)
- total += arcptr->arc_count;
- bb_graph[i].exec_count = total;
- bb_graph[i].count_valid = 1;
- changes = 1;
- }
- else if (bb_graph[i].pred_count == 0)
- {
- total = 0;
- for (arcptr = bb_graph[i].pred; arcptr;
- arcptr = arcptr->pred_next)
- total += arcptr->arc_count;
- bb_graph[i].exec_count = total;
- bb_graph[i].count_valid = 1;
- changes = 1;
- }
- }
- if (bb_graph[i].count_valid)
- {
- if (bb_graph[i].succ_count == 1)
- {
- total = 0;
- /* One of the counts will be invalid, but it is zero,
- so adding it in also doesn't hurt. */
- for (arcptr = bb_graph[i].succ; arcptr;
- arcptr = arcptr->succ_next)
- total += arcptr->arc_count;
- /* Calculate count for remaining arc by conservation. */
- total = bb_graph[i].exec_count - total;
- /* Search for the invalid arc, and set its count. */
- for (arcptr = bb_graph[i].succ; arcptr;
- arcptr = arcptr->succ_next)
- if (! arcptr->count_valid)
- break;
- if (! arcptr)
- abort ();
- arcptr->count_valid = 1;
- arcptr->arc_count = total;
- bb_graph[i].succ_count--;
-
- bb_graph[arcptr->target].pred_count--;
- changes = 1;
- }
- if (bb_graph[i].pred_count == 1)
- {
- total = 0;
- /* One of the counts will be invalid, but it is zero,
- so adding it in also doesn't hurt. */
- for (arcptr = bb_graph[i].pred; arcptr;
- arcptr = arcptr->pred_next)
- total += arcptr->arc_count;
- /* Calculate count for remaining arc by conservation. */
- total = bb_graph[i].exec_count - total;
- /* Search for the invalid arc, and set its count. */
- for (arcptr = bb_graph[i].pred; arcptr;
- arcptr = arcptr->pred_next)
- if (! arcptr->count_valid)
- break;
- if (! arcptr)
- abort ();
- arcptr->count_valid = 1;
- arcptr->arc_count = total;
- bb_graph[i].pred_count--;
-
- bb_graph[arcptr->source].succ_count--;
- changes = 1;
- }
- }
- }
- }
-
- /* If the graph has been correctly solved, every block will have a
- succ and pred count of zero. */
- for (i = 0; i < num_blocks; i++)
- if (bb_graph[i].succ_count || bb_graph[i].pred_count)
- abort ();
-}
-
-
-static void
-read_files ()
-{
- struct stat buf;
- struct bb_info_list *list_end = 0;
- struct bb_info_list *b_ptr;
- long total;
-
- /* Read and ignore the first word of the .da file, which is the count of
- how many numbers follow. */
- if (da_file && __read_long (&total, da_file, 8))
- abort();
-
- while (! feof (bbg_file))
- {
- b_ptr = (struct bb_info_list *) xmalloc (sizeof (struct bb_info_list));
-
- b_ptr->next = 0;
- if (list_end)
- list_end->next = b_ptr;
- else
- bb_graph_list = b_ptr;
- list_end = b_ptr;
-
- /* Read in the data in the .bbg file and reconstruct the program flow
- graph for one function. */
- create_program_flow_graph (b_ptr);
-
- /* Set the EOF condition if at the end of file. */
- ungetc (getc (bbg_file), bbg_file);
- }
-
- /* Check to make sure the .da file data is valid. */
-
- if (da_file)
- {
- if (feof (da_file))
- fnotice (stderr, ".da file contents exhausted too early\n");
- /* Should be at end of file now. */
- if (__read_long (&total, da_file, 8) == 0)
- fnotice (stderr, ".da file contents not exhausted\n");
- }
-
- /* Calculate all of the basic block execution counts and branch
- taken probabilities. */
-
- for (b_ptr = bb_graph_list; b_ptr; b_ptr = b_ptr->next)
- solve_program_flow_graph (b_ptr);
-
- /* Read in all of the data from the .bb file. This info will be accessed
- sequentially twice. */
- stat (bb_file_name, &buf);
- bb_data_size = buf.st_size / 4;
-
- bb_data = (char *) xmalloc ((unsigned) buf.st_size);
- fread (bb_data, sizeof (char), buf.st_size, bb_file);
-
- fclose (bb_file);
- if (da_file)
- fclose (da_file);
- fclose (bbg_file);
-}
-
-
-/* Scan the data in the .bb file to find all source files referenced,
- and the largest line number mentioned in each one. */
-
-static void
-scan_for_source_files ()
-{
- struct sourcefile *s_ptr = NULL;
- char *ptr;
- int count;
- long line_num;
-
- /* Search the bb_data to find:
- 1) The number of sources files contained herein, and
- 2) The largest line number for each source file. */
-
- ptr = bb_data;
- sources = 0;
- for (count = 0; count < bb_data_size; count++)
- {
- __fetch_long (&line_num, ptr, 4);
- ptr += 4;
- if (line_num == -1)
- {
- /* A source file name follows. Check to see if we already have
- a sourcefile structure for this file. */
- s_ptr = sources;
- while (s_ptr && strcmp (s_ptr->name, ptr))
- s_ptr = s_ptr->next;
-
- if (s_ptr == 0)
- {
- /* No sourcefile structure for this file name exists, create
- a new one, and append it to the front of the sources list. */
- s_ptr = (struct sourcefile *) xmalloc (sizeof(struct sourcefile));
- s_ptr->name = xmalloc (strlen ((char *) ptr) + 1);
- strcpy (s_ptr->name, (char *) ptr);
- s_ptr->maxlineno = 0;
- s_ptr->next = sources;
- sources = s_ptr;
- }
-
- /* Scan past the file name. */
- {
- long delim;
- do {
- count++;
- __fetch_long (&delim, ptr, 4);
- ptr += 4;
- } while (delim != line_num);
- }
- }
- else if (line_num == -2)
- {
- long delim;
-
- /* A function name follows. Ignore it. */
- do {
- count++;
- __fetch_long (&delim, ptr, 4);
- ptr += 4;
- } while (delim != line_num);
- }
- /* There will be a zero before the first file name, in which case s_ptr
- will still be uninitialized. So, only try to set the maxlineno
- field if line_num is non-zero. */
- else if (line_num > 0)
- {
- if (s_ptr->maxlineno <= line_num)
- s_ptr->maxlineno = line_num + 1;
- }
- else if (line_num < 0)
- {
- /* Don't know what this is, but it's garbage. */
- abort();
- }
- }
-}
-
-/* For calculating coverage at the function level. */
-
-static int function_source_lines;
-static int function_source_lines_executed;
-static int function_branches;
-static int function_branches_executed;
-static int function_branches_taken;
-static int function_calls;
-static int function_calls_executed;
-static char *function_name;
-
-/* Calculate the branch taken probabilities for all arcs branches at the
- end of this block. */
-
-static void
-calculate_branch_probs (current_graph, block_num, branch_probs, last_line_num)
- struct bb_info_list *current_graph;
- int block_num;
- struct arcdata **branch_probs;
- int last_line_num;
-{
- int total;
- struct adj_list *arcptr;
- struct arcdata *end_ptr, *a_ptr;
-
- total = current_graph->bb_graph[block_num].exec_count;
- for (arcptr = current_graph->bb_graph[block_num].succ; arcptr;
- arcptr = arcptr->succ_next)
- {
- /* Ignore fall through arcs as they aren't really branches. */
-
- if (arcptr->fall_through)
- continue;
-
- a_ptr = (struct arcdata *) xmalloc (sizeof (struct arcdata));
- if (total == 0)
- a_ptr->prob = -1;
- else
- a_ptr->prob = ((arcptr->arc_count * 100) + (total >> 1)) / total;
- a_ptr->call_insn = arcptr->fake;
-
- if (output_function_summary)
- {
- if (a_ptr->call_insn)
- {
- function_calls++;
- if (a_ptr->prob != -1)
- function_calls_executed++;
- }
- else
- {
- function_branches++;
- if (a_ptr->prob != -1)
- function_branches_executed++;
- if (a_ptr->prob > 0)
- function_branches_taken++;
- }
- }
-
- /* Append the new branch to the end of the list. */
- a_ptr->next = 0;
- if (! branch_probs[last_line_num])
- branch_probs[last_line_num] = a_ptr;
- else
- {
- end_ptr = branch_probs[last_line_num];
- while (end_ptr->next != 0)
- end_ptr = end_ptr->next;
- end_ptr->next = a_ptr;
- }
- }
-}
-
-/* Output summary info for a function. */
-
-static void
-function_summary ()
-{
- if (function_source_lines)
- fnotice (stdout, "%6.2f%% of %d source lines executed in function %s\n",
- (((double) function_source_lines_executed / function_source_lines)
- * 100), function_source_lines, function_name);
- else
- fnotice (stdout, "No executable source lines in function %s\n",
- function_name);
-
- if (output_branch_probs)
- {
- if (function_branches)
- {
- fnotice (stdout, "%6.2f%% of %d branches executed in function %s\n",
- (((double) function_branches_executed / function_branches)
- * 100), function_branches, function_name);
- fnotice (stdout,
- "%6.2f%% of %d branches taken at least once in function %s\n",
- (((double) function_branches_taken / function_branches)
- * 100), function_branches, function_name);
- }
- else
- fnotice (stdout, "No branches in function %s\n", function_name);
- if (function_calls)
- fnotice (stdout, "%6.2f%% of %d calls executed in function %s\n",
- (((double) function_calls_executed / function_calls)
- * 100), function_calls, function_name);
- else
- fnotice (stdout, "No calls in function %s\n", function_name);
- }
-}
-
-/* Calculate line execution counts, and output the data to a .tcov file. */
-
-static void
-output_data ()
-{
- /* When scanning data, this is true only if the data applies to the
- current source file. */
- int this_file;
- /* An array indexed by line number which indicates how many times that line
- was executed. */
- long *line_counts;
- /* An array indexed by line number which indicates whether the line was
- present in the bb file (i.e. whether it had code associate with it).
- Lines never executed are those which both exist, and have zero execution
- counts. */
- char *line_exists;
- /* An array indexed by line number, which contains a list of branch
- probabilities, one for each branch on that line. */
- struct arcdata **branch_probs = NULL;
- struct sourcefile *s_ptr;
- char *source_file_name;
- FILE *source_file;
- struct bb_info_list *current_graph;
- int count;
- char *cptr;
- long block_num;
- long line_num;
- long last_line_num = 0;
- int i;
- struct arcdata *a_ptr;
- /* Buffer used for reading in lines from the source file. */
- char string[STRING_SIZE];
- /* For calculating coverage at the file level. */
- int total_source_lines;
- int total_source_lines_executed;
- int total_branches;
- int total_branches_executed;
- int total_branches_taken;
- int total_calls;
- int total_calls_executed;
-
- /* Now, for each source file, allocate an array big enough to hold a count
- for each line. Scan through the bb_data, and when the file name matches
- the current file name, then for each following line number, increment
- the line number execution count indicated by the execution count of
- the appropriate basic block. */
-
- for (s_ptr = sources; s_ptr; s_ptr = s_ptr->next)
- {
- /* If this is a relative file name, and an object directory has been
- specified, then make it relative to the object directory name. */
- if (*s_ptr->name != '/' && object_directory != 0
- && *object_directory != '\0')
- {
- int objdir_count = strlen (object_directory);
- source_file_name = xmalloc (objdir_count + strlen (s_ptr->name) + 2);
- strcpy (source_file_name, object_directory);
- if (object_directory[objdir_count - 1] != '/')
- source_file_name[objdir_count++] = '/';
- strcpy (source_file_name + objdir_count, s_ptr->name);
- }
- else
- source_file_name = s_ptr->name;
-
- line_counts = (long *) xmalloc (sizeof (long) * s_ptr->maxlineno);
- bzero ((char *) line_counts, sizeof (long) * s_ptr->maxlineno);
- line_exists = xmalloc (s_ptr->maxlineno);
- bzero (line_exists, s_ptr->maxlineno);
- if (output_branch_probs)
- {
- branch_probs = (struct arcdata **) xmalloc (sizeof (struct arcdata *)
- * s_ptr->maxlineno);
- bzero ((char *) branch_probs,
- sizeof (struct arcdata *) * s_ptr->maxlineno);
- }
-
- /* There will be a zero at the beginning of the bb info, before the
- first list of line numbers, so must initialize block_num to 0. */
- block_num = 0;
- this_file = 0;
- current_graph = 0;
- {
- /* Pointer into the bb_data, incremented while scanning the data. */
- char *ptr = bb_data;
- for (count = 0; count < bb_data_size; count++)
- {
- long delim;
-
- __fetch_long (&line_num, ptr, 4);
- ptr += 4;
- if (line_num == -1)
- {
- /* Marks the beginning of a file name. Check to see whether
- this is the filename we are currently collecting data for. */
-
- if (strcmp (s_ptr->name, ptr))
- this_file = 0;
- else
- this_file = 1;
-
- /* Scan past the file name. */
- do {
- count++;
- __fetch_long (&delim, ptr, 4);
- ptr += 4;
- } while (delim != line_num);
- }
- else if (line_num == -2)
- {
- /* Marks the start of a new function. Advance to the next
- program flow graph. */
-
- if (! current_graph)
- current_graph = bb_graph_list;
- else
- {
- if (block_num == current_graph->num_blocks - 1)
- /* Last block falls through to exit. */
- ;
- else if (block_num == current_graph->num_blocks - 2)
- {
- if (output_branch_probs && this_file)
- calculate_branch_probs (current_graph, block_num,
- branch_probs, last_line_num);
- }
- else
- {
- fnotice (stderr,
- "didn't use all bb entries of graph, function %s\n",
- function_name);
- fnotice (stderr, "block_num = %ld, num_blocks = %d\n",
- block_num, current_graph->num_blocks);
- }
-
- current_graph = current_graph->next;
- block_num = 0;
-
- if (output_function_summary && this_file)
- function_summary ();
- }
-
- if (output_function_summary)
- {
- function_source_lines = 0;
- function_source_lines_executed = 0;
- function_branches = 0;
- function_branches_executed = 0;
- function_branches_taken = 0;
- function_calls = 0;
- function_calls_executed = 0;
- }
-
- /* Save the function name for later use. */
- function_name = ptr;
-
- /* Scan past the file name. */
- do {
- count++;
- __fetch_long (&delim, ptr, 4);
- ptr += 4;
- } while (delim != line_num);
- }
- else if (line_num == 0)
- {
- /* Marks the end of a block. */
-
- if (block_num >= current_graph->num_blocks)
- {
- fnotice (stderr, "ERROR: too many basic blocks in .bb file %s\n",
- function_name);
- abort ();
- }
-
- if (output_branch_probs && this_file)
- calculate_branch_probs (current_graph, block_num,
- branch_probs, last_line_num);
-
- block_num++;
- }
- else if (this_file)
- {
- if (output_function_summary)
- {
- if (line_exists[line_num] == 0)
- function_source_lines++;
- if (line_counts[line_num] == 0
- && current_graph->bb_graph[block_num].exec_count != 0)
- function_source_lines_executed++;
- }
-
- /* Accumulate execution data for this line number. */
-
- line_counts[line_num]
- += current_graph->bb_graph[block_num].exec_count;
- line_exists[line_num] = 1;
- last_line_num = line_num;
- }
- }
- }
-
- if (output_function_summary && this_file)
- function_summary ();
-
- /* Calculate summary test coverage statistics. */
-
- total_source_lines = 0;
- total_source_lines_executed = 0;
- total_branches = 0;
- total_branches_executed = 0;
- total_branches_taken = 0;
- total_calls = 0;
- total_calls_executed = 0;
-
- for (count = 1; count < s_ptr->maxlineno; count++)
- {
- if (line_exists[count])
- {
- total_source_lines++;
- if (line_counts[count])
- total_source_lines_executed++;
- }
- if (output_branch_probs)
- {
- for (a_ptr = branch_probs[count]; a_ptr; a_ptr = a_ptr->next)
- {
- if (a_ptr->call_insn)
- {
- total_calls++;
- if (a_ptr->prob != -1)
- total_calls_executed++;
- }
- else
- {
- total_branches++;
- if (a_ptr->prob != -1)
- total_branches_executed++;
- if (a_ptr->prob > 0)
- total_branches_taken++;
- }
- }
- }
- }
-
- if (total_source_lines)
- fnotice (stdout,
- "%6.2f%% of %d source lines executed in file %s\n",
- (((double) total_source_lines_executed / total_source_lines)
- * 100), total_source_lines, source_file_name);
- else
- fnotice (stdout, "No executable source lines in file %s\n",
- source_file_name);
-
- if (output_branch_probs)
- {
- if (total_branches)
- {
- fnotice (stdout, "%6.2f%% of %d branches executed in file %s\n",
- (((double) total_branches_executed / total_branches)
- * 100), total_branches, source_file_name);
- fnotice (stdout,
- "%6.2f%% of %d branches taken at least once in file %s\n",
- (((double) total_branches_taken / total_branches)
- * 100), total_branches, source_file_name);
- }
- else
- fnotice (stdout, "No branches in file %s\n", source_file_name);
- if (total_calls)
- fnotice (stdout, "%6.2f%% of %d calls executed in file %s\n",
- (((double) total_calls_executed / total_calls)
- * 100), total_calls, source_file_name);
- else
- fnotice (stdout, "No calls in file %s\n", source_file_name);
- }
-
- if (output_gcov_file)
- {
- /* Now the statistics are ready. Read in the source file one line
- at a time, and output that line to the gcov file preceded by
- its execution count if non zero. */
-
- source_file = fopen (source_file_name, "r");
- if (source_file == NULL)
- {
- fnotice (stderr, "Could not open source file %s.\n",
- source_file_name);
- free (line_counts);
- free (line_exists);
- continue;
- }
-
- count = strlen (source_file_name);
- cptr = rindex (s_ptr->name, '/');
- if (cptr)
- cptr = cptr + 1;
- else
- cptr = s_ptr->name;
- if (output_long_names && strcmp (cptr, input_file_name))
- {
- gcov_file_name = xmalloc (count + 7 + strlen (input_file_name));
-
- cptr = rindex (input_file_name, '/');
- if (cptr)
- strcpy (gcov_file_name, cptr + 1);
- else
- strcpy (gcov_file_name, input_file_name);
-
- strcat (gcov_file_name, ".");
-
- cptr = rindex (source_file_name, '/');
- if (cptr)
- strcat (gcov_file_name, cptr + 1);
- else
- strcat (gcov_file_name, source_file_name);
- }
- else
- {
- gcov_file_name = xmalloc (count + 6);
- cptr = rindex (source_file_name, '/');
- if (cptr)
- strcpy (gcov_file_name, cptr + 1);
- else
- strcpy (gcov_file_name, source_file_name);
- }
-
- /* Don't strip off the ending for compatibility with tcov, since
- this results in confusion if there is more than one file with
- the same basename, e.g. tmp.c and tmp.h. */
- strcat (gcov_file_name, ".gcov");
-
- gcov_file = fopen (gcov_file_name, "w");
-
- if (gcov_file == NULL)
- {
- fnotice (stderr, "Could not open output file %s.\n",
- gcov_file_name);
- fclose (source_file);
- free (line_counts);
- free (line_exists);
- continue;
- }
-
- fnotice (stdout, "Creating %s.\n", gcov_file_name);
-
- for (count = 1; count < s_ptr->maxlineno; count++)
- {
- char *retval;
- int len;
-
- retval = fgets (string, STRING_SIZE, source_file);
-
- /* For lines which don't exist in the .bb file, print nothing
- before the source line. For lines which exist but were never
- executed, print ###### before the source line. Otherwise,
- print the execution count before the source line. */
- /* There are 16 spaces of indentation added before the source
- line so that tabs won't be messed up. */
- if (line_exists[count])
- {
- if (line_counts[count])
- fprintf (gcov_file, "%12ld %s", line_counts[count],
- string);
- else
- fprintf (gcov_file, " ###### %s", string);
- }
- else
- fprintf (gcov_file, "\t\t%s", string);
-
- /* In case the source file line is larger than our buffer, keep
- reading and outputting lines until we get a newline. */
- len = strlen (string);
- while ((len == 0 || string[strlen (string) - 1] != '\n')
- && retval != NULL)
- {
- retval = fgets (string, STRING_SIZE, source_file);
- fputs (string, gcov_file);
- }
-
- if (output_branch_probs)
- {
- for (i = 0, a_ptr = branch_probs[count]; a_ptr;
- a_ptr = a_ptr->next, i++)
- {
- if (a_ptr->call_insn)
- {
- if (a_ptr->prob == -1)
- fnotice (gcov_file, "call %d never executed\n", i);
- else
- fnotice (gcov_file,
- "call %d returns = %d%%\n",
- i, 100 - a_ptr->prob);
- }
- else
- {
- if (a_ptr->prob == -1)
- fnotice (gcov_file, "branch %d never executed\n",
- i);
- else
- fnotice (gcov_file, "branch %d taken = %d%%\n", i,
- a_ptr->prob);
- }
- }
- }
-
- /* Gracefully handle errors while reading the source file. */
- if (retval == NULL)
- {
- fnotice (stderr,
- "Unexpected EOF while reading source file %s.\n",
- source_file_name);
- break;
- }
- }
-
- /* Handle all remaining source lines. There may be lines
- after the last line of code. */
-
- {
- char *retval = fgets (string, STRING_SIZE, source_file);
- while (retval != NULL)
- {
- int len;
-
- fprintf (gcov_file, "\t\t%s", string);
-
- /* In case the source file line is larger than our buffer, keep
- reading and outputting lines until we get a newline. */
- len = strlen (string);
- while ((len == 0 || string[strlen (string) - 1] != '\n')
- && retval != NULL)
- {
- retval = fgets (string, STRING_SIZE, source_file);
- fputs (string, gcov_file);
- }
-
- retval = fgets (string, STRING_SIZE, source_file);
- }
- }
-
- fclose (source_file);
- fclose (gcov_file);
- }
-
- free (line_counts);
- free (line_exists);
- }
-}