holochain_trace 0.6.1

tracing helpers
Documentation
use crate::writer::InMemoryWriter;
use chrono::SecondsFormat;
use std::path::PathBuf;
use tracing_core::field::Field;
use tracing_subscriber::field::Visit;

pub(crate) struct EventFieldFlameVisitor {
    pub samples: usize,
    name: &'static str,
}

impl EventFieldFlameVisitor {
    pub(crate) fn flame() -> Self {
        EventFieldFlameVisitor {
            samples: 0,
            name: "time.busy",
        }
    }
    pub(crate) fn ice() -> Self {
        EventFieldFlameVisitor {
            samples: 0,
            name: "time.idle",
        }
    }
}

impl Visit for EventFieldFlameVisitor {
    fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
        if field.name() == self.name {
            parse_time(&mut self.samples, value);
        }
    }
}

pub(crate) struct FlameTimed(InMemoryWriter);

impl FlameTimed {
    pub(crate) fn new(writer: InMemoryWriter) -> Self {
        Self(writer)
    }

    fn save_flame_graph(&mut self) -> Option<()> {
        let now = chrono::Local::now().to_rfc3339_opts(SecondsFormat::Secs, true);
        println!("data size {}", self.0.buf().unwrap().len());
        let reader = std::io::BufReader::new(&mut self.0);

        let out = std::fs::File::create(
            toml_path()
                .unwrap_or_else(|| PathBuf::from("."))
                .join(format!("tracing_flame_{now}.svg")),
        )
        .ok()
        .or_else(|| {
            eprintln!("failed to create flames inferno");
            None
        })?;
        let writer = std::io::BufWriter::new(out);

        let mut opts = inferno::flamegraph::Options::default();
        inferno::flamegraph::from_reader(&mut opts, reader, writer).unwrap();
        Some(())
    }
}

impl Drop for FlameTimed {
    fn drop(&mut self) {
        self.save_flame_graph();
    }
}

pub(crate) fn toml_path() -> Option<PathBuf> {
    let path = std::env::var_os("CARGO_MANIFEST_DIR").or_else(|| {
        println!("failed to get cargo manifest dir for flames");
        None
    })?;
    Some(PathBuf::from(path))
}

fn parse_time(samples: &mut usize, value: &dyn std::fmt::Debug) {
    let v = format!("{value:?}");
    if v.ends_with("ns") {
        if let Ok(v) = v.trim_end_matches("ns").parse::<f64>() {
            *samples = v as usize;
        }
    } else if v.ends_with("µs") {
        if let Ok(v) = v.trim_end_matches("µs").parse::<f64>() {
            *samples = (v * 1000.0) as usize;
        }
    } else if v.ends_with("ms") {
        if let Ok(v) = v.trim_end_matches("ms").parse::<f64>() {
            *samples = (v * 1000000.0) as usize;
        }
    } else if v.ends_with('s') {
        if let Ok(v) = v.trim_end_matches('s').parse::<f64>() {
            *samples = (v * 1000000000.0) as usize;
        }
    }
}