Skip to content

Commit 0a59de5

Browse files
authored
Fix missing line coverage (bazelbuild#3956)
closes bazelbuild#3948
1 parent ee57fdc commit 0a59de5

5 files changed

Lines changed: 45 additions & 4 deletions

File tree

.bazelci/presubmit.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ coverage_validation_post_shell_commands: &coverage_validation_post_shell_command
8888
; 1>&2 cat bazel-out/_coverage/_coverage_report.dat \
8989
; exit 1 \
9090
; }
91+
- |
92+
grep -q '^LF:[1-9]' bazel-out/_coverage/_coverage_report.dat \
93+
|| { 1>&2 echo "Coverage report has no line data" \
94+
; 1>&2 head -50 bazel-out/_coverage/_coverage_report.dat \
95+
; exit 1 \
96+
; }
9197
split_coverage_postprocessing_shell_commands: &split_coverage_postprocessing_shell_commands
9298
- echo "coverage --experimental_fetch_all_coverage_outputs" >> user.bazelrc
9399
- echo "coverage --experimental_split_coverage_postprocessing" >> user.bazelrc

rust/private/rust.bzl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,13 @@ def _rust_test_impl(ctx):
524524

525525
env["RUST_LLVM_COV"] = llvm_cov_path
526526
env["RUST_LLVM_PROFDATA"] = llvm_profdata_path
527+
528+
# Bazel's collect_coverage.sh checks both GENERATE_LLVM_LCOV and
529+
# CC_CODE_COVERAGE_SCRIPT before invoking the coverage collector.
530+
# These are not automatically wired for Starlark test rules so we
531+
# must set them explicitly.
532+
env["GENERATE_LLVM_LCOV"] = "1"
533+
env["CC_CODE_COVERAGE_SCRIPT"] = ctx.executable._collect_cc_coverage.path
527534
components = "{}/{}".format(ctx.label.workspace_root, ctx.label.package).split("/")
528535
env["CARGO_MANIFEST_DIR"] = "/".join([c for c in components if c])
529536
providers.append(RunEnvironmentInfo(

rust/private/rustc.bzl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1653,6 +1653,12 @@ def rustc_compile_action(
16531653
coverage_runfiles = []
16541654
if toolchain.llvm_cov and ctx.configuration.coverage_enabled and crate_info.is_test:
16551655
coverage_runfiles = [toolchain.llvm_cov, toolchain.llvm_profdata] + toolchain.llvm_lib
1656+
collect_cc_coverage = getattr(ctx.executable, "_collect_cc_coverage", None)
1657+
if not collect_cc_coverage:
1658+
collect_cc_coverage = getattr(ctx.file, "_collect_cc_coverage", None)
1659+
1660+
if collect_cc_coverage:
1661+
coverage_runfiles.append(collect_cc_coverage)
16561662

16571663
experimental_use_coverage_metadata_files = toolchain._experimental_use_coverage_metadata_files
16581664

util/collect_coverage/BUILD.bazel

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,14 @@ rust_binary(
44
name = "collect_coverage",
55
srcs = ["collect_coverage.rs"],
66
edition = "2018",
7+
# To ensure this tool is produced deterministically, debug info
8+
# is stripped out. On macOS, full symbol stripping is needed to
9+
# also remove N_OSO stab entries whose timestamps and sandbox
10+
# paths vary between builds.
11+
rustc_flags = select({
12+
"@platforms//os:linux": ["-Cstrip=debuginfo"],
13+
"@platforms//os:macos": ["-Cstrip=symbols"],
14+
"//conditions:default": [],
15+
}),
716
visibility = ["//visibility:public"],
817
)

util/collect_coverage/collect_coverage.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,12 @@ fn main() {
189189
.arg("-format=lcov")
190190
.arg("-instr-profile")
191191
.arg(&profdata_file)
192-
.arg("-ignore-filename-regex='.*external/.+'")
193-
.arg("-ignore-filename-regex='/tmp/.+'")
194-
.arg(format!("-path-equivalence=.,'{}'", execroot.display()))
192+
.arg("-ignore-filename-regex=.*external/.+")
193+
.arg("-ignore-filename-regex=/tmp/.+")
194+
.arg(format!("-path-equivalence=.,{}", execroot.display()))
195195
.arg(test_binary)
196-
.stdout(process::Stdio::piped());
196+
.stdout(process::Stdio::piped())
197+
.stderr(process::Stdio::piped());
197198

198199
debug_log!("Spawning {:#?}", llvm_cov_cmd);
199200
let child = llvm_cov_cmd
@@ -202,6 +203,18 @@ fn main() {
202203

203204
let output = child.wait_with_output().expect("llvm-cov process failed");
204205

206+
if !output.status.success() {
207+
let stderr = std::str::from_utf8(&output.stderr).unwrap_or("<non-utf8>");
208+
if stderr.contains("no coverage data found") {
209+
debug_log!("No coverage data found in binary; writing empty report");
210+
fs::write(&coverage_output_file, "").unwrap();
211+
fs::remove_file(&profdata_file).ok();
212+
return;
213+
}
214+
eprintln!("llvm-cov export failed:\n{}", stderr);
215+
process::exit(output.status.code().unwrap_or(1));
216+
}
217+
205218
// Parse the child process's stdout to a string now that it's complete.
206219
debug_log!("Parsing llvm-cov output");
207220
let report_str = std::str::from_utf8(&output.stdout).expect("Failed to parse llvm-cov output");

0 commit comments

Comments
 (0)