| use std::time::Instant; | |
| use rustc_data_structures::fx::FxHashSet; | |
| use rustc_data_structures::sync::Lock; | |
| use crate::DiagCtxtHandle; | |
| /// A high-level section of the compilation process. | |
| #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | |
| pub enum TimingSection { | |
| /// Time spent doing codegen. | |
| Codegen, | |
| /// Time spent linking. | |
| Linking, | |
| } | |
| /// Section with attached timestamp | |
| #[derive(Copy, Clone, Debug)] | |
| pub struct TimingRecord { | |
| pub section: TimingSection, | |
| /// Microseconds elapsed since some predetermined point in time (~start of the rustc process). | |
| pub timestamp: u128, | |
| } | |
| impl TimingRecord { | |
| fn from_origin(origin: Instant, section: TimingSection) -> Self { | |
| Self { section, timestamp: Instant::now().duration_since(origin).as_micros() } | |
| } | |
| pub fn section(&self) -> TimingSection { | |
| self.section | |
| } | |
| pub fn timestamp(&self) -> u128 { | |
| self.timestamp | |
| } | |
| } | |
| /// Manages emission of start/end section timings, enabled through `--json=timings`. | |
| pub struct TimingSectionHandler { | |
| /// Time when the compilation session started. | |
| /// If `None`, timing is disabled. | |
| origin: Option<Instant>, | |
| /// Sanity check to ensure that we open and close sections correctly. | |
| opened_sections: Lock<FxHashSet<TimingSection>>, | |
| } | |
| impl TimingSectionHandler { | |
| pub fn new(enabled: bool) -> Self { | |
| let origin = if enabled { Some(Instant::now()) } else { None }; | |
| Self { origin, opened_sections: Lock::new(FxHashSet::default()) } | |
| } | |
| /// Returns a RAII guard that will immediately emit a start the provided section, and then emit | |
| /// its end when it is dropped. | |
| pub fn section_guard<'a>( | |
| &self, | |
| diag_ctxt: DiagCtxtHandle<'a>, | |
| section: TimingSection, | |
| ) -> TimingSectionGuard<'a> { | |
| if self.is_enabled() && self.opened_sections.borrow().contains(§ion) { | |
| diag_ctxt | |
| .bug(format!("Section `{section:?}` was started again before it was finished")); | |
| } | |
| TimingSectionGuard::create(diag_ctxt, section, self.origin) | |
| } | |
| /// Start the provided section. | |
| pub fn start_section(&self, diag_ctxt: DiagCtxtHandle<'_>, section: TimingSection) { | |
| if let Some(origin) = self.origin { | |
| let mut opened = self.opened_sections.borrow_mut(); | |
| if !opened.insert(section) { | |
| diag_ctxt | |
| .bug(format!("Section `{section:?}` was started again before it was finished")); | |
| } | |
| diag_ctxt.emit_timing_section_start(TimingRecord::from_origin(origin, section)); | |
| } | |
| } | |
| /// End the provided section. | |
| pub fn end_section(&self, diag_ctxt: DiagCtxtHandle<'_>, section: TimingSection) { | |
| if let Some(origin) = self.origin { | |
| let mut opened = self.opened_sections.borrow_mut(); | |
| if !opened.remove(§ion) { | |
| diag_ctxt.bug(format!("Section `{section:?}` was ended before being started")); | |
| } | |
| diag_ctxt.emit_timing_section_end(TimingRecord::from_origin(origin, section)); | |
| } | |
| } | |
| fn is_enabled(&self) -> bool { | |
| self.origin.is_some() | |
| } | |
| } | |
| /// RAII wrapper for starting and ending section timings. | |
| pub struct TimingSectionGuard<'a> { | |
| dcx: DiagCtxtHandle<'a>, | |
| section: TimingSection, | |
| origin: Option<Instant>, | |
| } | |
| impl<'a> TimingSectionGuard<'a> { | |
| fn create(dcx: DiagCtxtHandle<'a>, section: TimingSection, origin: Option<Instant>) -> Self { | |
| if let Some(origin) = origin { | |
| dcx.emit_timing_section_start(TimingRecord::from_origin(origin, section)); | |
| } | |
| Self { dcx, section, origin } | |
| } | |
| } | |
| impl<'a> Drop for TimingSectionGuard<'a> { | |
| fn drop(&mut self) { | |
| if let Some(origin) = self.origin { | |
| self.dcx.emit_timing_section_end(TimingRecord::from_origin(origin, self.section)); | |
| } | |
| } | |
| } |