ssb 0.1.1

Simple benchmarking for Rust, with hierarchical call tree, based on fastrace.
Documentation
use std::sync::{Arc, Mutex, OnceLock};

use fastrace::collector::{Config, Reporter, SpanId, SpanRecord};

/// Stripped-down span data — the only fields bencher needs.
#[derive(Debug)]
pub(crate) struct RawSpan {
    pub name: String,
    pub duration_ns: u64,
    pub span_id: SpanId,
    pub parent_id: SpanId,
}

static SINK: OnceLock<Arc<Mutex<Vec<RawSpan>>>> = OnceLock::new();

fn sink() -> Arc<Mutex<Vec<RawSpan>>> {
    SINK.get_or_init(|| {
        let buf: Arc<Mutex<Vec<RawSpan>>> = Default::default();
        fastrace::set_reporter(BenchReporter(Arc::clone(&buf)), Config::default());
        buf
    })
    .clone()
}

struct BenchReporter(Arc<Mutex<Vec<RawSpan>>>);

impl Reporter for BenchReporter {
    fn report(&mut self, spans: Vec<SpanRecord>) {
        let mut buf = self.0.lock().unwrap();
        for s in spans {
            buf.push(RawSpan {
                name: s.name.into_owned(),
                duration_ns: s.duration_ns,
                span_id: s.span_id,
                parent_id: s.parent_id,
            });
        }
    }
}

/// Ensure the reporter is registered. Must be called before any benchmark run.
pub(crate) fn init() {
    sink();
}

/// Flush all pending spans to the reporter, then take and return them.
pub(crate) fn drain() -> Vec<RawSpan> {
    fastrace::flush();
    std::mem::take(&mut *sink().lock().unwrap())
}