@smallexample
file:@var{source_file_name}
-function:@var{line_number},@var{execution_count},@var{function_name}
+function:@var{start_line_number},@var{end_line_number},@var{execution_count},@var{function_name}
lcount:@var{line number},@var{execution_count},@var{has_unexecuted_block}
branch:@var{line_number},@var{branch_coverage_type}
notexec (Branch not executed)
taken (Branch executed and taken)
nottaken (Branch executed, but not taken)
+@end smallexample
There can be multiple @var{file} entries in an intermediate gcov
file. All entries following a @var{file} pertain to that source file
-until the next @var{file} entry.
-@end smallexample
+until the next @var{file} entry. If there are multiple functions that
+start on a single line, then corresponding lcount is repeated multiple
+times.
Here is a sample when @option{-i} is used in conjunction with @option{-b} option:
@smallexample
-file:array.cc
-function:11,1,_Z3sumRKSt6vectorIPiSaIS0_EE
-function:22,1,main
-lcount:11,1,0
-lcount:12,1,0
-lcount:14,1,0
-branch:14,taken
-lcount:26,1,0
-branch:28,nottaken
+file:tmp.cpp
+function:7,7,0,_ZN3FooIcEC2Ev
+function:7,7,1,_ZN3FooIiEC2Ev
+function:8,8,0,_ZN3FooIcE3incEv
+function:8,8,2,_ZN3FooIiE3incEv
+function:18,37,1,main
+lcount:7,0,1
+lcount:7,1,0
+lcount:8,0,1
+lcount:8,2,0
+lcount:18,1,0
+lcount:21,1,0
+branch:21,taken
+branch:21,nottaken
+lcount:23,1,0
+branch:23,taken
+branch:23,nottaken
+lcount:24,1,0
+branch:24,taken
+branch:24,nottaken
+lcount:25,1,0
+lcount:27,11,0
+branch:27,taken
+branch:27,taken
+lcount:28,10,0
+lcount:30,1,1
+branch:30,nottaken
+branch:30,taken
+lcount:32,1,0
+branch:32,nottaken
+branch:32,taken
+lcount:33,0,1
+branch:33,notexec
+branch:33,notexec
+lcount:35,1,0
+branch:35,taken
+branch:35,nottaken
+lcount:36,1,0
@end smallexample
@item -k
Running @command{gcov} with your program's source file names as arguments
will now produce a listing of the code along with frequency of execution
-for each line. For example, if your program is called @file{tmp.c}, this
+for each line. For example, if your program is called @file{tmp.cpp}, this
is what you see when you use the basic @command{gcov} facility:
@smallexample
-$ gcc -fprofile-arcs -ftest-coverage tmp.c
+$ g++ -fprofile-arcs -ftest-coverage tmp.cpp
$ a.out
-$ gcov tmp.c
-File 'tmp.c'
-Lines executed:90.00% of 10
-Creating 'tmp.c.gcov'
+$ gcov tmp.cpp -m
+File 'tmp.cpp'
+Lines executed:92.86% of 14
+Creating 'tmp.cpp.gcov'
@end smallexample
-The file @file{tmp.c.gcov} contains output from @command{gcov}.
+The file @file{tmp.cpp.gcov} contains output from @command{gcov}.
Here is a sample:
@smallexample
- -: 0:Source:tmp.c
+ -: 0:Source:tmp.cpp
-: 0:Graph:tmp.gcno
-: 0:Data:tmp.gcda
-: 0:Runs:1
-: 0:Programs:1
-: 1:#include <stdio.h>
-: 2:
- -: 3:int main (void)
- 1: 4:@{
- 1: 5: int i, total;
- -: 6:
- 1: 7: total = 0;
- -: 8:
- 11: 9: for (i = 0; i < 10; i++)
- 10: 10: total += i;
- -: 11:
- 1: 12: if (total != 45)
- #####: 13: printf ("Failure\n");
- -: 14: else
- 1: 15: printf ("Success\n");
- 1: 16: return 0;
- -: 17:@}
+ -: 3:template<class T>
+ -: 4:class Foo
+ -: 5:@{
+ -: 6: public:
+ 1*: 7: Foo(): b (1000) @{@}
+------------------
+Foo<char>::Foo():
+ #####: 7: Foo(): b (1000) @{@}
+------------------
+Foo<int>::Foo():
+ 1: 7: Foo(): b (1000) @{@}
+------------------
+ 2*: 8: void inc () @{ b++; @}
+------------------
+Foo<char>::inc():
+ #####: 8: void inc () @{ b++; @}
+------------------
+Foo<int>::inc():
+ 2: 8: void inc () @{ b++; @}
+------------------
+ -: 9:
+ -: 10: private:
+ -: 11: int b;
+ -: 12:@};
+ -: 13:
+ -: 14:template class Foo<int>;
+ -: 15:template class Foo<char>;
+ -: 16:
+ -: 17:int
+ 1: 18:main (void)
+ -: 19:@{
+ -: 20: int i, total;
+ 1: 21: Foo<int> counter;
+ -: 22:
+ 1: 23: counter.inc();
+ 1: 24: counter.inc();
+ 1: 25: total = 0;
+ -: 26:
+ 11: 27: for (i = 0; i < 10; i++)
+ 10: 28: total += i;
+ -: 29:
+ 1*: 30: int v = total > 100 ? 1 : 2;
+ -: 31:
+ 1: 32: if (total != 45)
+ #####: 33: printf ("Failure\n");
+ -: 34: else
+ 1: 35: printf ("Success\n");
+ 1: 36: return 0;
+ -: 37:@}
@end smallexample
+Note that line 7 is shown in the report multiple times. First occurrence
+presents total number of execution of the line and the next two belong
+to instances of class Foo constructors. As you can also see, line 30 contains
+some unexecuted basic blocks and thus execution count has asterisk symbol.
+
When you use the @option{-a} option, you will get individual block
counts, and the output looks like this:
@smallexample
- -: 0:Source:tmp.c
+ -: 0:Source:tmp.cpp
-: 0:Graph:tmp.gcno
-: 0:Data:tmp.gcda
-: 0:Runs:1
-: 0:Programs:1
-: 1:#include <stdio.h>
-: 2:
- -: 3:int main (void)
- 1: 4:@{
- 1: 4-block 0
- 1: 5: int i, total;
- -: 6:
- 1: 7: total = 0;
- -: 8:
- 11: 9: for (i = 0; i < 10; i++)
- 11: 9-block 0
- 10: 10: total += i;
- 10: 10-block 0
- -: 11:
- 1: 12: if (total != 45)
- 1: 12-block 0
- #####: 13: printf ("Failure\n");
- $$$$$: 13-block 0
- -: 14: else
- 1: 15: printf ("Success\n");
- 1: 15-block 0
- 1: 16: return 0;
- 1: 16-block 0
- -: 17:@}
+ -: 3:template<class T>
+ -: 4:class Foo
+ -: 5:@{
+ -: 6: public:
+ 1*: 7: Foo(): b (1000) @{@}
+------------------
+Foo<char>::Foo():
+ #####: 7: Foo(): b (1000) @{@}
+------------------
+Foo<int>::Foo():
+ 1: 7: Foo(): b (1000) @{@}
+------------------
+ 2*: 8: void inc () @{ b++; @}
+------------------
+Foo<char>::inc():
+ #####: 8: void inc () @{ b++; @}
+------------------
+Foo<int>::inc():
+ 2: 8: void inc () @{ b++; @}
+------------------
+ -: 9:
+ -: 10: private:
+ -: 11: int b;
+ -: 12:@};
+ -: 13:
+ -: 14:template class Foo<int>;
+ -: 15:template class Foo<char>;
+ -: 16:
+ -: 17:int
+ 1: 18:main (void)
+ -: 19:@{
+ -: 20: int i, total;
+ 1: 21: Foo<int> counter;
+ 1: 21-block 0
+ -: 22:
+ 1: 23: counter.inc();
+ 1: 23-block 0
+ 1: 24: counter.inc();
+ 1: 24-block 0
+ 1: 25: total = 0;
+ -: 26:
+ 11: 27: for (i = 0; i < 10; i++)
+ 1: 27-block 0
+ 11: 27-block 1
+ 10: 28: total += i;
+ 10: 28-block 0
+ -: 29:
+ 1*: 30: int v = total > 100 ? 1 : 2;
+ 1: 30-block 0
+ %%%%%: 30-block 1
+ 1: 30-block 2
+ -: 31:
+ 1: 32: if (total != 45)
+ 1: 32-block 0
+ #####: 33: printf ("Failure\n");
+ %%%%%: 33-block 0
+ -: 34: else
+ 1: 35: printf ("Success\n");
+ 1: 35-block 0
+ 1: 36: return 0;
+ 1: 36-block 0
+ -: 37:@}
@end smallexample
In this mode, each basic block is only shown on one line -- the last
Because of the way GCC instruments calls, a call count can be shown
after a line with no individual blocks.
-As you can see, line 13 contains a basic block that was not executed.
+As you can see, line 33 contains a basic block that was not executed.
@need 450
When you use the @option{-b} option, your output looks like this:
@smallexample
-$ gcov -b tmp.c
-File 'tmp.c'
-Lines executed:90.00% of 10
-Branches executed:80.00% of 5
-Taken at least once:80.00% of 5
-Calls executed:50.00% of 2
-Creating 'tmp.c.gcov'
-@end smallexample
-
-Here is a sample of a resulting @file{tmp.c.gcov} file:
-
-@smallexample
- -: 0:Source:tmp.c
+ -: 0:Source:tmp.cpp
-: 0:Graph:tmp.gcno
-: 0:Data:tmp.gcda
-: 0:Runs:1
-: 0:Programs:1
-: 1:#include <stdio.h>
-: 2:
- -: 3:int main (void)
-function main called 1 returned 1 blocks executed 75%
- 1: 4:@{
- 1: 5: int i, total;
- -: 6:
- 1: 7: total = 0;
- -: 8:
- 11: 9: for (i = 0; i < 10; i++)
+ -: 3:template<class T>
+ -: 4:class Foo
+ -: 5:@{
+ -: 6: public:
+ 1*: 7: Foo(): b (1000) @{@}
+------------------
+Foo<char>::Foo():
+function Foo<char>::Foo() called 0 returned 0% blocks executed 0%
+ #####: 7: Foo(): b (1000) @{@}
+------------------
+Foo<int>::Foo():
+function Foo<int>::Foo() called 1 returned 100% blocks executed 100%
+ 1: 7: Foo(): b (1000) @{@}
+------------------
+ 2*: 8: void inc () @{ b++; @}
+------------------
+Foo<char>::inc():
+function Foo<char>::inc() called 0 returned 0% blocks executed 0%
+ #####: 8: void inc () @{ b++; @}
+------------------
+Foo<int>::inc():
+function Foo<int>::inc() called 2 returned 100% blocks executed 100%
+ 2: 8: void inc () @{ b++; @}
+------------------
+ -: 9:
+ -: 10: private:
+ -: 11: int b;
+ -: 12:@};
+ -: 13:
+ -: 14:template class Foo<int>;
+ -: 15:template class Foo<char>;
+ -: 16:
+ -: 17:int
+function main called 1 returned 100% blocks executed 81%
+ 1: 18:main (void)
+ -: 19:@{
+ -: 20: int i, total;
+ 1: 21: Foo<int> counter;
+call 0 returned 100%
+branch 1 taken 100% (fallthrough)
+branch 2 taken 0% (throw)
+ -: 22:
+ 1: 23: counter.inc();
+call 0 returned 100%
+branch 1 taken 100% (fallthrough)
+branch 2 taken 0% (throw)
+ 1: 24: counter.inc();
+call 0 returned 100%
+branch 1 taken 100% (fallthrough)
+branch 2 taken 0% (throw)
+ 1: 25: total = 0;
+ -: 26:
+ 11: 27: for (i = 0; i < 10; i++)
branch 0 taken 91% (fallthrough)
branch 1 taken 9%
- 10: 10: total += i;
- -: 11:
- 1: 12: if (total != 45)
+ 10: 28: total += i;
+ -: 29:
+ 1*: 30: int v = total > 100 ? 1 : 2;
+branch 0 taken 0% (fallthrough)
+branch 1 taken 100%
+ -: 31:
+ 1: 32: if (total != 45)
branch 0 taken 0% (fallthrough)
branch 1 taken 100%
- #####: 13: printf ("Failure\n");
+ #####: 33: printf ("Failure\n");
call 0 never executed
- -: 14: else
- 1: 15: printf ("Success\n");
-call 0 called 1 returned 100%
- 1: 16: return 0;
- -: 17:@}
+branch 1 never executed
+branch 2 never executed
+ -: 34: else
+ 1: 35: printf ("Success\n");
+call 0 returned 100%
+branch 1 taken 100% (fallthrough)
+branch 2 taken 0% (throw)
+ 1: 36: return 0;
+ -: 37:@}
@end smallexample
For each function, a line is printed showing how many times the function
#define INCLUDE_ALGORITHM
#define INCLUDE_VECTOR
#define INCLUDE_STRING
+#define INCLUDE_MAP
+#define INCLUDE_SET
#include "system.h"
#include "coretypes.h"
#include "tm.h"
cycle.arc = NULL;
}
+/* Describes a single line of source. Contains a chain of basic blocks
+ with code on it. */
+
+struct line_info
+{
+ /* Default constructor. */
+ line_info ();
+
+ /* Return true when NEEDLE is one of basic blocks the line belongs to. */
+ bool has_block (block_t *needle);
+
+ /* Execution count. */
+ gcov_type count;
+
+ /* Branches from blocks that end on this line. */
+ vector<arc_t *> branches;
+
+ /* blocks which start on this line. Used in all-blocks mode. */
+ vector<block_t *> blocks;
+
+ unsigned exists : 1;
+ unsigned unexceptional : 1;
+ unsigned has_unexecuted_block : 1;
+};
+
+line_info::line_info (): count (0), branches (), blocks (), exists (false),
+ unexceptional (0), has_unexecuted_block (0)
+{
+}
+
+bool
+line_info::has_block (block_t *needle)
+{
+ return std::find (blocks.begin (), blocks.end (), needle) != blocks.end ();
+}
+
/* Describes a single function. Contains an array of basic blocks. */
typedef struct function_info
function_info ();
~function_info ();
+ /* Return true when line N belongs to the function in source file SRC_IDX.
+ The line must be defined in body of the function, can't be inlined. */
+ bool group_line_p (unsigned n, unsigned src_idx);
+
/* Name of function. */
char *name;
char *demangled_name;
/* The graph contains at least one fake incoming edge. */
unsigned has_catch : 1;
+ /* True when the function is artificial and does not exist
+ in a source file. */
+ unsigned artificial : 1;
+
+ /* True when multiple functions start at a line in a source file. */
+ unsigned is_group : 1;
+
/* Array of basic blocks. Like in GCC, the entry block is
at blocks[0] and the exit block is at blocks[1]. */
#define ENTRY_BLOCK (0)
gcov_type *counts;
unsigned num_counts;
- /* First line number & file. */
- unsigned line;
+ /* First line number. */
+ unsigned start_line;
+
+ /* First line column. */
+ unsigned start_column;
+
+ /* Last line number. */
+ unsigned end_line;
+
+ /* Index of source file where the function is defined. */
unsigned src;
- /* Next function in same source file. */
- struct function_info *next_file_fn;
+ /* Vector of line information. */
+ vector<line_info> lines;
/* Next function. */
struct function_info *next;
} function_t;
+/* Function info comparer that will sort functions according to starting
+ line. */
+
+struct function_line_start_cmp
+{
+ inline bool operator() (const function_info *lhs,
+ const function_info *rhs)
+ {
+ return (lhs->start_line == rhs->start_line
+ ? lhs->start_column < rhs->start_column
+ : lhs->start_line < rhs->start_line);
+ }
+};
+
/* Describes coverage of a file or function. */
typedef struct coverage_info
char *name;
} coverage_t;
-/* Describes a single line of source. Contains a chain of basic blocks
- with code on it. */
-
-struct line_info
-{
- /* Default constructor. */
- line_info ();
-
- /* Return true when NEEDLE is one of basic blocks the line belongs to. */
- bool has_block (block_t *needle);
-
- /* Execution count. */
- gcov_type count;
-
- /* Branches from blocks that end on this line. */
- vector<arc_t *> branches;
-
- /* blocks which start on this line. Used in all-blocks mode. */
- vector<block_t *> blocks;
-
- unsigned exists : 1;
- unsigned unexceptional : 1;
- unsigned has_unexecuted_block : 1;
-};
-
-line_info::line_info (): count (0), branches (), blocks (), exists (false),
- unexceptional (0), has_unexecuted_block (0)
-{
-}
-
-bool
-line_info::has_block (block_t *needle)
-{
- return std::find (blocks.begin (), blocks.end (), needle) != blocks.end ();
-}
-
/* Describes a file mentioned in the block graph. Contains an array
of line info. */
/* Default constructor. */
source_info ();
+ vector<function_t *> get_functions_at_location (unsigned line_num) const;
+
+ /* Index of the source_info in sources vector. */
+ unsigned index;
+
/* Canonical name of source file. */
char *name;
time_t file_time;
/* Functions in this source file. These are in ascending line
number order. */
- function_t *functions;
+ vector <function_t *> functions;
};
-source_info::source_info (): name (NULL), file_time (), lines (),
- coverage (), functions (NULL)
+source_info::source_info (): index (0), name (NULL), file_time (),
+ lines (), coverage (), functions ()
{
}
+vector<function_t *>
+source_info::get_functions_at_location (unsigned line_num) const
+{
+ vector<function_t *> r;
+
+ for (vector<function_t *>::const_iterator it = functions.begin ();
+ it != functions.end (); it++)
+ {
+ if ((*it)->start_line == line_num && (*it)->src == index)
+ r.push_back (*it);
+ }
+
+ std::sort (r.begin (), r.end (), function_line_start_cmp ());
+
+ return r;
+}
+
class name_map
{
public:
function_info::function_info (): name (NULL), demangled_name (NULL),
ident (0), lineno_checksum (0), cfg_checksum (0), has_catch (0),
+ artificial (0), is_group (0),
blocks (), blocks_executed (0), counts (NULL), num_counts (0),
- line (0), src (0), next_file_fn (NULL), next (NULL)
+ start_line (0), start_column (0), end_line (0), src (0), lines (), next (NULL)
{
}
free (name);
}
+bool function_info::group_line_p (unsigned n, unsigned src_idx)
+{
+ return is_group && src == src_idx && start_line <= n && n <= end_line;
+}
+
/* Cycle detection!
There are a bajillion algorithms that do this. Boost's function is named
hawick_cycles, so I used the algorithm by K. A. Hawick and H. A. James in
return optind;
}
+/* Output intermediate LINE sitting on LINE_NUM to output file F. */
+
+static void
+output_intermediate_line (FILE *f, line_info *line, unsigned line_num)
+{
+ if (!line->exists)
+ return;
+
+ fprintf (f, "lcount:%u,%s,%d\n", line_num,
+ format_gcov (line->count, 0, -1),
+ line->has_unexecuted_block);
+
+ vector<arc_t *>::const_iterator it;
+ if (flag_branches)
+ for (it = line->branches.begin (); it != line->branches.end ();
+ it++)
+ {
+ if (!(*it)->is_unconditional && !(*it)->is_call_non_return)
+ {
+ const char *branch_type;
+ /* branch:<line_num>,<branch_coverage_type>
+ branch_coverage_type
+ : notexec (Branch not executed)
+ : taken (Branch executed and taken)
+ : nottaken (Branch executed, but not taken)
+ */
+ if ((*it)->src->count)
+ branch_type
+ = ((*it)->count > 0) ? "taken" : "nottaken";
+ else
+ branch_type = "notexec";
+ fprintf (f, "branch:%d,%s\n", line_num, branch_type);
+ }
+ }
+}
+
/* Output the result in intermediate format used by 'lcov'.
The intermediate format contains a single file named 'foo.cc.gcov',
static void
output_intermediate_file (FILE *gcov_file, source_info *src)
{
- unsigned line_num; /* current line number. */
- const line_info *line; /* current line info ptr. */
- function_t *fn; /* current function info ptr. */
-
fprintf (gcov_file, "file:%s\n", src->name); /* source file name */
- for (fn = src->functions; fn; fn = fn->next_file_fn)
+ std::sort (src->functions.begin (), src->functions.end (),
+ function_line_start_cmp ());
+ for (vector<function_t *>::iterator it = src->functions.begin ();
+ it != src->functions.end (); it++)
{
/* function:<name>,<line_number>,<execution_count> */
- fprintf (gcov_file, "function:%d,%s,%s\n", fn->line,
- format_gcov (fn->blocks[0].count, 0, -1),
- flag_demangled_names ? fn->demangled_name : fn->name);
+ fprintf (gcov_file, "function:%d,%d,%s,%s\n", (*it)->start_line,
+ (*it)->end_line, format_gcov ((*it)->blocks[0].count, 0, -1),
+ flag_demangled_names ? (*it)->demangled_name : (*it)->name);
}
- for (line_num = 1, line = &src->lines[line_num];
- line_num < src->lines.size ();
- line_num++, line++)
+ for (unsigned line_num = 0; line_num <= src->lines.size (); line_num++)
{
- if (line->exists)
- fprintf (gcov_file, "lcount:%u,%s,%d\n", line_num,
- format_gcov (line->count, 0, -1), line->has_unexecuted_block);
- if (flag_branches)
- for (vector<arc_t *>::const_iterator it = line->branches.begin ();
- it != line->branches.end (); it++)
- {
- if (!(*it)->is_unconditional && !(*it)->is_call_non_return)
- {
- const char *branch_type;
- /* branch:<line_num>,<branch_coverage_type>
- branch_coverage_type
- : notexec (Branch not executed)
- : taken (Branch executed and taken)
- : nottaken (Branch executed, but not taken)
- */
- if ((*it)->src->count)
- branch_type = ((*it)->count > 0) ? "taken" : "nottaken";
- else
- branch_type = "notexec";
- fprintf (gcov_file, "branch:%d,%s\n", line_num, branch_type);
- }
- }
+ vector<function_t *> fns = src->get_functions_at_location (line_num);
+
+ /* Print first group functions that begin on the line. */
+ for (vector<function_t *>::iterator it2 = fns.begin ();
+ it2 != fns.end (); it2++)
+ {
+ vector<line_info> &lines = (*it2)->lines;
+ for (unsigned i = 0; i < lines.size (); i++)
+ {
+ line_info *line = &lines[i];
+ output_intermediate_line (gcov_file, line, line_num + i);
+ }
+ }
+
+ /* Follow with lines associated with the source file. */
+ output_intermediate_line (gcov_file, &src->lines[line_num], line_num);
}
}
+/* Function start pair. */
+struct function_start
+{
+ unsigned source_file_idx;
+ unsigned start_line;
+};
+
+/* Traits class for function start hash maps below. */
+
+struct function_start_pair_hash : typed_noop_remove <function_start>
+{
+ typedef function_start value_type;
+ typedef function_start compare_type;
+
+ static hashval_t
+ hash (const function_start &ref)
+ {
+ inchash::hash hstate (0);
+ hstate.add_int (ref.source_file_idx);
+ hstate.add_int (ref.start_line);
+ return hstate.end ();
+ }
+
+ static bool
+ equal (const function_start &ref1, const function_start &ref2)
+ {
+ return (ref1.source_file_idx == ref2.source_file_idx
+ && ref1.start_line == ref2.start_line);
+ }
+
+ static void
+ mark_deleted (function_start &ref)
+ {
+ ref.start_line = ~1U;
+ }
+
+ static void
+ mark_empty (function_start &ref)
+ {
+ ref.start_line = ~2U;
+ }
+
+ static bool
+ is_deleted (const function_start &ref)
+ {
+ return ref.start_line == ~1U;
+ }
+
+ static bool
+ is_empty (const function_start &ref)
+ {
+ return ref.start_line == ~2U;
+ }
+};
+
/* Process a single input file. */
static void
return;
read_count_file (fns);
+
+ hash_map<function_start_pair_hash, function_t *> fn_map;
+
+ /* Identify group functions. */
+ for (function_t *f = fns; f; f = f->next)
+ if (!f->artificial)
+ {
+ function_start needle;
+ needle.source_file_idx = f->src;
+ needle.start_line = f->start_line;
+
+ function_t **slot = fn_map.get (needle);
+ if (slot)
+ {
+ gcc_assert ((*slot)->end_line == f->end_line);
+ (*slot)->is_group = 1;
+ f->is_group = 1;
+ }
+ else
+ fn_map.put (needle, f);
+ }
+
while (fns)
{
function_t *fn = fns;
if (fn->counts || no_data_file)
{
unsigned src = fn->src;
- unsigned line = fn->line;
unsigned block_no;
- function_t *probe, **prev;
-
- /* Now insert it into the source file's list of
- functions. Normally functions will be encountered in
- ascending order, so a simple scan is quick. Note we're
- building this list in reverse order. */
- for (prev = &sources[src].functions;
- (probe = *prev); prev = &probe->next_file_fn)
- if (probe->line <= line)
- break;
- fn->next_file_fn = probe;
- *prev = fn;
- /* Mark last line in files touched by function. */
- for (block_no = 0; block_no != fn->blocks.size (); block_no++)
+ /* Process only non-artificial functions. */
+ if (!fn->artificial)
{
- block_t *block = &fn->blocks[block_no];
- for (unsigned i = 0; i < block->locations.size (); i++)
- {
- unsigned s = block->locations[i].source_file_idx;
-
- /* Sort lines of locations. */
- sort (block->locations[i].lines.begin (),
- block->locations[i].lines.end ());
+ source_info *s = &sources[src];
+ s->functions.push_back (fn);
- if (!block->locations[i].lines.empty ())
+ /* Mark last line in files touched by function. */
+ for (block_no = 0; block_no != fn->blocks.size (); block_no++)
+ {
+ block_t *block = &fn->blocks[block_no];
+ for (unsigned i = 0; i < block->locations.size (); i++)
{
- unsigned last_line
- = block->locations[i].lines.back () + 1;
- if (last_line > sources[s].lines.size ())
- sources[s].lines.resize (last_line);
+ /* Sort lines of locations. */
+ sort (block->locations[i].lines.begin (),
+ block->locations[i].lines.end ());
+
+ if (!block->locations[i].lines.empty ())
+ {
+ s = &sources[block->locations[i].source_file_idx];
+ unsigned last_line
+ = block->locations[i].lines.back ();
+
+ /* Record new lines for the function. */
+ if (last_line >= s->lines.size ())
+ {
+ /* Record new lines for a source file. */
+ s->lines.resize (last_line + 1);
+ }
+ }
}
+
+ /* Allocate lines for group function, following start_line
+ and end_line information of the function. */
+ if (fn->is_group)
+ fn->lines.resize (fn->end_line - fn->start_line + 1);
}
+
+ solve_flow_graph (fn);
+ if (fn->has_catch)
+ find_exception_blocks (fn);
}
- solve_flow_graph (fn);
- if (fn->has_catch)
- find_exception_blocks (fn);
*fn_end = fn;
fn_end = &fn->next;
}
for (fn = functions; fn; fn = fn->next)
{
coverage_t coverage;
+ if (fn->artificial)
+ continue;
memset (&coverage, 0, sizeof (coverage));
coverage.name = flag_demangled_names ? fn->demangled_name : fn->name;
src = &sources.back ();
src->name = canon;
src->coverage.name = src->name;
+ src->index = idx;
if (source_length
#if HAVE_DOS_BASED_FILE_SYSTEM
/* You lose if separators don't match exactly in the
if (tag == GCOV_TAG_FUNCTION)
{
char *function_name;
- unsigned ident, lineno;
+ unsigned ident;
unsigned lineno_checksum, cfg_checksum;
ident = gcov_read_unsigned ();
lineno_checksum = gcov_read_unsigned ();
cfg_checksum = gcov_read_unsigned ();
function_name = xstrdup (gcov_read_string ());
+ unsigned artificial = gcov_read_unsigned ();
unsigned src_idx = find_source (gcov_read_string ());
- lineno = gcov_read_unsigned ();
+ unsigned start_line = gcov_read_unsigned ();
+ unsigned start_column = gcov_read_unsigned ();
+ unsigned end_line = gcov_read_unsigned ();
fn = new function_t;
fn->name = function_name;
fn->lineno_checksum = lineno_checksum;
fn->cfg_checksum = cfg_checksum;
fn->src = src_idx;
- fn->line = lineno;
+ fn->start_line = start_line;
+ fn->start_column = start_column;
+ fn->end_line = end_line;
+ fn->artificial = artificial;
- fn->next_file_fn = NULL;
fn->next = NULL;
*fns_end = fn;
fns_end = &fn->next;
fn->blocks_executed++;
for (unsigned i = 0; i < block->locations.size (); i++)
{
- source_info *src = &sources[block->locations[i].source_file_idx];
-
+ unsigned src_idx = block->locations[i].source_file_idx;
vector<unsigned> &lines = block->locations[i].lines;
+
+ block->cycle.arc = NULL;
+ block->cycle.ident = ~0U;
+
for (unsigned j = 0; j < lines.size (); j++)
{
- line = &src->lines[lines[j]];
- if (coverage)
+ unsigned ln = lines[j];
+
+ /* Line belongs to a function that is in a group. */
+ if (fn->group_line_p (ln, src_idx))
{
- if (!line->exists)
- coverage->lines++;
- if (!line->count && block->count)
- coverage->lines_executed++;
+ gcc_assert (lines[j] - fn->start_line < fn->lines.size ());
+ line = &(fn->lines[lines[j] - fn->start_line]);
+ line->exists = 1;
+ if (!block->exceptional)
+ {
+ line->unexceptional = 1;
+ if (block->count == 0)
+ line->has_unexecuted_block = 1;
+ }
+ line->count += block->count;
}
- line->exists = 1;
- if (!block->exceptional)
+ else
{
- line->unexceptional = 1;
- if (block->count == 0)
- line->has_unexecuted_block = 1;
+ gcc_assert (ln < sources[src_idx].lines.size ());
+ line = &(sources[src_idx].lines[ln]);
+ if (coverage)
+ {
+ if (!line->exists)
+ coverage->lines++;
+ if (!line->count && block->count)
+ coverage->lines_executed++;
+ }
+ line->exists = 1;
+ if (!block->exceptional)
+ {
+ line->unexceptional = 1;
+ if (block->count == 0)
+ line->has_unexecuted_block = 1;
+ }
+ line->count += block->count;
}
- line->count += block->count;
}
- }
- block->cycle.arc = NULL;
- block->cycle.ident = ~0U;
- has_any_line = true;
- if (!ix || ix + 1 == fn->blocks.size ())
- /* Entry or exit block */;
- else if (line != NULL)
- {
- line->blocks.push_back (block);
+ has_any_line = true;
- if (flag_branches)
+ if (!ix || ix + 1 == fn->blocks.size ())
+ /* Entry or exit block. */;
+ else if (line != NULL)
{
- arc_t *arc;
+ line->blocks.push_back (block);
- for (arc = block->succ; arc; arc = arc->succ_next)
+ if (flag_branches)
{
- line->branches.push_back (arc);
- if (coverage && !arc->is_unconditional)
- add_branch_counts (coverage, arc);
+ arc_t *arc;
+
+ for (arc = block->succ; arc; arc = arc->succ_next)
+ line->branches.push_back (arc);
}
}
}
fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name);
}
+/* Accumulate info for LINE that belongs to SRC source file. If ADD_COVERAGE
+ is set to true, update source file summary. */
+
+static void accumulate_line_info (line_info *line, source_info *src,
+ bool add_coverage)
+{
+ if (add_coverage)
+ for (vector<arc_info *>::iterator it = line->branches.begin ();
+ it != line->branches.end (); it++)
+ add_branch_counts (&src->coverage, *it);
+
+ if (!line->blocks.empty ())
+ {
+ /* The user expects the line count to be the number of times
+ a line has been executed. Simply summing the block count
+ will give an artificially high number. The Right Thing
+ is to sum the entry counts to the graph of blocks on this
+ line, then find the elementary cycles of the local graph
+ and add the transition counts of those cycles. */
+ gcov_type count = 0;
+
+ /* Cycle detection. */
+ for (vector<block_t *>::iterator it = line->blocks.begin ();
+ it != line->blocks.end (); it++)
+ {
+ for (arc_t *arc = (*it)->pred; arc; arc = arc->pred_next)
+ if (!line->has_block (arc->src))
+ count += arc->count;
+ for (arc_t *arc = (*it)->succ; arc; arc = arc->succ_next)
+ arc->cs_count = arc->count;
+ }
+
+ /* Now, add the count of loops entirely on this line. */
+ count += get_cycles_count (*line);
+ line->count = count;
+ }
+
+ if (line->exists && add_coverage)
+ {
+ src->coverage.lines++;
+ if (line->count)
+ src->coverage.lines_executed++;
+ }
+}
+
/* Accumulate the line counts of a file. */
static void
accumulate_line_counts (source_info *src)
{
- function_t *fn, *fn_p, *fn_n;
- unsigned ix = 0;
-
- /* Reverse the function order. */
- for (fn = src->functions, fn_p = NULL; fn; fn_p = fn, fn = fn_n)
+ /* First work on group functions. */
+ for (vector<function_t *>::iterator it = src->functions.begin ();
+ it != src->functions.end (); it++)
{
- fn_n = fn->next_file_fn;
- fn->next_file_fn = fn_p;
+ function_info *fn = *it;
+
+ if (fn->src != src->index || !fn->is_group)
+ continue;
+
+ for (vector<line_info>::iterator it2 = fn->lines.begin ();
+ it2 != fn->lines.end (); it2++)
+ {
+ line_info *line = &(*it2);
+ accumulate_line_info (line, src, false);
+ }
}
- src->functions = fn_p;
- for (vector<line_info>::reverse_iterator it = src->lines.rbegin ();
- it != src->lines.rend (); it++)
- {
- line_info *line = &(*it);
- if (!line->blocks.empty ())
- {
- /* The user expects the line count to be the number of times
- a line has been executed. Simply summing the block count
- will give an artificially high number. The Right Thing
- is to sum the entry counts to the graph of blocks on this
- line, then find the elementary cycles of the local graph
- and add the transition counts of those cycles. */
- gcov_type count = 0;
-
- /* Sum the entry arcs. */
- for (vector<block_t *>::iterator it = line->blocks.begin ();
- it != line->blocks.end (); it++)
- {
- arc_t *arc;
+ /* Work on global lines that line in source file SRC. */
+ for (vector<line_info>::iterator it = src->lines.begin ();
+ it != src->lines.end (); it++)
+ accumulate_line_info (&(*it), src, true);
- for (arc = (*it)->pred; arc; arc = arc->pred_next)
- if (flag_branches)
- add_branch_counts (&src->coverage, arc);
- }
+ /* If not using intermediate mode, sum lines of group functions and
+ add them to lines that live in a source file. */
+ if (!flag_intermediate_format)
+ for (vector<function_t *>::iterator it = src->functions.begin ();
+ it != src->functions.end (); it++)
+ {
+ function_info *fn = *it;
- /* Cycle detection. */
- for (vector<block_t *>::iterator it = line->blocks.begin ();
- it != line->blocks.end (); it++)
- {
- for (arc_t *arc = (*it)->pred; arc; arc = arc->pred_next)
- if (!line->has_block (arc->src))
- count += arc->count;
- for (arc_t *arc = (*it)->succ; arc; arc = arc->succ_next)
- arc->cs_count = arc->count;
- }
+ if (fn->src != src->index || !fn->is_group)
+ continue;
- /* Now, add the count of loops entirely on this line. */
- count += get_cycles_count (*line);
- line->count = count;
- }
+ for (unsigned i = 0; i < fn->lines.size (); i++)
+ {
+ line_info *fn_line = &fn->lines[i];
+ if (fn_line->exists)
+ {
+ unsigned ln = fn->start_line + i;
+ line_info *src_line = &src->lines[ln];
- if (line->exists)
- {
- src->coverage.lines++;
- if (line->count)
- src->coverage.lines_executed++;
- }
+ if (!src_line->exists)
+ src->coverage.lines++;
+ if (!src_line->count && fn_line->count)
+ src->coverage.lines_executed++;
- ix++;
- }
+ src_line->count += fn_line->count;
+ src_line->exists = 1;
+
+ if (fn_line->has_unexecuted_block)
+ src_line->has_unexecuted_block = 1;
+
+ if (fn_line->unexceptional)
+ src_line->unexceptional = 1;
+ }
+ }
+ }
}
/* Output information about ARC number IX. Returns nonzero if
if (flag_use_colors)
{
pad_count_string (s);
- s = SGR_SEQ (COLOR_BG_MAGENTA COLOR_SEPARATOR COLOR_FG_WHITE);
+ s.insert (0, SGR_SEQ (COLOR_BG_MAGENTA
+ COLOR_SEPARATOR COLOR_FG_WHITE));
s += SGR_RESET;
}
else
fprintf (f, "%s:%5u", s.c_str (), line_num);
}
+static void
+print_source_line (FILE *f, const vector<const char *> &source_lines,
+ unsigned line)
+{
+ gcc_assert (line >= 1);
+ gcc_assert (line <= source_lines.size ());
+
+ fprintf (f, ":%s\n", source_lines[line - 1]);
+}
+
+/* Output line details for LINE and print it to F file. LINE lives on
+ LINE_NUM. */
+
+static void
+output_line_details (FILE *f, const line_info *line, unsigned line_num)
+{
+ if (flag_all_blocks)
+ {
+ arc_t *arc;
+ int ix, jx;
+
+ ix = jx = 0;
+ for (vector<block_t *>::const_iterator it = line->blocks.begin ();
+ it != line->blocks.end (); it++)
+ {
+ if (!(*it)->is_call_return)
+ {
+ output_line_beginning (f, line->exists,
+ (*it)->exceptional, false,
+ (*it)->count, line_num,
+ "%%%%%", "$$$$$");
+ fprintf (f, "-block %2d", ix++);
+ if (flag_verbose)
+ fprintf (f, " (BB %u)", (*it)->id);
+ fprintf (f, "\n");
+ }
+ if (flag_branches)
+ for (arc = (*it)->succ; arc; arc = arc->succ_next)
+ jx += output_branch_count (f, jx, arc);
+ }
+ }
+ else if (flag_branches)
+ {
+ int ix;
+
+ ix = 0;
+ for (vector<arc_t *>::const_iterator it = line->branches.begin ();
+ it != line->branches.end (); it++)
+ ix += output_branch_count (f, ix, (*it));
+ }
+}
+
+/* Output detail statistics about function FN to file F. */
+
+static void
+output_function_details (FILE *f, const function_info *fn)
+{
+ if (!flag_branches)
+ return;
+
+ arc_t *arc = fn->blocks[EXIT_BLOCK].pred;
+ gcov_type return_count = fn->blocks[EXIT_BLOCK].count;
+ gcov_type called_count = fn->blocks[ENTRY_BLOCK].count;
+
+ for (; arc; arc = arc->pred_next)
+ if (arc->fake)
+ return_count -= arc->count;
+
+ fprintf (f, "function %s",
+ flag_demangled_names ? fn->demangled_name : fn->name);
+ fprintf (f, " called %s",
+ format_gcov (called_count, 0, -1));
+ fprintf (f, " returned %s",
+ format_gcov (return_count, called_count, 0));
+ fprintf (f, " blocks executed %s",
+ format_gcov (fn->blocks_executed, fn->blocks.size () - 2,
+ 0));
+ fprintf (f, "\n");
+}
+
/* 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. */
output_lines (FILE *gcov_file, const source_info *src)
{
#define DEFAULT_LINE_START " -: 0:"
+#define FN_SEPARATOR "------------------\n"
FILE *source_file;
- unsigned line_num; /* current line number. */
- const line_info *line; /* current line info ptr. */
- const char *retval = ""; /* status of source file reading. */
- function_t *fn = NULL;
+ const char *retval;
fprintf (gcov_file, DEFAULT_LINE_START "Source:%s\n", src->coverage.name);
if (!multiple_files)
source_file = fopen (src->name, "r");
if (!source_file)
- {
- fnotice (stderr, "Cannot open source file %s\n", src->name);
- retval = NULL;
- }
+ fnotice (stderr, "Cannot open source file %s\n", src->name);
else if (src->file_time == 0)
fprintf (gcov_file, DEFAULT_LINE_START "Source is newer than graph\n");
- if (flag_branches)
- fn = src->functions;
+ vector<const char *> source_lines;
+ if (source_file)
+ while ((retval = read_line (source_file)) != NULL)
+ source_lines.push_back (xstrdup (retval));
- for (line_num = 1, line = &src->lines[line_num];
- line_num < src->lines.size (); line_num++, line++)
+ unsigned line_start_group = 0;
+ vector<function_t *> fns;
+
+ for (unsigned line_num = 1; line_num <= source_lines.size (); line_num++)
{
- for (; fn && fn->line == line_num; fn = fn->next_file_fn)
+ if (line_num >= src->lines.size ())
{
- arc_t *arc = fn->blocks[EXIT_BLOCK].pred;
- gcov_type return_count = fn->blocks[EXIT_BLOCK].count;
- gcov_type called_count = fn->blocks[ENTRY_BLOCK].count;
-
- for (; arc; arc = arc->pred_next)
- if (arc->fake)
- return_count -= arc->count;
-
- fprintf (gcov_file, "function %s", flag_demangled_names ?
- fn->demangled_name : fn->name);
- fprintf (gcov_file, " called %s",
- format_gcov (called_count, 0, -1));
- fprintf (gcov_file, " returned %s",
- format_gcov (return_count, called_count, 0));
- fprintf (gcov_file, " blocks executed %s",
- format_gcov (fn->blocks_executed, fn->blocks.size () - 2,
- 0));
- fprintf (gcov_file, "\n");
+ fprintf (gcov_file, "%9s:%5u", "-", line_num);
+ print_source_line (gcov_file, source_lines, line_num);
+ continue;
}
- if (retval)
- retval = read_line (source_file);
+ const line_info *line = &src->lines[line_num];
+
+ if (line_start_group == 0)
+ {
+ fns = src->get_functions_at_location (line_num);
+ if (fns.size () > 1)
+ line_start_group = fns[0]->end_line;
+ else if (fns.size () == 1)
+ {
+ function_t *fn = fns[0];
+ output_function_details (gcov_file, fn);
+ }
+ }
/* For lines which don't exist in the .bb file, print '-' before
the source line. For lines which exist but were never
There are 16 spaces of indentation added before the source
line so that tabs won't be messed up. */
output_line_beginning (gcov_file, line->exists, line->unexceptional,
- line->has_unexecuted_block, line->count, line_num,
- "=====", "#####");
- fprintf (gcov_file, ":%s\n", retval ? retval : "/*EOF*/");
+ line->has_unexecuted_block, line->count,
+ line_num, "=====", "#####");
- if (flag_all_blocks)
- {
- arc_t *arc;
- int ix, jx;
+ print_source_line (gcov_file, source_lines, line_num);
+ output_line_details (gcov_file, line, line_num);
- ix = jx = 0;
- for (vector<block_t *>::const_iterator it = line->blocks.begin ();
- it != line->blocks.end (); it++)
+ if (line_start_group == line_num)
+ {
+ for (vector<function_t *>::iterator it = fns.begin ();
+ it != fns.end (); it++)
{
- if (!(*it)->is_call_return)
+ function_info *fn = *it;
+ vector<line_info> &lines = fn->lines;
+
+ fprintf (gcov_file, FN_SEPARATOR);
+
+ string fn_name
+ = flag_demangled_names ? fn->demangled_name : fn->name;
+
+ if (flag_use_colors)
{
+ fn_name.insert (0, SGR_SEQ (COLOR_FG_CYAN));
+ fn_name += SGR_RESET;
+ }
+
+ fprintf (gcov_file, "%s:\n", fn_name.c_str ());
+
+ output_function_details (gcov_file, fn);
+
+ /* Print all lines covered by the function. */
+ for (unsigned i = 0; i < lines.size (); i++)
+ {
+ line_info *line = &lines[i];
+ unsigned l = fn->start_line + i;
+
+ /* For lines which don't exist in the .bb file, print '-'
+ before the source line. For lines which exist but
+ were never executed, print '#####' or '=====' 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. */
output_line_beginning (gcov_file, line->exists,
- (*it)->exceptional, false,
- (*it)->count, line_num,
- "%%%%%", "$$$$$");
- fprintf (gcov_file, "-block %2d", ix++);
- if (flag_verbose)
- fprintf (gcov_file, " (BB %u)", (*it)->id);
- fprintf (gcov_file, "\n");
+ line->unexceptional,
+ line->has_unexecuted_block,
+ line->count,
+ l, "=====", "#####");
+
+ print_source_line (gcov_file, source_lines, l);
+ output_line_details (gcov_file, line, l);
}
- if (flag_branches)
- for (arc = (*it)->succ; arc; arc = arc->succ_next)
- jx += output_branch_count (gcov_file, jx, arc);
}
- }
- else if (flag_branches)
- {
- int ix;
- ix = 0;
- for (vector<arc_t *>::const_iterator it = line->branches.begin ();
- it != line->branches.end (); it++)
- ix += output_branch_count (gcov_file, ix, (*it));
+ fprintf (gcov_file, FN_SEPARATOR);
+ line_start_group = 0;
}
}
- /* Handle all remaining source lines. There may be lines after the
- last line of code. */
- if (retval)
- {
- for (; (retval = read_line (source_file)); line_num++)
- fprintf (gcov_file, "%9s:%5u:%s\n", "-", line_num, retval);
- }
-
if (source_file)
fclose (source_file);
}