Skip to main content

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(#[automatically_derived]
impl ::core::marker::Copy for TimingSection { }Copy, #[automatically_derived]
impl ::core::clone::Clone for TimingSection {
    #[inline]
    fn clone(&self) -> TimingSection { *self }
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for TimingSection {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                TimingSection::Codegen => "Codegen",
                TimingSection::Linking => "Linking",
            })
    }
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for TimingSection {
    #[inline]
    fn eq(&self, other: &TimingSection) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for TimingSection {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_receiver_is_total_eq(&self) {}
}Eq, #[automatically_derived]
impl ::core::hash::Hash for TimingSection {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        ::core::hash::Hash::hash(&__self_discr, state)
    }
}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(#[automatically_derived]
impl ::core::marker::Copy for TimingRecord { }Copy, #[automatically_derived]
impl ::core::clone::Clone for TimingRecord {
    #[inline]
    fn clone(&self) -> TimingRecord {
        let _: ::core::clone::AssertParamIsClone<TimingSection>;
        let _: ::core::clone::AssertParamIsClone<u128>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for TimingRecord {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "TimingRecord",
            "section", &self.section, "timestamp", &&self.timestamp)
    }
}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(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("Section `{0:?}` was started again before it was finished",
                section))
    })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(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("Section `{0:?}` was started again before it was finished",
                section))
    })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(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("Section `{0:?}` was ended before being started",
                section))
    })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}