paralegal_flow/
stats.rs

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
use std::{
    fmt::Display,
    sync::{Arc, Mutex},
    time::{Duration, Instant},
};

use paralegal_spdg::utils::TruncatedHumanTime;
use trait_enum::DerefMut;

/// Statsistics that are counted as durations
#[derive(Debug, Clone, Copy, strum::AsRefStr, PartialEq, Eq, enum_map::Enum)]
pub enum TimedStat {
    /// How long the rust compiler ran before our plugin got called (currently
    /// isn't accurate)
    Rustc,
    /// How long the flowistry PDG cosntruction took in total.
    Flowistry,
    /// How long it took to convert the flowistry graph to a
    /// [`paralegal_spdg::ProgramDescription`]
    Conversion,
    /// How long it took to serialize the SPDG
    Serialization,
    /// How long it took to collect and dump the MIR
    MirEmission,
}

struct StatsInner {
    timed: enum_map::EnumMap<TimedStat, Option<Duration>>,
    started: Instant,
}

impl Default for StatsInner {
    fn default() -> Self {
        Self {
            started: std::time::Instant::now(),
            timed: Default::default(),
        }
    }
}

impl StatsInner {
    fn record_timed(&mut self, stat: TimedStat, duration: Duration) {
        *self.timed[stat].get_or_insert(Duration::ZERO) += duration
    }
}

#[derive(Clone)]
pub struct Stats(Arc<Mutex<StatsInner>>);

impl Stats {
    fn inner_mut(&self) -> impl DerefMut<Target = StatsInner> + '_ {
        self.0.as_ref().lock().unwrap()
    }

    pub fn record_timed(&self, stat: TimedStat, duration: Duration) {
        self.inner_mut().record_timed(stat, duration)
    }

    pub fn get_timed(&self, stat: TimedStat) -> Duration {
        self.0.lock().unwrap().timed[stat].unwrap_or(Duration::ZERO)
    }

    pub fn elapsed(&self) -> Duration {
        self.0.lock().unwrap().started.elapsed()
    }

    pub fn measure<R>(&self, stat: TimedStat, target: impl FnOnce() -> R) -> R {
        let start = Instant::now();
        let r = target();
        self.record_timed(stat, start.elapsed());
        r
    }
}

impl Default for Stats {
    fn default() -> Self {
        Self(Arc::new(Mutex::new(Default::default())))
    }
}

impl Display for Stats {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let borrow = self.0.as_ref().lock().unwrap();
        for (s, dur) in borrow.timed {
            if let Some(dur) = dur {
                write!(f, "{}: {} ", s.as_ref(), TruncatedHumanTime::from(dur))?;
            }
        }
        write!(
            f,
            "current runtime: {}",
            TruncatedHumanTime::from(borrow.started.elapsed())
        )?;
        Ok(())
    }
}