1
- use crate :: llvm;
2
-
3
1
use crate :: common:: CodegenCx ;
4
2
use crate :: coverageinfo;
3
+ use crate :: llvm;
5
4
5
+ use llvm:: coverageinfo:: CounterMappingRegion ;
6
6
use log:: debug;
7
- use rustc_codegen_ssa:: coverageinfo:: map:: * ;
8
- use rustc_codegen_ssa:: traits:: { BaseTypeMethods , ConstMethods , MiscMethods } ;
7
+ use rustc_codegen_ssa:: coverageinfo:: map:: { Counter , CounterExpression , Region } ;
8
+ use rustc_codegen_ssa:: traits:: { BaseTypeMethods , ConstMethods } ;
9
9
use rustc_data_structures:: fx:: FxHashMap ;
10
10
use rustc_llvm:: RustString ;
11
- use rustc_middle:: ty:: Instance ;
12
- use rustc_middle:: { bug, mir} ;
13
11
14
- use std:: collections:: BTreeMap ;
15
12
use std:: ffi:: CString ;
16
- use std:: path:: PathBuf ;
17
-
18
- // FIXME(richkadel): Complete all variations of generating and exporting the coverage map to LLVM.
19
- // The current implementation is an initial foundation with basic capabilities (Counters, but not
20
- // CounterExpressions, etc.).
21
13
22
14
/// Generates and exports the Coverage Map.
23
15
///
@@ -32,174 +24,123 @@ use std::path::PathBuf;
32
24
/// undocumented details in Clang's implementation (that may or may not be important) were also
33
25
/// replicated for Rust's Coverage Map.
34
26
pub fn finalize < ' ll , ' tcx > ( cx : & CodegenCx < ' ll , ' tcx > ) {
35
- let mut coverage_writer = CoverageMappingWriter :: new ( cx) ;
36
-
37
27
let function_coverage_map = cx. coverage_context ( ) . take_function_coverage_map ( ) ;
28
+ if function_coverage_map. is_empty ( ) {
29
+ // This module has no functions with coverage instrumentation
30
+ return ;
31
+ }
32
+
33
+ let mut mapgen = CoverageMapGenerator :: new ( ) ;
38
34
39
35
// Encode coverage mappings and generate function records
40
36
let mut function_records = Vec :: < & ' ll llvm:: Value > :: new ( ) ;
41
37
let coverage_mappings_buffer = llvm:: build_byte_buffer ( |coverage_mappings_buffer| {
42
38
for ( instance, function_coverage) in function_coverage_map. into_iter ( ) {
43
- if let Some ( function_record) = coverage_writer. write_function_mappings_and_record (
44
- instance,
45
- function_coverage,
46
- coverage_mappings_buffer,
47
- ) {
48
- function_records. push ( function_record) ;
49
- }
39
+ debug ! ( "Generate coverage map for: {:?}" , instance) ;
40
+
41
+ let mangled_function_name = cx. tcx . symbol_name ( instance) . to_string ( ) ;
42
+ let function_source_hash = function_coverage. source_hash ( ) ;
43
+ let ( expressions, counter_regions) =
44
+ function_coverage. get_expressions_and_counter_regions ( ) ;
45
+
46
+ let old_len = coverage_mappings_buffer. len ( ) ;
47
+ mapgen. write_coverage_mappings ( expressions, counter_regions, coverage_mappings_buffer) ;
48
+ let mapping_data_size = coverage_mappings_buffer. len ( ) - old_len;
49
+ debug_assert ! (
50
+ mapping_data_size > 0 ,
51
+ "Every `FunctionCoverage` should have at least one counter"
52
+ ) ;
53
+
54
+ let function_record = mapgen. make_function_record (
55
+ cx,
56
+ mangled_function_name,
57
+ function_source_hash,
58
+ mapping_data_size,
59
+ ) ;
60
+ function_records. push ( function_record) ;
50
61
}
51
62
} ) ;
52
63
53
- // Encode all filenames covered in this module, ordered by `file_id`
64
+ // Encode all filenames referenced by counters/expressions in this module
54
65
let filenames_buffer = llvm:: build_byte_buffer ( |filenames_buffer| {
55
- coverageinfo:: write_filenames_section_to_buffer (
56
- & coverage_writer. filenames ,
57
- filenames_buffer,
58
- ) ;
66
+ coverageinfo:: write_filenames_section_to_buffer ( & mapgen. filenames , filenames_buffer) ;
59
67
} ) ;
60
68
61
- if coverage_mappings_buffer. len ( ) > 0 {
62
- // Generate the LLVM IR representation of the coverage map and store it in a well-known
63
- // global constant.
64
- coverage_writer. write_coverage_map (
65
- function_records,
66
- filenames_buffer,
67
- coverage_mappings_buffer,
68
- ) ;
69
- }
69
+ // Generate the LLVM IR representation of the coverage map and store it in a well-known global
70
+ mapgen. save_generated_coverage_map (
71
+ cx,
72
+ function_records,
73
+ filenames_buffer,
74
+ coverage_mappings_buffer,
75
+ ) ;
70
76
}
71
77
72
- struct CoverageMappingWriter < ' a , ' ll , ' tcx > {
73
- cx : & ' a CodegenCx < ' ll , ' tcx > ,
78
+ struct CoverageMapGenerator {
74
79
filenames : Vec < CString > ,
75
80
filename_to_index : FxHashMap < CString , u32 > ,
76
81
}
77
82
78
- impl < ' a , ' ll , ' tcx > CoverageMappingWriter < ' a , ' ll , ' tcx > {
79
- fn new ( cx : & ' a CodegenCx < ' ll , ' tcx > ) -> Self {
80
- Self { cx , filenames : Vec :: new ( ) , filename_to_index : FxHashMap :: < CString , u32 > :: default ( ) }
83
+ impl CoverageMapGenerator {
84
+ fn new ( ) -> Self {
85
+ Self { filenames : Vec :: new ( ) , filename_to_index : FxHashMap :: default ( ) }
81
86
}
82
87
83
- /// For the given function, get the coverage region data, stream it to the given buffer, and
84
- /// then generate and return a new function record.
85
- fn write_function_mappings_and_record (
88
+ /// Using the `expressions` and `counter_regions` collected for the current function, generate
89
+ /// the `mapping_regions` and `virtual_file_mapping`, and capture any new filenames. Then use
90
+ /// LLVM APIs to encode the `virtual_file_mapping`, `expressions`, and `mapping_regions` into
91
+ /// the given `coverage_mappings` byte buffer, compliant with the LLVM Coverage Mapping format.
92
+ fn write_coverage_mappings (
86
93
& mut self ,
87
- instance : Instance < ' tcx > ,
88
- mut function_coverage : FunctionCoverage ,
94
+ expressions : Vec < CounterExpression > ,
95
+ counter_regions : impl Iterator < Item = ( Counter , & ' a Region ) > ,
89
96
coverage_mappings_buffer : & RustString ,
90
- ) -> Option < & ' ll llvm:: Value > {
91
- let cx = self . cx ;
92
- let coverageinfo: & mir:: CoverageInfo = cx. tcx . coverageinfo ( instance. def_id ( ) ) ;
93
- debug ! (
94
- "Generate coverage map for: {:?}, num_counters: {}, num_expressions: {}" ,
95
- instance, coverageinfo. num_counters, coverageinfo. num_expressions
96
- ) ;
97
- debug_assert ! ( coverageinfo. num_counters > 0 ) ;
98
-
99
- let regions_in_file_order = function_coverage. regions_in_file_order ( cx. sess ( ) . source_map ( ) ) ;
100
- if regions_in_file_order. len ( ) == 0 {
101
- return None ;
97
+ ) {
98
+ let mut counter_regions = counter_regions. collect :: < Vec < _ > > ( ) ;
99
+ if counter_regions. is_empty ( ) {
100
+ return ;
102
101
}
103
102
104
- // Stream the coverage mapping regions for the function (`instance`) to the buffer, and
105
- // compute the data byte size used.
106
- let old_len = coverage_mappings_buffer. len ( ) ;
107
- self . regions_to_mappings ( regions_in_file_order, coverage_mappings_buffer) ;
108
- let mapping_data_size = coverage_mappings_buffer. len ( ) - old_len;
109
- debug_assert ! ( mapping_data_size > 0 ) ;
110
-
111
- let mangled_function_name = cx. tcx . symbol_name ( instance) . to_string ( ) ;
112
- let name_ref = coverageinfo:: compute_hash ( & mangled_function_name) ;
113
- let function_source_hash = function_coverage. source_hash ( ) ;
114
-
115
- // Generate and return the function record
116
- let name_ref_val = cx. const_u64 ( name_ref) ;
117
- let mapping_data_size_val = cx. const_u32 ( mapping_data_size as u32 ) ;
118
- let func_hash_val = cx. const_u64 ( function_source_hash) ;
119
- Some ( cx. const_struct (
120
- & [ name_ref_val, mapping_data_size_val, func_hash_val] ,
121
- /*packed=*/ true ,
122
- ) )
123
- }
124
-
125
- /// For each coverage region, extract its coverage data from the earlier coverage analysis.
126
- /// Use LLVM APIs to convert the data into buffered bytes compliant with the LLVM Coverage
127
- /// Mapping format.
128
- fn regions_to_mappings (
129
- & mut self ,
130
- regions_in_file_order : BTreeMap < PathBuf , BTreeMap < CoverageLoc , ( usize , CoverageKind ) > > ,
131
- coverage_mappings_buffer : & RustString ,
132
- ) {
133
103
let mut virtual_file_mapping = Vec :: new ( ) ;
134
- let mut mapping_regions = coverageinfo:: SmallVectorCounterMappingRegion :: new ( ) ;
135
- let mut expressions = coverageinfo:: SmallVectorCounterExpression :: new ( ) ;
136
-
137
- for ( file_id, ( file_path, file_coverage_regions) ) in
138
- regions_in_file_order. into_iter ( ) . enumerate ( )
139
- {
140
- let file_id = file_id as u32 ;
141
- let filename = CString :: new ( file_path. to_string_lossy ( ) . to_string ( ) )
142
- . expect ( "null error converting filename to C string" ) ;
143
- debug ! ( " file_id: {} = '{:?}'" , file_id, filename) ;
144
- let filenames_index = match self . filename_to_index . get ( & filename) {
145
- Some ( index) => * index,
146
- None => {
147
- let index = self . filenames . len ( ) as u32 ;
148
- self . filenames . push ( filename. clone ( ) ) ;
149
- self . filename_to_index . insert ( filename, index) ;
150
- index
104
+ let mut mapping_regions = Vec :: new ( ) ;
105
+ let mut current_file_path = None ;
106
+ let mut current_file_id = 0 ;
107
+
108
+ // Convert the list of (Counter, Region) pairs to an array of `CounterMappingRegion`, sorted
109
+ // by filename and position. Capture any new files to compute the `CounterMappingRegion`s
110
+ // `file_id` (indexing files referenced by the current function), and construct the
111
+ // function-specific `virtual_file_mapping` from `file_id` to its index in the module's
112
+ // `filenames` array.
113
+ counter_regions. sort_unstable_by_key ( |( _counter, region) | * region) ;
114
+ for ( counter, region) in counter_regions {
115
+ let ( file_path, start_line, start_col, end_line, end_col) = region. file_start_and_end ( ) ;
116
+ let same_file = current_file_path. as_ref ( ) . map_or ( false , |p| p == file_path) ;
117
+ if !same_file {
118
+ if current_file_path. is_some ( ) {
119
+ current_file_id += 1 ;
151
120
}
152
- } ;
153
- virtual_file_mapping. push ( filenames_index) ;
154
-
155
- let mut mapping_indexes = vec ! [ 0 as u32 ; file_coverage_regions. len( ) ] ;
156
- for ( mapping_index, ( region_id, _) ) in file_coverage_regions. values ( ) . enumerate ( ) {
157
- mapping_indexes[ * region_id] = mapping_index as u32 ;
158
- }
159
-
160
- for ( region_loc, ( region_id, region_kind) ) in file_coverage_regions. into_iter ( ) {
161
- let mapping_index = mapping_indexes[ region_id] ;
162
- match region_kind {
163
- CoverageKind :: Counter => {
164
- debug ! (
165
- " Counter {}, file_id: {}, region_loc: {}" ,
166
- mapping_index, file_id, region_loc
167
- ) ;
168
- mapping_regions. push_from (
169
- mapping_index,
170
- file_id,
171
- region_loc. start_line ,
172
- region_loc. start_col ,
173
- region_loc. end_line ,
174
- region_loc. end_col ,
175
- ) ;
176
- }
177
- CoverageKind :: CounterExpression ( lhs, op, rhs) => {
178
- debug ! (
179
- " CounterExpression {} = {} {:?} {}, file_id: {}, region_loc: {:?}" ,
180
- mapping_index, lhs, op, rhs, file_id, region_loc,
181
- ) ;
182
- mapping_regions. push_from (
183
- mapping_index,
184
- file_id,
185
- region_loc. start_line ,
186
- region_loc. start_col ,
187
- region_loc. end_line ,
188
- region_loc. end_col ,
189
- ) ;
190
- expressions. push_from ( op, lhs, rhs) ;
191
- }
192
- CoverageKind :: Unreachable => {
193
- debug ! (
194
- " Unreachable region, file_id: {}, region_loc: {:?}" ,
195
- file_id, region_loc,
196
- ) ;
197
- bug ! ( "Unreachable region not expected and not yet handled!" )
198
- // FIXME(richkadel): implement and call
199
- // mapping_regions.push_from(...) for unreachable regions
121
+ current_file_path = Some ( file_path. clone ( ) ) ;
122
+ let filename = CString :: new ( file_path. to_string_lossy ( ) . to_string ( ) )
123
+ . expect ( "null error converting filename to C string" ) ;
124
+ debug ! ( " file_id: {} = '{:?}'" , current_file_id, filename) ;
125
+ let filenames_index = match self . filename_to_index . get ( & filename) {
126
+ Some ( index) => * index,
127
+ None => {
128
+ let index = self . filenames . len ( ) as u32 ;
129
+ self . filenames . push ( filename. clone ( ) ) ;
130
+ self . filename_to_index . insert ( filename. clone ( ) , index) ;
131
+ index
200
132
}
201
- }
133
+ } ;
134
+ virtual_file_mapping. push ( filenames_index) ;
202
135
}
136
+ mapping_regions. push ( CounterMappingRegion :: code_region (
137
+ counter,
138
+ current_file_id,
139
+ start_line,
140
+ start_col,
141
+ end_line,
142
+ end_col,
143
+ ) ) ;
203
144
}
204
145
205
146
// Encode and append the current function's coverage mapping data
@@ -211,14 +152,35 @@ impl<'a, 'll, 'tcx> CoverageMappingWriter<'a, 'll, 'tcx> {
211
152
) ;
212
153
}
213
154
214
- fn write_coverage_map (
155
+ /// Generate and return the function record `Value`
156
+ fn make_function_record (
157
+ & mut self ,
158
+ cx : & CodegenCx < ' ll , ' tcx > ,
159
+ mangled_function_name : String ,
160
+ function_source_hash : u64 ,
161
+ mapping_data_size : usize ,
162
+ ) -> & ' ll llvm:: Value {
163
+ let name_ref = coverageinfo:: compute_hash ( & mangled_function_name) ;
164
+ let name_ref_val = cx. const_u64 ( name_ref) ;
165
+ let mapping_data_size_val = cx. const_u32 ( mapping_data_size as u32 ) ;
166
+ let func_hash_val = cx. const_u64 ( function_source_hash) ;
167
+ cx. const_struct (
168
+ & [ name_ref_val, mapping_data_size_val, func_hash_val] ,
169
+ /*packed=*/ true ,
170
+ )
171
+ }
172
+
173
+ /// Combine the filenames and coverage mappings buffers, construct coverage map header and the
174
+ /// array of function records, and combine everything into the complete coverage map. Save the
175
+ /// coverage map data into the LLVM IR as a static global using a specific, well-known section
176
+ /// and name.
177
+ fn save_generated_coverage_map (
215
178
self ,
179
+ cx : & CodegenCx < ' ll , ' tcx > ,
216
180
function_records : Vec < & ' ll llvm:: Value > ,
217
181
filenames_buffer : Vec < u8 > ,
218
182
mut coverage_mappings_buffer : Vec < u8 > ,
219
183
) {
220
- let cx = self . cx ;
221
-
222
184
// Concatenate the encoded filenames and encoded coverage mappings, and add additional zero
223
185
// bytes as-needed to ensure 8-byte alignment.
224
186
let mut coverage_size = coverage_mappings_buffer. len ( ) ;
0 commit comments