Skip to main content

logicaffeine_compile/vm/
perf_map.rs

1//! `LOGOS_PERF_MAP` — name the JIT's machine code for `perf`.
2//!
3//! Linux `perf` symbolizes a JIT's anonymous executable memory through `/tmp/perf-<pid>.map`: one
4//! `START SIZE name` line per code region, addresses and sizes in hex. When the env var is set (and
5//! non-empty, non-`"0"`), the VM appends a line each time native code is installed for a function or
6//! a loop region, named after the LOGOS source (`logos::fib [jit]`, `logos::main::loop@42 [jit]`), so a
7//! profile of `largo run --interpret` reads in the program's own terms instead of `[unknown]`.
8//!
9//! `LOGOS_JIT_CODE=<dir>` keeps the machine code itself: each extent's bytes go to `<dir>/<n>.bin`, listed in
10//! `<dir>/index.tsv` as `n START SIZE name` (hex START and SIZE, the map's names), for a disassembler.
11
12use std::io::Write;
13use std::sync::atomic::{AtomicU8, Ordering};
14use std::sync::Mutex;
15
16/// Tri-state cache of the env check (0 = unread, 1 = on, 2 = off).
17static ENABLED: AtomicU8 = AtomicU8::new(0);
18
19static FILE: Mutex<Option<std::fs::File>> = Mutex::new(None);
20
21/// Whether `LOGOS_PERF_MAP` requests the map (set, non-empty, not `"0"`).
22pub 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
36/// The map file `perf` reads for this process.
37pub fn map_path() -> std::path::PathBuf {
38    std::path::PathBuf::from(format!("/tmp/perf-{}.map", std::process::id()))
39}
40
41/// The symbol a natively compiled function is named under.
42pub fn function_symbol(name: &str) -> String {
43    format!(
44        "logos::{} [jit]",
45        if name.is_empty() { "<anonymous>" } else { name }
46    )
47}
48
49/// The symbol a natively compiled loop region is named under: its enclosing function and the pc of
50/// its loop head.
51pub 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
62/// One map line. Pure — the unit under test.
63pub fn format_line(start: usize, len: usize, symbol: &str) -> String {
64    format!("{start:x} {len:x} {symbol}\n")
65}
66
67/// Append one line per code extent iff the map is enabled. A write failure disables nothing and
68/// never reaches the program: the map is observability only.
69pub 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
95/// The directory `LOGOS_JIT_CODE` names, if any.
96fn 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
106/// Whether a caller has anything to record: the perf map or the code dump is on.
107pub fn observed() -> bool {
108    enabled() || code_dir().is_some()
109}
110
111/// Record installed native code under `symbol`: one perf-map line per extent, and its bytes when `LOGOS_JIT_CODE`
112/// names a directory. Each extent must be sealed code the native tier owns, readable for its whole length.
113pub 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        // SAFETY: the caller hands over extents of installed, sealed native code (read + execute), which the native
131        // tier keeps mapped for the life of the process.
132        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;