]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
New upstream version 1.52.0~beta.3+dfsg1
[rustc.git] / compiler / rustc_codegen_llvm / src / coverageinfo / mod.rs
1 use crate::llvm;
2
3 use crate::builder::Builder;
4 use crate::common::CodegenCx;
5
6 use libc::c_uint;
7 use llvm::coverageinfo::CounterMappingRegion;
8 use rustc_codegen_ssa::coverageinfo::map::{CounterExpression, FunctionCoverage};
9 use rustc_codegen_ssa::traits::{
10 BaseTypeMethods, CoverageInfoBuilderMethods, CoverageInfoMethods, MiscMethods, StaticMethods,
11 };
12 use rustc_data_structures::fx::FxHashMap;
13 use rustc_llvm::RustString;
14 use rustc_middle::mir::coverage::{
15 CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId, Op,
16 };
17 use rustc_middle::ty::Instance;
18
19 use std::cell::RefCell;
20 use std::ffi::CString;
21
22 use tracing::debug;
23
24 pub mod mapgen;
25
26 const VAR_ALIGN_BYTES: usize = 8;
27
28 /// A context object for maintaining all state needed by the coverageinfo module.
29 pub struct CrateCoverageContext<'tcx> {
30 // Coverage data for each instrumented function identified by DefId.
31 pub(crate) function_coverage_map: RefCell<FxHashMap<Instance<'tcx>, FunctionCoverage<'tcx>>>,
32 }
33
34 impl<'tcx> CrateCoverageContext<'tcx> {
35 pub fn new() -> Self {
36 Self { function_coverage_map: Default::default() }
37 }
38
39 pub fn take_function_coverage_map(&self) -> FxHashMap<Instance<'tcx>, FunctionCoverage<'tcx>> {
40 self.function_coverage_map.replace(FxHashMap::default())
41 }
42 }
43
44 impl CoverageInfoMethods for CodegenCx<'ll, 'tcx> {
45 fn coverageinfo_finalize(&self) {
46 mapgen::finalize(self)
47 }
48 }
49
50 impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
51 /// Calls llvm::createPGOFuncNameVar() with the given function instance's mangled function name.
52 /// The LLVM API returns an llvm::GlobalVariable containing the function name, with the specific
53 /// variable name and linkage required by LLVM InstrProf source-based coverage instrumentation.
54 fn create_pgo_func_name_var(&self, instance: Instance<'tcx>) -> Self::Value {
55 let llfn = self.cx.get_fn(instance);
56 let mangled_fn_name = CString::new(self.tcx.symbol_name(instance).name)
57 .expect("error converting function name to C string");
58 unsafe { llvm::LLVMRustCoverageCreatePGOFuncNameVar(llfn, mangled_fn_name.as_ptr()) }
59 }
60
61 fn set_function_source_hash(
62 &mut self,
63 instance: Instance<'tcx>,
64 function_source_hash: u64,
65 ) -> bool {
66 if let Some(coverage_context) = self.coverage_context() {
67 debug!(
68 "ensuring function source hash is set for instance={:?}; function_source_hash={}",
69 instance, function_source_hash,
70 );
71 let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
72 coverage_map
73 .entry(instance)
74 .or_insert_with(|| FunctionCoverage::new(self.tcx, instance))
75 .set_function_source_hash(function_source_hash);
76 true
77 } else {
78 false
79 }
80 }
81
82 fn add_coverage_counter(
83 &mut self,
84 instance: Instance<'tcx>,
85 id: CounterValueReference,
86 region: CodeRegion,
87 ) -> bool {
88 if let Some(coverage_context) = self.coverage_context() {
89 debug!(
90 "adding counter to coverage_map: instance={:?}, id={:?}, region={:?}",
91 instance, id, region,
92 );
93 let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
94 coverage_map
95 .entry(instance)
96 .or_insert_with(|| FunctionCoverage::new(self.tcx, instance))
97 .add_counter(id, region);
98 true
99 } else {
100 false
101 }
102 }
103
104 fn add_coverage_counter_expression(
105 &mut self,
106 instance: Instance<'tcx>,
107 id: InjectedExpressionId,
108 lhs: ExpressionOperandId,
109 op: Op,
110 rhs: ExpressionOperandId,
111 region: Option<CodeRegion>,
112 ) -> bool {
113 if let Some(coverage_context) = self.coverage_context() {
114 debug!(
115 "adding counter expression to coverage_map: instance={:?}, id={:?}, {:?} {:?} {:?}; \
116 region: {:?}",
117 instance, id, lhs, op, rhs, region,
118 );
119 let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
120 coverage_map
121 .entry(instance)
122 .or_insert_with(|| FunctionCoverage::new(self.tcx, instance))
123 .add_counter_expression(id, lhs, op, rhs, region);
124 true
125 } else {
126 false
127 }
128 }
129
130 fn add_coverage_unreachable(&mut self, instance: Instance<'tcx>, region: CodeRegion) -> bool {
131 if let Some(coverage_context) = self.coverage_context() {
132 debug!(
133 "adding unreachable code to coverage_map: instance={:?}, at {:?}",
134 instance, region,
135 );
136 let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
137 coverage_map
138 .entry(instance)
139 .or_insert_with(|| FunctionCoverage::new(self.tcx, instance))
140 .add_unreachable_region(region);
141 true
142 } else {
143 false
144 }
145 }
146 }
147
148 pub(crate) fn write_filenames_section_to_buffer<'a>(
149 filenames: impl IntoIterator<Item = &'a CString>,
150 buffer: &RustString,
151 ) {
152 let c_str_vec = filenames.into_iter().map(|cstring| cstring.as_ptr()).collect::<Vec<_>>();
153 unsafe {
154 llvm::LLVMRustCoverageWriteFilenamesSectionToBuffer(
155 c_str_vec.as_ptr(),
156 c_str_vec.len(),
157 buffer,
158 );
159 }
160 }
161
162 pub(crate) fn write_mapping_to_buffer(
163 virtual_file_mapping: Vec<u32>,
164 expressions: Vec<CounterExpression>,
165 mapping_regions: Vec<CounterMappingRegion>,
166 buffer: &RustString,
167 ) {
168 unsafe {
169 llvm::LLVMRustCoverageWriteMappingToBuffer(
170 virtual_file_mapping.as_ptr(),
171 virtual_file_mapping.len() as c_uint,
172 expressions.as_ptr(),
173 expressions.len() as c_uint,
174 mapping_regions.as_ptr(),
175 mapping_regions.len() as c_uint,
176 buffer,
177 );
178 }
179 }
180 pub(crate) fn hash_str(strval: &str) -> u64 {
181 let strval = CString::new(strval).expect("null error converting hashable str to C string");
182 unsafe { llvm::LLVMRustCoverageHashCString(strval.as_ptr()) }
183 }
184
185 pub(crate) fn hash_bytes(bytes: Vec<u8>) -> u64 {
186 unsafe { llvm::LLVMRustCoverageHashByteArray(bytes.as_ptr().cast(), bytes.len()) }
187 }
188
189 pub(crate) fn mapping_version() -> u32 {
190 unsafe { llvm::LLVMRustCoverageMappingVersion() }
191 }
192
193 pub(crate) fn save_cov_data_to_mod<'ll, 'tcx>(
194 cx: &CodegenCx<'ll, 'tcx>,
195 cov_data_val: &'ll llvm::Value,
196 ) {
197 let covmap_var_name = llvm::build_string(|s| unsafe {
198 llvm::LLVMRustCoverageWriteMappingVarNameToString(s);
199 })
200 .expect("Rust Coverage Mapping var name failed UTF-8 conversion");
201 debug!("covmap var name: {:?}", covmap_var_name);
202
203 let covmap_section_name = llvm::build_string(|s| unsafe {
204 llvm::LLVMRustCoverageWriteMapSectionNameToString(cx.llmod, s);
205 })
206 .expect("Rust Coverage section name failed UTF-8 conversion");
207 debug!("covmap section name: {:?}", covmap_section_name);
208
209 let llglobal = llvm::add_global(cx.llmod, cx.val_ty(cov_data_val), &covmap_var_name);
210 llvm::set_initializer(llglobal, cov_data_val);
211 llvm::set_global_constant(llglobal, true);
212 llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage);
213 llvm::set_section(llglobal, &covmap_section_name);
214 llvm::set_alignment(llglobal, VAR_ALIGN_BYTES);
215 cx.add_used_global(llglobal);
216 }
217
218 pub(crate) fn save_func_record_to_mod<'ll, 'tcx>(
219 cx: &CodegenCx<'ll, 'tcx>,
220 func_name_hash: u64,
221 func_record_val: &'ll llvm::Value,
222 is_used: bool,
223 ) {
224 // Assign a name to the function record. This is used to merge duplicates.
225 //
226 // In LLVM, a "translation unit" (effectively, a `Crate` in Rust) can describe functions that
227 // are included-but-not-used. If (or when) Rust generates functions that are
228 // included-but-not-used, note that a dummy description for a function included-but-not-used
229 // in a Crate can be replaced by full description provided by a different Crate. The two kinds
230 // of descriptions play distinct roles in LLVM IR; therefore, assign them different names (by
231 // appending "u" to the end of the function record var name, to prevent `linkonce_odr` merging.
232 let func_record_var_name =
233 format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" });
234 debug!("function record var name: {:?}", func_record_var_name);
235
236 let func_record_section_name = llvm::build_string(|s| unsafe {
237 llvm::LLVMRustCoverageWriteFuncSectionNameToString(cx.llmod, s);
238 })
239 .expect("Rust Coverage function record section name failed UTF-8 conversion");
240 debug!("function record section name: {:?}", func_record_section_name);
241
242 let llglobal = llvm::add_global(cx.llmod, cx.val_ty(func_record_val), &func_record_var_name);
243 llvm::set_initializer(llglobal, func_record_val);
244 llvm::set_global_constant(llglobal, true);
245 llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage);
246 llvm::set_visibility(llglobal, llvm::Visibility::Hidden);
247 llvm::set_section(llglobal, &func_record_section_name);
248 llvm::set_alignment(llglobal, VAR_ALIGN_BYTES);
249 llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name);
250 cx.add_used_global(llglobal);
251 }