1use anyhow::Result;
11use std::collections::HashSet;
12use std::fs::File;
13use std::io::{BufWriter, Write};
14use std::sync::{Mutex, OnceLock};
15
16struct RuntimeConfig {
18 trace_reads: HashSet<String>,
19 trace_writer: Option<Mutex<BufWriter<File>>>,
20 perf_writer: Option<Mutex<BufWriter<File>>>,
21}
22
23static RUNTIME_CONFIG: OnceLock<RuntimeConfig> = OnceLock::new();
24
25thread_local! {
26 static TRACE_HG2_ACTIVE: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
31}
32
33pub fn init_runtime(
38 trace_reads: Option<&str>,
39 trace_log_path: Option<&str>,
40 perf_path: Option<&str>,
41) -> Result<()> {
42 let reads = trace_reads
43 .map(|raw| {
44 raw.split(',')
45 .filter_map(|token| {
46 let t = token.trim();
47 (!t.is_empty()).then(|| t.to_string())
48 })
49 .collect::<HashSet<_>>()
50 })
51 .unwrap_or_default();
52 let trace_writer = match (trace_log_path, reads.is_empty()) {
53 (Some(path), false) => Some(Mutex::new(BufWriter::new(File::create(path)?))),
54 _ => None,
55 };
56 let perf_writer = match perf_path {
57 Some(path) => Some(Mutex::new(BufWriter::new(File::create(path)?))),
58 None => None,
59 };
60 let _ = RUNTIME_CONFIG.set(RuntimeConfig {
61 trace_reads: reads,
62 trace_writer,
63 perf_writer,
64 });
65 Ok(())
66}
67
68#[inline]
70pub fn should_trace_read(read_id: &str) -> bool {
71 if let Some(config) = RUNTIME_CONFIG.get() {
72 if config.trace_reads.contains(read_id) {
73 return true;
74 }
75 }
76 if let Ok(raw) = std::env::var("CIRI_TRACE_READS") {
77 for token in raw.split(',') {
78 let t = token.trim();
79 if !t.is_empty() && t == read_id {
80 return true;
81 }
82 }
83 }
84 false
85}
86
87#[inline]
89pub fn cli_trace_enabled() -> bool {
90 RUNTIME_CONFIG
91 .get()
92 .is_some_and(|config| !config.trace_reads.is_empty())
93}
94
95#[inline]
97pub fn with_trace_hg2_scope<T>(enabled: bool, f: impl FnOnce() -> T) -> T {
98 let prev = TRACE_HG2_ACTIVE.with(|flag| {
99 let prev = flag.get();
100 flag.set(enabled);
101 prev
102 });
103 let out = f();
104 TRACE_HG2_ACTIVE.with(|flag| flag.set(prev));
105 out
106}
107
108#[inline]
110pub fn trace_hg2_enabled() -> bool {
111 TRACE_HG2_ACTIVE.with(|flag| flag.get())
112 || matches!(std::env::var("CIRI_TRACE_HG2"), Ok(v) if !v.is_empty() && v != "0")
113}
114
115#[inline]
117pub fn scan1_profile_enabled() -> bool {
118 RUNTIME_CONFIG
119 .get()
120 .is_some_and(|config| config.perf_writer.is_some())
121 || matches!(std::env::var("CIRI_PROFILE_SCAN1"), Ok(v) if !v.is_empty() && v != "0")
122}
123
124#[inline]
126pub fn scan2_profile_enabled() -> bool {
127 RUNTIME_CONFIG
128 .get()
129 .is_some_and(|config| config.perf_writer.is_some())
130 || matches!(std::env::var("CIRI_PROFILE_SCAN2"), Ok(v) if !v.is_empty() && v != "0")
131}
132
133pub fn emit_trace_line(line: &str) {
135 if let Some(config) = RUNTIME_CONFIG.get() {
136 if let Some(writer) = &config.trace_writer {
137 let mut writer = writer.lock().unwrap();
138 let _ = writeln!(writer, "{}", line);
139 let _ = writer.flush();
140 return;
141 }
142 }
143 eprintln!("{}", line);
144}
145
146pub fn emit_perf_line(line: &str) {
148 if let Some(config) = RUNTIME_CONFIG.get() {
149 if let Some(writer) = &config.perf_writer {
150 let mut writer = writer.lock().unwrap();
151 let _ = writeln!(writer, "{}", line);
152 let _ = writer.flush();
153 return;
154 }
155 }
156 eprintln!("{}", line);
157}