25

Today I was tasked with adding unit test coverage analysis to our code base. Today is also the day iOS 7.1 is released along with XCode 5.1. From the release notes:

The gcov tool for code coverage testing has been reimplemented. The new version uses the llvm-cov tool from the LLVM project. It is functionally equivalent to the old version for all significant features. The location of gcov within Xcode has also moved, use xcrun to invoke it. If you find problems, please file bug reports. For this release, you can still use the old version of gcov from GCC, which is available as gcov-4.2. 11919694 updated

I realized this only after following several instructional blog posts, getting my environment set up properly - generating .gcda/.gcno files in the simulator's build folders upon testing - and having the report generating tools here try to parse them into a report. (that is a ./getcov script which gathers your environment variables to pass to lcov-1.10 scripts to generate the report)

The first hurdle was that the new bundled gcov program doesn't support the -v argument to get the version, which is the first step of lcov's initialization. Seemed like a non-starter already, but reading the release notes above I modified the lcov script to use the old gcov-4.2 version and got that solved.

However, lcov errored out very early in processing my coverage data files. This generated a report with maybe the first 10 or so files alphabetically in my project. Not particularly useful. The error output was minimal and unhelpful as well:

geninfo: ERROR: GCOV failed for (build_artifacts)/(class_that_errored).gcda!

I modified the lcov script to print the error it was getting (which only yielded 11 unfortunately, couldn't find any reference in the gcov(-io).c code) and to continue operation instead of quitting, so I was left with a lot more files in the report, but still probably 85% of my source files had errored out as above.

The only pattern I could discern between the files that successfully wound up in the report and the ones that threw an error was that any file that used an in-line block declaration failed. None of the files that passed used blocks in any fashion, and all the files I've checked that failed contain blocks. Strange.

Then I figured out I could open the individual .gcda files in CoverStory, including the ones that had errored in the lcov script. In the message window beneath the coverage report, all the files that had errored had the warning messages:

(class_that_errored).gcno:no lines for '__copy_helper_block_'

(class_that_errored).gcno:no lines for '__destroy_helper_block_'

My best hypothesis at this point is that the new XCode 5.1 is generating .gcda files that the old gcov-4.2 program isn't equipped to deal with regarding block declarations.

But I've exhausted everything I can think to try, so I'm here to ask if anybody has a piece of knowledge that I've missed, or has any ideas to further the debugging effort. Or if anyone is successfully measuring test coverage since today's XCode 5.1 update with the new gcov, I'd love to hear about any changes you had to make as well.

ThomasW
  • 16,981
  • 4
  • 79
  • 106
LeffelMania
  • 12,765
  • 4
  • 32
  • 34
  • Also need an answer to this issue - I'm having exactly the same issue with Xcode 5.1. Our code coverage dropped from 52% coverage of 16,800 lines to about 57% coverage of 12,000 lines. We're also seeing issues with the two block lines mentioned. – David Doyle Mar 13 '14 at 11:46
  • I'm using gcovr (available here: https://pypi.python.org/pypi/gcovr) to interpret the coverage results, and that's getting even more inaccurate answers than CoverStory gets. The GCDA files it fails to process generate an error of "MySourceFile.m: No such file or directory". Strangely, invoking gcov directly on the GCDA file in question seems to create the appropriate file. If anyone can shed any light on this, please create an answer below! – David Doyle Mar 14 '14 at 13:18
  • Additionally, function measurement—as opposed to source/line measurement—seems to be absent. Before Xcode 5.1, lcov was able to measure both. – Endersstocker Mar 24 '14 at 10:49
  • @Endersstocker Not sure why this has changed, but if you explicitly instruct lcov to derive the data using the --derive-func-data argument, the original behavior is restored. I just added the argument to the gather_coverage() function in Jon Reid's getcov shell script. – jstevenco Mar 27 '14 at 19:14
  • @jstevenco I've tried adding that to the gather_coverage() function - this doesn't seem to fix the issue unfortunately. Classes that use blocks still seem to not be getting coverage. – David Doyle Apr 22 '14 at 09:24
  • @David-Doyle Actually, my comment was aimed at @ Endersstocker's observation about function measurement not working. Not sure what is going on with the block issue, but in my limited evaluation, I am getting coverage of classes/methods that include blocks (e.g., I am using AFNetworking 2.0 with success/failure completion blocks). Any noticeable pattern on problem block contents? – jstevenco Apr 22 '14 at 17:02
  • I've a suspicion (not proven yet but I will look into) that its on classes that create blocks for internal use, rather than classes that supply a block to another class. This isn't proven yet though. – David Doyle Apr 24 '14 at 16:43
  • Unfortunately, I haven't been able to reproduce the issue in a test class yet. However: the root cause is definitely in llvm-cov. For the gcda files which don't get any coverage generated at all, running llvm-cov on them directly generates not one .gcov file, but two - the first is genuine (reports: "File '/Users/me/Dev/MyProject/MyFramework/Controller/DGDViewController.m' Lines executed:57.58% of 858") with the right coverage data, but the second seems to be blank (reports: "File 'MyFramework/Controller/DGDViewController.m' Lines executed:nan% of 0"). Still not able to reproduce this though! – David Doyle Apr 28 '14 at 13:53
  • @LeffelMania were you able to solve this ? I have the same problem – Kunal Balani Apr 29 '14 at 18:30
  • I've switched my coverage analysis to use `gcovr`; I gave up on this `lcov` setup. – LeffelMania Apr 29 '14 at 19:58
  • Did that solve the problem? I'm still using gcovr and get the same issue ... – David Doyle May 01 '14 at 08:38
  • Yup, it's working for me. My whole setup is setting the required build settings for generating coverage symbols (`Generate Test Coverage Files` and `Instrument Program Flow`), running my tests, and generating my report by running `gcovr --html -html-details -f ".*MyClasses.*" -e ".*MyExclusions.*" -o "coverage-report/index.html"` in the build objects directory. (With directory maintenance added, of course) – LeffelMania May 01 '14 at 20:56
  • @LeffelMania What version of gcovr are you using? I'm using 3.1 (which appears to be the latest but I may not be looking in the right place ...) and this seems to have a similar issue (Got error: Unexpected number of edges (in -[MyController layoutViews]). Invalid .gcda File!) ...? – David Doyle May 02 '14 at 10:16
  • Weird. I'm on 3.1 as well and have been having good luck with it. Not sure I can help. :-/ – LeffelMania May 02 '14 at 17:51
  • I was investigating the issue that all files with blocks in them are missing in the coverage report generated by gcovr, when stumbling upon this question. Desperately I tried building with Xcode 6 beta 3 ... and the issue seems to be gone! Upgrading to the Xcode beta is not an option for me, but at least I know that it will be fixed in a future Xcode version! – Lysann Schlegel Jul 15 '14 at 16:16

4 Answers4

13

The problem is in the LCOV 1.10 geninfo script. It tests for the current version of gcov. It does this by parsing the version string. Since gcov now points to llvm-cov, the version string is parsed incorrectly.

The solution is to modify geninfo’s get_gcov_version() subroutine. In line 1868, change -v to --version. Then replace line 1874 with:

if ($version_string =~ m/LLVM/)
{
    info("Found llvm-cov\n");
    $result = 0x40201;
}
elsif ($version_string =~ /(\d+)\.(\d+)(\.(\d+))?/)

 

The modified subroutine should look like this:

sub get_gcov_version()
{
    local *HANDLE;
    my $version_string;
    my $result;

    open(GCOV_PIPE, "-|", "$gcov_tool --version")
        or die("ERROR: cannot retrieve gcov version!\n");
    $version_string = <GCOV_PIPE>;
    close(GCOV_PIPE);

    $result = 0;
    if ($version_string =~ m/LLVM/)
    {
        info("Found llvm-cov\n");
        $result = 0x40201;
    }
    elsif ($version_string =~ /(\d+)\.(\d+)(\.(\d+))?/)
    {
        if (defined($4))
        {
            info("Found gcov version: $1.$2.$4\n");
            $result = $1 << 16 | $2 << 8 | $4;
        }
        else
        {
            info("Found gcov version: $1.$2\n");
            $result = $1 << 16 | $2 << 8;
        }
    }
    return ($result, $version_string);
}

 

NOTE: Make sure --gcov-tool is not set to gcov-4.2.

Endersstocker
  • 1,061
  • 9
  • 26
  • Have tried switching to using the XcodeCoverage tool as above, and it seems even more broken than gcovr is with Xcode 5.1. Its not even able to produce any report at all with my current setup ... – David Doyle Mar 14 '14 at 13:47
  • @DavidDoyle: Does getcov return errors or do you just receive a blank report? – Endersstocker Mar 14 '14 at 14:44
  • @DavidDoyle: Can you let me know what they are? – Endersstocker Mar 14 '14 at 16:04
  • Too many to post in one go, so I'll break them up into three parts: – David Doyle Mar 14 '14 at 16:06
  • gcov: Unknown command line argument '-v'. Try: '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/gcov -help' gcov: Did you mean '-a'? gcov: Not enough positional command line arguments specified! Must specify at least 1 positional arguments: See: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/gcov -help – David Doyle Mar 14 '14 at 16:07
  • geninfo: Use of uninitialized value $version_string in pattern match (m//) at ./XcodeCoverage/lcov-1.10/bin/geninfo line 1874. geninfo: Use of uninitialized value $version_string in pattern match (m//) at ./XcodeCoverage/lcov-1.10/bin/geninfo line 1879. geninfo: Use of uninitialized value $gcov_version_string in pattern match (m//) at ./XcodeCoverage/lcov-1.10/bin/geninfo line 3627. geninfo: Use of uninitialized value $gcov_version_string in pattern match (m//) at ./XcodeCoverage/lcov-1.10/bin/geninfo line 3627. – David Doyle Mar 14 '14 at 16:08
  • Scanning /Users/david/Dev/MyGit/Build/MyProj/Obj.root/MyApp.build/Debug-iphonesimulator/MyApp.build/Objects-normal/i386 for .da files ... geninfo: ERROR: no .da files found in /Users/david/Dev/MyGit/Build/MyProj/Obj.root/MyApp.build/Debug-iphonesimulator/MyApp.build/Objects-normal/i386! – David Doyle Mar 14 '14 at 16:08
  • @DavidDoyle: Did you change `-v` to `--version` on line **1868** in the **geninfo** get_gcov_version() subroutine? That should address the '-v' error. – Endersstocker Mar 14 '14 at 16:10
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/49765/discussion-between-david-doyle-and-endersstocker) – David Doyle Mar 14 '14 at 16:14
  • 4
    To summarise: This does seem to fix the issue with lcov, but the original issue seen (anything with a block in it doesn't appear in the coverage report) is still present ... – David Doyle Mar 17 '14 at 09:57
  • In this case and all those solutions offered so far, function measurement (as opposed to source/line measurement) is absent. Before Xcode 5.1, lcov was able to measure both. – Endersstocker Mar 24 '14 at 10:47
  • using the XcodeCoverage tool found at `https://github.com/jonreid/XcodeCoverage.git` will also solve the `gcov: Unknown command line argument '-v'.` error, however its seems like classes with blocks will throw an error and stop generating the coverage file, leading to very misleading results. We have a class that uses blocks that begins with the letter `A` so we went from a very decent amount of coverage to 1%. – Chris Mar 28 '14 at 15:06
  • `NOTE: Make sure --gcov-tool is not set to gcov-4.2.1` Is there a specific version that is working for you? Or should version any above this work? – Chris Mar 28 '14 at 15:15
2

Rather than modifying geninfo I created an llvm-cov-wrapper script and used lcov's --gcov-tool command line option:

#!/bin/bash
# llvm-cov wrapper to make it behave more like gcov

if [ "$1" = "-v" ]; then
    echo "llvm-cov-wrapper 4.2.1"
    exit 0
else
    /usr/bin/gcov $*
fi
Neil Gall
  • 579
  • 4
  • 11
2

Actually I noticed that lcov is invoking gcov with -b option and gcov-4.2 will crash on this option (raise segmentation fault). If I remove the -b option from getinfo, then even though it still shows some error info, the gcov file can still be generated.

That is probably why coverstory can still give coverage output. So I guess the workaround is to remove -b option from lcov. And also as you suggested, ignore the error in getinfo

Yiwei
  • 21
  • 1
0

For anyone new to this thread, note that lcov-1.11 is out. This eliminates the issue with gcov -v compatibility. However, I am using XCode 6.1 and still getting errors on some files.

geninfo: WARNING: /Users/XXX/MyFile.gcno: found unrecognized record format - skipping

Note that you can add --ignore-errors graph to skip over these errors, but the problem isn't really solved.