[Legacy] Flushing iOS 7 Code Coverage Data 

 November 23, 2013

by Jon Reid


Update: Apple took this to heart, and fixed things in Xcode 5.1.

Bug or feature? “You have to call __gcov_flush() to collect coverage data with the iOS simulator.” According to Apple, this is a feature. But not if you actually want to measure your code coverage.

Code coverage… oy vey! Back in the days of running on iOS 6 using Xcode 4, measuring code coverage for unit tests was fairly straightforward. A set of coverage scripts I published made it easier still.

Then along came iOS 7 and Xcode 5. Coverage still worked if you continued to run on iOS 6. But on iOS 7, you’d get “ERROR: no .gcda files found” indicating that coverage data wasn’t being captured. “Well, maybe I need to switch from SenTestingKit to Apple’s new XCTest framework.” Nope, that didn’t help.

Call __gcov_flush() to Write Out Code Coverage Data

But I want to call it just once, after all tests have finished.

Folks starting posting questions on the Apple Developer Forums. Apple’s answer was, “You have to call __gcov_flush() to collect coverage data with the iOS simulator.” There was no need to do this before because coverage data is also written when the app exits, which apparently used to happen at the conclusion of unit tests but “we have fixed that.”

Okay, so when do you call __gcov_flush()? The simplest way is to swizzle tearDown(). The problem with this that __gcov_flush() will be invoked after every test—for one of my projects, that’s over a thousand times! But I want to call it just once, after all tests have finished.

Test Observers to the Rescue

Dave MacLachlan, author of CoverStory, offers a better way, made available through Google Toolbox for Mac (GTM). SenTestingKit and XCTest both have mechanisms for test observers, which Dave puts to work. Here’s how to put his code into your tests:

  1. Get a copy of the latest GTM source code. You can get it straight from the official repository. I’ve also made a mirror on GitHub.
  2. Add UnitTesting/GTMCodeCoverageApp.h and .m to your application. Don’t put them in your test target, they belong in your application target.
    If you use XCTest, add GTM_USING_XCTEST=1 to the “Preprocessor Macros” build setting for your application target.
  3. If you use SenTestingKit, add GTMCodeCoverageTestsST.m to your test target.
    If you use XCTest, add GTMCodeCoverageTestsXC.m instead.
  4. Add GTM_IS_COVERAGE_BUILD=1 to “Preprocessor Macros” for your coverage configuration. Do this at the project level so that it goes into both the app target and the test target.

And voilà, your code coverage data is back. Thanks, Dave!

Remember, this is legacy information. Apple fixed things in subsequent releases of Xcode, so we don’t need to do all this anymore.

In Which I Embrace Dot Notation …

Jon Reid

Programming was fun when I was a kid. But working in Silicon Valley, I saw poor code led to fear, with real human costs. Looking for ways to make my life better, I learned about Extreme Programming, including unit testing, test-driven development (TDD), and refactoring. Programming became fun again! I've now been doing TDD in Apple environments for 20 years. I'm committed to software crafting as a discipline, hoping we can all reach greater effectiveness and joy. Now a coach with Industrial Logic!

  • Dear Jon

    I have the following setup:
    * StaticLibrary
    * Application that depends upon StaticLibrary
    and I would like to generate a coverage report only for StaticLibrary.

    I set up the environment as suggested above (except that my StaticLibrary target plays the role of your application target). I can generate the coverage report for StaticLibrary but using the setup above Application fails to link and I get

    Undefined symbols for architecture i386:
    "___gcov_flush", referenced from:
    -[UIApplication(GTMCodeCoverage) gtm_gcov_flush] in libStaticLibrary.a(GTMCodeCoverageApp.o)
    "_llvm_gcda_emit_arcs", referenced from:
    ___llvm_gcov_writeout in libStaticLibrary.a(SampleComponent.o)
    ___llvm_gcov_writeout in libStaticLibrary.a(StaticLibrary.o)
    ___llvm_gcov_writeout in libStaticLibrary.a(GTMCodeCoverageApp.o)
    "_llvm_gcda_emit_function", referenced from:
    ld: symbol(s) not found for architecture i386
    clang: error: linker command failed with exit code 1 (use -v to see invocation)

    If I set
    Generate Test Coverage File = YES
    Instrument Program Flow = YES
    for Application then it builds OK. But I would not like to mandate these settings for the application.
    Can you help me what could be wrong?

    Thanks in advance!

  • Thank you Jon Reid your post helped me :)

    Hey is there a possibility to hide or ignore the GTMCodeCoverageApp.m file in my code coverage report?

    • Even though GTMCodeCoverageApp.m gets built into the main app, I put it with my other test support files in a TestSupport folder. Then in the exclude_data() function, I exclude "TestSupport/*" and that takes care of everything in that folder.

  • Hi ,
    for get the code coverage i have follow the step
    1)write Flush in viewController.m

    - (void)viewDidLoad{
    __gcov_flush() ;
    [super viewDidLoad];
    return TRUE;

    2)Make test case file.

    - (void)setUp{
    controller = [[ViewController alloc]init];
    [super setUp];
    - (void)tearDown{
    __gcov_flush() ;
    [super tearDown];

    if(![controller Hello])
    XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__);

    3)Compile it .

    But here i couldn’t get correct code coverage.
    I have been written three function in Viewcontroller.m Hello(), Hello1(),Hello2()

    without unit test case ,it gives the code coverage .
    How it give the code coverage?

    Can give me idea to get correct unit test coverage as per written function ?


  • Having upgraded to XCode 5.1 lcov started to fail processing coverage data. It says:

    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

    I assume this is because XCode 5 installed a new version of gcov and its switches and options seem to have changed.

    It seems that the old gcov was saved as gcov-4.2 so I added the –gvov-tool switch to the lcov command line in getcov.sh but all I could get was an empty coverage report.

    Have you bumped in the same issue?

    Do you have a solution for this?

    Thanks in advance.

  • Hi,

    I’ve followed through the tutorial (both this and ‘How to Measure Code Coverage’). Adding Google’s toolkit seems to result in this error:

    Undefined symbols for architecture i386:
    "___gcov_flush", referenced from:
    -[GTMCodeCoverageTests stopObserving] in GTMCodeCoverageTestsXC.o
    ld: symbol(s) not found for architecture i386
    clang: error: linker command failed with exit code 1 (use -v to see invocation)

    can anyone advise?

    • Dave, it looks like you haven’t enabled code coverage for your build configuration. Make sure to enable “Generate Test Coverage Files” and “Instrument Program Flow”.

      We don’t need the GTM workaround anymore with Xcode 5.1 — but you’ll still need those flags for coverage.

  • Hi,

    I am a test engineer and I am trying to do code coverage for an iOS application. I am using XCode 5.0.2 and gcovr 3.1 to test and perform code coverage for iOS 7 devices. I initially had issues with .gcda file generation issue. I followed your blog and resolved the same.

    I have set ‘Generate test coverage files’ to ‘Yes’, ‘Instrument Program flow’ to ‘Yes’ for the Debug module only. I have also set ‘ -fprofile-arcs -ftest-coverage’ for Debug module in ‘Other C Flags’. I have added ‘__gcov_flush()’ in my app code when application terminates.

    Now .gcda files are generating without issues. I copy that to my workspace in Home directory. From that location, I try to run code coverage for the application. I execute the gcovr command mentioning –object-directory and gcov command runs. I am getting “Gathered coveraged data for 0 files” at the end.

    Kindly help me to resolve the same.

    • As per Jon’s https://twitter.com/qcoding/status/515594893373472768, this entire workaround seems to be redundant with Xcode 6.0.1.

      Start by taking out the GTM_USING_XCTEST=1 and GTM_IS_COVERAGE_BUILD=1 preprocessor macros (fortunately Xcode 6’s search now looks in your project file, not just the source files, so that’s easy).

      Nuke the DerivedData folder and re-run tests. If, like me, you get good .gcda files from that, you can remove the Google Toolbox code from your project (unless of course you’re using Google Toolbox for other reasons).

  • How to exclude View Controller Default methods from Code Coverage.
    like ViewDidLoad,ViewWillAppear,ViewDidReceiveMemoryWarning…….

  • Hi Jon,
    I am using Xcode 7.3.1
    I have few libraries as .xcodeproj
    I am executing recorded UI tests.
    I have set flags (“Generate Test Coverage Files” and “Instrument Program Flow”) in my library project as well as main project. I have enabled “Generate Coverage Data” in Coverage scheme of main project.
    I am getting coverage for main project only. I want coverage of my libraries.
    Please help..

    • Raphael,
      First, note that XcodeCoverage will only measure Objective-C and C line coverage. It won’t measure Swift. That’s because it uses gcov, which Apple has replaced.

      Edit the script getcov and find the gather_coverage() function. You should see a line like

      LCOV --capture --derive-func-data -b "${SRCROOT}" -d "${OBJ_DIR}" -o "${LCOV_INFO}"

      That only covers the main target. To add other targets, you’ll need to capture them explicitly into separate info files, then combine them. First, let’s edit the envcov.sh script. Define the location of your framework’s object files. Something like this:


      With that definition in place, add it to cleancov:

      LCOV --zerocounters -d "${MYLIB_OBJ}"

      Now over in getcov, we need to edit gather_coverage(). Capture the coverage data from your framework to a separate .info file:

      LCOV --capture -b "${SRCROOT}/path-to-MyLib-source" -d "${MYLIB_OBJ}" -o MyLib.info

      Once we’ve collected .info for each unit, it’s time to append them together to the main report:

      LCOV -a ${LCOV_INFO} -a MyLib.info -o ${LCOV_INFO}

      Hope this helps. I’m sure I’ve missed a detail here or there, but this should get you close. Good luck!

  • {"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}