1 use crate::common
::CodegenCx
;
2 use crate::coverageinfo
;
5 use llvm
::coverageinfo
::CounterMappingRegion
;
6 use rustc_codegen_ssa
::coverageinfo
::map
::{Counter, CounterExpression}
;
7 use rustc_codegen_ssa
::traits
::{BaseTypeMethods, ConstMethods}
;
8 use rustc_data_structures
::fx
::FxIndexSet
;
9 use rustc_llvm
::RustString
;
10 use rustc_middle
::mir
::coverage
::CodeRegion
;
12 use std
::ffi
::CString
;
16 /// Generates and exports the Coverage Map.
18 /// This Coverage Map complies with Coverage Mapping Format version 3 (zero-based encoded as 2),
19 /// as defined at [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/llvmorg-8.0.0/llvm/docs/CoverageMappingFormat.rst#llvm-code-coverage-mapping-format)
20 /// and published in Rust's current (July 2020) fork of LLVM. This version is supported by the
21 /// LLVM coverage tools (`llvm-profdata` and `llvm-cov`) bundled with Rust's fork of LLVM.
23 /// Consequently, Rust's bundled version of Clang also generates Coverage Maps compliant with
24 /// version 3. Clang's implementation of Coverage Map generation was referenced when implementing
25 /// this Rust version, and though the format documentation is very explicit and detailed, some
26 /// undocumented details in Clang's implementation (that may or may not be important) were also
27 /// replicated for Rust's Coverage Map.
28 pub fn finalize
<'ll
, 'tcx
>(cx
: &CodegenCx
<'ll
, 'tcx
>) {
29 let function_coverage_map
= cx
.coverage_context().take_function_coverage_map();
30 if function_coverage_map
.is_empty() {
31 // This module has no functions with coverage instrumentation
35 let mut mapgen
= CoverageMapGenerator
::new();
37 // Encode coverage mappings and generate function records
38 let mut function_records
= Vec
::<&'ll llvm
::Value
>::new();
39 let coverage_mappings_buffer
= llvm
::build_byte_buffer(|coverage_mappings_buffer
| {
40 for (instance
, function_coverage
) in function_coverage_map
.into_iter() {
41 debug
!("Generate coverage map for: {:?}", instance
);
43 let mangled_function_name
= cx
.tcx
.symbol_name(instance
).to_string();
44 let function_source_hash
= function_coverage
.source_hash();
45 let (expressions
, counter_regions
) =
46 function_coverage
.get_expressions_and_counter_regions();
48 let old_len
= coverage_mappings_buffer
.len();
49 mapgen
.write_coverage_mappings(expressions
, counter_regions
, coverage_mappings_buffer
);
50 let mapping_data_size
= coverage_mappings_buffer
.len() - old_len
;
52 mapping_data_size
> 0,
53 "Every `FunctionCoverage` should have at least one counter"
56 let function_record
= mapgen
.make_function_record(
58 mangled_function_name
,
62 function_records
.push(function_record
);
66 // Encode all filenames referenced by counters/expressions in this module
67 let filenames_buffer
= llvm
::build_byte_buffer(|filenames_buffer
| {
68 coverageinfo
::write_filenames_section_to_buffer(&mapgen
.filenames
, filenames_buffer
);
71 // Generate the LLVM IR representation of the coverage map and store it in a well-known global
72 mapgen
.save_generated_coverage_map(
76 coverage_mappings_buffer
,
80 struct CoverageMapGenerator
{
81 filenames
: FxIndexSet
<CString
>,
84 impl CoverageMapGenerator
{
86 Self { filenames: FxIndexSet::default() }
89 /// Using the `expressions` and `counter_regions` collected for the current function, generate
90 /// the `mapping_regions` and `virtual_file_mapping`, and capture any new filenames. Then use
91 /// LLVM APIs to encode the `virtual_file_mapping`, `expressions`, and `mapping_regions` into
92 /// the given `coverage_mappings` byte buffer, compliant with the LLVM Coverage Mapping format.
93 fn write_coverage_mappings(
95 expressions
: Vec
<CounterExpression
>,
96 counter_regions
: impl Iterator
<Item
= (Counter
, &'a CodeRegion
)>,
97 coverage_mappings_buffer
: &RustString
,
99 let mut counter_regions
= counter_regions
.collect
::<Vec
<_
>>();
100 if counter_regions
.is_empty() {
104 let mut virtual_file_mapping
= Vec
::new();
105 let mut mapping_regions
= Vec
::new();
106 let mut current_file_name
= None
;
107 let mut current_file_id
= 0;
109 // Convert the list of (Counter, CodeRegion) pairs to an array of `CounterMappingRegion`, sorted
110 // by filename and position. Capture any new files to compute the `CounterMappingRegion`s
111 // `file_id` (indexing files referenced by the current function), and construct the
112 // function-specific `virtual_file_mapping` from `file_id` to its index in the module's
113 // `filenames` array.
114 counter_regions
.sort_unstable_by_key(|(_counter
, region
)| *region
);
115 for (counter
, region
) in counter_regions
{
116 let CodeRegion { file_name, start_line, start_col, end_line, end_col }
= *region
;
117 let same_file
= current_file_name
.as_ref().map_or(false, |p
| *p
== file_name
);
119 if current_file_name
.is_some() {
120 current_file_id
+= 1;
122 current_file_name
= Some(file_name
);
123 let c_filename
= CString
::new(file_name
.to_string())
124 .expect("null error converting filename to C string");
125 debug
!(" file_id: {} = '{:?}'", current_file_id
, c_filename
);
126 let (filenames_index
, _
) = self.filenames
.insert_full(c_filename
);
127 virtual_file_mapping
.push(filenames_index
as u32);
129 mapping_regions
.push(CounterMappingRegion
::code_region(
139 // Encode and append the current function's coverage mapping data
140 coverageinfo
::write_mapping_to_buffer(
141 virtual_file_mapping
,
144 coverage_mappings_buffer
,
148 /// Generate and return the function record `Value`
149 fn make_function_record(
151 cx
: &CodegenCx
<'ll
, 'tcx
>,
152 mangled_function_name
: String
,
153 function_source_hash
: u64,
154 mapping_data_size
: usize,
155 ) -> &'ll llvm
::Value
{
156 let name_ref
= coverageinfo
::compute_hash(&mangled_function_name
);
157 let name_ref_val
= cx
.const_u64(name_ref
);
158 let mapping_data_size_val
= cx
.const_u32(mapping_data_size
as u32);
159 let func_hash_val
= cx
.const_u64(function_source_hash
);
161 &[name_ref_val
, mapping_data_size_val
, func_hash_val
],
166 /// Combine the filenames and coverage mappings buffers, construct coverage map header and the
167 /// array of function records, and combine everything into the complete coverage map. Save the
168 /// coverage map data into the LLVM IR as a static global using a specific, well-known section
170 fn save_generated_coverage_map(
172 cx
: &CodegenCx
<'ll
, 'tcx
>,
173 function_records
: Vec
<&'ll llvm
::Value
>,
174 filenames_buffer
: Vec
<u8>,
175 mut coverage_mappings_buffer
: Vec
<u8>,
177 // Concatenate the encoded filenames and encoded coverage mappings, and add additional zero
178 // bytes as-needed to ensure 8-byte alignment.
179 let mut coverage_size
= coverage_mappings_buffer
.len();
180 let filenames_size
= filenames_buffer
.len();
181 let remaining_bytes
=
182 (filenames_size
+ coverage_size
) % coverageinfo
::COVMAP_VAR_ALIGN_BYTES
;
183 if remaining_bytes
> 0 {
184 let pad
= coverageinfo
::COVMAP_VAR_ALIGN_BYTES
- remaining_bytes
;
185 coverage_mappings_buffer
.append(&mut [0].repeat(pad
));
186 coverage_size
+= pad
;
188 let filenames_and_coverage_mappings
= [filenames_buffer
, coverage_mappings_buffer
].concat();
189 let filenames_and_coverage_mappings_val
=
190 cx
.const_bytes(&filenames_and_coverage_mappings
[..]);
193 "cov map: n_records = {}, filenames_size = {}, coverage_size = {}, 0-based version = {}",
194 function_records
.len(),
197 coverageinfo
::mapping_version()
200 // Create the coverage data header
201 let n_records_val
= cx
.const_u32(function_records
.len() as u32);
202 let filenames_size_val
= cx
.const_u32(filenames_size
as u32);
203 let coverage_size_val
= cx
.const_u32(coverage_size
as u32);
204 let version_val
= cx
.const_u32(coverageinfo
::mapping_version());
205 let cov_data_header_val
= cx
.const_struct(
206 &[n_records_val
, filenames_size_val
, coverage_size_val
, version_val
],
210 // Create the function records array
211 let name_ref_from_u64
= cx
.type_i64();
212 let mapping_data_size_from_u32
= cx
.type_i32();
213 let func_hash_from_u64
= cx
.type_i64();
214 let function_record_ty
= cx
.type_struct(
215 &[name_ref_from_u64
, mapping_data_size_from_u32
, func_hash_from_u64
],
218 let function_records_val
= cx
.const_array(function_record_ty
, &function_records
[..]);
220 // Create the complete LLVM coverage data value to add to the LLVM IR
221 let cov_data_val
= cx
.const_struct(
222 &[cov_data_header_val
, function_records_val
, filenames_and_coverage_mappings_val
],
226 // Save the coverage data value to LLVM IR
227 coverageinfo
::save_map_to_mod(cx
, cov_data_val
);