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

Add support for LLVM coverage mapping format versions 5 and 6 #91207

Merged
merged 2 commits into from
Dec 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 32 additions & 11 deletions compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
use rustc_hir::def_id::{DefId, DefIdSet};
use rustc_llvm::RustString;
use rustc_middle::mir::coverage::CodeRegion;
use rustc_middle::ty::TyCtxt;
use rustc_span::Symbol;

use std::ffi::CString;
Expand All @@ -17,10 +18,11 @@ use tracing::debug;

/// Generates and exports the Coverage Map.
///
/// This Coverage Map complies with Coverage Mapping Format version 4 (zero-based encoded as 3),
/// as defined at [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/docs/CoverageMappingFormat.rst#llvm-code-coverage-mapping-format)
/// and published in Rust's November 2020 fork of LLVM. This version is supported by the LLVM
/// coverage tools (`llvm-profdata` and `llvm-cov`) bundled with Rust's fork of LLVM.
/// Rust Coverage Map generation supports LLVM Coverage Mapping Format versions
/// 5 (LLVM 12, only) and 6 (zero-based encoded as 4 and 5, respectively), 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).
/// These versions are supported by the LLVM coverage tools (`llvm-profdata` and `llvm-cov`)
/// bundled with Rust's fork of LLVM.
///
/// Consequently, Rust's bundled version of Clang also generates Coverage Maps compliant with
/// the same version. Clang's implementation of Coverage Map generation was referenced when
Expand All @@ -30,11 +32,12 @@ use tracing::debug;
pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
let tcx = cx.tcx;

// Ensure LLVM supports Coverage Map Version 4 (encoded as a zero-based value: 3).
// If not, the LLVM Version must be less than 11.
// Ensure the installed version of LLVM supports at least Coverage Map
// Version 5 (encoded as a zero-based value: 4), which was introduced with
// LLVM 12.
let version = coverageinfo::mapping_version();
if version != 3 {
tcx.sess.fatal("rustc option `-Z instrument-coverage` requires LLVM 11 or higher.");
if version < 4 {
tcx.sess.fatal("rustc option `-Z instrument-coverage` requires LLVM 12 or higher.");
}

debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name());
Expand All @@ -57,7 +60,7 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
return;
}

let mut mapgen = CoverageMapGenerator::new();
let mut mapgen = CoverageMapGenerator::new(tcx, version);

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

impl CoverageMapGenerator {
fn new() -> Self {
Self { filenames: FxIndexSet::default() }
fn new(tcx: TyCtxt<'_>, version: u32) -> Self {
let mut filenames = FxIndexSet::default();
if version >= 5 {
// LLVM Coverage Mapping Format version 6 (zero-based encoded as 5)
// requires setting the first filename to the compilation directory.
// Since rustc generates coverage maps with relative paths, the
// compilation directory can be combined with the the relative paths
// to get absolute paths, if needed.
let working_dir = tcx
.sess
.opts
.working_dir
.remapped_path_if_available()
.to_string_lossy()
.to_string();
let c_filename =
CString::new(working_dir).expect("null error converting filename to C string");
filenames.insert(c_filename);
}
Self { filenames }
}

/// Using the `expressions` and `counter_regions` collected for the current function, generate
Expand Down
42 changes: 40 additions & 2 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ pub type InlineAsmDiagHandler = unsafe extern "C" fn(&SMDiagnostic, *const c_voi
pub mod coverageinfo {
use super::coverage_map;

/// Aligns with [llvm::coverage::CounterMappingRegion::RegionKind](https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L206-L222)
/// Aligns with [llvm::coverage::CounterMappingRegion::RegionKind](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L209-L230)
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub enum RegionKind {
Expand All @@ -700,11 +700,16 @@ pub mod coverageinfo {
/// A GapRegion is like a CodeRegion, but its count is only set as the
/// line execution count when its the only region in the line.
GapRegion = 3,

/// A BranchRegion represents leaf-level boolean expressions and is
/// associated with two counters, each representing the number of times the
/// expression evaluates to true or false.
BranchRegion = 4,
}

/// This struct provides LLVM's representation of a "CoverageMappingRegion", encoded into the
/// coverage map, in accordance with the
/// [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/docs/CoverageMappingFormat.rst#llvm-code-coverage-mapping-format).
/// [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).
/// The struct composes fields representing the `Counter` type and value(s) (injected counter
/// ID, or expression type and operands), the source file (an indirect index into a "filenames
/// array", encoded separately), and source location (start and end positions of the represented
Expand All @@ -717,6 +722,10 @@ pub mod coverageinfo {
/// The counter type and type-dependent counter data, if any.
counter: coverage_map::Counter,

/// If the `RegionKind` is a `BranchRegion`, this represents the counter
/// for the false branch of the region.
false_counter: coverage_map::Counter,

/// An indirect reference to the source filename. In the LLVM Coverage Mapping Format, the
/// file_id is an index into a function-specific `virtual_file_mapping` array of indexes
/// that, in turn, are used to look up the filename for this region.
Expand Down Expand Up @@ -754,6 +763,7 @@ pub mod coverageinfo {
) -> Self {
Self {
counter,
false_counter: coverage_map::Counter::zero(),
file_id,
expanded_file_id: 0,
start_line,
Expand All @@ -764,6 +774,31 @@ pub mod coverageinfo {
}
}

// This function might be used in the future; the LLVM API is still evolving, as is coverage
// support.
#[allow(dead_code)]
crate fn branch_region(
counter: coverage_map::Counter,
false_counter: coverage_map::Counter,
file_id: u32,
start_line: u32,
start_col: u32,
end_line: u32,
end_col: u32,
) -> Self {
Self {
counter,
false_counter,
file_id,
expanded_file_id: 0,
start_line,
start_col,
end_line,
end_col,
kind: RegionKind::BranchRegion,
}
}

// This function might be used in the future; the LLVM API is still evolving, as is coverage
// support.
#[allow(dead_code)]
Expand All @@ -777,6 +812,7 @@ pub mod coverageinfo {
) -> Self {
Self {
counter: coverage_map::Counter::zero(),
false_counter: coverage_map::Counter::zero(),
file_id,
expanded_file_id,
start_line,
Expand All @@ -799,6 +835,7 @@ pub mod coverageinfo {
) -> Self {
Self {
counter: coverage_map::Counter::zero(),
false_counter: coverage_map::Counter::zero(),
file_id,
expanded_file_id: 0,
start_line,
Expand All @@ -822,6 +859,7 @@ pub mod coverageinfo {
) -> Self {
Self {
counter,
false_counter: coverage_map::Counter::zero(),
file_id,
expanded_file_id: 0,
start_line,
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use rustc_middle::mir::coverage::{CounterValueReference, MappedExpressionIndex};

/// Aligns with [llvm::coverage::Counter::CounterKind](https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L206-L222)
/// Aligns with [llvm::coverage::Counter::CounterKind](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L95)
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub enum CounterKind {
Expand All @@ -17,7 +17,7 @@ pub enum CounterKind {
/// `instrprof.increment()`)
/// * For `CounterKind::Expression`, `id` is the index into the coverage map's array of
/// counter expressions.
/// Aligns with [llvm::coverage::Counter](https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L99-L100)
/// Aligns with [llvm::coverage::Counter](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L102-L103)
/// Important: The Rust struct layout (order and types of fields) must match its C++ counterpart.
#[derive(Copy, Clone, Debug)]
#[repr(C)]
Expand Down Expand Up @@ -59,15 +59,15 @@ impl Counter {
}
}

/// Aligns with [llvm::coverage::CounterExpression::ExprKind](https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L147)
/// Aligns with [llvm::coverage::CounterExpression::ExprKind](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L150)
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub enum ExprKind {
Subtract = 0,
Add = 1,
}

/// Aligns with [llvm::coverage::CounterExpression](https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L148-L149)
/// Aligns with [llvm::coverage::CounterExpression](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L151-L152)
/// Important: The Rust struct layout (order and types of fields) must match its C++
/// counterpart.
#[derive(Copy, Clone, Debug)]
Expand Down
9 changes: 7 additions & 2 deletions compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ using namespace llvm;

struct LLVMRustCounterMappingRegion {
coverage::Counter Count;
coverage::Counter FalseCount;
uint32_t FileID;
uint32_t ExpandedFileID;
uint32_t LineStart;
Expand Down Expand Up @@ -53,7 +54,7 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer(
MappingRegions.reserve(NumMappingRegions);
for (const auto &Region : makeArrayRef(RustMappingRegions, NumMappingRegions)) {
MappingRegions.emplace_back(
Region.Count, Region.FileID, Region.ExpandedFileID,
Region.Count, Region.FalseCount, Region.FileID, Region.ExpandedFileID,
Region.LineStart, Region.ColumnStart, Region.LineEnd, Region.ColumnEnd,
Region.Kind);
}
Expand Down Expand Up @@ -108,5 +109,9 @@ extern "C" void LLVMRustCoverageWriteMappingVarNameToString(RustStringRef Str) {
}

extern "C" uint32_t LLVMRustCoverageMappingVersion() {
return coverage::CovMapVersion::Version4;
#if LLVM_VERSION_GE(13, 0)
return coverage::CovMapVersion::Version6;
#else
return coverage::CovMapVersion::Version5;
#endif
}
6 changes: 3 additions & 3 deletions compiler/rustc_middle/src/mir/coverage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ rustc_index::newtype_index! {
impl ExpressionOperandId {
/// An expression operand for a "zero counter", as described in the following references:
///
/// * <https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/docs/CoverageMappingFormat.rst#counter>
/// * <https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/docs/CoverageMappingFormat.rst#tag>
/// * <https://github.com/rust-lang/llvm-project/blob/rustc/11.0-2020-10-12/llvm/docs/CoverageMappingFormat.rst#counter-expressions>
/// * <https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#counter>
/// * <https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#tag>
/// * <https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#counter-expressions>
///
/// This operand can be used to count two or more separate code regions with a single counter,
/// if they run sequentially with no branches, by injecting the `Counter` in a `BasicBlock` for
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ This document describes how to enable and use the LLVM instrumentation-based cov
When `-Z instrument-coverage` is enabled, the Rust compiler enhances rust-based libraries and binaries by:

- 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.
- Embedding additional information in the data section of each library and binary (using the [LLVM Code Coverage Mapping Format] _Version 4_, supported _only_ in LLVM 11 and up), to define the code regions (start and end positions in the source code) being counted.
- 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.

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.

Expand Down Expand Up @@ -123,7 +123,7 @@ If `LLVM_PROFILE_FILE` contains a path to a non-existent directory, the missing

## Installing LLVM coverage tools

LLVM's supplies two tools—`llvm-profdata` and `llvm-cov`—that process coverage data and generate reports. There are several ways to find and/or install these tools, but note that the coverage mapping data generated by the Rust compiler requires LLVM version 11 or higher. (`llvm-cov --version` typically shows the tool's LLVM version number.):
LLVM's supplies two tools—`llvm-profdata` and `llvm-cov`—that process coverage data and generate reports. There are several ways to find and/or install these tools, but note that the coverage mapping data generated by the Rust compiler requires LLVM version 12 or higher. (`llvm-cov --version` typically shows the tool's LLVM version number.):

- The LLVM tools may be installed (or installable) directly to your OS (such as via `apt-get`, for Linux).
- If you are building the Rust compiler from source, you can optionally use the bundled LLVM tools, built from source. Those tool binaries can typically be found in your build platform directory at something like: `rust/build/x86_64-unknown-linux-gnu/llvm/bin/llvm-*`.
Expand Down
6 changes: 6 additions & 0 deletions src/test/run-make-fulldeps/coverage-llvmir/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# needs-profiler-support

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

-include ../coverage/coverage_tools.mk

BASEDIR=../coverage-llvmir
Expand Down
7 changes: 7 additions & 0 deletions src/test/run-make-fulldeps/coverage-reports/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# needs-profiler-support
# ignore-windows-gnu

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

# FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works
# properly. Since we only have GCC on the CI ignore the test for now.

Expand Down Expand Up @@ -115,6 +121,7 @@ endif
"$(LLVM_BIN_DIR)"/llvm-cov show \
$(DEBUG_FLAG) \
$(LLVM_COV_IGNORE_FILES) \
--compilation-dir=. \
--Xdemangler="$(RUST_DEMANGLER)" \
--show-line-counts-or-regions \
--instr-profile="$(TMPDIR)"/$@.profdata \
Expand Down