use std::cell::RefCell;
use std::io::Write;
use std::sync::OnceLock;
use crate::vm::OpCode;
const OP_COUNT: usize = 86;
const OP_NAMES: [&str; OP_COUNT] = [
"MOVE",
"LOADI",
"LOADF",
"LOADK",
"LOADKX",
"LOADFALSE",
"LFALSESKIP",
"LOADTRUE",
"LOADNIL",
"GETUPVAL",
"SETUPVAL",
"GETTABUP",
"GETTABLE",
"GETI",
"GETFIELD",
"SETTABUP",
"SETTABLE",
"SETI",
"SETFIELD",
"NEWTABLE",
"SELF",
"ADDI",
"ADDK",
"SUBK",
"MULK",
"MODK",
"POWK",
"DIVK",
"IDIVK",
"BANDK",
"BORK",
"BXORK",
"SHRI",
"SHLI",
"ADD",
"SUB",
"MUL",
"MOD",
"POW",
"DIV",
"IDIV",
"BAND",
"BOR",
"BXOR",
"SHL",
"SHR",
"MMBIN",
"MMBINI",
"MMBINK",
"UNM",
"BNOT",
"NOT",
"LEN",
"CONCAT",
"CLOSE",
"TBC",
"JMP",
"EQ",
"LT",
"LE",
"EQK",
"EQI",
"LTI",
"LEI",
"GTI",
"GEI",
"TEST",
"TESTSET",
"CALL",
"TAILCALL",
"RETURN",
"RETURN0",
"RETURN1",
"FORLOOP",
"FORPREP",
"TFORPREP",
"TFORCALL",
"TFORLOOP",
"SETLIST",
"CLOSURE",
"VARARG",
"VARARGPREP",
"EXTRAARG",
"ERRNNIL",
"VARARGPACK",
"GETVARG",
];
thread_local! {
static COUNTS: RefCell<[u64; OP_COUNT]> = RefCell::new([0; OP_COUNT]);
}
fn enabled() -> bool {
static ENABLED: OnceLock<bool> = OnceLock::new();
*ENABLED.get_or_init(|| std::env::var_os("LUA_RS_OPCODE_PROFILE").is_some())
}
#[inline(always)]
pub fn record(op: OpCode) {
if !enabled() {
return;
}
COUNTS.with(|counts| {
counts.borrow_mut()[op as usize] += 1;
});
}
pub fn snapshot() -> [u64; OP_COUNT] {
COUNTS.with(|counts| *counts.borrow())
}
pub fn reset() {
COUNTS.with(|counts| counts.borrow_mut().fill(0));
}
pub fn write_tsv(mut writer: impl Write) -> std::io::Result<()> {
let counts = snapshot();
let total: u64 = counts.iter().sum();
let mut rows: Vec<(usize, u64)> = counts
.iter()
.copied()
.enumerate()
.filter(|(_, count)| *count != 0)
.collect();
rows.sort_by(|a, b| b.1.cmp(&a.1).then_with(|| a.0.cmp(&b.0)));
writeln!(writer, "opcode\tcount\tpct")?;
for (idx, count) in rows {
let pct = if total == 0 {
0.0
} else {
100.0 * count as f64 / total as f64
};
writeln!(writer, "{}\t{}\t{:.2}", OP_NAMES[idx], count, pct)?;
}
writeln!(writer, "TOTAL\t{}\t100.00", total)
}
pub fn write_report_from_env() -> std::io::Result<()> {
let Some(path) = std::env::var_os("LUA_RS_OPCODE_PROFILE") else {
return Ok(());
};
if path == "-" {
return write_tsv(std::io::stderr().lock());
}
let file = std::fs::File::create(path)?;
write_tsv(file)
}