Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support coverage #1118

Closed
ulfjack opened this issue Apr 5, 2016 · 77 comments
Closed

Support coverage #1118

ulfjack opened this issue Apr 5, 2016 · 77 comments
Assignees
Labels
coverage P1 I'll work on this now. (Assignee required) type: feature request
Milestone

Comments

@ulfjack
Copy link
Contributor

ulfjack commented Apr 5, 2016

We've gotten a number of requests to support coverage. Here's an email I wrote in response to one of those requests, which I'm reposting here as a feature request:

Ideally, we'd want blaze coverage to exist and work in Bazel, just as it does in Blaze. The code is mostly there, but not quite. The coverage command itself doesn't actually do a lot of work; it sets --collect_code_coverage and a default --instrumentation_filter, and it optionally runs a tool to generate an HTML report (which is almost never used internally, where we push to a service instead). Otherwise it's identical to the test command, and the two flags can be manually set.

Internally, we have a wrapper script (collect_coverage.sh) that wraps an individual test invocation and uses a tool (lcov_merger.par) to merge the coverage data from that invocation, which both aren't in Bazel, and Bazel also doesn't setup the environment correctly to tell the wrapper script about output locations (StandaloneTestStrategy.java).

If you actually tried, you'd get an error like this one:
ERROR: /usr/local/google/home/ulfjack/Projects/os-bazel/src/test/cpp/BUILD:5:1: output 'src/test/cpp/blaze_util_test/coverage.dat' was not created.

It doesn't seem very hard to make the necessary Bazel changes, but we can't export the scripts as-is.

From my reading of the lcov documentation, lcov --capture looks a lot like what lcov_merger.par does, though I can't be completely certain (the docs are a bit scarce). It may make sense for us to integrate with lcov rather than export lcov_merger.par (or rewrite from scratch).

@ulfjack ulfjack added type: feature request P2 We'll consider working on this in future. (Assignee optional) labels Apr 5, 2016
@ulfjack
Copy link
Contributor Author

ulfjack commented Apr 5, 2016

From the lcov docs, I'd have gone for lcov -a, not lcov --capture.

@damienmg damienmg modified the milestone: 0.6 Jun 14, 2016
@marczych
Copy link
Contributor

Any update on this?

I'm very interested in gathering code coverage using Bazel in the near future. It looks like the 0.6 release is slated for 2016-11 assuming that the roadmap is up to date. How could I gather code coverage metrics for my code in Bazel if I needed it in the next month or two?

@ulfjack
Copy link
Contributor Author

ulfjack commented Aug 30, 2016

No updates. We'll likely not be able to work on this before October.

Java or C++ code? Or some other language?

@marczych
Copy link
Contributor

C++. I see some support for gcov in the CROSSTOOL as well as the --collect_code_coverage flag but it's failing with the following error:

ERROR: /home/<redacted>/test/BUILD:3:1: output 'test/test/coverage.dat' was not created.

@ulfjack
Copy link
Contributor Author

ulfjack commented Aug 31, 2016

Is that a build or a test run? Can you give the bazel command lines you're running?

@marczych
Copy link
Contributor

I made a really simple test workspace to test on:

[marc@marc-desktop] - [~/code/bazel-external-deps] - [Wed Aug 31, 02:15:15]
[I]> bazel clean
INFO: Starting clean (this may take a while). Consider using --expunge_async if the clean takes more than several minutes.
[marc@marc-desktop] - [~/code/bazel-external-deps] - [Wed Aug 31, 02:15:19]
[I]> bazel build --collect_code_coverage --test_output=all ...
INFO: Found 1 target...
Target //test:test up-to-date:
  bazel-bin/test/test
INFO: Elapsed time: 1.883s, Critical Path: 0.51s
[marc@marc-desktop] - [~/code/bazel-external-deps] - [Wed Aug 31, 02:15:24]
[I]> bazel test --collect_code_coverage --test_output=all ...
INFO: Found 1 test target...
INFO: From Testing //test:test:
==================== Test output for //test:test:
Running test!
================================================================================
ERROR: /home/marc/code/bazel-external-deps/test/BUILD:3:1: output 'test/test/coverage.dat' was not created.
ERROR: /home/marc/code/bazel-external-deps/test/BUILD:3:1: not all outputs were created.
Target //test:test up-to-date:
  bazel-bin/test/test
INFO: Elapsed time: 0.320s, Critical Path: 0.09s
//test:test                                                              PASSED in 0.1s

Executed 1 out of 1 test: 1 test passes.
There were tests whose specified size is too big. Use the --test_verbose_timeout_warnings command line option to see which ones these are.
One or more non-test targets failed to build.

So, curiously, the build and test passed but the test command exited with 1 because One or more non-test targets failed to build..

//test:test is just a cc_test with a single source file.

@marczych
Copy link
Contributor

marczych commented Sep 1, 2016

I also found test.pic.gcno and baseline_coverage.dat in bazel-out after bazel build --collect_code_coverage ...:

bazel-out
├── local-fastbuild
│   ├── bin
│   │   └── test
│   │       ├── _objs
│   │       │   └── test
│   │       │       └── test
│   │       │           ├── test.pic.d
│   │       │           ├── test.pic.gcno
│   │       │           └── test.pic.o
│   │       ├── test
│   │       ├── test-2.params
│   │       ├── test.runfiles
│   │       │   ├── bazelexternaldeps
│   │       │   │   └── test
│   │       │   │       └── test -> /home/marc/.cache/bazel/_bazel_marc/ad6921e960253c5ea1a81aa9e8086de8/execroot/bazel-external-deps/bazel-out/local-fastbuild/bin/test/test
│   │       │   └── MANIFEST
│   │       └── test.runfiles_manifest
│   ├── genfiles
│   └── testlogs
│       └── test
│           └── test
│               ├── baseline_coverage.dat
│               ├── test.cache_status
│               ├── test.log
│               └── test.xml
├── stable-status.txt
└── volatile-status.txt

13 directories, 14 files

@ddwolf
Copy link

ddwolf commented Oct 18, 2016

is this being processed?

@damienmg
Copy link
Contributor

Sorry forgot to assign it but @hermione521 is working on it this quarter.

@damienmg
Copy link
Contributor

Also /cc @lberki

@marczych
Copy link
Contributor

Just in case anybody was wondering or wanted to get some code coverage support before it's fully incorporated into Bazel, I came up with a usable workaround:

  • Build without sandboxing.
  • Add these compiler flags on the build and test command:
        "--linkopt=--coverage",
        "--linkopt=-lgcov",
        "--linkopt=-lgcc",
        "--linkopt=-lc",
        # Don't allow cached test results. We need the app to run each time to generate gcda files
        "--nocache_test_results",
  • That will put the various coverage files in bazel-out which get updated when the binaries are executed.
  • You can then run your lcov command in bazel-out to generate a report.

I left out a lot of details but that's the general approach that is mostly working for me.

bazel-io pushed a commit that referenced this issue Nov 24, 2016
…and.

Progress on #1118.

--
MOS_MIGRATED_REVID=140121703
bazel-io pushed a commit that referenced this issue Nov 24, 2016
- open source CoverageCommand.java
- add a collect-coverage.sh script
- update test-setup.sh to be compatible with the coverage collector
- update StandaloneTestStrategy to provide the necessary env variables
- update StandaloneTestStrategy to set the right command line for coverage
- add support for C++ coverage

An HTML report can then be generated with genhtml like this:
genhtml -o report/ -p "$(readlink -f bazel-<project>)" path/to/coverage.dat

Progress on #1118.

--
MOS_MIGRATED_REVID=140125715
bazel-io pushed a commit that referenced this issue Nov 24, 2016
Progress on #1118.

--
MOS_MIGRATED_REVID=140128848
@ulfjack
Copy link
Contributor Author

ulfjack commented Nov 29, 2016

With the above patches, I was able to get C++ coverage results on Linux, with a local install of lcov (it currently uses a hard-coded path to lcov). We have a follow-up change to add support for Java / Jacoco.

@hermione521
Copy link
Contributor

hermione521 commented Jan 30, 2017

For reference, the 4 commits for java coverage are here:
7ecf871
af1e3c2
af3c412
af878d0

@hanwen
Copy link
Contributor

hanwen commented Jan 30, 2017

looks like the code is there (thanks!), but I couldn't find any documentation.

@hanwen
Copy link
Contributor

hanwen commented Jan 30, 2017

also, I can't find anything to generate HTML reports. Or is there a standard tool to interpret the .dat files that I overlooked?

@ulfjack
Copy link
Contributor Author

ulfjack commented Jan 30, 2017 via email

bazel-io pushed a commit that referenced this issue Apr 17, 2018
I get failing tests without this flag in coverage mode.

Workaround is to pass --linkopt=-fprofile-arcs in Bazel.

Here are the steps that work at this time (with this change):
bazel coverage --strategy=TestRunner=standalone //my/funky:test
genhtml -o report-output-directory/ path/to/coverage.dat

Some progress on #1118.

PiperOrigin-RevId: 193193164
@tmarkovich
Copy link

tmarkovich commented Apr 19, 2018

I'm trying to get coverage reports for a C++ project on OS X 10.12.6.

╰─ bazel version
Build label: 0.12.0-homebrew
Build target: bazel-out/darwin-opt/bin/src/main/java/com/google/devtools/build/lib/bazel/BazelServer_deploy.jar
Build time: Fri Oct 2 08:08:38 +50246 (1523467123718)
Build timestamp: 1523467123718
Build timestamp as int: 1523467123718
╰─ gcov --version
Apple LLVM version 9.0.0 (clang-900.0.39.2)
  Optimized build.
  Default target: x86_64-apple-darwin16.7.0
  Host CPU: skylake
╰─ clang --version
Apple LLVM version 9.0.0 (clang-900.0.39.2)
Target: x86_64-apple-darwin16.7.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

When I try and us either bazel coverage test... or bazel test test:test-graph-debug --collect_code_coverage (even when I explicitly add "-fprofile-arcs" to linkopts) I get the following output:

╰─ bazel coverage test:test-graph-debug
INFO: Using default value for --instrumentation_filter: "//test".
INFO: Override the above default with --instrumentation_filter
INFO: Analysed target //test:test-graph-debug (0 packages loaded).
INFO: Found 1 test target...
ERROR: /private/var/tmp/_bazel_thomasmarkovich/1f9f2436753d5519b9fc860f09b2662d/external/boost/BUILD.bazel:1098:1: Linking of rule '@boost//:serialization' failed (Exit 1)
ld: library not found for -lgcov
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Target //test:test-graph-debug failed to build
Use --verbose_failures to see the command lines of failed build steps.
INFO: Elapsed time: 26.643s, Critical Path: 11.58s
FAILED: Build did NOT complete successfully

Executed 0 out of 1 test: 1 fails to build.

It appears to occur because

def _coverage_feature(use_llvm_format):
doesn't handle the darwin case. The darwin branch should be (although there is no such thing at the moment):

    compile_flags = flags("-fprofile-arcs", "-ftest-coverage")
    link_flags = flags("-fprofile-arcs")

This appears related to this issue: #2275

As a work around, I explicitly changed my build rule to be:

    copts = [
        "-fprofile-arcs",
        "-ftest-coverage",
    ],
    linkopts = [
        "-fprofile-arcs",
    ],

with the hope that I could then collect the gcno and gcda artifacts to generate these artifacts. I can't seem to find where these artifacts are put in the various bazel directories when I run without specifying GCOV* env variables. By specifying the environment variables using the action_env command line arg like so bazel test test:test-graph-debug --action_env=GCOV_PREFIX="/Users/thomasmarkovich/Documents/knowledge-graph/coverage" --action_env=GCOV_PREFIX_STRIP=17, my program runs cleaning and provides the following minor complaint at the end:

[==========] 45 tests from 4 test cases ran. (2 ms total)
[  PASSED  ] 45 tests.
profiling: /Users/thomasmarkovich/Documents/knowledge-graph/coverage//gtest_main.gcda: cannot open: Operation not permitted
profiling: /Users/thomasmarkovich/Documents/knowledge-graph/coverage//test-graph.gcda: cannot open: Operation not permitted

Note the double slash. This appears to largely be a permissions issue, but I can't for the life of me figure out how to fix this.

By contrast, when I locate the test-graph-debug executable directly, and run the following, I get the following:

╰─ export GCOV_PREFIX="/Users/thomasmarkovich/Documents/knowledge-graph/coverage/"
╰─ export GCOV_PREFIX_STRIP=17
╰─ ./test-graph-debug
...
[==========] 45 tests from 4 test cases ran. (2 ms total)
[  PASSED  ] 45 tests.
╰─ ls /Users/thomasmarkovich/Documents/knowledge-graph/coverage/
gtest_main.gcda test-graph.gcda

This is to say that gcov appears to be working fine. I just can't figure out how to get bazel to directly write these files for me, where to find them, or where to get the gcno file that's constructed at compile time; either with the bazel coverage tools or with my own home-rolled solution. Any suggestions?

@siddharthab
Copy link
Contributor

In collect_coverage.sh, path to lcov is hardcoded. Can we make it configurable some how? Maybe just use PATH, or an environment variable. On darwin, it is not easy to install lcov to /usr/bin. Using brew or other package managers will install it in /usr/local/bin or some other location.

/usr/bin/lcov -c --no-external --ignore-errors graph \

@ulfjack
Copy link
Contributor Author

ulfjack commented May 7, 2018

@siddharthab that's fair. It's not obvious to me how best to do that. We probably need to create a 'coverage toolchain' to put everything in the right place, but we might get away with piggybacking on the cc_configure script for now.

@siddharthab
Copy link
Contributor

Actually I realised that on macOS, we will mostly be taking the llvm coverage path. So this might not be that important. I was hitting this only because the stat command in the if clause has an extra flag for bsd stat.

@ulfjack
Copy link
Contributor Author

ulfjack commented May 7, 2018

I made some recent changes that should improve the llvm coverage path. I think they're not released yet. See #5036 (also contains rudimentary instructions for how to use it). Let me know if that works for you.

@siddharthab
Copy link
Contributor

Yes, I was able to get coverage reports on macOS with llvm following your instructions with just one patch -- #5162

I also needed to remove sandbox directory segments from the paths in the profdata file but other than that, everything worked as you said.

@siddharthab
Copy link
Contributor

A question: do you plan on keeping coverage.dat as lcov intermediate format. Are all languages expected to do the same? I am asking because some of us are trying to pipe coverage information from coverage.dat to end user presentation systems. But I am not sure which format to expect in coverage.dat.

@ulfjack
Copy link
Contributor Author

ulfjack commented May 17, 2018

I'm not sure. @iirina is currently working on this, and we'll send out a proposal soon.

@eugene-bulkin
Copy link

I'd second @siddharthab's concern from earlier, some environments still don't have /usr/bin/lcov available at all times, and it may be installed elsewhere.

@siddharthab
Copy link
Contributor

@eugene-bulkin Note that lcov is only needed when using gcc as the compiler. So if your different environment happens to be macOS and you are using the default toolchain, or if you are using an LLVM toolchain, then you don't really need it.

@eugene-bulkin
Copy link

@siddharthab My environment, for example, does use gcc as the compiler, so it still might help some people to take it from an environment variable or toolchain.

@ulfjack
Copy link
Contributor Author

ulfjack commented May 18, 2018

There isn't a way to inject the path to lcov at the moment. It is straightforward to add an env variable for this, but I'd like to hear from Irina first.

@iirina
Copy link
Contributor

iirina commented May 18, 2018

I'm currently doing some performance measurements to see if it's worth keeping lcov or not. This will also have an impact on the coverage format that Bazel will output. I'll come back with data.

@robfig
Copy link

robfig commented May 31, 2018

Any indications on whether lcov is here to stay? I'm trying to integrate code coverage into our CI pipeline and wondering if we should wait.

@ulfjack
Copy link
Contributor Author

ulfjack commented Jun 4, 2018

@iirina has a doc (which she unfortunately hasn't shared yet), where she proposes that we should go with cobertura XML as the format. We've been looking at gcovr to generate it from C/C++ coverage, and there are a lot of other tools available as well.

@iirina iirina added coverage P1 I'll work on this now. (Assignee required) and removed P2 We'll consider working on this in future. (Assignee optional) labels Jun 5, 2018
@iirina
Copy link
Contributor

iirina commented Jun 5, 2018

Here is the document. Please comment, any feedback is welcomed.

We'll gradually change the standard coverage format to Cobertura XML. Both lcov and Cobertura XML will be available for a transitioning period.

@th0br0
Copy link

th0br0 commented Jun 22, 2018

Could the code coverage approach also consider an interface for setting up static analysis tools (e.g. clang-tidy)?

@robfig
Copy link

robfig commented Jul 30, 2018

Very much looking forward to this. Will this new set of work include collecting baseline coverage?

https://github.com/bazelbuild/bazel/blob/master/src/main/java/com/google/devtools/build/lib/runtime/commands/CoverageCommand.java#L61

@iirina
Copy link
Contributor

iirina commented Jul 31, 2018

@th0br0 Static analysis was not considered as a part of the code coverage collection. There are no plans to support it right now.

@robfig Yes, it will be part of it. I opened #5716 to track progress if you want to.

@iirina
Copy link
Contributor

iirina commented Aug 14, 2018

I will close this issue since its scope is very wide. To track code coverage progress you can check:

Further issues have the coverage label.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
coverage P1 I'll work on this now. (Assignee required) type: feature request
Projects
None yet
Development

No branches or pull requests