I have come across a number of cases where gcov 4.0 and beyond produces incorrect output, causing false reporting of code coverage. For example, lines beyond the end of the source file are marked as executable with zero coverage, and some lines are marked with zero coverage even though they are directly after a simple statement that was marked as being executed. These problems are consistently reproducible on our platforms here.
I have confirmed that these problems did *not* occur in gcov 3.3.2, 3.3.3, or 3.4.3. I have further confirmed that they *do* occur in gcov 4.0.1 and gcov 4.1.0 (20051001). See below for more details on these versions. Here's a simple test case that demonstrates the two problems I list above: % g++ -fprofile-arcs -ftest-coverage -c mytest.cpp -o mytest.o % g++ -fprofile-arcs -ftest-coverage mytest.o -o mytest % ./mytest % gcov mytest.cpp ; cat mytest.cpp.gcov -------------- mytest.cpp ---------------- #include <string> // this global string causes gcov to think there's executable lines // beyond the end of the file, and that they have not been executed std::string globalVar = ""; std::string doSomething() { // instantiating this string and calling a method on it causes the // 'return' line to be marked as having never been executed std::string localVar; localVar.size(); return localVar; } int main(int, char **) { doSomething(); return 0; } ------------------------------------------ Under gcc/gcov 4.0.1 and 4.1.0 this produces: -: 0:Source:mytest.cpp -: 0:Graph:mytest.gcno -: 0:Data:mytest.gcda -: 0:Runs:1 -: 0:Programs:1 -: 1:#include <string> -: 2: -: 3:// this global string causes gcov to think there's executable lines -: 4:// beyond the end of the file 1: 5:std::string globalVar = ""; -: 6: -: 7:std::string 1: 8:doSomething() -: 9:{ -: 10: // instantiating this string and calling a method on it causes the -: 11: // 'return' line to be marked as executable but with zero coverage 1: 12: std::string localVar; 1: 13: localVar.size(); #####: 14: return localVar; -: 15:} -: 16: -: 17:int 1: 18:main(int, char **) -: 19:{ 1: 20: doSomething(); 1: 21: return 0; 1: 22:} 1: 23:/*EOF*/ #####: 24:/*EOF*/ Note that the lines marked with "#####" mean that gcov thought that these were executable lines of code, and that they where not actually executed. For comparison, the same test case compiled using gcc/gcov 3.3.3 (20040412) generates what I believe is more correct: -: 0:Source:mytest.cpp -: 0:Object:mytest.bb -: 1:#include <string> -: 2: -: 3:// this global string causes gcov to think there's executable lines -: 4:// beyond the end of the file 4: 5:std::string globalVar = ""; -: 6: -: 7:std::string -: 8:doSomething() 1: 9:{ -: 10: // instantiating this string and calling a method on it causes the -: 11: // 'return' line to be marked as executable but with zero coverage 1: 12: std::string localVar; 1: 13: localVar.size(); 1: 14: return localVar; -: 15:} -: 16: -: 17:int -: 18:main(int, char **) 1: 19:{ 1: 20: doSomething(); 1: 21: return 0; 1: 22:} Here's the output of gcc -v for both of the 4.x versions that I have confirmed these problems with: Using built-in specs. Target: x86_64-redhat-linux-gnu Configured with: ../gcc-4.0.1/configure x86_64-redhat-linux-gnu --prefix=/pixar/d2/sets/tools-30 --exec-prefix=/pixar/d2/sets/tools-30 --bindir=/pixar/d2/sets/tools-30/bin --sbindir=/pixar/d2/sets/tools-30/sbin --sysconfdir=/pixar/d2/sets/tools-30/etc --datadir=/pixar/d2/sets/tools-30/share --includedir=/pixar/d2/sets/tools-30/include --libdir=/pixar/d2/sets/tools-30/lib --libexecdir=/pixar/d2/sets/tools-30/libexec --localstatedir=/pixar/d2/sets/tools-30/var --sharedstatedir=/pixar/d2/sets/tools-30/com --mandir=/pixar/d2/sets/tools-30/man --infodir=/pixar/d2/sets/tools-30/info --enable-version-specific-runtime-libs --enable-languages=c++,objc,f95 --enable-threads=posix --enable-shared Thread model: posix gcc version 4.0.1 Using built-in specs. Target: x86_64-unknown-linux-gnu Configured with: ../configure --prefix=/data/gcc/gcc-4.1-20051001 --enable-languages=c,c++,objc,obj-c++ --enable-version-specific-runtime-libs Thread model: posix gcc version 4.1.0 20051001 (experimental) I suspect that this is something to do with allocating objects on the stack (std::string in the example above, though I've tried this with other stl container classes as well as other non-stl classes). For example, in the case of coverage being produced beyond the end of the file for gcov 4.x, if I supply the -b argument to gcov I get this at the end: 1: 22:} function _GLOBAL__I_globalVar called 1 returned 100% blocks executed 100% 1: 23:/*EOF*/ call 0 returned 100% function _GLOBAL__D_globalVar called 0 returned 0% blocks executed 0% #####: 24:/*EOF*/ call 0 never executed Also, in the test case above, if I change the return value of doSomething() to be an int instead of a std::string then the problem does not occur. As another interesting datapoint, the above information is for running on a Fedora Core 2 machine, but I get similar behavior using Apple's 4.0.0 compiler that they ship with OSX 10.4 (Tiger) - However, in that case the /*EOF*/ coverage error on line 24 doe not occur. Only the one on line 14 occurs. Perhaps that helps to isolate some part of this problem. Please let me know if there is any more information that I can provide you to help reproduce this problem. We are actively using gcov to quantify the code coverage of our automated tests, so we'd love to see this problem get fixed and I'm happy to help out as much as I can. Cheers, Martin. -- Summary: [4.x Regression] incorrect gcov code coverage output in 4.0 and 4.1 Product: gcc Version: 4.1.0 Status: UNCONFIRMED Severity: major Priority: P2 Component: gcov/profile AssignedTo: unassigned at gcc dot gnu dot org ReportedBy: reddy at pixar dot com GCC build triplet: x86_64-redhat-linux-gnu GCC host triplet: x86_64-redhat-linux-gnu GCC target triplet: x86_64-redhat-linux-gnu http://gcc.gnu.org/bugzilla/show_bug.cgi?id=24550