rustc_errors/
timings.rs

1use std::time::Instant;
2
3use rustc_data_structures::fx::FxHashSet;
4use rustc_data_structures::sync::Lock;
5
6use crate::DiagCtxtHandle;
7
8/// A high-level section of the compilation process.
9#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
10pub enum TimingSection {
11    /// Time spent doing codegen.
12    Codegen,
13    /// Time spent linking.
14    Linking,
15}
16
17/// Section with attached timestamp
18#[derive(Copy, Clone, Debug)]
19pub struct TimingRecord {
20    pub section: TimingSection,
21    /// Microseconds elapsed since some predetermined point in time (~start of the rustc process).
22    pub timestamp: u128,
23}
24
25impl TimingRecord {
26    fn from_origin(origin: Instant, section: TimingSection) -> Self {
27        Self { section, timestamp: Instant::now().duration_since(origin).as_micros() }
28    }
29
30    pub fn section(&self) -> TimingSection {
31        self.section
32    }
33
34    pub fn timestamp(&self) -> u128 {
35        self.timestamp
36    }
37}
38
39/// Manages emission of start/end section timings, enabled through `--json=timings`.
40pub struct TimingSectionHandler {
41    /// Time when the compilation session started.
42    /// If `None`, timing is disabled.
43    origin: Option<Instant>,
44    /// Sanity check to ensure that we open and close sections correctly.
45    opened_sections: Lock<FxHashSet<TimingSection>>,
46}
47
48impl TimingSectionHandler {
49    pub fn new(enabled: bool) -> Self {
50        let origin = if enabled { Some(Instant::now()) } else { None };
51        Self { origin, opened_sections: Lock::new(FxHashSet::default()) }
52    }
53
54    /// Returns a RAII guard that will immediately emit a start the provided section, and then emit
55    /// its end when it is dropped.
56    pub fn section_guard<'a>(
57        &self,
58        diag_ctxt: DiagCtxtHandle<'a>,
59        section: TimingSection,
60    ) -> TimingSectionGuard<'a> {
61        if self.is_enabled() && self.opened_sections.borrow().contains(&section) {
62            diag_ctxt
63                .bug(format!("Section `{section:?}` was started again before it was finished"));
64        }
65
66        TimingSectionGuard::create(diag_ctxt, section, self.origin)
67    }
68
69    /// Start the provided section.
70    pub fn start_section(&self, diag_ctxt: DiagCtxtHandle<'_>, section: TimingSection) {
71        if let Some(origin) = self.origin {
72            let mut opened = self.opened_sections.borrow_mut();
73            if !opened.insert(section) {
74                diag_ctxt
75                    .bug(format!("Section `{section:?}` was started again before it was finished"));
76            }
77
78            diag_ctxt.emit_timing_section_start(TimingRecord::from_origin(origin, section));
79        }
80    }
81
82    /// End the provided section.
83    pub fn end_section(&self, diag_ctxt: DiagCtxtHandle<'_>, section: TimingSection) {
84        if let Some(origin) = self.origin {
85            let mut opened = self.opened_sections.borrow_mut();
86            if !opened.remove(&section) {
87                diag_ctxt.bug(format!("Section `{section:?}` was ended before being started"));
88            }
89
90            diag_ctxt.emit_timing_section_end(TimingRecord::from_origin(origin, section));
91        }
92    }
93
94    fn is_enabled(&self) -> bool {
95        self.origin.is_some()
96    }
97}
98
99/// RAII wrapper for starting and ending section timings.
100pub struct TimingSectionGuard<'a> {
101    dcx: DiagCtxtHandle<'a>,
102    section: TimingSection,
103    origin: Option<Instant>,
104}
105
106impl<'a> TimingSectionGuard<'a> {
107    fn create(dcx: DiagCtxtHandle<'a>, section: TimingSection, origin: Option<Instant>) -> Self {
108        if let Some(origin) = origin {
109            dcx.emit_timing_section_start(TimingRecord::from_origin(origin, section));
110        }
111        Self { dcx, section, origin }
112    }
113}
114
115impl<'a> Drop for TimingSectionGuard<'a> {
116    fn drop(&mut self) {
117        if let Some(origin) = self.origin {
118            self.dcx.emit_timing_section_end(TimingRecord::from_origin(origin, self.section));
119        }
120    }
121}