This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[gcov] Tidy it
- From: Nathan Sidwell <nathan at codesourcery dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Wed, 07 Aug 2002 00:20:27 +0100
- Subject: [gcov] Tidy it
- Organization: Codesourcery LLC
Hi,
I've installed this patch as obvious, even though it is
rather large. It breaks a rather large function into
more manageable bits, uses an array of struct rather
than several arrays of scalar, and tidies a few other things.
Oh, and fixes a memory leak I discovered in the process.
No functional changes.
booted & tested on i686-pc-linux-gnu, installed
nathan
--
Dr Nathan Sidwell :: http://www.codesourcery.com :: CodeSourcery LLC
'But that's a lie.' - 'Yes it is. What's your point?'
nathan@codesourcery.com : http://www.cs.bris.ac.uk/~nathan/ : nathan@acm.org
Index: gcov.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/gcov.c,v
retrieving revision 1.45
diff -c -3 -p -r1.45 gcov.c
*** gcov.c 5 Aug 2002 22:16:50 -0000 1.45
--- gcov.c 6 Aug 2002 22:42:48 -0000
*************** struct sourcefile *sources;
*** 103,109 ****
/* One of these is dynamically created whenever we identify an arc in the
function. */
! struct adj_list {
int source;
int target;
gcov_type arc_count;
--- 103,110 ----
/* One of these is dynamically created whenever we identify an arc in the
function. */
! struct adj_list
! {
int source;
int target;
gcov_type arc_count;
*************** struct adj_list {
*** 122,128 ****
/* 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;
gcov_type succ_count;
--- 123,130 ----
/* 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;
gcov_type succ_count;
*************** struct arcdata
*** 149,161 ****
/* 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;
--- 151,187 ----
/* 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;
};
+ /* Used to hold information about each line. */
+ struct line_info
+ {
+ gcov_type count; /* execution count */
+ struct arcdata *branches; /* list of branch probabilities for line. */
+ unsigned exists : 1; /* has code associated with it. */
+ };
+
+ struct coverage
+ {
+ int lines;
+ int lines_executed;
+
+ int branches;
+ int branches_executed;
+ int branches_taken;
+
+ int calls;
+ int calls_executed;
+
+ char *name;
+ };
+
/* Holds a list of function basic block graphs. */
static struct bb_info_list *bb_graph_list = 0;
*************** static char *bb_data;
*** 187,197 ****
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;
--- 213,218 ----
*************** static void process_args PARAMS ((int, c
*** 235,241 ****
static void open_files PARAMS ((void));
static void read_files PARAMS ((void));
static void scan_for_source_files PARAMS ((void));
! static void output_data PARAMS ((void));
static void print_usage PARAMS ((int)) ATTRIBUTE_NORETURN;
static void print_version PARAMS ((void)) ATTRIBUTE_NORETURN;
static void init_arc PARAMS ((struct adj_list *, int, int, struct bb_info *));
--- 256,262 ----
static void open_files PARAMS ((void));
static void read_files PARAMS ((void));
static void scan_for_source_files PARAMS ((void));
! static void output_data PARAMS ((struct sourcefile *));
static void print_usage PARAMS ((int)) ATTRIBUTE_NORETURN;
static void print_version PARAMS ((void)) ATTRIBUTE_NORETURN;
static void init_arc PARAMS ((struct adj_list *, int, int, struct bb_info *));
*************** static struct adj_list *reverse_arcs PAR
*** 243,252 ****
static gcov_type *read_profile PARAMS ((char *, long, int));
static void create_program_flow_graph PARAMS ((struct bb_info_list *));
static void solve_program_flow_graph PARAMS ((struct bb_info_list *));
! static void calculate_branch_probs PARAMS ((struct bb_info_list *, int,
! struct arcdata **, int));
! static void function_summary PARAMS ((void));
! static const char *format_hwint PARAMS ((HOST_WIDEST_INT, HOST_WIDEST_INT, int));
extern int main PARAMS ((int, char **));
--- 264,282 ----
static gcov_type *read_profile PARAMS ((char *, long, int));
static void create_program_flow_graph PARAMS ((struct bb_info_list *));
static void solve_program_flow_graph PARAMS ((struct bb_info_list *));
! static void accumulate_branch_counts PARAMS ((struct coverage *,
! struct arcdata *));
! static void calculate_branch_probs PARAMS ((struct bb_info *,
! struct line_info *,
! struct coverage *));
! static void function_summary PARAMS ((struct coverage *, const char *));
! static void init_line_info PARAMS ((struct line_info *,
! struct coverage *, long));
! static void output_line_info PARAMS ((FILE *, const struct line_info *,
! const struct coverage *, long));
! static char *make_gcov_file_name PARAMS ((char *));
! static const char *format_hwint PARAMS ((HOST_WIDEST_INT, HOST_WIDEST_INT,
! int));
extern int main PARAMS ((int, char **));
*************** main (argc, argv)
*** 255,260 ****
--- 285,292 ----
int argc;
char **argv;
{
+ struct sourcefile *s_ptr;
+
gcc_init_libintl ();
process_args (argc, argv);
*************** main (argc, argv)
*** 265,271 ****
scan_for_source_files ();
! output_data ();
return 0;
}
--- 297,304 ----
scan_for_source_files ();
! for (s_ptr = sources; s_ptr; s_ptr = s_ptr->next)
! output_data (s_ptr);
return 0;
}
*************** init_arc (arcptr, source, target, bb_gra
*** 530,536 ****
bb_graph[target].pred_count++;
}
-
/* Reverse the arcs on an arc list. */
static struct adj_list *
--- 563,568 ----
*************** scan_for_source_files ()
*** 1037,1113 ****
}
}
- /* 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;
{
gcov_type 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));
a_ptr->total = total;
! if (total == 0)
! a_ptr->hits = 0;
! else
! a_ptr->hits = arcptr->arc_count;
a_ptr->call_insn = arcptr->fake;
! if (output_function_summary)
! {
! if (a_ptr->call_insn)
! {
! function_calls++;
! if (a_ptr->total != 0)
! function_calls_executed++;
! }
! else
! {
! function_branches++;
! if (a_ptr->total != 0)
! function_branches_executed++;
! if (a_ptr->hits > 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;
! }
}
}
--- 1069,1129 ----
}
}
! /* Increment totals in FUNCTION according to arc A_PTR. */
!
! static void
! accumulate_branch_counts (function, a_ptr)
! struct coverage *function;
! struct arcdata *a_ptr;
! {
! if (a_ptr->call_insn)
! {
! function->calls++;
! if (a_ptr->total)
! function->calls_executed++;
! }
! else
! {
! function->branches++;
! if (a_ptr->total)
! function->branches_executed++;
! if (a_ptr->hits)
! function->branches_taken++;
! }
! }
/* Calculate the branch taken probabilities for all arcs branches at the
end of this block. */
static void
! calculate_branch_probs (block_ptr, line_info, function)
! struct bb_info *block_ptr;
! struct line_info *line_info;
! struct coverage *function;
{
gcov_type total;
struct adj_list *arcptr;
! total = block_ptr->exec_count;
! for (arcptr = block_ptr->succ; arcptr; arcptr = arcptr->succ_next)
{
+ struct arcdata *a_ptr;
+
/* Ignore fall through arcs as they aren't really branches. */
if (arcptr->fall_through)
continue;
a_ptr = (struct arcdata *) xmalloc (sizeof (struct arcdata));
a_ptr->total = total;
! a_ptr->hits = total ? arcptr->arc_count : 0;
a_ptr->call_insn = arcptr->fake;
! if (function)
! accumulate_branch_counts (function, a_ptr);
! /* Prepend the new branch to the list. */
! a_ptr->next = line_info->branches;
! line_info->branches = a_ptr;
}
}
*************** format_hwint (top, bottom, dp)
*** 1162,1663 ****
/* Output summary info for a function. */
static void
! function_summary ()
{
! if (function_source_lines)
! fnotice (stdout, "%s of %d source lines executed in function %s\n",
! format_hwint (function_source_lines_executed,
! function_source_lines, 2),
! 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, "%s of %d branches executed in function %s\n",
! format_hwint (function_branches_executed,
! function_branches, 2),
! function_branches, function_name);
fnotice (stdout,
! "%s of %d branches taken at least once in function %s\n",
! format_hwint (function_branches_taken,
! function_branches, 2),
! function_branches, function_name);
}
else
! fnotice (stdout, "No branches in function %s\n", function_name);
! if (function_calls)
! fnotice (stdout, "%s of %d calls executed in function %s\n",
! format_hwint (function_calls_executed,
! function_calls, 2),
! 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. */
! gcov_type *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;
long 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)
{
! source_file_name = s_ptr->name;
!
! line_counts = (gcov_type *) xcalloc (sizeof (gcov_type), s_ptr->maxlineno);
! line_exists = xcalloc (1, s_ptr->maxlineno);
! if (output_branch_probs)
! branch_probs = (struct arcdata **)
! xcalloc (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->total != 0)
! total_calls_executed++;
}
else
{
! total_branches++;
! if (a_ptr->total != 0)
! total_branches_executed++;
! if (a_ptr->hits > 0)
! total_branches_taken++;
}
}
}
- }
-
- if (total_source_lines)
- fnotice (stdout,
- "%s of %d source lines executed in file %s\n",
- format_hwint (total_source_lines_executed,
- total_source_lines, 2),
- 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, "%s of %d branches executed in file %s\n",
! format_hwint (total_branches_executed,
! total_branches, 2),
! total_branches, source_file_name);
! fnotice (stdout,
! "%s of %d branches taken at least once in file %s\n",
! format_hwint (total_branches_taken,
! total_branches, 2),
! total_branches, source_file_name);
}
! else
! fnotice (stdout, "No branches in file %s\n", source_file_name);
! if (total_calls)
! fnotice (stdout, "%s of %d calls executed in file %s\n",
! format_hwint (total_calls_executed, total_calls, 2),
! 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. */
! char const *retval;
!
! /* Generate an output file name. LONG_OUTPUT_NAMES and
! PRESERVE_PATHS affect name generation. With
! preserve_paths we create a filename from all path
! components of the source file, replacing '/' with '#',
! without it we simply take the basename component. With
! long_output_names we prepend the processed name of the
! input file to each output name (except when the current
! source file is the input file, so you don't get a double
! concatenation). The two components are separated by
! '##'. Also '.' filename components are removed and '..'
! components are renamed to '^'. */
! gcov_file_name = xmalloc (strlen (source_file_name)
! + strlen (input_file_name) + 10);
! gcov_file_name[0] = 0;
! if (output_long_names && strcmp (source_file_name, input_file_name))
{
! /* Generate the input filename part. */
! cptr = preserve_paths ? NULL : strrchr (input_file_name, '/');
! cptr = cptr ? cptr + 1 : input_file_name;
! strcat (gcov_file_name, cptr);
! strcat (gcov_file_name, "##");
}
- /* Generate the source filename part. */
- cptr = preserve_paths ? NULL : strrchr (source_file_name, '/');
- cptr = cptr ? cptr + 1 : source_file_name;
- strcat (gcov_file_name, cptr);
! if (preserve_paths)
{
! /* Convert '/' to '#', remove '/./', convert '/../' to
! '/^/' */
! char *prev;
!
! for (cptr = gcov_file_name;
! (cptr = strchr ((prev = cptr), '/'));)
! {
! unsigned shift = 0;
!
! if (prev + 1 == cptr && prev[0] == '.')
! {
! /* Remove '.' */
! shift = 2;
! }
! else if (prev + 2 == cptr
! && prev[0] == '.' && prev[1] == '.')
! {
! /* Convert '..' */
! shift = 1;
! prev[1] = '^';
! }
! else
! *cptr++ = '#';
! if (shift)
! {
! cptr = prev;
! do
! prev[0] = prev[shift];
! while (*prev++);
! }
! }
}
! /* 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);
! free (line_counts);
! free (line_exists);
! continue;
! }
! fnotice (stdout, "Creating %s.\n", gcov_file_name);
! fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, source_file_name);
! fprintf (gcov_file, "%9s:%5d:Object:%s\n", "-", 0, bb_file_name);
!
! source_file = fopen (source_file_name, "r");
! if (source_file == NULL)
! fnotice (stderr, "Could not open source file %s.\n",
! source_file_name);
! else
! {
! struct stat status;
! if (!fstat (fileno (source_file), &status)
! && status.st_mtime > bb_file_time)
{
! fnotice (stderr, "Warning: source file %s is newer than %s\n",
! source_file_name, bb_file_name);
! fprintf (gcov_file, "%9s:%5d:Source is newer than compiler output\n", "-", 0);
}
}
! for (retval = source_file ? "" : NULL, count = 1;
! count < s_ptr->maxlineno; count++)
{
! /* For lines which don't exist in the .bb file, print
! '-' 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. */
! fprintf (gcov_file, "%9s:%5ld:",
! !line_exists[count] ? "-"
! : !line_counts[count] ? "#####"
! : format_hwint (line_counts[count], 0, -1), count);
!
! if (retval)
{
! do
! {
! retval = fgets (string, STRING_SIZE, source_file);
! if (!retval)
! {
! fnotice (stderr,
! "Unexpected EOF while reading source file %s.\n",
! source_file_name);
! break;
! }
! fputs (retval, gcov_file);
! }
! while (!retval[0] || retval[strlen (retval) - 1] != '\n');
}
! if (!retval)
! fputs ("??\n", 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->total == 0)
! fnotice (gcov_file, "call %2d never executed\n", i);
! else
! fnotice
! (gcov_file, "call %2d returns %s\n", i,
! format_hwint (a_ptr->total - a_ptr->hits,
! a_ptr->total,
! -output_branch_counts));
! }
! else
! {
! if (a_ptr->total == 0)
! fnotice (gcov_file, "branch %2d never executed\n",
! i);
! else
! fnotice
! (gcov_file, "branch %2d taken %s\n", i,
! format_hwint (a_ptr->hits, a_ptr->total,
! -output_branch_counts));
! }
! }
! }
}
!
! /* Handle all remaining source lines. There may be lines
! after the last line of code. */
! if (retval)
{
! for (; (retval = fgets (string, STRING_SIZE, source_file));
! count++)
! {
! fprintf (gcov_file, "%9s:%5ld:%s", "-", count, retval);
!
! while (!retval[0] || retval[strlen (retval) - 1] != '\n')
! {
! retval = fgets (string, STRING_SIZE, source_file);
! if (!retval)
! break;
! fputs (retval, gcov_file);
! }
! }
}
! if (source_file)
! fclose (source_file);
if (ferror (gcov_file))
fnotice (stderr, "Error writing output file %s.\n",
gcov_file_name);
fclose (gcov_file);
}
! free (line_counts);
! free (line_exists);
}
}
--- 1178,1634 ----
/* Output summary info for a function. */
static void
! function_summary (function, title)
! struct coverage *function;
! const char *title;
{
! if (function->lines)
! fnotice (stdout, "%s of %d lines executed in %s %s\n",
! format_hwint (function->lines_executed,
! function->lines, 2),
! function->lines, title, function->name);
else
! fnotice (stdout, "No executable lines in %s %s\n",
! title, function->name);
if (output_branch_probs)
{
! if (function->branches)
{
! fnotice (stdout, "%s of %d branches executed in %s %s\n",
! format_hwint (function->branches_executed,
! function->branches, 2),
! function->branches, title, function->name);
fnotice (stdout,
! "%s of %d branches taken at least once in %s %s\n",
! format_hwint (function->branches_taken,
! function->branches, 2),
! function->branches, title, function->name);
}
else
! fnotice (stdout, "No branches in %s %s\n", title, function->name);
! if (function->calls)
! fnotice (stdout, "%s of %d calls executed in %s %s\n",
! format_hwint (function->calls_executed,
! function->calls, 2),
! function->calls, title, function->name);
else
! fnotice (stdout, "No calls in %s %s\n", title, function->name);
}
}
! /* Generate an output file name. LONG_OUTPUT_NAMES and PRESERVE_PATHS
! affect name generation. With preserve_paths we create a filename
! from all path components of the source file, replacing '/' with
! '#', without it we simply take the basename component. With
! long_output_names we prepend the processed name of the input file
! to each output name (except when the current source file is the
! input file, so you don't get a double concatenation). The two
! components are separated by '##'. Also '.' filename components are
! removed and '..' components are renamed to '^'. */
!
! static char *
! make_gcov_file_name (src_name)
! char *src_name;
! {
! char *cptr;
! char *name = xmalloc (strlen (src_name) + strlen (input_file_name) + 10);
!
! name[0] = 0;
! if (output_long_names && strcmp (src_name, input_file_name))
! {
! /* Generate the input filename part. */
! cptr = preserve_paths ? NULL : strrchr (input_file_name, '/');
! cptr = cptr ? cptr + 1 : input_file_name;
! strcat (name, cptr);
! strcat (name, "##");
! }
!
! /* Generate the source filename part. */
! cptr = preserve_paths ? NULL : strrchr (src_name, '/');
! cptr = cptr ? cptr + 1 : src_name;
! strcat (name, cptr);
!
! if (preserve_paths)
! {
! /* Convert '/' to '#', remove '/./', convert '/../' to '/^/' */
! char *prev;
!
! for (cptr = name; (cptr = strchr ((prev = cptr), '/'));)
! {
! unsigned shift = 0;
!
! if (prev + 1 == cptr && prev[0] == '.')
! {
! /* Remove '.' */
! shift = 2;
! }
! else if (prev + 2 == cptr && prev[0] == '.' && prev[1] == '.')
! {
! /* Convert '..' */
! shift = 1;
! prev[1] = '^';
! }
! else
! *cptr++ = '#';
! if (shift)
! {
! cptr = prev;
! do
! prev[0] = prev[shift];
! while (*prev++);
! }
! }
! }
!
! /* 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 (name, ".gcov");
! return name;
! }
!
! /* Scan through the bb_data, and when the file name matches the
! source file name, then for each following line number, increment
! the line number execution count indicated by the execution count of
! the appropriate basic block. */
static void
! init_line_info (line_info, total, maxlineno)
! struct line_info *line_info;
! struct coverage *total;
! long maxlineno;
{
! long block_num = 0; /* current block number */
! struct bb_info *block_ptr = NULL; /* current block ptr */
! struct coverage function;
! struct coverage *func_ptr = NULL;
! struct bb_info_list *current_graph = NULL; /* Graph for current function. */
! int is_this_file = 0; /* We're scanning a block from the desired file. */
! char *ptr = bb_data;
long count;
long line_num;
! struct line_info *line_ptr; /* line info ptr. */
!
! memset (&function, 0, sizeof (function));
! if (output_function_summary)
! func_ptr = &function;
!
! for (count = 0; count < bb_data_size; count++)
{
! __fetch_long (&line_num, ptr, 4);
! ptr += 4;
! if (line_num < 0)
{
! long delim;
!
! 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. */
! is_this_file = !strcmp (total->name, ptr);
}
! 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 && is_this_file)
! calculate_branch_probs (block_ptr, line_ptr, func_ptr);
}
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);
}
+ if (func_ptr && is_this_file)
+ function_summary (func_ptr, "function");
+ current_graph = current_graph->next;
}
+ block_num = 0;
+ block_ptr = current_graph->bb_graph;
+ memset (&function, 0, sizeof (function));
+ function.name = ptr;
+ }
+ else
+ {
+ fnotice (stderr, "ERROR: unexpected line number %ld\n", line_num);
+ abort ();
}
! /* Scan past the string. */
! for (delim = 0; delim != line_num; count++)
! {
! __fetch_long (&delim, ptr, 4);
! ptr += 4;
! }
! }
! else if (!line_num)
{
! /* Marks the end of a block. */
! if (block_num >= current_graph->num_blocks)
{
! fnotice (stderr, "ERROR: too many basic blocks in function %s\n",
! function.name);
! abort ();
}
!
! if (output_branch_probs && is_this_file)
! calculate_branch_probs (block_ptr, line_ptr, func_ptr);
!
! block_num++;
! block_ptr++;
}
! else if (is_this_file)
{
! if (line_num >= maxlineno)
{
! fnotice (stderr, "ERROR: out of range line number in function %s\n",
! function.name);
! abort ();
}
! line_ptr = &line_info[line_num];
! if (func_ptr)
{
! if (!line_ptr->exists)
! func_ptr->lines++;
! if (!line_ptr->count && block_ptr->exec_count)
! func_ptr->lines_executed++;
}
! /* Accumulate execution data for this line number. */
! line_ptr->count += block_ptr->exec_count;
! line_ptr->exists = 1;
! }
! }
!
! if (func_ptr && is_this_file)
! function_summary (func_ptr, "function");
!
! /* Calculate summary test coverage statistics. */
! for (line_num = 1, line_ptr = &line_info[line_num];
! line_num < maxlineno; line_num++, line_ptr++)
! {
! struct arcdata *a_ptr, *prev, *next;
!
! if (line_ptr->exists)
! {
! total->lines++;
! if (line_ptr->count)
! total->lines_executed++;
! }
! /* Total and reverse the branch information. */
! for (a_ptr = line_ptr->branches, prev = NULL; a_ptr; a_ptr = next)
! {
! next = a_ptr->next;
! a_ptr->next = prev;
! prev = a_ptr;
! accumulate_branch_counts (total, a_ptr);
! }
! line_ptr->branches = prev;
! }
! }
! /* Read in the source file one line at a time, and output that line to
! the gcov file preceded by its execution count and other
! information. */
! static void
! output_line_info (gcov_file, line_info, total, maxlineno)
! FILE *gcov_file;
! const struct line_info *line_info;
! const struct coverage *total;
! long maxlineno;
! {
! FILE *source_file;
! long line_num; /* current line number */
! const struct line_info *line_ptr; /* current line info ptr. */
! char string[STRING_SIZE]; /* line buffer. */
! char const *retval = ""; /* status of source file reading. */
!
! fprintf (gcov_file, "%9s:%5d:Source:%s\n", "-", 0, total->name);
! fprintf (gcov_file, "%9s:%5d:Object:%s\n", "-", 0, bb_file_name);
!
! source_file = fopen (total->name, "r");
! if (!source_file)
! {
! fnotice (stderr, "Could not open source file %s.\n", total->name);
! retval = NULL;
! }
! else
! {
! struct stat status;
!
! if (!fstat (fileno (source_file), &status)
! && status.st_mtime > bb_file_time)
! {
! fnotice (stderr, "Warning: source file %s is newer than %s\n",
! total->name, bb_file_name);
! fprintf (gcov_file, "%9s:%5d:Source is newer than compiler output\n",
! "-", 0);
! }
! }
! for (line_num = 1, line_ptr = &line_info[line_num];
! line_num < maxlineno; line_num++, line_ptr++)
! {
! /* For lines which don't exist in the .bb file, print '-' 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. */
! fprintf (gcov_file, "%9s:%5ld:",
! !line_ptr->exists ? "-"
! : !line_ptr->count ? "#####"
! : format_hwint (line_ptr->count, 0, -1), line_num);
!
! if (retval)
! {
! /* Copy source line. */
! do
! {
! retval = fgets (string, STRING_SIZE, source_file);
! if (!retval)
{
! fnotice (stderr,
! "Unexpected EOF while reading source file %s.\n",
! total->name);
! break;
}
+ fputs (retval, gcov_file);
}
+ while (!retval[0] || retval[strlen (retval) - 1] != '\n');
+ }
+ if (!retval)
+ fputs ("??\n", gcov_file);
+
+ if (output_branch_probs)
+ {
+ int i;
+ struct arcdata *a_ptr;
! for (i = 0, a_ptr = line_ptr->branches; a_ptr;
! a_ptr = a_ptr->next, i++)
{
! if (a_ptr->call_insn)
{
! if (a_ptr->total == 0)
! fnotice (gcov_file, "call %2d never executed\n", i);
! else
! fnotice
! (gcov_file, "call %2d returns %s\n", i,
! format_hwint (a_ptr->total - a_ptr->hits,
! a_ptr->total,
! -output_branch_counts));
}
! else
{
! if (a_ptr->total == 0)
! fnotice (gcov_file, "branch %2d never executed\n", i);
! else
! fnotice
! (gcov_file, "branch %2d taken %s\n", i,
! format_hwint (a_ptr->hits, a_ptr->total,
! -output_branch_counts));
! }
}
! }
! }
!
! /* Handle all remaining source lines. There may be lines after the
! last line of code. */
! if (retval)
! {
! for (; (retval = fgets (string, STRING_SIZE, source_file)); line_num++)
! {
! fprintf (gcov_file, "%9s:%5ld:%s", "-", line_num, retval);
!
! while (!retval[0] || retval[strlen (retval) - 1] != '\n')
{
! retval = fgets (string, STRING_SIZE, source_file);
! if (!retval)
! break;
! fputs (retval, gcov_file);
}
+ }
+ }
+
+ if (source_file)
+ fclose (source_file);
+ }
+
+ /* Calculate line execution counts, and output a .gcov file for source
+ file S_PTR. 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. */
+
+ static void
+ output_data (s_ptr)
+ struct sourcefile *s_ptr;
+ {
+ struct line_info *line_info /* line info data */
+ = (struct line_info *) xcalloc (s_ptr->maxlineno,
+ sizeof (struct line_info));
+ long line_num;
+ struct coverage total;
+
+ memset (&total, 0, sizeof (total));
+ total.name = s_ptr->name;
+
+ init_line_info (line_info, &total, s_ptr->maxlineno);
+ function_summary (&total, "file");
! 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 information. */
!
! char *gcov_file_name = make_gcov_file_name (total.name);
! FILE *gcov_file = fopen (gcov_file_name, "w");
!
! if (gcov_file)
! {
! fnotice (stdout, "Creating %s.\n", gcov_file_name);
! output_line_info (gcov_file, line_info, &total, s_ptr->maxlineno);
if (ferror (gcov_file))
fnotice (stderr, "Error writing output file %s.\n",
gcov_file_name);
fclose (gcov_file);
}
+ else
+ fnotice (stderr, "Could not open output file %s.\n", gcov_file_name);
+ free (gcov_file_name);
+ }
+
+ /* Free data. */
+ for (line_num = 1; line_num != s_ptr->maxlineno; line_num++)
+ {
+ struct arcdata *branch, *next;
! for (branch = line_info[line_num].branches; branch; branch = next)
! {
! next = branch->next;
! free (branch);
! }
}
+ free (line_info);
}