Skip to content

Commit 0c57fab

Browse files
committed
Add conditional support for coverage map version 6
This commit augments Swatinem's initial commit in uncommitted PR #90047, which was a great starting point, but did not fully support LLVM Coverage Mapping Format version 6. Version 6 requires adding the compilation directory when file paths are relative, and since Rustc coverage maps use relative paths, we should add the expected compilation directory entry. Note, however, that with the compilation directory, coverage reports from `llvm-cov show` can now report file names (when the report includes more than one file) with the full absolute path to the file. This would be a problem for test results, but the workaround (for the rust coverage tests) is to include an additional `llvm-cov show` parameter: `--compilation-dir=.`
1 parent 566ad8d commit 0c57fab

File tree

4 files changed

+47
-13
lines changed

4 files changed

+47
-13
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

+33-12
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
99
use rustc_hir::def_id::{DefId, DefIdSet};
1010
use rustc_llvm::RustString;
1111
use rustc_middle::mir::coverage::CodeRegion;
12+
use rustc_middle::ty::TyCtxt;
1213
use rustc_span::Symbol;
1314

1415
use std::ffi::CString;
@@ -17,9 +18,10 @@ use tracing::debug;
1718

1819
/// Generates and exports the Coverage Map.
1920
///
20-
/// This Coverage Map complies with Coverage Mapping Format version 5 (zero-based encoded as 4),
21-
/// as defined at [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#llvm-code-coverage-mapping-format).
22-
/// This version is supported by the LLVM coverage tools (`llvm-profdata` and `llvm-cov`)
21+
/// Rust Coverage Map generation supports LLVM Coverage Mapping Format versions
22+
/// 5 (LLVM 12, only) and 6 (zero-based encoded as 4 and 5, respectively), as defined at
23+
/// [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#llvm-code-coverage-mapping-format).
24+
/// These versions are supported by the LLVM coverage tools (`llvm-profdata` and `llvm-cov`)
2325
/// bundled with Rust's fork of LLVM.
2426
///
2527
/// Consequently, Rust's bundled version of Clang also generates Coverage Maps compliant with
@@ -30,12 +32,13 @@ use tracing::debug;
3032
pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
3133
let tcx = cx.tcx;
3234

33-
// While our bundled LLVM might support Coverage Map Version 6
34-
// (encoded as a zero-based value: 5), we clamp that to Version 5,
35-
// as Version 6 would require us to use the 0-th filename as a path prefix
36-
// for all other relative paths, which we don't take advantage of right now.
37-
let _version = coverageinfo::mapping_version();
38-
let version = 4;
35+
// Ensure the installed version of LLVM supports at least Coverage Map
36+
// Version 5 (encoded as a zero-based value: 4), which was introduced with
37+
// LLVM 12.
38+
let version = coverageinfo::mapping_version();
39+
if version < 4 {
40+
tcx.sess.fatal("rustc option `-Z instrument-coverage` requires LLVM 12 or higher.");
41+
}
3942

4043
debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name());
4144

@@ -57,7 +60,7 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
5760
return;
5861
}
5962

60-
let mut mapgen = CoverageMapGenerator::new();
63+
let mut mapgen = CoverageMapGenerator::new(tcx, version);
6164

6265
// Encode coverage mappings and generate function records
6366
let mut function_data = Vec::new();
@@ -112,8 +115,26 @@ struct CoverageMapGenerator {
112115
}
113116

114117
impl CoverageMapGenerator {
115-
fn new() -> Self {
116-
Self { filenames: FxIndexSet::default() }
118+
fn new(tcx: TyCtxt<'_>, version: u32) -> Self {
119+
let mut filenames = FxIndexSet::default();
120+
if version >= 5 {
121+
// LLVM Coverage Mapping Format version 6 (zero-based encoded as 5)
122+
// requires setting the first filename to the compilation directory.
123+
// Since rustc generates coverage maps with relative paths, the
124+
// compilation directory can be combined with the the relative paths
125+
// to get absolute paths, if needed.
126+
let working_dir = tcx
127+
.sess
128+
.opts
129+
.working_dir
130+
.remapped_path_if_available()
131+
.to_string_lossy()
132+
.to_string();
133+
let c_filename =
134+
CString::new(working_dir).expect("null error converting filename to C string");
135+
filenames.insert(c_filename);
136+
}
137+
Self { filenames }
117138
}
118139

119140
/// Using the `expressions` and `counter_regions` collected for the current function, generate

src/doc/unstable-book/src/compiler-flags/instrument-coverage.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ This document describes how to enable and use the LLVM instrumentation-based cov
2020
When `-Z instrument-coverage` is enabled, the Rust compiler enhances rust-based libraries and binaries by:
2121

2222
- Automatically injecting calls to an LLVM intrinsic ([`llvm.instrprof.increment`]), at functions and branches in compiled code, to increment counters when conditional sections of code are executed.
23-
- Embedding additional information in the data section of each library and binary (using the [LLVM Code Coverage Mapping Format] _Version 5_, supported _only_ in LLVM 12 and up), to define the code regions (start and end positions in the source code) being counted.
23+
- Embedding additional information in the data section of each library and binary (using the [LLVM Code Coverage Mapping Format] _Version 5_, if compiling with LLVM 12, or _Version 6_, if compiling with LLVM 13 or higher), to define the code regions (start and end positions in the source code) being counted.
2424

2525
When running a coverage-instrumented program, the counter values are written to a `profraw` file at program termination. LLVM bundles tools that read the counter results, combine those results with the coverage map (embedded in the program binary), and generate coverage reports in multiple formats.
2626

src/test/run-make-fulldeps/coverage-llvmir/Makefile

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# needs-profiler-support
22

3+
# Rust coverage maps support LLVM Coverage Mapping Format versions 5 and 6,
4+
# corresponding with LLVM versions 12 and 13, respectively.
5+
# When upgrading LLVM versions, consider whether to enforce a minimum LLVM
6+
# version during testing, with an additional directive at the top of this file
7+
# that sets, for example: `min-llvm-version: 12.0`
8+
39
-include ../coverage/coverage_tools.mk
410

511
BASEDIR=../coverage-llvmir

src/test/run-make-fulldeps/coverage-reports/Makefile

+7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
# needs-profiler-support
22
# ignore-windows-gnu
33

4+
# Rust coverage maps support LLVM Coverage Mapping Format versions 5 and 6,
5+
# corresponding with LLVM versions 12 and 13, respectively.
6+
# When upgrading LLVM versions, consider whether to enforce a minimum LLVM
7+
# version during testing, with an additional directive at the top of this file
8+
# that sets, for example: `min-llvm-version: 12.0`
9+
410
# FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works
511
# properly. Since we only have GCC on the CI ignore the test for now.
612

@@ -115,6 +121,7 @@ endif
115121
"$(LLVM_BIN_DIR)"/llvm-cov show \
116122
$(DEBUG_FLAG) \
117123
$(LLVM_COV_IGNORE_FILES) \
124+
--compilation-dir=. \
118125
--Xdemangler="$(RUST_DEMANGLER)" \
119126
--show-line-counts-or-regions \
120127
--instr-profile="$(TMPDIR)"/[email protected] \

0 commit comments

Comments
 (0)