]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_transform/src/coverage/mod.rs
Auto merge of #89905 - matthiaskrgr:rev_89709_entirely, r=michaelwoerister
[rust.git] / compiler / rustc_mir_transform / src / coverage / mod.rs
1 pub mod query;
2
3 mod counters;
4 mod debug;
5 mod graph;
6 mod spans;
7
8 #[cfg(test)]
9 mod tests;
10
11 use counters::CoverageCounters;
12 use graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph};
13 use spans::{CoverageSpan, CoverageSpans};
14
15 use crate::MirPass;
16
17 use rustc_data_structures::graph::WithNumNodes;
18 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
19 use rustc_data_structures::sync::Lrc;
20 use rustc_index::vec::IndexVec;
21 use rustc_middle::hir;
22 use rustc_middle::hir::map::blocks::FnLikeNode;
23 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
24 use rustc_middle::mir::coverage::*;
25 use rustc_middle::mir::dump_enabled;
26 use rustc_middle::mir::{
27     self, BasicBlock, BasicBlockData, Coverage, SourceInfo, Statement, StatementKind, Terminator,
28     TerminatorKind,
29 };
30 use rustc_middle::ty::TyCtxt;
31 use rustc_span::def_id::DefId;
32 use rustc_span::source_map::SourceMap;
33 use rustc_span::{CharPos, ExpnKind, Pos, SourceFile, Span, Symbol};
34
35 /// A simple error message wrapper for `coverage::Error`s.
36 #[derive(Debug)]
37 struct Error {
38     message: String,
39 }
40
41 impl Error {
42     pub fn from_string<T>(message: String) -> Result<T, Error> {
43         Err(Self { message })
44     }
45 }
46
47 /// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected
48 /// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen
49 /// to construct the coverage map.
50 pub struct InstrumentCoverage;
51
52 impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
53     fn run_pass(&self, tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) {
54         let mir_source = mir_body.source;
55
56         // If the InstrumentCoverage pass is called on promoted MIRs, skip them.
57         // See: https://github.com/rust-lang/rust/pull/73011#discussion_r438317601
58         if mir_source.promoted.is_some() {
59             trace!(
60                 "InstrumentCoverage skipped for {:?} (already promoted for Miri evaluation)",
61                 mir_source.def_id()
62             );
63             return;
64         }
65
66         let hir_id = tcx.hir().local_def_id_to_hir_id(mir_source.def_id().expect_local());
67         let is_fn_like = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some();
68
69         // Only instrument functions, methods, and closures (not constants since they are evaluated
70         // at compile time by Miri).
71         // FIXME(#73156): Handle source code coverage in const eval, but note, if and when const
72         // expressions get coverage spans, we will probably have to "carve out" space for const
73         // expressions from coverage spans in enclosing MIR's, like we do for closures. (That might
74         // be tricky if const expressions have no corresponding statements in the enclosing MIR.
75         // Closures are carved out by their initial `Assign` statement.)
76         if !is_fn_like {
77             trace!("InstrumentCoverage skipped for {:?} (not an FnLikeNode)", mir_source.def_id());
78             return;
79         }
80
81         match mir_body.basic_blocks()[mir::START_BLOCK].terminator().kind {
82             TerminatorKind::Unreachable => {
83                 trace!("InstrumentCoverage skipped for unreachable `START_BLOCK`");
84                 return;
85             }
86             _ => {}
87         }
88
89         let codegen_fn_attrs = tcx.codegen_fn_attrs(mir_source.def_id());
90         if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_COVERAGE) {
91             return;
92         }
93
94         trace!("InstrumentCoverage starting for {:?}", mir_source.def_id());
95         Instrumentor::new(&self.name(), tcx, mir_body).inject_counters();
96         trace!("InstrumentCoverage done for {:?}", mir_source.def_id());
97     }
98 }
99
100 struct Instrumentor<'a, 'tcx> {
101     pass_name: &'a str,
102     tcx: TyCtxt<'tcx>,
103     mir_body: &'a mut mir::Body<'tcx>,
104     source_file: Lrc<SourceFile>,
105     fn_sig_span: Span,
106     body_span: Span,
107     basic_coverage_blocks: CoverageGraph,
108     coverage_counters: CoverageCounters,
109 }
110
111 impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
112     fn new(pass_name: &'a str, tcx: TyCtxt<'tcx>, mir_body: &'a mut mir::Body<'tcx>) -> Self {
113         let source_map = tcx.sess.source_map();
114         let def_id = mir_body.source.def_id();
115         let (some_fn_sig, hir_body) = fn_sig_and_body(tcx, def_id);
116
117         let body_span = get_body_span(tcx, hir_body, mir_body);
118
119         let source_file = source_map.lookup_source_file(body_span.lo());
120         let fn_sig_span = match some_fn_sig.filter(|fn_sig| {
121             fn_sig.span.ctxt() == body_span.ctxt()
122                 && Lrc::ptr_eq(&source_file, &source_map.lookup_source_file(fn_sig.span.lo()))
123         }) {
124             Some(fn_sig) => fn_sig.span.with_hi(body_span.lo()),
125             None => body_span.shrink_to_lo(),
126         };
127
128         debug!(
129             "instrumenting {}: {:?}, fn sig span: {:?}, body span: {:?}",
130             if tcx.is_closure(def_id) { "closure" } else { "function" },
131             def_id,
132             fn_sig_span,
133             body_span
134         );
135
136         let function_source_hash = hash_mir_source(tcx, hir_body);
137         let basic_coverage_blocks = CoverageGraph::from_mir(mir_body);
138         Self {
139             pass_name,
140             tcx,
141             mir_body,
142             source_file,
143             fn_sig_span,
144             body_span,
145             basic_coverage_blocks,
146             coverage_counters: CoverageCounters::new(function_source_hash),
147         }
148     }
149
150     fn inject_counters(&'a mut self) {
151         let tcx = self.tcx;
152         let mir_source = self.mir_body.source;
153         let def_id = mir_source.def_id();
154         let fn_sig_span = self.fn_sig_span;
155         let body_span = self.body_span;
156
157         let mut graphviz_data = debug::GraphvizData::new();
158         let mut debug_used_expressions = debug::UsedExpressions::new();
159
160         let dump_mir = dump_enabled(tcx, self.pass_name, def_id);
161         let dump_graphviz = dump_mir && tcx.sess.opts.debugging_opts.dump_mir_graphviz;
162         let dump_spanview = dump_mir && tcx.sess.opts.debugging_opts.dump_mir_spanview.is_some();
163
164         if dump_graphviz {
165             graphviz_data.enable();
166             self.coverage_counters.enable_debug();
167         }
168
169         if dump_graphviz || level_enabled!(tracing::Level::DEBUG) {
170             debug_used_expressions.enable();
171         }
172
173         ////////////////////////////////////////////////////
174         // Compute `CoverageSpan`s from the `CoverageGraph`.
175         let coverage_spans = CoverageSpans::generate_coverage_spans(
176             &self.mir_body,
177             fn_sig_span,
178             body_span,
179             &self.basic_coverage_blocks,
180         );
181
182         if dump_spanview {
183             debug::dump_coverage_spanview(
184                 tcx,
185                 self.mir_body,
186                 &self.basic_coverage_blocks,
187                 self.pass_name,
188                 body_span,
189                 &coverage_spans,
190             );
191         }
192
193         ////////////////////////////////////////////////////
194         // Create an optimized mix of `Counter`s and `Expression`s for the `CoverageGraph`. Ensure
195         // every `CoverageSpan` has a `Counter` or `Expression` assigned to its `BasicCoverageBlock`
196         // and all `Expression` dependencies (operands) are also generated, for any other
197         // `BasicCoverageBlock`s not already associated with a `CoverageSpan`.
198         //
199         // Intermediate expressions (used to compute other `Expression` values), which have no
200         // direct associate to any `BasicCoverageBlock`, are returned in the method `Result`.
201         let intermediate_expressions_or_error = self
202             .coverage_counters
203             .make_bcb_counters(&mut self.basic_coverage_blocks, &coverage_spans);
204
205         let (result, intermediate_expressions) = match intermediate_expressions_or_error {
206             Ok(intermediate_expressions) => {
207                 // If debugging, add any intermediate expressions (which are not associated with any
208                 // BCB) to the `debug_used_expressions` map.
209                 if debug_used_expressions.is_enabled() {
210                     for intermediate_expression in &intermediate_expressions {
211                         debug_used_expressions.add_expression_operands(intermediate_expression);
212                     }
213                 }
214
215                 ////////////////////////////////////////////////////
216                 // Remove the counter or edge counter from of each `CoverageSpan`s associated
217                 // `BasicCoverageBlock`, and inject a `Coverage` statement into the MIR.
218                 //
219                 // `Coverage` statements injected from `CoverageSpan`s will include the code regions
220                 // (source code start and end positions) to be counted by the associated counter.
221                 //
222                 // These `CoverageSpan`-associated counters are removed from their associated
223                 // `BasicCoverageBlock`s so that the only remaining counters in the `CoverageGraph`
224                 // are indirect counters (to be injected next, without associated code regions).
225                 self.inject_coverage_span_counters(
226                     coverage_spans,
227                     &mut graphviz_data,
228                     &mut debug_used_expressions,
229                 );
230
231                 ////////////////////////////////////////////////////
232                 // For any remaining `BasicCoverageBlock` counters (that were not associated with
233                 // any `CoverageSpan`), inject `Coverage` statements (_without_ code region `Span`s)
234                 // to ensure `BasicCoverageBlock` counters that other `Expression`s may depend on
235                 // are in fact counted, even though they don't directly contribute to counting
236                 // their own independent code region's coverage.
237                 self.inject_indirect_counters(&mut graphviz_data, &mut debug_used_expressions);
238
239                 // Intermediate expressions will be injected as the final step, after generating
240                 // debug output, if any.
241                 ////////////////////////////////////////////////////
242
243                 (Ok(()), intermediate_expressions)
244             }
245             Err(e) => (Err(e), Vec::new()),
246         };
247
248         if graphviz_data.is_enabled() {
249             // Even if there was an error, a partial CoverageGraph can still generate a useful
250             // graphviz output.
251             debug::dump_coverage_graphviz(
252                 tcx,
253                 self.mir_body,
254                 self.pass_name,
255                 &self.basic_coverage_blocks,
256                 &self.coverage_counters.debug_counters,
257                 &graphviz_data,
258                 &intermediate_expressions,
259                 &debug_used_expressions,
260             );
261         }
262
263         if let Err(e) = result {
264             bug!("Error processing: {:?}: {:?}", self.mir_body.source.def_id(), e.message)
265         };
266
267         // Depending on current `debug_options()`, `alert_on_unused_expressions()` could panic, so
268         // this check is performed as late as possible, to allow other debug output (logs and dump
269         // files), which might be helpful in analyzing unused expressions, to still be generated.
270         debug_used_expressions.alert_on_unused_expressions(&self.coverage_counters.debug_counters);
271
272         ////////////////////////////////////////////////////
273         // Finally, inject the intermediate expressions collected along the way.
274         for intermediate_expression in intermediate_expressions {
275             inject_intermediate_expression(self.mir_body, intermediate_expression);
276         }
277     }
278
279     /// Inject a counter for each `CoverageSpan`. There can be multiple `CoverageSpan`s for a given
280     /// BCB, but only one actual counter needs to be incremented per BCB. `bb_counters` maps each
281     /// `bcb` to its `Counter`, when injected. Subsequent `CoverageSpan`s for a BCB that already has
282     /// a `Counter` will inject an `Expression` instead, and compute its value by adding `ZERO` to
283     /// the BCB `Counter` value.
284     ///
285     /// If debugging, add every BCB `Expression` associated with a `CoverageSpan`s to the
286     /// `used_expression_operands` map.
287     fn inject_coverage_span_counters(
288         &mut self,
289         coverage_spans: Vec<CoverageSpan>,
290         graphviz_data: &mut debug::GraphvizData,
291         debug_used_expressions: &mut debug::UsedExpressions,
292     ) {
293         let tcx = self.tcx;
294         let source_map = tcx.sess.source_map();
295         let body_span = self.body_span;
296         let file_name = Symbol::intern(&self.source_file.name.prefer_remapped().to_string_lossy());
297
298         let mut bcb_counters = IndexVec::from_elem_n(None, self.basic_coverage_blocks.num_nodes());
299         for covspan in coverage_spans {
300             let bcb = covspan.bcb;
301             let span = covspan.span;
302             let counter_kind = if let Some(&counter_operand) = bcb_counters[bcb].as_ref() {
303                 self.coverage_counters.make_identity_counter(counter_operand)
304             } else if let Some(counter_kind) = self.bcb_data_mut(bcb).take_counter() {
305                 bcb_counters[bcb] = Some(counter_kind.as_operand_id());
306                 debug_used_expressions.add_expression_operands(&counter_kind);
307                 counter_kind
308             } else {
309                 bug!("Every BasicCoverageBlock should have a Counter or Expression");
310             };
311             graphviz_data.add_bcb_coverage_span_with_counter(bcb, &covspan, &counter_kind);
312
313             debug!(
314                 "Calling make_code_region(file_name={}, source_file={:?}, span={}, body_span={})",
315                 file_name,
316                 self.source_file,
317                 source_map.span_to_diagnostic_string(span),
318                 source_map.span_to_diagnostic_string(body_span)
319             );
320
321             inject_statement(
322                 self.mir_body,
323                 counter_kind,
324                 self.bcb_leader_bb(bcb),
325                 Some(make_code_region(source_map, file_name, &self.source_file, span, body_span)),
326             );
327         }
328     }
329
330     /// `inject_coverage_span_counters()` looped through the `CoverageSpan`s and injected the
331     /// counter from the `CoverageSpan`s `BasicCoverageBlock`, removing it from the BCB in the
332     /// process (via `take_counter()`).
333     ///
334     /// Any other counter associated with a `BasicCoverageBlock`, or its incoming edge, but not
335     /// associated with a `CoverageSpan`, should only exist if the counter is an `Expression`
336     /// dependency (one of the expression operands). Collect them, and inject the additional
337     /// counters into the MIR, without a reportable coverage span.
338     fn inject_indirect_counters(
339         &mut self,
340         graphviz_data: &mut debug::GraphvizData,
341         debug_used_expressions: &mut debug::UsedExpressions,
342     ) {
343         let mut bcb_counters_without_direct_coverage_spans = Vec::new();
344         for (target_bcb, target_bcb_data) in self.basic_coverage_blocks.iter_enumerated_mut() {
345             if let Some(counter_kind) = target_bcb_data.take_counter() {
346                 bcb_counters_without_direct_coverage_spans.push((None, target_bcb, counter_kind));
347             }
348             if let Some(edge_counters) = target_bcb_data.take_edge_counters() {
349                 for (from_bcb, counter_kind) in edge_counters {
350                     bcb_counters_without_direct_coverage_spans.push((
351                         Some(from_bcb),
352                         target_bcb,
353                         counter_kind,
354                     ));
355                 }
356             }
357         }
358
359         // If debug is enabled, validate that every BCB or edge counter not directly associated
360         // with a coverage span is at least indirectly associated (it is a dependency of a BCB
361         // counter that _is_ associated with a coverage span).
362         debug_used_expressions.validate(&bcb_counters_without_direct_coverage_spans);
363
364         for (edge_from_bcb, target_bcb, counter_kind) in bcb_counters_without_direct_coverage_spans
365         {
366             debug_used_expressions.add_unused_expression_if_not_found(
367                 &counter_kind,
368                 edge_from_bcb,
369                 target_bcb,
370             );
371
372             match counter_kind {
373                 CoverageKind::Counter { .. } => {
374                     let inject_to_bb = if let Some(from_bcb) = edge_from_bcb {
375                         // The MIR edge starts `from_bb` (the outgoing / last BasicBlock in
376                         // `from_bcb`) and ends at `to_bb` (the incoming / first BasicBlock in the
377                         // `target_bcb`; also called the `leader_bb`).
378                         let from_bb = self.bcb_last_bb(from_bcb);
379                         let to_bb = self.bcb_leader_bb(target_bcb);
380
381                         let new_bb = inject_edge_counter_basic_block(self.mir_body, from_bb, to_bb);
382                         graphviz_data.set_edge_counter(from_bcb, new_bb, &counter_kind);
383                         debug!(
384                             "Edge {:?} (last {:?}) -> {:?} (leader {:?}) requires a new MIR \
385                             BasicBlock {:?}, for unclaimed edge counter {}",
386                             edge_from_bcb,
387                             from_bb,
388                             target_bcb,
389                             to_bb,
390                             new_bb,
391                             self.format_counter(&counter_kind),
392                         );
393                         new_bb
394                     } else {
395                         let target_bb = self.bcb_last_bb(target_bcb);
396                         graphviz_data.add_bcb_dependency_counter(target_bcb, &counter_kind);
397                         debug!(
398                             "{:?} ({:?}) gets a new Coverage statement for unclaimed counter {}",
399                             target_bcb,
400                             target_bb,
401                             self.format_counter(&counter_kind),
402                         );
403                         target_bb
404                     };
405
406                     inject_statement(self.mir_body, counter_kind, inject_to_bb, None);
407                 }
408                 CoverageKind::Expression { .. } => {
409                     inject_intermediate_expression(self.mir_body, counter_kind)
410                 }
411                 _ => bug!("CoverageKind should be a counter"),
412             }
413         }
414     }
415
416     #[inline]
417     fn bcb_leader_bb(&self, bcb: BasicCoverageBlock) -> BasicBlock {
418         self.bcb_data(bcb).leader_bb()
419     }
420
421     #[inline]
422     fn bcb_last_bb(&self, bcb: BasicCoverageBlock) -> BasicBlock {
423         self.bcb_data(bcb).last_bb()
424     }
425
426     #[inline]
427     fn bcb_data(&self, bcb: BasicCoverageBlock) -> &BasicCoverageBlockData {
428         &self.basic_coverage_blocks[bcb]
429     }
430
431     #[inline]
432     fn bcb_data_mut(&mut self, bcb: BasicCoverageBlock) -> &mut BasicCoverageBlockData {
433         &mut self.basic_coverage_blocks[bcb]
434     }
435
436     #[inline]
437     fn format_counter(&self, counter_kind: &CoverageKind) -> String {
438         self.coverage_counters.debug_counters.format_counter(counter_kind)
439     }
440 }
441
442 fn inject_edge_counter_basic_block(
443     mir_body: &mut mir::Body<'tcx>,
444     from_bb: BasicBlock,
445     to_bb: BasicBlock,
446 ) -> BasicBlock {
447     let span = mir_body[from_bb].terminator().source_info.span.shrink_to_hi();
448     let new_bb = mir_body.basic_blocks_mut().push(BasicBlockData {
449         statements: vec![], // counter will be injected here
450         terminator: Some(Terminator {
451             source_info: SourceInfo::outermost(span),
452             kind: TerminatorKind::Goto { target: to_bb },
453         }),
454         is_cleanup: false,
455     });
456     let edge_ref = mir_body[from_bb]
457         .terminator_mut()
458         .successors_mut()
459         .find(|successor| **successor == to_bb)
460         .expect("from_bb should have a successor for to_bb");
461     *edge_ref = new_bb;
462     new_bb
463 }
464
465 fn inject_statement(
466     mir_body: &mut mir::Body<'tcx>,
467     counter_kind: CoverageKind,
468     bb: BasicBlock,
469     some_code_region: Option<CodeRegion>,
470 ) {
471     debug!(
472         "  injecting statement {:?} for {:?} at code region: {:?}",
473         counter_kind, bb, some_code_region
474     );
475     let data = &mut mir_body[bb];
476     let source_info = data.terminator().source_info;
477     let statement = Statement {
478         source_info,
479         kind: StatementKind::Coverage(Box::new(Coverage {
480             kind: counter_kind,
481             code_region: some_code_region,
482         })),
483     };
484     data.statements.insert(0, statement);
485 }
486
487 // Non-code expressions are injected into the coverage map, without generating executable code.
488 fn inject_intermediate_expression(mir_body: &mut mir::Body<'tcx>, expression: CoverageKind) {
489     debug_assert!(if let CoverageKind::Expression { .. } = expression { true } else { false });
490     debug!("  injecting non-code expression {:?}", expression);
491     let inject_in_bb = mir::START_BLOCK;
492     let data = &mut mir_body[inject_in_bb];
493     let source_info = data.terminator().source_info;
494     let statement = Statement {
495         source_info,
496         kind: StatementKind::Coverage(Box::new(Coverage { kind: expression, code_region: None })),
497     };
498     data.statements.push(statement);
499 }
500
501 /// Convert the Span into its file name, start line and column, and end line and column
502 fn make_code_region(
503     source_map: &SourceMap,
504     file_name: Symbol,
505     source_file: &Lrc<SourceFile>,
506     span: Span,
507     body_span: Span,
508 ) -> CodeRegion {
509     let (start_line, mut start_col) = source_file.lookup_file_pos(span.lo());
510     let (end_line, end_col) = if span.hi() == span.lo() {
511         let (end_line, mut end_col) = (start_line, start_col);
512         // Extend an empty span by one character so the region will be counted.
513         let CharPos(char_pos) = start_col;
514         if span.hi() == body_span.hi() {
515             start_col = CharPos(char_pos - 1);
516         } else {
517             end_col = CharPos(char_pos + 1);
518         }
519         (end_line, end_col)
520     } else {
521         source_file.lookup_file_pos(span.hi())
522     };
523     let start_line = source_map.doctest_offset_line(&source_file.name, start_line);
524     let end_line = source_map.doctest_offset_line(&source_file.name, end_line);
525     CodeRegion {
526         file_name,
527         start_line: start_line as u32,
528         start_col: start_col.to_u32() + 1,
529         end_line: end_line as u32,
530         end_col: end_col.to_u32() + 1,
531     }
532 }
533
534 fn fn_sig_and_body<'tcx>(
535     tcx: TyCtxt<'tcx>,
536     def_id: DefId,
537 ) -> (Option<&'tcx rustc_hir::FnSig<'tcx>>, &'tcx rustc_hir::Body<'tcx>) {
538     // FIXME(#79625): Consider improving MIR to provide the information needed, to avoid going back
539     // to HIR for it.
540     let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local");
541     let fn_body_id = hir::map::associated_body(hir_node).expect("HIR node is a function with body");
542     (hir::map::fn_sig(hir_node), tcx.hir().body(fn_body_id))
543 }
544
545 fn get_body_span<'tcx>(
546     tcx: TyCtxt<'tcx>,
547     hir_body: &rustc_hir::Body<'tcx>,
548     mir_body: &mut mir::Body<'tcx>,
549 ) -> Span {
550     let mut body_span = hir_body.value.span;
551     let def_id = mir_body.source.def_id();
552
553     if tcx.is_closure(def_id) {
554         // If the MIR function is a closure, and if the closure body span
555         // starts from a macro, but it's content is not in that macro, try
556         // to find a non-macro callsite, and instrument the spans there
557         // instead.
558         loop {
559             let expn_data = body_span.ctxt().outer_expn_data();
560             if expn_data.is_root() {
561                 break;
562             }
563             if let ExpnKind::Macro { .. } = expn_data.kind {
564                 body_span = expn_data.call_site;
565             } else {
566                 break;
567             }
568         }
569     }
570
571     body_span
572 }
573
574 fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx rustc_hir::Body<'tcx>) -> u64 {
575     // FIXME(cjgillot) Stop hashing HIR manually here.
576     let mut hcx = tcx.create_no_span_stable_hashing_context();
577     let mut stable_hasher = StableHasher::new();
578     let owner = hir_body.id().hir_id.owner;
579     let bodies = &tcx.hir_owner_nodes(owner).as_ref().unwrap().bodies;
580     hcx.with_hir_bodies(false, owner, bodies, |hcx| {
581         hir_body.value.hash_stable(hcx, &mut stable_hasher)
582     });
583     stable_hasher.finish()
584 }