1 //! Like `std::time::Instant`, but also measures memory & CPU cycles.
2 use std::{
3     fmt,
4     time::{Duration, Instant},
5 };
6 
7 use crate::MemoryUsage;
8 
9 pub struct StopWatch {
10     time: Instant,
11     #[cfg(target_os = "linux")]
12     counter: Option<perf_event::Counter>,
13     memory: Option<MemoryUsage>,
14 }
15 
16 pub struct StopWatchSpan {
17     pub time: Duration,
18     pub instructions: Option<u64>,
19     pub memory: Option<MemoryUsage>,
20 }
21 
22 impl StopWatch {
start() -> StopWatch23     pub fn start() -> StopWatch {
24         #[cfg(target_os = "linux")]
25         let counter = {
26             // When debugging rust-analyzer using rr, the perf-related syscalls cause it to abort.
27             // We allow disabling perf by setting the env var `RA_DISABLE_PERF`.
28 
29             use once_cell::sync::Lazy;
30             static PERF_ENABLED: Lazy<bool> =
31                 Lazy::new(|| std::env::var_os("RA_DISABLE_PERF").is_none());
32 
33             if *PERF_ENABLED {
34                 let mut counter = perf_event::Builder::new()
35                     .build()
36                     .map_err(|err| eprintln!("Failed to create perf counter: {}", err))
37                     .ok();
38                 if let Some(counter) = &mut counter {
39                     if let Err(err) = counter.enable() {
40                         eprintln!("Failed to start perf counter: {}", err)
41                     }
42                 }
43                 counter
44             } else {
45                 None
46             }
47         };
48         let time = Instant::now();
49         StopWatch {
50             time,
51             #[cfg(target_os = "linux")]
52             counter,
53             memory: None,
54         }
55     }
memory(mut self, yes: bool) -> StopWatch56     pub fn memory(mut self, yes: bool) -> StopWatch {
57         if yes {
58             self.memory = Some(MemoryUsage::now());
59         }
60         self
61     }
elapsed(&mut self) -> StopWatchSpan62     pub fn elapsed(&mut self) -> StopWatchSpan {
63         let time = self.time.elapsed();
64 
65         #[cfg(target_os = "linux")]
66         let instructions = self.counter.as_mut().and_then(|it| {
67             it.read().map_err(|err| eprintln!("Failed to read perf counter: {}", err)).ok()
68         });
69         #[cfg(not(target_os = "linux"))]
70         let instructions = None;
71 
72         let memory = self.memory.map(|it| MemoryUsage::now() - it);
73         StopWatchSpan { time, instructions, memory }
74     }
75 }
76 
77 impl fmt::Display for StopWatchSpan {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result78     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79         write!(f, "{:.2?}", self.time)?;
80         if let Some(mut instructions) = self.instructions {
81             let mut prefix = "";
82             if instructions > 10000 {
83                 instructions /= 1000;
84                 prefix = "k";
85             }
86             if instructions > 10000 {
87                 instructions /= 1000;
88                 prefix = "m";
89             }
90             if instructions > 10000 {
91                 instructions /= 1000;
92                 prefix = "g";
93             }
94             write!(f, ", {}{}instr", instructions, prefix)?;
95         }
96         if let Some(memory) = self.memory {
97             write!(f, ", {}", memory)?;
98         }
99         Ok(())
100     }
101 }
102