1use std::collections::HashMap;
15use std::sync::atomic::{AtomicBool, Ordering};
16use std::sync::{Mutex, OnceLock};
17use std::time::Instant;
18
19fn enabled() -> bool {
20 static ON: OnceLock<bool> = OnceLock::new();
21 *ON.get_or_init(|| std::env::var("ZSHRS_LOG").is_ok_and(|v| v.contains("ftime")))
22}
23
24static DIRTY: AtomicBool = AtomicBool::new(false);
25
26#[allow(clippy::type_complexity)]
27fn table() -> &'static Mutex<HashMap<String, (u128, u32)>> {
28 static T: OnceLock<Mutex<HashMap<String, (u128, u32)>>> = OnceLock::new();
29 T.get_or_init(|| Mutex::new(HashMap::new()))
30}
31
32pub fn start(name: &str) -> Option<(String, Instant)> {
34 if !enabled() {
35 return None;
36 }
37 Some((name.to_string(), Instant::now()))
38}
39
40pub fn stop(span: Option<(String, Instant)>) {
42 let Some((name, t0)) = span else { return };
43 let ns = t0.elapsed().as_nanos();
44 if let Ok(mut t) = table().lock() {
45 let e = t.entry(name).or_insert((0, 0));
46 e.0 += ns;
47 e.1 += 1;
48 }
49 DIRTY.store(true, Ordering::Relaxed);
50}
51
52pub fn dump_and_reset() {
55 if !enabled() || !DIRTY.swap(false, Ordering::Relaxed) {
56 return;
57 }
58 let Ok(mut t) = table().lock() else { return };
59 let mut rows: Vec<(String, u128, u32)> = t.iter().map(|(k, v)| (k.clone(), v.0, v.1)).collect();
60 rows.sort_by(|a, b| b.1.cmp(&a.1));
61 let mut out = String::from(" total_ms calls name (inclusive)\n");
62 for (name, ns, calls) in rows.iter().take(40) {
63 out.push_str(&format!(
64 "{:10.3} {:7} {}\n",
65 *ns as f64 / 1e6,
66 calls,
67 name
68 ));
69 }
70 let _ = std::fs::write("/tmp/ftime.log", out);
71 t.clear();
72}