logicaffeine_compile/vm/
perf_map.rs1use std::io::Write;
13use std::sync::atomic::{AtomicU8, Ordering};
14use std::sync::Mutex;
15
16static ENABLED: AtomicU8 = AtomicU8::new(0);
18
19static FILE: Mutex<Option<std::fs::File>> = Mutex::new(None);
20
21pub fn enabled() -> bool {
23 match ENABLED.load(Ordering::Relaxed) {
24 1 => true,
25 2 => false,
26 _ => {
27 let on = std::env::var("LOGOS_PERF_MAP")
28 .map(|v| !v.is_empty() && v != "0")
29 .unwrap_or(false);
30 ENABLED.store(if on { 1 } else { 2 }, Ordering::Relaxed);
31 on
32 }
33 }
34}
35
36pub fn map_path() -> std::path::PathBuf {
38 std::path::PathBuf::from(format!("/tmp/perf-{}.map", std::process::id()))
39}
40
41pub fn function_symbol(name: &str) -> String {
43 format!(
44 "logos::{} [jit]",
45 if name.is_empty() { "<anonymous>" } else { name }
46 )
47}
48
49pub fn region_symbol(enclosing: &str, head_pc: usize) -> String {
52 format!(
53 "logos::{}::loop@{head_pc} [jit]",
54 if enclosing.is_empty() {
55 "main"
56 } else {
57 enclosing
58 }
59 )
60}
61
62pub fn format_line(start: usize, len: usize, symbol: &str) -> String {
64 format!("{start:x} {len:x} {symbol}\n")
65}
66
67pub fn record(extents: &[(usize, usize)], symbol: &str) {
70 if !enabled() || extents.is_empty() {
71 return;
72 }
73 let Ok(mut guard) = FILE.lock() else { return };
74 if guard.is_none() {
75 *guard = std::fs::OpenOptions::new()
76 .create(true)
77 .append(true)
78 .open(map_path())
79 .ok();
80 }
81 if let Some(file) = guard.as_mut() {
82 let text: String = extents
83 .iter()
84 .map(|&(start, len)| format_line(start, len, symbol))
85 .collect();
86 let _ = file.write_all(text.as_bytes());
87 let _ = file.flush();
88 }
89}
90
91static CODE_DIR: std::sync::OnceLock<Option<std::path::PathBuf>> = std::sync::OnceLock::new();
92
93static CODE_INDEX: Mutex<usize> = Mutex::new(0);
94
95fn code_dir() -> Option<&'static std::path::Path> {
97 CODE_DIR
98 .get_or_init(|| {
99 std::env::var_os("LOGOS_JIT_CODE")
100 .filter(|v| !v.is_empty())
101 .map(std::path::PathBuf::from)
102 })
103 .as_deref()
104}
105
106pub fn observed() -> bool {
108 enabled() || code_dir().is_some()
109}
110
111pub fn record_code(extents: &[(usize, usize)], symbol: &str) {
114 record(extents, symbol);
115 let Some(dir) = code_dir() else { return };
116 let Ok(mut next) = CODE_INDEX.lock() else {
117 return;
118 };
119 if *next == 0 && std::fs::create_dir_all(dir).is_err() {
120 return;
121 }
122 let Ok(mut index) = std::fs::OpenOptions::new()
123 .create(true)
124 .append(true)
125 .open(dir.join("index.tsv"))
126 else {
127 return;
128 };
129 for &(start, len) in extents {
130 let bytes = unsafe { std::slice::from_raw_parts(start as *const u8, len) };
133 if std::fs::write(dir.join(format!("{}.bin", *next)), bytes).is_ok() {
134 let _ =
135 index.write_all(format!("{}\t{start:x}\t{len:x}\t{symbol}\n", *next).as_bytes());
136 }
137 *next += 1;
138 }
139}
140
141#[cfg(test)]
142#[path = "../../../logicaffeine_tests/unit/logicaffeine_compile/vm/perf_map/tests.rs"]
143mod tests;