]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/profile/src/stop_watch.rs
Auto merge of #101969 - reez12g:issue-101306, r=reez12g
[rust.git] / src / tools / rust-analyzer / crates / profile / src / stop_watch.rs
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 {
23     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     }
56     pub fn memory(mut self, yes: bool) -> StopWatch {
57         if yes {
58             self.memory = Some(MemoryUsage::now());
59         }
60         self
61     }
62     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 {
78     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 }