1use std::sync::OnceLock;
4use std::time::Instant;
5
6static ENABLED: OnceLock<bool> = OnceLock::new();
7
8pub fn enable() {
11 let _ = ENABLED.set(true);
12}
13
14pub(crate) struct Span<'a> {
15 task: &'a str,
16 stage: &'static str,
17 started: Option<Instant>,
18 counts: Vec<(&'static str, String)>,
19}
20
21pub(crate) fn span<'a>(task: &'a str, stage: &'static str) -> Span<'a> {
22 let enabled = *ENABLED
23 .get_or_init(|| std::env::var_os("QK_PROFILE_CACHE").is_some_and(|value| value == "1"));
24 Span {
25 task,
26 stage,
27 started: enabled.then(Instant::now),
28 counts: Vec::new(),
29 }
30}
31
32impl Span<'_> {
33 pub(crate) fn count(&mut self, name: &'static str, value: u64) {
35 if self.started.is_some() {
36 self.counts.push((name, value.to_string()));
37 }
38 }
39
40 pub(crate) fn note(&mut self, name: &'static str, value: &str) {
43 if self.started.is_some() {
44 let value: String = value.chars().filter(|ch| !ch.is_whitespace()).collect();
45 self.counts.push((name, value));
46 }
47 }
48}
49
50impl Drop for Span<'_> {
51 fn drop(&mut self) {
52 if let Some(started) = self.started {
53 let mut line = format!(
54 "qk profile: task={} stage={} ms={:.3}",
55 self.task,
56 self.stage,
57 started.elapsed().as_secs_f64() * 1000.0
58 );
59 for (name, value) in &self.counts {
60 line.push_str(&format!(" {name}={value}"));
61 }
62 line.push('\n');
63 qk_executor::report::output(true, line.as_bytes());
64 }
65 }
66}