use std::sync::Arc;
use std::sync::LazyLock;
use std::time::Instant;
#[derive(Debug, Clone, Default)]
pub struct DebugTap {
ds: Option<Arc<str>>,
}
impl DebugTap {
pub fn off() -> Self {
Self { ds: None }
}
pub fn for_datasource(name: impl Into<String>) -> Self {
ANY_TAP_ENABLED.store(true, std::sync::atomic::Ordering::Relaxed);
LazyLock::force(&PROCESS_START);
Self {
ds: Some(Arc::from(name.into())),
}
}
pub fn enabled(&self) -> bool {
self.ds.is_some()
}
pub fn ds(&self) -> &str {
self.ds.as_deref().unwrap_or("")
}
}
macro_rules! tapline {
($tap:expr, $tag:literal, $($arg:tt)*) => {
if $tap.enabled() {
tracing::info!(
target: "vantage_diorama::debug",
"{:<10} {:<8} {}",
$tap.ds(),
$tag,
format_args!($($arg)*),
);
}
};
}
pub(crate) use tapline;
pub fn dur(ms: u64) -> String {
if ms < 1_000 {
format!("{ms}ms")
} else if ms < 60_000 {
format!("{:.1}s", ms as f64 / 1000.0)
} else {
format!("{}m{:02}s", ms / 60_000, (ms % 60_000) / 1000)
}
}
pub fn bytes(n: usize) -> String {
const KB: usize = 1024;
const MB: usize = KB * 1024;
if n < KB {
format!("{n}B")
} else if n < MB {
format!("{}KB", n / KB)
} else {
format!("{:.1}MB", n as f64 / MB as f64)
}
}
pub fn num(n: usize) -> String {
let s = n.to_string();
let mut out = String::with_capacity(s.len() + s.len() / 3);
for (i, c) in s.chars().enumerate() {
if i > 0 && (s.len() - i).is_multiple_of(3) {
out.push(',');
}
out.push(c);
}
out
}
pub fn pct(held: usize, total: usize) -> String {
if total == 0 {
return "—".into();
}
let p = held as f64 / total as f64 * 100.0;
if p >= 10.0 {
format!("{p:.0}%")
} else {
format!("{p:.1}%")
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct ProcessStats {
pub uptime_ms: u64,
pub cpu_ms: u64,
pub peak_rss_bytes: u64,
}
static ANY_TAP_ENABLED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
pub fn any_tap_enabled() -> bool {
ANY_TAP_ENABLED.load(std::sync::atomic::Ordering::Relaxed)
}
static PROCESS_START: LazyLock<Instant> = LazyLock::new(Instant::now);
pub fn process_stats() -> ProcessStats {
let uptime_ms = PROCESS_START.elapsed().as_millis() as u64;
#[cfg(unix)]
{
let mut usage: libc::rusage = unsafe { std::mem::zeroed() };
if unsafe { libc::getrusage(libc::RUSAGE_SELF, &mut usage) } == 0 {
let tv_ms = |tv: libc::timeval| tv.tv_sec as u64 * 1000 + tv.tv_usec as u64 / 1000;
#[cfg(target_os = "macos")]
let peak = usage.ru_maxrss as u64;
#[cfg(not(target_os = "macos"))]
let peak = usage.ru_maxrss as u64 * 1024;
return ProcessStats {
uptime_ms,
cpu_ms: tv_ms(usage.ru_utime) + tv_ms(usage.ru_stime),
peak_rss_bytes: peak,
};
}
}
ProcessStats {
uptime_ms,
..Default::default()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tap_is_off_by_default_and_carries_the_datasource_name() {
let off = DebugTap::default();
assert!(!off.enabled());
assert_eq!(off.ds(), "");
let on = DebugTap::for_datasource("librarian");
assert!(on.enabled());
assert_eq!(on.ds(), "librarian");
}
#[test]
fn process_stats_reports_nonzero_cpu_and_rss() {
let mut x = 0u64;
for i in 0..5_000_000u64 {
x = x.wrapping_add(i);
}
std::hint::black_box(x);
let s = process_stats();
#[cfg(unix)]
{
assert!(
s.peak_rss_bytes > 0,
"peak RSS should be measurable on unix"
);
assert!(s.cpu_ms > 0, "cpu time should be nonzero after busy loop");
}
let _ = s.uptime_ms; }
}