use anyhow::Result;
use std::collections::HashSet;
use std::fs::File;
use std::io::{BufWriter, Write};
use std::sync::{Mutex, OnceLock};
struct RuntimeConfig {
trace_reads: HashSet<String>,
trace_writer: Option<Mutex<BufWriter<File>>>,
perf_writer: Option<Mutex<BufWriter<File>>>,
}
static RUNTIME_CONFIG: OnceLock<RuntimeConfig> = OnceLock::new();
thread_local! {
static TRACE_HG2_ACTIVE: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
}
pub fn init_runtime(
trace_reads: Option<&str>,
trace_log_path: Option<&str>,
perf_path: Option<&str>,
) -> Result<()> {
let reads = trace_reads
.map(|raw| {
raw.split(',')
.filter_map(|token| {
let t = token.trim();
(!t.is_empty()).then(|| t.to_string())
})
.collect::<HashSet<_>>()
})
.unwrap_or_default();
let trace_writer = match (trace_log_path, reads.is_empty()) {
(Some(path), false) => Some(Mutex::new(BufWriter::new(File::create(path)?))),
_ => None,
};
let perf_writer = match perf_path {
Some(path) => Some(Mutex::new(BufWriter::new(File::create(path)?))),
None => None,
};
let _ = RUNTIME_CONFIG.set(RuntimeConfig {
trace_reads: reads,
trace_writer,
perf_writer,
});
Ok(())
}
#[inline]
pub fn should_trace_read(read_id: &str) -> bool {
if let Some(config) = RUNTIME_CONFIG.get() {
if config.trace_reads.contains(read_id) {
return true;
}
}
if let Ok(raw) = std::env::var("CIRI_TRACE_READS") {
for token in raw.split(',') {
let t = token.trim();
if !t.is_empty() && t == read_id {
return true;
}
}
}
false
}
#[inline]
pub fn cli_trace_enabled() -> bool {
RUNTIME_CONFIG
.get()
.is_some_and(|config| !config.trace_reads.is_empty())
}
#[inline]
pub fn with_trace_hg2_scope<T>(enabled: bool, f: impl FnOnce() -> T) -> T {
let prev = TRACE_HG2_ACTIVE.with(|flag| {
let prev = flag.get();
flag.set(enabled);
prev
});
let out = f();
TRACE_HG2_ACTIVE.with(|flag| flag.set(prev));
out
}
#[inline]
pub fn trace_hg2_enabled() -> bool {
TRACE_HG2_ACTIVE.with(|flag| flag.get())
|| matches!(std::env::var("CIRI_TRACE_HG2"), Ok(v) if !v.is_empty() && v != "0")
}
#[inline]
pub fn scan1_profile_enabled() -> bool {
RUNTIME_CONFIG
.get()
.is_some_and(|config| config.perf_writer.is_some())
|| matches!(std::env::var("CIRI_PROFILE_SCAN1"), Ok(v) if !v.is_empty() && v != "0")
}
#[inline]
pub fn scan2_profile_enabled() -> bool {
RUNTIME_CONFIG
.get()
.is_some_and(|config| config.perf_writer.is_some())
|| matches!(std::env::var("CIRI_PROFILE_SCAN2"), Ok(v) if !v.is_empty() && v != "0")
}
pub fn emit_trace_line(line: &str) {
if let Some(config) = RUNTIME_CONFIG.get() {
if let Some(writer) = &config.trace_writer {
let mut writer = writer.lock().unwrap();
let _ = writeln!(writer, "{}", line);
let _ = writer.flush();
return;
}
}
eprintln!("{}", line);
}
pub fn emit_perf_line(line: &str) {
if let Some(config) = RUNTIME_CONFIG.get() {
if let Some(writer) = &config.perf_writer {
let mut writer = writer.lock().unwrap();
let _ = writeln!(writer, "{}", line);
let _ = writer.flush();
return;
}
}
eprintln!("{}", line);
}