2020-07-02 18:27:15 +00:00
|
|
|
use crate::llvm;
|
|
|
|
|
2020-06-22 06:29:08 +00:00
|
|
|
use crate::builder::Builder;
|
|
|
|
use crate::common::CodegenCx;
|
2020-07-02 18:27:15 +00:00
|
|
|
|
|
|
|
use libc::c_uint;
|
2020-07-29 06:09:16 +00:00
|
|
|
use llvm::coverageinfo::CounterMappingRegion;
|
2020-08-15 11:42:13 +00:00
|
|
|
use rustc_codegen_ssa::coverageinfo::map::{CounterExpression, FunctionCoverage};
|
2020-07-02 18:27:15 +00:00
|
|
|
use rustc_codegen_ssa::traits::{
|
LLVM IR coverage encoding aligns closer to Clang's
I found some areas for improvement while attempting to debug the
SegFault issue when running rust programs compiled using MSVC, with
coverage instrumentation.
I discovered that LLVM's coverage writer was generating incomplete
function name variable names (that's not a typo: the name of the
variable that holds a function name).
The existing implementation used one-up numbers to distinguish
variables, and correcting the names did not fix the MSVC coverage bug,
but the fix in this PR makes the names and resulting LLVM IR easier to
follow and more consistent with Clang's implementation.
I also changed the way the `-Zinstrument-coverage` option is supported
in symbol_export.rs. The original implementation was incorrect, and the
corrected version matches the handling for `-Zprofile-generate`, as it
turns out.
(An argument could be made that maybe `-Zinstrument-coverage` should
automatically enable `-Cprofile-generate`. In fact, if
`-Cprofile-generate` is analagous to Clang's `-fprofile-generate`, as
some documentation implies, Clang always requires this flag for its
implementation of source-based code coverage. This would require a
little more validation, and if implemented, would probably require
updating some of the user-facing messages related to
`-Cprofile-generate` to not be so specific to the PGO use case.)
None of these changes fixed the MSVC coverage problems, but they should
still be welcome improvements.
Lastly, I added some additional FIXME comments in instrument_coverage.rs
describing issues I found with the generated LLVM IR that would be
resolved if the coverage instrumentation is injected with a `Statement`
instead of as a new `BasicBlock`. I describe seven advantages of this
change, but it requires some discussion before making a change like
this.
2020-08-06 05:53:11 +00:00
|
|
|
BaseTypeMethods, CoverageInfoBuilderMethods, CoverageInfoMethods, MiscMethods, StaticMethods,
|
2020-07-02 18:27:15 +00:00
|
|
|
};
|
2020-06-22 06:29:08 +00:00
|
|
|
use rustc_data_structures::fx::FxHashMap;
|
2020-07-02 18:27:15 +00:00
|
|
|
use rustc_llvm::RustString;
|
2020-08-15 11:42:13 +00:00
|
|
|
use rustc_middle::mir::coverage::{
|
|
|
|
CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionIndex, Op,
|
|
|
|
};
|
2020-06-22 06:29:08 +00:00
|
|
|
use rustc_middle::ty::Instance;
|
|
|
|
|
|
|
|
use std::cell::RefCell;
|
2020-07-02 18:27:15 +00:00
|
|
|
use std::ffi::CString;
|
|
|
|
|
2020-08-15 11:42:13 +00:00
|
|
|
use tracing::debug;
|
|
|
|
|
2020-07-02 18:27:15 +00:00
|
|
|
pub mod mapgen;
|
|
|
|
|
|
|
|
const COVMAP_VAR_ALIGN_BYTES: usize = 8;
|
2020-06-22 06:29:08 +00:00
|
|
|
|
|
|
|
/// A context object for maintaining all state needed by the coverageinfo module.
|
|
|
|
pub struct CrateCoverageContext<'tcx> {
|
|
|
|
// Coverage region data for each instrumented function identified by DefId.
|
2020-08-15 11:42:13 +00:00
|
|
|
pub(crate) function_coverage_map: RefCell<FxHashMap<Instance<'tcx>, FunctionCoverage>>,
|
2020-06-22 06:29:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'tcx> CrateCoverageContext<'tcx> {
|
|
|
|
pub fn new() -> Self {
|
2020-07-02 18:27:15 +00:00
|
|
|
Self { function_coverage_map: Default::default() }
|
2020-06-22 06:29:08 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 11:42:13 +00:00
|
|
|
pub fn take_function_coverage_map(&self) -> FxHashMap<Instance<'tcx>, FunctionCoverage> {
|
2020-07-02 18:27:15 +00:00
|
|
|
self.function_coverage_map.replace(FxHashMap::default())
|
2020-06-22 06:29:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CoverageInfoMethods for CodegenCx<'ll, 'tcx> {
|
|
|
|
fn coverageinfo_finalize(&self) {
|
2020-07-02 18:27:15 +00:00
|
|
|
mapgen::finalize(self)
|
2020-06-22 06:29:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
|
LLVM IR coverage encoding aligns closer to Clang's
I found some areas for improvement while attempting to debug the
SegFault issue when running rust programs compiled using MSVC, with
coverage instrumentation.
I discovered that LLVM's coverage writer was generating incomplete
function name variable names (that's not a typo: the name of the
variable that holds a function name).
The existing implementation used one-up numbers to distinguish
variables, and correcting the names did not fix the MSVC coverage bug,
but the fix in this PR makes the names and resulting LLVM IR easier to
follow and more consistent with Clang's implementation.
I also changed the way the `-Zinstrument-coverage` option is supported
in symbol_export.rs. The original implementation was incorrect, and the
corrected version matches the handling for `-Zprofile-generate`, as it
turns out.
(An argument could be made that maybe `-Zinstrument-coverage` should
automatically enable `-Cprofile-generate`. In fact, if
`-Cprofile-generate` is analagous to Clang's `-fprofile-generate`, as
some documentation implies, Clang always requires this flag for its
implementation of source-based code coverage. This would require a
little more validation, and if implemented, would probably require
updating some of the user-facing messages related to
`-Cprofile-generate` to not be so specific to the PGO use case.)
None of these changes fixed the MSVC coverage problems, but they should
still be welcome improvements.
Lastly, I added some additional FIXME comments in instrument_coverage.rs
describing issues I found with the generated LLVM IR that would be
resolved if the coverage instrumentation is injected with a `Statement`
instead of as a new `BasicBlock`. I describe seven advantages of this
change, but it requires some discussion before making a change like
this.
2020-08-06 05:53:11 +00:00
|
|
|
/// Calls llvm::createPGOFuncNameVar() with the given function instance's mangled function name.
|
|
|
|
/// The LLVM API returns an llvm::GlobalVariable containing the function name, with the specific
|
|
|
|
/// variable name and linkage required by LLVM InstrProf source-based coverage instrumentation.
|
|
|
|
fn create_pgo_func_name_var(&self, instance: Instance<'tcx>) -> Self::Value {
|
|
|
|
let llfn = self.cx.get_fn(instance);
|
|
|
|
let mangled_fn_name = CString::new(self.tcx.symbol_name(instance).name)
|
|
|
|
.expect("error converting function name to C string");
|
|
|
|
unsafe { llvm::LLVMRustCoverageCreatePGOFuncNameVar(llfn, mangled_fn_name.as_ptr()) }
|
|
|
|
}
|
|
|
|
|
2020-06-22 06:29:08 +00:00
|
|
|
fn add_counter_region(
|
|
|
|
&mut self,
|
|
|
|
instance: Instance<'tcx>,
|
2020-07-02 18:27:15 +00:00
|
|
|
function_source_hash: u64,
|
2020-08-15 11:42:13 +00:00
|
|
|
id: CounterValueReference,
|
|
|
|
region: CodeRegion,
|
2020-06-22 06:29:08 +00:00
|
|
|
) {
|
|
|
|
debug!(
|
2020-08-15 11:42:13 +00:00
|
|
|
"adding counter to coverage_regions: instance={:?}, function_source_hash={}, id={:?}, \
|
2020-08-02 03:03:59 +00:00
|
|
|
at {:?}",
|
|
|
|
instance, function_source_hash, id, region,
|
2020-06-22 06:29:08 +00:00
|
|
|
);
|
2020-07-02 18:27:15 +00:00
|
|
|
let mut coverage_regions = self.coverage_context().function_coverage_map.borrow_mut();
|
|
|
|
coverage_regions
|
|
|
|
.entry(instance)
|
2020-07-25 04:14:28 +00:00
|
|
|
.or_insert_with(|| FunctionCoverage::new(self.tcx, instance))
|
2020-08-02 03:03:59 +00:00
|
|
|
.add_counter(function_source_hash, id, region);
|
2020-06-22 06:29:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn add_counter_expression_region(
|
|
|
|
&mut self,
|
|
|
|
instance: Instance<'tcx>,
|
2020-08-15 11:42:13 +00:00
|
|
|
id: InjectedExpressionIndex,
|
|
|
|
lhs: ExpressionOperandId,
|
|
|
|
op: Op,
|
|
|
|
rhs: ExpressionOperandId,
|
|
|
|
region: CodeRegion,
|
2020-06-22 06:29:08 +00:00
|
|
|
) {
|
|
|
|
debug!(
|
2020-08-15 11:42:13 +00:00
|
|
|
"adding counter expression to coverage_regions: instance={:?}, id={:?}, {:?} {:?} {:?}, \
|
2020-08-02 03:03:59 +00:00
|
|
|
at {:?}",
|
2020-08-15 11:42:13 +00:00
|
|
|
instance, id, lhs, op, rhs, region,
|
2020-06-22 06:29:08 +00:00
|
|
|
);
|
2020-07-02 18:27:15 +00:00
|
|
|
let mut coverage_regions = self.coverage_context().function_coverage_map.borrow_mut();
|
|
|
|
coverage_regions
|
|
|
|
.entry(instance)
|
2020-07-25 04:14:28 +00:00
|
|
|
.or_insert_with(|| FunctionCoverage::new(self.tcx, instance))
|
2020-08-15 11:42:13 +00:00
|
|
|
.add_counter_expression(id, lhs, op, rhs, region);
|
2020-06-22 06:29:08 +00:00
|
|
|
}
|
|
|
|
|
2020-08-15 11:42:13 +00:00
|
|
|
fn add_unreachable_region(&mut self, instance: Instance<'tcx>, region: CodeRegion) {
|
2020-06-22 06:29:08 +00:00
|
|
|
debug!(
|
2020-08-02 03:03:59 +00:00
|
|
|
"adding unreachable code to coverage_regions: instance={:?}, at {:?}",
|
|
|
|
instance, region,
|
2020-06-22 06:29:08 +00:00
|
|
|
);
|
2020-07-02 18:27:15 +00:00
|
|
|
let mut coverage_regions = self.coverage_context().function_coverage_map.borrow_mut();
|
|
|
|
coverage_regions
|
|
|
|
.entry(instance)
|
2020-07-25 04:14:28 +00:00
|
|
|
.or_insert_with(|| FunctionCoverage::new(self.tcx, instance))
|
2020-08-02 03:03:59 +00:00
|
|
|
.add_unreachable_region(region);
|
2020-07-02 18:27:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-08 03:42:29 +00:00
|
|
|
pub(crate) fn write_filenames_section_to_buffer<'a>(
|
|
|
|
filenames: impl IntoIterator<Item = &'a CString>,
|
|
|
|
buffer: &RustString,
|
|
|
|
) {
|
|
|
|
let c_str_vec = filenames.into_iter().map(|cstring| cstring.as_ptr()).collect::<Vec<_>>();
|
2020-07-02 18:27:15 +00:00
|
|
|
unsafe {
|
|
|
|
llvm::LLVMRustCoverageWriteFilenamesSectionToBuffer(
|
|
|
|
c_str_vec.as_ptr(),
|
|
|
|
c_str_vec.len(),
|
|
|
|
buffer,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn write_mapping_to_buffer(
|
|
|
|
virtual_file_mapping: Vec<u32>,
|
2020-07-25 04:14:28 +00:00
|
|
|
expressions: Vec<CounterExpression>,
|
|
|
|
mut mapping_regions: Vec<CounterMappingRegion>,
|
2020-07-02 18:27:15 +00:00
|
|
|
buffer: &RustString,
|
|
|
|
) {
|
|
|
|
unsafe {
|
|
|
|
llvm::LLVMRustCoverageWriteMappingToBuffer(
|
|
|
|
virtual_file_mapping.as_ptr(),
|
|
|
|
virtual_file_mapping.len() as c_uint,
|
|
|
|
expressions.as_ptr(),
|
2020-07-25 04:14:28 +00:00
|
|
|
expressions.len() as c_uint,
|
|
|
|
mapping_regions.as_mut_ptr(),
|
|
|
|
mapping_regions.len() as c_uint,
|
2020-07-02 18:27:15 +00:00
|
|
|
buffer,
|
|
|
|
);
|
2020-06-22 06:29:08 +00:00
|
|
|
}
|
|
|
|
}
|
2020-07-02 18:27:15 +00:00
|
|
|
|
|
|
|
pub(crate) fn compute_hash(name: &str) -> u64 {
|
|
|
|
let name = CString::new(name).expect("null error converting hashable name to C string");
|
|
|
|
unsafe { llvm::LLVMRustCoverageComputeHash(name.as_ptr()) }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn mapping_version() -> u32 {
|
|
|
|
unsafe { llvm::LLVMRustCoverageMappingVersion() }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn save_map_to_mod<'ll, 'tcx>(
|
|
|
|
cx: &CodegenCx<'ll, 'tcx>,
|
|
|
|
cov_data_val: &'ll llvm::Value,
|
|
|
|
) {
|
|
|
|
let covmap_var_name = llvm::build_string(|s| unsafe {
|
|
|
|
llvm::LLVMRustCoverageWriteMappingVarNameToString(s);
|
|
|
|
})
|
|
|
|
.expect("Rust Coverage Mapping var name failed UTF-8 conversion");
|
|
|
|
debug!("covmap var name: {:?}", covmap_var_name);
|
|
|
|
|
|
|
|
let covmap_section_name = llvm::build_string(|s| unsafe {
|
|
|
|
llvm::LLVMRustCoverageWriteSectionNameToString(cx.llmod, s);
|
|
|
|
})
|
|
|
|
.expect("Rust Coverage section name failed UTF-8 conversion");
|
|
|
|
debug!("covmap section name: {:?}", covmap_section_name);
|
|
|
|
|
|
|
|
let llglobal = llvm::add_global(cx.llmod, cx.val_ty(cov_data_val), &covmap_var_name);
|
|
|
|
llvm::set_initializer(llglobal, cov_data_val);
|
|
|
|
llvm::set_global_constant(llglobal, true);
|
|
|
|
llvm::set_linkage(llglobal, llvm::Linkage::InternalLinkage);
|
|
|
|
llvm::set_section(llglobal, &covmap_section_name);
|
|
|
|
llvm::set_alignment(llglobal, COVMAP_VAR_ALIGN_BYTES);
|
|
|
|
cx.add_used_global(llglobal);
|
|
|
|
}
|