1use std::time::Instant;
2
3use rustc_data_structures::fx::FxHashSet;
4use rustc_data_structures::sync::Lock;
5
6use crate::DiagCtxtHandle;
7
8#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
10pub enum TimingSection {
11 Codegen,
13 Linking,
15}
16
17#[derive(Copy, Clone, Debug)]
19pub struct TimingRecord {
20 pub section: TimingSection,
21 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
39pub struct TimingSectionHandler {
41 origin: Option<Instant>,
44 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 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(§ion) {
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 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 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(§ion) {
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
99pub 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}