Created attachment 23882 [details] 1.png, 2.png, src/ (has 3 source files) Gcov is generating a lot of useless output when it comes to function coverage. The problem really came to our attention when looking at classes with virtual destructors like the one in the following class example: [code] class ClassA { public: ClassA(); virtual ~ClassA(); }; class ClassB : public ClassA { public: ClassB(); ~ClassB(); }; [/code] The list of function names then contains three destructors for ClassA (two of which are apparently not called, one which is) and three destructors for ClassB (two of which are apparently not called, one which is). They all have the same signature. When disabling then genhtml option --demangle-cpp, or by viewing the .gcov files, you can see, that each of these destructors (of ClassA for example) has a different name. Example (excerpt of gcov file for classA.cpp): Function '_ZN6ClassAD0Ev' Lines executed:0.00% of 3 Branches executed:0.00% of 2 Taken at least once:0.00% of 2 Calls executed:0.00% of 3 Function '_ZN6ClassAD1Ev' No executable lines Branches executed:0.00% of 2 Taken at least once:0.00% of 2 Calls executed:0.00% of 3 Function '_ZN6ClassAD2Ev' No executable lines Branches executed:100.00% of 2 Taken at least once:50.00% of 2 Calls executed:66.67% of 3 The mangled names differ only in the parts: D0, D1, D2. It's odd that one destructor apparently has 3 executable lines (in the attached source, you can see it's only one line), and that the two others have no executable lines, yet they have branches that can be covered. These "IDs" 'D0', 'D1', 'D2' are given to the mangled function names by the gcc compiler, yet I am unsure of what the difference is supposed to be. Nevertheless, it isn't really of interest to the person analyzing the coverage of his code, what gcc generates in the background. Only those desctructors, that the programmer can actually see in HIS code, should show up in the list of covered (or not covered) functions. This goes for the other functions that show up in the example's list as well: global destructors keyed to _ZN6ClassAC2Ev global constructors keyed to _ZN6ClassAC2Ev __static_initialization_and_destruction_0(int, int) It leads to wrong numbers for the function and branch coverage of what you really want to see. To reproduce: I wrote a two classes: ClassA and ClassB. ClassA has a public constructor (no args, just prints something) and a public virtual destructor (no args, just prints something). ClassB inherits from ClassA (publicly) and has a public constructor (no args, just prints something) and a public destructor (no args, just prints something). The main file just creates an instance of ClassB, stores it in a pointer to ClassA and calls delete on that pointer variable. The source files were compiled using: g++ -fprofile-arcs -ftest-coverage -o prog main.cpp classA.cpp Then ./prog was run once. A .info file was generated using lcov (version 1.9) in the following command: lcov -d . -c -f -o NewInfo.info The final html overview was generated using genhtml (also version 1.9) in the following command: genhtml --demangle-cpp --prefix . -o html/ NewInfo.info Alternatively, to reproduce, you can also use the attached source files. The attachment also include two pictures, displaying a listing of all the functions that have apparently been covered or not covered. Hope this info helps solve the problem. Chris
>The mangled names differ only in the parts: D0, D1, D2. This is because the ABI defines these names. IIRC this has been improved in 4.5 or 4.6 where only one gets defined now and the rest are aliases.
Thank you for your reply. Using GCC 4.6 (and with it the newest version of GCOV), does generate a slightly different output, but it doesn't show any improvement in terms of function coverage. I'm still getting multiple destructors, which seem to differ from one another (though I don't see 'D1' here): function _ZN6ClassAD0Ev called 0 returned 0% blocks executed 0% function _ZN6ClassAD2Ev called 1 returned 100% blocks executed 80% 1: 9:ClassA::~ClassA() -: 10:{ 1: 11: std::cout << "Bey, ClassA!" << std::endl; call 0 returned 100% call 1 returned 100% 1: 12:} call 0 never executed call 1 never executed branch 2 taken 0% (fallthrough) branch 3 taken 100% call 4 never executed I'm not sure what all this stuff about branches is that I have going on here, but I don't have an in my destructor, so I assume it is code generated by GCC in the generated destructors that are showing up in the coverage. It might be useful to know that these exist in the background, but this info is confusing and useless when analyzing ones own code coverage. On a side note: These also show up when generating documents with lcov. My course of action was the same as before, also leaving the source files unaltered.
I confirm that, it's caused by generated deleting dtor: virtual ClassB::~ClassB() (struct ClassB * const this) { <bb 2>: ClassB::~ClassB (this_2(D)); operator delete (this_2(D), 8); return; } The dtor is given the same source line as it's abstract origin and all statements in the function are given line after the origin. Resulting in bogus output: $ gcov -b tc.gcda ... function _ZN6ClassBD0Ev called 1 returned 100% blocks executed 100% function _ZN6ClassBD2Ev called 1 returned 100% blocks executed 100% 3: 10:ClassB::~ClassB() -: 11:{ 1: 12: std::cout << "Bey, ClassB!" << std::endl; call 0 returned 100% call 1 returned 100% 2: 13:} call 0 returned 100% // ClassB::~ClassB (this_2(D)); call 1 returned 100% // operator delete (this_2(D), 8); -: 14: First idea about how to fix the issues is to ignore all fns with an abstract origin? diff --git a/gcc/coverage.c b/gcc/coverage.c index d4d371e..0cf5ebb 100644 --- a/gcc/coverage.c +++ b/gcc/coverage.c @@ -642,6 +642,11 @@ coverage_begin_function (unsigned lineno_checksum, unsigned cfg_checksum) if (no_coverage || !bbg_file_name) return 0; + /* Do not output any abstract origin function. */ + tree abstract = DECL_ABSTRACT_ORIGIN (current_function_decl); + if (abstract) + return 0; + xloc = expand_location (DECL_SOURCE_LOCATION (current_function_decl)); /* Announce function */ I'm going to discuss that with Honza.
Example of template instantiation: $ cat test.cpp template<class T> class Foo { public: Foo() { b = 123; } void test() { b = 111; } private: int b; }; template class Foo<float>; template class Foo<int>; int main() { Foo<int> xx; xx.test(); return 0; } $ cat test.cpp.gcov: -: 1:template<class T> -: 2:class Foo -: 3:{ -: 4: public: 1: 5: Foo() -: 6: { 1: 7: b = 123; 1: 8: } -: 9: 1: 10: void test() { b = 111; } -: 11: -: 12: private: -: 13: int b; -: 14:}; -: 15: -: 16:template class Foo<float>; -: 17:template class Foo<int>; -: 18: 1: 19:int main() -: 20:{ 1: 21: Foo<int> xx; 1: 22: xx.test(); -: 23: 1: 24: return 0; -: 25:} However LLVM does: /home/marxin/Programming/testcases/gcov-problems/test.cpp: 1| |template<class T> 2| |class Foo 3| |{ 4| | public: 5| | Foo() 6| 1| { 7| 1| b = 123; 8| 1| } ------------------ | Unexecuted instantiation: _ZN3FooIfEC2Ev ------------------ | _ZN3FooIiEC2Ev: | 6| 1| { | 7| 1| b = 123; | 8| 1| } ------------------ 9| | 10| 1| void test() { b = 111; } ------------------ | Unexecuted instantiation: _ZN3FooIfE4testEv ------------------ | _ZN3FooIiE4testEv: | 10| 1| void test() { b = 111; } ------------------ 11| | 12| | private: 13| | int b; 14| |}; 15| | 16| |template class Foo<float>; 17| |template class Foo<int>; 18| | 19| |int main() 20| 1|{ 21| 1| Foo<int> xx; 22| 1| xx.test(); 23| 1| 24| 1| return 0; 25| 1|} Which is more precise.
They basically provide info for all clones of a function: ------------------ | _ZN3FooIfEC2Ev: | 6| 2| { | 7| 2| b = 123; | 8| 2| } ------------------ | _ZN3FooIiEC2Ev: | 6| 1| { | 7| 1| b = 123; | 8| 1| } ------------------
GCC also has information that there are multiple functions pointing to a same line of code: $ gcov-dump test.gcno | grep FUNCTION test.gcno: 01000000: 11:FUNCTION ident=108032747, lineno_checksum=0xc563f9a6, cfg_checksum=0x21286892, `main' test.cpp:19 test.gcno: 01000000: 14:FUNCTION ident=1482441317, lineno_checksum=0xa9f23a94, cfg_checksum=0xa43083b8, `_ZN3FooIiE4testEv' test.cpp:10 test.gcno: 01000000: 13:FUNCTION ident=402991310, lineno_checksum=0x0169e3ec, cfg_checksum=0xa43083b8, `_ZN3FooIiEC2Ev' test.cpp:5 test.gcno: 01000000: 14:FUNCTION ident=1097691245, lineno_checksum=0x30c3089c, cfg_checksum=0xa43083b8, `_ZN3FooIfE4testEv' test.cpp:10 test.gcno: 01000000: 13:FUNCTION ident=56186600, lineno_checksum=0x9a359dca, cfg_checksum=0xa43083b8, `_ZN3FooIfEC2Ev' test.cpp:5 What we need to do is to properly present that.
Author: marxin Date: Thu Nov 9 09:11:17 2017 New Revision: 254562 URL: https://gcc.gnu.org/viewcvs?rev=254562&root=gcc&view=rev Log: 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. Modified: trunk/gcc/ChangeLog trunk/gcc/coverage.c trunk/gcc/doc/gcov.texi trunk/gcc/gcov-dump.c trunk/gcc/gcov.c
Fixed.