about summary refs log tree commit diff
path: root/compiler/rustc_errors/src/timings.rs
blob: 0d82f3e8db8bc1d91f4b7904b4ebb9e2b072107a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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(&section) {
            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(&section) {
                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));
        }
    }
}