Commit 136ca74e by Martin Liska Committed by Martin Liska

GCOV: support multiple functions per a line (PR gcov-profile/48463)

2017-11-09  Martin Liska  <mliska@suse.cz>

	PR gcov-profile/48463
	* coverage.c (coverage_begin_function): Output also end locus
	of a function and information whether the function is
	artificial.
	* gcov-dump.c (tag_function): Parse and print the information.
	* gcov.c (INCLUDE_MAP): Add include.
	(INCLUDE_SET): Likewise.
	(struct line_info): Move earlier in the source file because
	of vector<line_info> in function_info structure.
	(line_info::line_info): Likewise.
	(line_info::has_block): Likewise.
	(struct source_info): Add new member index.
	(source_info::get_functions_at_location): New function.
	(function_info::group_line_p): New function.
	(output_intermediate_line): New function.
	(output_intermediate_file): Use the mentioned function.
	(struct function_start): New.
	(struct function_start_pair_hash): Likewise.
	(process_file): Add code that identifies group functions.
	Assign lines either to global or function scope.
	(generate_results): Skip artificial functions.
	(find_source): Assign index for each source file.
	(read_graph_file): Read new flag artificial and end_line.
	(add_line_counts): Assign it either to global of function scope.
	(accumulate_line_counts): Isolate core of the function to
	accumulate_line_info and call it for both function and global
	scope lines.
	(accumulate_line_info): New function.
	(output_line_beginning): Fix GNU coding style.
	(print_source_line): New function.
	(output_line_details): Likewise.
	(output_function_details): Likewise.
	(output_lines): Iterate both source (global) scope and function
	scope.
	(struct function_line_start_cmp): New class.
	* doc/gcov.texi: Reflect changes in documentation.

From-SVN: r254562
parent 6bc322a1
2017-11-09 Martin Liska <mliska@suse.cz>
PR gcov-profile/48463
* coverage.c (coverage_begin_function): Output also end locus
of a function and information whether the function is
artificial.
* gcov-dump.c (tag_function): Parse and print the information.
* gcov.c (INCLUDE_MAP): Add include.
(INCLUDE_SET): Likewise.
(struct line_info): Move earlier in the source file because
of vector<line_info> in function_info structure.
(line_info::line_info): Likewise.
(line_info::has_block): Likewise.
(struct source_info): Add new member index.
(source_info::get_functions_at_location): New function.
(function_info::group_line_p): New function.
(output_intermediate_line): New function.
(output_intermediate_file): Use the mentioned function.
(struct function_start): New.
(struct function_start_pair_hash): Likewise.
(process_file): Add code that identifies group functions.
Assign lines either to global or function scope.
(generate_results): Skip artificial functions.
(find_source): Assign index for each source file.
(read_graph_file): Read new flag artificial and end_line.
(add_line_counts): Assign it either to global of function scope.
(accumulate_line_counts): Isolate core of the function to
accumulate_line_info and call it for both function and global
scope lines.
(accumulate_line_info): New function.
(output_line_beginning): Fix GNU coding style.
(print_source_line): New function.
(output_line_details): Likewise.
(output_function_details): Likewise.
(output_lines): Iterate both source (global) scope and function
scope.
(struct function_line_start_cmp): New class.
* doc/gcov.texi: Reflect changes in documentation.
2017-11-09 Jakub Jelinek <jakub@redhat.com> 2017-11-09 Jakub Jelinek <jakub@redhat.com>
PR debug/82837 PR debug/82837
...@@ -663,8 +663,15 @@ coverage_begin_function (unsigned lineno_checksum, unsigned cfg_checksum) ...@@ -663,8 +663,15 @@ coverage_begin_function (unsigned lineno_checksum, unsigned cfg_checksum)
gcov_write_unsigned (cfg_checksum); gcov_write_unsigned (cfg_checksum);
gcov_write_string (IDENTIFIER_POINTER gcov_write_string (IDENTIFIER_POINTER
(DECL_ASSEMBLER_NAME (current_function_decl))); (DECL_ASSEMBLER_NAME (current_function_decl)));
gcov_write_unsigned (DECL_ARTIFICIAL (current_function_decl));
gcov_write_filename (xloc.file); gcov_write_filename (xloc.file);
gcov_write_unsigned (xloc.line); gcov_write_unsigned (xloc.line);
gcov_write_unsigned (xloc.column);
expanded_location endloc = expand_location (cfun->function_end_locus);
/* Function can start in a single file and end in another one. */
gcov_write_unsigned (endloc.file == xloc.file ? endloc.line : xloc.line);
gcov_write_length (offset); gcov_write_length (offset);
return !gcov_is_error (); return !gcov_is_error ();
......
...@@ -193,7 +193,7 @@ Write counts in human readable format (like 24k). ...@@ -193,7 +193,7 @@ Write counts in human readable format (like 24k).
@smallexample @smallexample
file:@var{source_file_name} 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} lcount:@var{line number},@var{execution_count},@var{has_unexecuted_block}
branch:@var{line_number},@var{branch_coverage_type} branch:@var{line_number},@var{branch_coverage_type}
...@@ -201,24 +201,55 @@ Where the @var{branch_coverage_type} is ...@@ -201,24 +201,55 @@ Where the @var{branch_coverage_type} is
notexec (Branch not executed) notexec (Branch not executed)
taken (Branch executed and taken) taken (Branch executed and taken)
nottaken (Branch executed, but not taken) nottaken (Branch executed, but not taken)
@end smallexample
There can be multiple @var{file} entries in an intermediate gcov There can be multiple @var{file} entries in an intermediate gcov
file. All entries following a @var{file} pertain to that source file file. All entries following a @var{file} pertain to that source file
until the next @var{file} entry. until the next @var{file} entry. If there are multiple functions that
@end smallexample 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: Here is a sample when @option{-i} is used in conjunction with @option{-b} option:
@smallexample @smallexample
file:array.cc file:tmp.cpp
function:11,1,_Z3sumRKSt6vectorIPiSaIS0_EE function:7,7,0,_ZN3FooIcEC2Ev
function:22,1,main function:7,7,1,_ZN3FooIiEC2Ev
lcount:11,1,0 function:8,8,0,_ZN3FooIcE3incEv
lcount:12,1,0 function:8,8,2,_ZN3FooIiE3incEv
lcount:14,1,0 function:18,37,1,main
branch:14,taken lcount:7,0,1
lcount:26,1,0 lcount:7,1,0
branch:28,nottaken 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 @end smallexample
@item -k @item -k
...@@ -391,79 +422,158 @@ source file compiled with @option{-fprofile-arcs}, an accompanying ...@@ -391,79 +422,158 @@ source file compiled with @option{-fprofile-arcs}, an accompanying
Running @command{gcov} with your program's source file names as arguments 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 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: is what you see when you use the basic @command{gcov} facility:
@smallexample @smallexample
$ gcc -fprofile-arcs -ftest-coverage tmp.c $ g++ -fprofile-arcs -ftest-coverage tmp.cpp
$ a.out $ a.out
$ gcov tmp.c $ gcov tmp.cpp -m
File 'tmp.c' File 'tmp.cpp'
Lines executed:90.00% of 10 Lines executed:92.86% of 14
Creating 'tmp.c.gcov' Creating 'tmp.cpp.gcov'
@end smallexample @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: Here is a sample:
@smallexample @smallexample
-: 0:Source:tmp.c -: 0:Source:tmp.cpp
-: 0:Graph:tmp.gcno -: 0:Graph:tmp.gcno
-: 0:Data:tmp.gcda -: 0:Data:tmp.gcda
-: 0:Runs:1 -: 0:Runs:1
-: 0:Programs:1 -: 0:Programs:1
-: 1:#include <stdio.h> -: 1:#include <stdio.h>
-: 2: -: 2:
-: 3:int main (void) -: 3:template<class T>
1: 4:@{ -: 4:class Foo
1: 5: int i, total; -: 5:@{
-: 6: -: 6: public:
1: 7: total = 0; 1*: 7: Foo(): b (1000) @{@}
-: 8: ------------------
11: 9: for (i = 0; i < 10; i++) Foo<char>::Foo():
10: 10: total += i; #####: 7: Foo(): b (1000) @{@}
-: 11: ------------------
1: 12: if (total != 45) Foo<int>::Foo():
#####: 13: printf ("Failure\n"); 1: 7: Foo(): b (1000) @{@}
-: 14: else ------------------
1: 15: printf ("Success\n"); 2*: 8: void inc () @{ b++; @}
1: 16: return 0; ------------------
-: 17:@} 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 @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 When you use the @option{-a} option, you will get individual block
counts, and the output looks like this: counts, and the output looks like this:
@smallexample @smallexample
-: 0:Source:tmp.c -: 0:Source:tmp.cpp
-: 0:Graph:tmp.gcno -: 0:Graph:tmp.gcno
-: 0:Data:tmp.gcda -: 0:Data:tmp.gcda
-: 0:Runs:1 -: 0:Runs:1
-: 0:Programs:1 -: 0:Programs:1
-: 1:#include <stdio.h> -: 1:#include <stdio.h>
-: 2: -: 2:
-: 3:int main (void) -: 3:template<class T>
1: 4:@{ -: 4:class Foo
1: 4-block 0 -: 5:@{
1: 5: int i, total; -: 6: public:
-: 6: 1*: 7: Foo(): b (1000) @{@}
1: 7: total = 0; ------------------
-: 8: Foo<char>::Foo():
11: 9: for (i = 0; i < 10; i++) #####: 7: Foo(): b (1000) @{@}
11: 9-block 0 ------------------
10: 10: total += i; Foo<int>::Foo():
10: 10-block 0 1: 7: Foo(): b (1000) @{@}
-: 11: ------------------
1: 12: if (total != 45) 2*: 8: void inc () @{ b++; @}
1: 12-block 0 ------------------
#####: 13: printf ("Failure\n"); Foo<char>::inc():
$$$$$: 13-block 0 #####: 8: void inc () @{ b++; @}
-: 14: else ------------------
1: 15: printf ("Success\n"); Foo<int>::inc():
1: 15-block 0 2: 8: void inc () @{ b++; @}
1: 16: return 0; ------------------
1: 16-block 0 -: 9:
-: 17:@} -: 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 @end smallexample
In this mode, each basic block is only shown on one line -- the last In this mode, each basic block is only shown on one line -- the last
...@@ -477,53 +587,94 @@ block, the branch and call counts of the block will be shown, if the ...@@ -477,53 +587,94 @@ block, the branch and call counts of the block will be shown, if the
Because of the way GCC instruments calls, a call count can be shown Because of the way GCC instruments calls, a call count can be shown
after a line with no individual blocks. 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 @need 450
When you use the @option{-b} option, your output looks like this: When you use the @option{-b} option, your output looks like this:
@smallexample @smallexample
$ gcov -b tmp.c -: 0:Source:tmp.cpp
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:Graph:tmp.gcno -: 0:Graph:tmp.gcno
-: 0:Data:tmp.gcda -: 0:Data:tmp.gcda
-: 0:Runs:1 -: 0:Runs:1
-: 0:Programs:1 -: 0:Programs:1
-: 1:#include <stdio.h> -: 1:#include <stdio.h>
-: 2: -: 2:
-: 3:int main (void) -: 3:template<class T>
function main called 1 returned 1 blocks executed 75% -: 4:class Foo
1: 4:@{ -: 5:@{
1: 5: int i, total; -: 6: public:
-: 6: 1*: 7: Foo(): b (1000) @{@}
1: 7: total = 0; ------------------
-: 8: Foo<char>::Foo():
11: 9: for (i = 0; i < 10; i++) 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 0 taken 91% (fallthrough)
branch 1 taken 9% branch 1 taken 9%
10: 10: total += i; 10: 28: total += i;
-: 11: -: 29:
1: 12: if (total != 45) 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 0 taken 0% (fallthrough)
branch 1 taken 100% branch 1 taken 100%
#####: 13: printf ("Failure\n"); #####: 33: printf ("Failure\n");
call 0 never executed call 0 never executed
-: 14: else branch 1 never executed
1: 15: printf ("Success\n"); branch 2 never executed
call 0 called 1 returned 100% -: 34: else
1: 16: return 0; 1: 35: printf ("Success\n");
-: 17:@} call 0 returned 100%
branch 1 taken 100% (fallthrough)
branch 2 taken 0% (throw)
1: 36: return 0;
-: 37:@}
@end smallexample @end smallexample
For each function, a line is printed showing how many times the function For each function, a line is printed showing how many times the function
......
...@@ -308,9 +308,15 @@ tag_function (const char *filename ATTRIBUTE_UNUSED, ...@@ -308,9 +308,15 @@ tag_function (const char *filename ATTRIBUTE_UNUSED,
name = gcov_read_string (); name = gcov_read_string ();
printf (", `%s'", name ? name : "NULL"); printf (", `%s'", name ? name : "NULL");
unsigned artificial = gcov_read_unsigned ();
name = gcov_read_string (); name = gcov_read_string ();
printf (" %s", name ? name : "NULL"); printf (" %s", name ? name : "NULL");
printf (":%u", gcov_read_unsigned ()); unsigned line_start = gcov_read_unsigned ();
unsigned column_start = gcov_read_unsigned ();
unsigned line_end = gcov_read_unsigned ();
printf (":%u:%u:%u", line_start, column_start, line_end);
if (artificial)
printf (", artificial");
} }
} }
} }
......
...@@ -34,6 +34,8 @@ along with Gcov; see the file COPYING3. If not see ...@@ -34,6 +34,8 @@ along with Gcov; see the file COPYING3. If not see
#define INCLUDE_ALGORITHM #define INCLUDE_ALGORITHM
#define INCLUDE_VECTOR #define INCLUDE_VECTOR
#define INCLUDE_STRING #define INCLUDE_STRING
#define INCLUDE_MAP
#define INCLUDE_SET
#include "system.h" #include "system.h"
#include "coretypes.h" #include "coretypes.h"
#include "tm.h" #include "tm.h"
...@@ -183,6 +185,42 @@ block_info::block_info (): succ (NULL), pred (NULL), num_succ (0), num_pred (0), ...@@ -183,6 +185,42 @@ block_info::block_info (): succ (NULL), pred (NULL), num_succ (0), num_pred (0),
cycle.arc = NULL; 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. */ /* Describes a single function. Contains an array of basic blocks. */
typedef struct function_info typedef struct function_info
...@@ -190,6 +228,10 @@ typedef struct function_info ...@@ -190,6 +228,10 @@ typedef struct function_info
function_info (); 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. */ /* Name of function. */
char *name; char *name;
char *demangled_name; char *demangled_name;
...@@ -200,6 +242,13 @@ typedef struct function_info ...@@ -200,6 +242,13 @@ typedef struct function_info
/* The graph contains at least one fake incoming edge. */ /* The graph contains at least one fake incoming edge. */
unsigned has_catch : 1; 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 /* Array of basic blocks. Like in GCC, the entry block is
at blocks[0] and the exit block is at blocks[1]. */ at blocks[0] and the exit block is at blocks[1]. */
#define ENTRY_BLOCK (0) #define ENTRY_BLOCK (0)
...@@ -211,17 +260,39 @@ typedef struct function_info ...@@ -211,17 +260,39 @@ typedef struct function_info
gcov_type *counts; gcov_type *counts;
unsigned num_counts; unsigned num_counts;
/* First line number & file. */ /* First line number. */
unsigned line; 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; unsigned src;
/* Next function in same source file. */ /* Vector of line information. */
struct function_info *next_file_fn; vector<line_info> lines;
/* Next function. */ /* Next function. */
struct function_info *next; struct function_info *next;
} function_t; } 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. */ /* Describes coverage of a file or function. */
typedef struct coverage_info typedef struct coverage_info
...@@ -239,42 +310,6 @@ typedef struct coverage_info ...@@ -239,42 +310,6 @@ typedef struct coverage_info
char *name; char *name;
} coverage_t; } 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 /* Describes a file mentioned in the block graph. Contains an array
of line info. */ of line info. */
...@@ -283,6 +318,11 @@ struct source_info ...@@ -283,6 +318,11 @@ struct source_info
/* Default constructor. */ /* Default constructor. */
source_info (); 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. */ /* Canonical name of source file. */
char *name; char *name;
time_t file_time; time_t file_time;
...@@ -294,14 +334,31 @@ struct source_info ...@@ -294,14 +334,31 @@ struct source_info
/* Functions in this source file. These are in ascending line /* Functions in this source file. These are in ascending line
number order. */ number order. */
function_t *functions; vector <function_t *> functions;
}; };
source_info::source_info (): name (NULL), file_time (), lines (), source_info::source_info (): index (0), name (NULL), file_time (),
coverage (), functions (NULL) 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 class name_map
{ {
public: public:
...@@ -495,8 +552,9 @@ extern int main (int, char **); ...@@ -495,8 +552,9 @@ extern int main (int, char **);
function_info::function_info (): name (NULL), demangled_name (NULL), function_info::function_info (): name (NULL), demangled_name (NULL),
ident (0), lineno_checksum (0), cfg_checksum (0), has_catch (0), 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), 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)
{ {
} }
...@@ -518,6 +576,11 @@ function_info::~function_info () ...@@ -518,6 +576,11 @@ function_info::~function_info ()
free (name); 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! /* Cycle detection!
There are a bajillion algorithms that do this. Boost's function is named 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 hawick_cycles, so I used the algorithm by K. A. Hawick and H. A. James in
...@@ -889,6 +952,42 @@ process_args (int argc, char **argv) ...@@ -889,6 +952,42 @@ process_args (int argc, char **argv)
return optind; 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'. /* Output the result in intermediate format used by 'lcov'.
The intermediate format contains a single file named 'foo.cc.gcov', The intermediate format contains a single file named 'foo.cc.gcov',
...@@ -902,50 +1001,95 @@ file 'foo.cc.gcov' similar to the above example. */ ...@@ -902,50 +1001,95 @@ file 'foo.cc.gcov' similar to the above example. */
static void static void
output_intermediate_file (FILE *gcov_file, source_info *src) 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 */ 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> */ /* function:<name>,<line_number>,<execution_count> */
fprintf (gcov_file, "function:%d,%s,%s\n", fn->line, fprintf (gcov_file, "function:%d,%d,%s,%s\n", (*it)->start_line,
format_gcov (fn->blocks[0].count, 0, -1), (*it)->end_line, format_gcov ((*it)->blocks[0].count, 0, -1),
flag_demangled_names ? fn->demangled_name : fn->name); flag_demangled_names ? (*it)->demangled_name : (*it)->name);
} }
for (line_num = 1, line = &src->lines[line_num]; for (unsigned line_num = 0; line_num <= src->lines.size (); line_num++)
line_num < src->lines.size ();
line_num++, line++)
{ {
if (line->exists) vector<function_t *> fns = src->get_functions_at_location (line_num);
fprintf (gcov_file, "lcount:%u,%s,%d\n", line_num,
format_gcov (line->count, 0, -1), line->has_unexecuted_block); /* Print first group functions that begin on the line. */
if (flag_branches) for (vector<function_t *>::iterator it2 = fns.begin ();
for (vector<arc_t *>::const_iterator it = line->branches.begin (); it2 != fns.end (); it2++)
it != line->branches.end (); it++)
{ {
if (!(*it)->is_unconditional && !(*it)->is_call_non_return) vector<line_info> &lines = (*it2)->lines;
for (unsigned i = 0; i < lines.size (); i++)
{ {
const char *branch_type; line_info *line = &lines[i];
/* branch:<line_num>,<branch_coverage_type> output_intermediate_line (gcov_file, line, line_num + i);
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);
} }
} }
/* 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. */ /* Process a single input file. */
static void static void
...@@ -959,6 +1103,28 @@ process_file (const char *file_name) ...@@ -959,6 +1103,28 @@ process_file (const char *file_name)
return; return;
read_count_file (fns); 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) while (fns)
{ {
function_t *fn = fns; function_t *fn = fns;
...@@ -968,20 +1134,13 @@ process_file (const char *file_name) ...@@ -968,20 +1134,13 @@ process_file (const char *file_name)
if (fn->counts || no_data_file) if (fn->counts || no_data_file)
{ {
unsigned src = fn->src; unsigned src = fn->src;
unsigned line = fn->line;
unsigned block_no; unsigned block_no;
function_t *probe, **prev;
/* Process only non-artificial functions. */
/* Now insert it into the source file's list of if (!fn->artificial)
functions. Normally functions will be encountered in {
ascending order, so a simple scan is quick. Note we're source_info *s = &sources[src];
building this list in reverse order. */ s->functions.push_back (fn);
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. */ /* Mark last line in files touched by function. */
for (block_no = 0; block_no != fn->blocks.size (); block_no++) for (block_no = 0; block_no != fn->blocks.size (); block_no++)
...@@ -989,25 +1148,36 @@ process_file (const char *file_name) ...@@ -989,25 +1148,36 @@ process_file (const char *file_name)
block_t *block = &fn->blocks[block_no]; block_t *block = &fn->blocks[block_no];
for (unsigned i = 0; i < block->locations.size (); i++) for (unsigned i = 0; i < block->locations.size (); i++)
{ {
unsigned s = block->locations[i].source_file_idx;
/* Sort lines of locations. */ /* Sort lines of locations. */
sort (block->locations[i].lines.begin (), sort (block->locations[i].lines.begin (),
block->locations[i].lines.end ()); block->locations[i].lines.end ());
if (!block->locations[i].lines.empty ()) if (!block->locations[i].lines.empty ())
{ {
s = &sources[block->locations[i].source_file_idx];
unsigned last_line unsigned last_line
= block->locations[i].lines.back () + 1; = block->locations[i].lines.back ();
if (last_line > sources[s].lines.size ())
sources[s].lines.resize (last_line); /* 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); solve_flow_graph (fn);
if (fn->has_catch) if (fn->has_catch)
find_exception_blocks (fn); find_exception_blocks (fn);
}
*fn_end = fn; *fn_end = fn;
fn_end = &fn->next; fn_end = &fn->next;
} }
...@@ -1057,6 +1227,8 @@ generate_results (const char *file_name) ...@@ -1057,6 +1227,8 @@ generate_results (const char *file_name)
for (fn = functions; fn; fn = fn->next) for (fn = functions; fn; fn = fn->next)
{ {
coverage_t coverage; coverage_t coverage;
if (fn->artificial)
continue;
memset (&coverage, 0, sizeof (coverage)); memset (&coverage, 0, sizeof (coverage));
coverage.name = flag_demangled_names ? fn->demangled_name : fn->name; coverage.name = flag_demangled_names ? fn->demangled_name : fn->name;
...@@ -1237,6 +1409,7 @@ find_source (const char *file_name) ...@@ -1237,6 +1409,7 @@ find_source (const char *file_name)
src = &sources.back (); src = &sources.back ();
src->name = canon; src->name = canon;
src->coverage.name = src->name; src->coverage.name = src->name;
src->index = idx;
if (source_length if (source_length
#if HAVE_DOS_BASED_FILE_SYSTEM #if HAVE_DOS_BASED_FILE_SYSTEM
/* You lose if separators don't match exactly in the /* You lose if separators don't match exactly in the
...@@ -1328,15 +1501,18 @@ read_graph_file (void) ...@@ -1328,15 +1501,18 @@ read_graph_file (void)
if (tag == GCOV_TAG_FUNCTION) if (tag == GCOV_TAG_FUNCTION)
{ {
char *function_name; char *function_name;
unsigned ident, lineno; unsigned ident;
unsigned lineno_checksum, cfg_checksum; unsigned lineno_checksum, cfg_checksum;
ident = gcov_read_unsigned (); ident = gcov_read_unsigned ();
lineno_checksum = gcov_read_unsigned (); lineno_checksum = gcov_read_unsigned ();
cfg_checksum = gcov_read_unsigned (); cfg_checksum = gcov_read_unsigned ();
function_name = xstrdup (gcov_read_string ()); function_name = xstrdup (gcov_read_string ());
unsigned artificial = gcov_read_unsigned ();
unsigned src_idx = find_source (gcov_read_string ()); 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 = new function_t;
fn->name = function_name; fn->name = function_name;
...@@ -1350,9 +1526,11 @@ read_graph_file (void) ...@@ -1350,9 +1526,11 @@ read_graph_file (void)
fn->lineno_checksum = lineno_checksum; fn->lineno_checksum = lineno_checksum;
fn->cfg_checksum = cfg_checksum; fn->cfg_checksum = cfg_checksum;
fn->src = src_idx; 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; fn->next = NULL;
*fns_end = fn; *fns_end = fn;
fns_end = &fn->next; fns_end = &fn->next;
...@@ -2266,12 +2444,34 @@ add_line_counts (coverage_t *coverage, function_t *fn) ...@@ -2266,12 +2444,34 @@ add_line_counts (coverage_t *coverage, function_t *fn)
fn->blocks_executed++; fn->blocks_executed++;
for (unsigned i = 0; i < block->locations.size (); i++) 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; vector<unsigned> &lines = block->locations[i].lines;
block->cycle.arc = NULL;
block->cycle.ident = ~0U;
for (unsigned j = 0; j < lines.size (); j++) for (unsigned j = 0; j < lines.size (); j++)
{ {
line = &src->lines[lines[j]]; unsigned ln = lines[j];
/* Line belongs to a function that is in a group. */
if (fn->group_line_p (ln, src_idx))
{
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;
}
else
{
gcc_assert (ln < sources[src_idx].lines.size ());
line = &(sources[src_idx].lines[ln]);
if (coverage) if (coverage)
{ {
if (!line->exists) if (!line->exists)
...@@ -2289,12 +2489,11 @@ add_line_counts (coverage_t *coverage, function_t *fn) ...@@ -2289,12 +2489,11 @@ add_line_counts (coverage_t *coverage, function_t *fn)
line->count += block->count; line->count += block->count;
} }
} }
block->cycle.arc = NULL;
block->cycle.ident = ~0U;
has_any_line = true; has_any_line = true;
if (!ix || ix + 1 == fn->blocks.size ()) if (!ix || ix + 1 == fn->blocks.size ())
/* Entry or exit block */; /* Entry or exit block. */;
else if (line != NULL) else if (line != NULL)
{ {
line->blocks.push_back (block); line->blocks.push_back (block);
...@@ -2304,10 +2503,7 @@ add_line_counts (coverage_t *coverage, function_t *fn) ...@@ -2304,10 +2503,7 @@ add_line_counts (coverage_t *coverage, function_t *fn)
arc_t *arc; arc_t *arc;
for (arc = block->succ; arc; arc = arc->succ_next) for (arc = block->succ; arc; arc = arc->succ_next)
{
line->branches.push_back (arc); line->branches.push_back (arc);
if (coverage && !arc->is_unconditional)
add_branch_counts (coverage, arc);
} }
} }
} }
...@@ -2317,26 +2513,17 @@ add_line_counts (coverage_t *coverage, function_t *fn) ...@@ -2317,26 +2513,17 @@ add_line_counts (coverage_t *coverage, function_t *fn)
fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name); fnotice (stderr, "%s:no lines for '%s'\n", bbg_file_name, fn->name);
} }
/* Accumulate the line counts of a file. */ /* Accumulate info for LINE that belongs to SRC source file. If ADD_COVERAGE
is set to true, update source file summary. */
static void static void accumulate_line_info (line_info *line, source_info *src,
accumulate_line_counts (source_info *src) bool add_coverage)
{ {
function_t *fn, *fn_p, *fn_n; if (add_coverage)
unsigned ix = 0; for (vector<arc_info *>::iterator it = line->branches.begin ();
it != line->branches.end (); it++)
/* Reverse the function order. */ add_branch_counts (&src->coverage, *it);
for (fn = src->functions, fn_p = NULL; fn; fn_p = fn, fn = fn_n)
{
fn_n = fn->next_file_fn;
fn->next_file_fn = fn_p;
}
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 ()) if (!line->blocks.empty ())
{ {
/* The user expects the line count to be the number of times /* The user expects the line count to be the number of times
...@@ -2347,17 +2534,6 @@ accumulate_line_counts (source_info *src) ...@@ -2347,17 +2534,6 @@ accumulate_line_counts (source_info *src)
and add the transition counts of those cycles. */ and add the transition counts of those cycles. */
gcov_type count = 0; 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;
for (arc = (*it)->pred; arc; arc = arc->pred_next)
if (flag_branches)
add_branch_counts (&src->coverage, arc);
}
/* Cycle detection. */ /* Cycle detection. */
for (vector<block_t *>::iterator it = line->blocks.begin (); for (vector<block_t *>::iterator it = line->blocks.begin ();
it != line->blocks.end (); it++) it != line->blocks.end (); it++)
...@@ -2374,14 +2550,75 @@ accumulate_line_counts (source_info *src) ...@@ -2374,14 +2550,75 @@ accumulate_line_counts (source_info *src)
line->count = count; line->count = count;
} }
if (line->exists) if (line->exists && add_coverage)
{ {
src->coverage.lines++; src->coverage.lines++;
if (line->count) if (line->count)
src->coverage.lines_executed++; src->coverage.lines_executed++;
} }
}
/* Accumulate the line counts of a file. */
static void
accumulate_line_counts (source_info *src)
{
/* First work on group functions. */
for (vector<function_t *>::iterator it = src->functions.begin ();
it != src->functions.end (); it++)
{
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);
}
}
/* 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);
/* 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;
if (fn->src != src->index || !fn->is_group)
continue;
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 (!src_line->exists)
src->coverage.lines++;
if (!src_line->count && fn_line->count)
src->coverage.lines_executed++;
src_line->count += fn_line->count;
src_line->exists = 1;
if (fn_line->has_unexecuted_block)
src_line->has_unexecuted_block = 1;
ix++; if (fn_line->unexceptional)
src_line->unexceptional = 1;
}
}
} }
} }
...@@ -2500,7 +2737,8 @@ output_line_beginning (FILE *f, bool exists, bool unexceptional, ...@@ -2500,7 +2737,8 @@ output_line_beginning (FILE *f, bool exists, bool unexceptional,
if (flag_use_colors) if (flag_use_colors)
{ {
pad_count_string (s); 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; s += SGR_RESET;
} }
else else
...@@ -2538,6 +2776,86 @@ output_line_beginning (FILE *f, bool exists, bool unexceptional, ...@@ -2538,6 +2776,86 @@ output_line_beginning (FILE *f, bool exists, bool unexceptional,
fprintf (f, "%s:%5u", s.c_str (), line_num); 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 /* 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 the gcov file preceded by its execution count and other
information. */ information. */
...@@ -2546,12 +2864,10 @@ static void ...@@ -2546,12 +2864,10 @@ static void
output_lines (FILE *gcov_file, const source_info *src) output_lines (FILE *gcov_file, const source_info *src)
{ {
#define DEFAULT_LINE_START " -: 0:" #define DEFAULT_LINE_START " -: 0:"
#define FN_SEPARATOR "------------------\n"
FILE *source_file; FILE *source_file;
unsigned line_num; /* current line number. */ const char *retval;
const line_info *line; /* current line info ptr. */
const char *retval = ""; /* status of source file reading. */
function_t *fn = NULL;
fprintf (gcov_file, DEFAULT_LINE_START "Source:%s\n", src->coverage.name); fprintf (gcov_file, DEFAULT_LINE_START "Source:%s\n", src->coverage.name);
if (!multiple_files) if (!multiple_files)
...@@ -2565,43 +2881,40 @@ output_lines (FILE *gcov_file, const source_info *src) ...@@ -2565,43 +2881,40 @@ output_lines (FILE *gcov_file, const source_info *src)
source_file = fopen (src->name, "r"); source_file = fopen (src->name, "r");
if (!source_file) if (!source_file)
{
fnotice (stderr, "Cannot open source file %s\n", src->name); fnotice (stderr, "Cannot open source file %s\n", src->name);
retval = NULL;
}
else if (src->file_time == 0) else if (src->file_time == 0)
fprintf (gcov_file, DEFAULT_LINE_START "Source is newer than graph\n"); fprintf (gcov_file, DEFAULT_LINE_START "Source is newer than graph\n");
if (flag_branches) vector<const char *> source_lines;
fn = src->functions; if (source_file)
while ((retval = read_line (source_file)) != NULL)
source_lines.push_back (xstrdup (retval));
unsigned line_start_group = 0;
vector<function_t *> fns;
for (line_num = 1, line = &src->lines[line_num]; for (unsigned line_num = 1; line_num <= source_lines.size (); line_num++)
line_num < src->lines.size (); line_num++, line++)
{ {
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; fprintf (gcov_file, "%9s:%5u", "-", line_num);
gcov_type return_count = fn->blocks[EXIT_BLOCK].count; print_source_line (gcov_file, source_lines, line_num);
gcov_type called_count = fn->blocks[ENTRY_BLOCK].count; continue;
}
for (; arc; arc = arc->pred_next) const line_info *line = &src->lines[line_num];
if (arc->fake)
return_count -= arc->count;
fprintf (gcov_file, "function %s", flag_demangled_names ? if (line_start_group == 0)
fn->demangled_name : fn->name); {
fprintf (gcov_file, " called %s", fns = src->get_functions_at_location (line_num);
format_gcov (called_count, 0, -1)); if (fns.size () > 1)
fprintf (gcov_file, " returned %s", line_start_group = fns[0]->end_line;
format_gcov (return_count, called_count, 0)); else if (fns.size () == 1)
fprintf (gcov_file, " blocks executed %s", {
format_gcov (fn->blocks_executed, fn->blocks.size () - 2, function_t *fn = fns[0];
0)); output_function_details (gcov_file, fn);
fprintf (gcov_file, "\n"); }
} }
if (retval)
retval = read_line (source_file);
/* For lines which don't exist in the .bb file, print '-' before /* For lines which don't exist in the .bb file, print '-' before
the source line. For lines which exist but were never the source line. For lines which exist but were never
...@@ -2610,52 +2923,62 @@ output_lines (FILE *gcov_file, const source_info *src) ...@@ -2610,52 +2923,62 @@ output_lines (FILE *gcov_file, const source_info *src)
There are 16 spaces of indentation added before the source There are 16 spaces of indentation added before the source
line so that tabs won't be messed up. */ line so that tabs won't be messed up. */
output_line_beginning (gcov_file, line->exists, line->unexceptional, output_line_beginning (gcov_file, line->exists, line->unexceptional,
line->has_unexecuted_block, line->count, line_num, line->has_unexecuted_block, line->count,
"=====", "#####"); line_num, "=====", "#####");
fprintf (gcov_file, ":%s\n", retval ? retval : "/*EOF*/");
if (flag_all_blocks) print_source_line (gcov_file, source_lines, line_num);
{ output_line_details (gcov_file, line, line_num);
arc_t *arc;
int ix, jx;
ix = jx = 0; if (line_start_group == line_num)
for (vector<block_t *>::const_iterator it = line->blocks.begin ();
it != line->blocks.end (); it++)
{ {
if (!(*it)->is_call_return) for (vector<function_t *>::iterator it = fns.begin ();
it != fns.end (); it++)
{ {
output_line_beginning (gcov_file, line->exists, function_info *fn = *it;
(*it)->exceptional, false, vector<line_info> &lines = fn->lines;
(*it)->count, line_num,
"%%%%%", "$$$$$"); fprintf (gcov_file, FN_SEPARATOR);
fprintf (gcov_file, "-block %2d", ix++);
if (flag_verbose) string fn_name
fprintf (gcov_file, " (BB %u)", (*it)->id); = flag_demangled_names ? fn->demangled_name : fn->name;
fprintf (gcov_file, "\n");
} if (flag_use_colors)
if (flag_branches) {
for (arc = (*it)->succ; arc; arc = arc->succ_next) fn_name.insert (0, SGR_SEQ (COLOR_FG_CYAN));
jx += output_branch_count (gcov_file, jx, arc); fn_name += SGR_RESET;
}
} }
else if (flag_branches)
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++)
{ {
int ix; line_info *line = &lines[i];
unsigned l = fn->start_line + i;
ix = 0; /* For lines which don't exist in the .bb file, print '-'
for (vector<arc_t *>::const_iterator it = line->branches.begin (); before the source line. For lines which exist but
it != line->branches.end (); it++) were never executed, print '#####' or '=====' before
ix += output_branch_count (gcov_file, ix, (*it)); 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,
line->unexceptional,
line->has_unexecuted_block,
line->count,
l, "=====", "#####");
print_source_line (gcov_file, source_lines, l);
output_line_details (gcov_file, line, l);
} }
} }
/* Handle all remaining source lines. There may be lines after the fprintf (gcov_file, FN_SEPARATOR);
last line of code. */ line_start_group = 0;
if (retval) }
{
for (; (retval = read_line (source_file)); line_num++)
fprintf (gcov_file, "%9s:%5u:%s\n", "-", line_num, retval);
} }
if (source_file) if (source_file)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment