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