use std::sync::OnceLock;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::time::Instant;
#[derive(Clone, Copy, PartialEq, Eq)]
enum Sink {
Off,
Log,
LogAndStderr,
}
static SINK: OnceLock<Sink> = OnceLock::new();
static START: OnceLock<Instant> = OnceLock::new();
static PREV_US: AtomicU64 = AtomicU64::new(0);
fn sink() -> Sink {
*SINK.get_or_init(|| match std::env::var("ZSHRS_STARTUP_TRACE") {
Err(_) => Sink::Off,
Ok(v) if v.is_empty() || v == "0" => Sink::Off,
Ok(v) if v.eq_ignore_ascii_case("stderr") => Sink::LogAndStderr,
Ok(_) => Sink::Log,
})
}
fn origin() -> Instant {
*START.get_or_init(Instant::now)
}
pub fn start() {
let _ = origin();
}
pub fn init(t0: Instant) {
let _ = START.set(t0);
let _ = sink();
}
pub fn elapsed_us() -> u64 {
u64::try_from(origin().elapsed().as_micros()).unwrap_or(u64::MAX)
}
pub fn mark(label: &str) {
let sink = sink();
if sink == Sink::Off {
return;
}
let total_us = elapsed_us();
let prev_us = PREV_US.swap(total_us, Ordering::Relaxed);
let delta_us = total_us.saturating_sub(prev_us);
tracing::info!(
target: "startup",
phase = label,
total_us,
delta_us,
"startup phase"
);
if sink == Sink::LogAndStderr {
eprintln!(
"startup: {:>9.3}ms (+{:>8.3}ms) {label}",
total_us as f64 / 1000.0,
delta_us as f64 / 1000.0
);
}
}
pub fn mark_first_prompt() {
static FIRED: AtomicBool = AtomicBool::new(false);
if FIRED.swap(true, Ordering::Relaxed) {
return;
}
mark("FIRST PROMPT PAINTED");
}