use std::sync::{Mutex, OnceLock};
use std::time::Instant;
static PROCESS_START: OnceLock<Instant> = OnceLock::new();
static MILESTONES: Mutex<Vec<(&'static str, u64)>> = Mutex::new(Vec::new());
pub fn mark_process_start() {
let _ = PROCESS_START.set(Instant::now());
}
pub fn mark(label: &'static str) {
let Some(start) = PROCESS_START.get() else {
return;
};
let elapsed_ms = start.elapsed().as_millis() as u64;
if let Ok(mut milestones) = MILESTONES.lock() {
milestones.push((label, elapsed_ms));
}
}
pub fn log_summary() {
let Some(start) = PROCESS_START.get() else {
return;
};
let total_ms = start.elapsed().as_millis() as u64;
let Ok(mut milestones) = MILESTONES.lock() else {
return;
};
let line = milestones
.iter()
.map(|(label, ms)| format!("{label}={ms}ms"))
.collect::<Vec<_>>()
.join(" ");
milestones.clear();
tracing::info!(target: "startup", "startup {line} event_loop={total_ms}ms");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn milestones_accumulate_and_summary_drains() {
mark_process_start();
mark("alpha");
mark("beta");
{
let milestones = MILESTONES.lock().unwrap();
let labels: Vec<&str> = milestones.iter().map(|(l, _)| *l).collect();
assert!(labels.contains(&"alpha"));
assert!(labels.contains(&"beta"));
}
log_summary();
assert!(MILESTONES.lock().unwrap().is_empty());
}
}