use crate::{CompiledModule, ProfilingAgent};
use anyhow::Result;
use std::io::{self, BufWriter, Write};
use std::process;
use std::{fs::File, sync::Mutex};
use wasmtime_environ::EntityRef as _;
static PERFMAP_FILE: Mutex<Option<File>> = Mutex::new(None);
pub struct PerfMapAgent;
impl PerfMapAgent {
pub fn new() -> Result<Self> {
let mut file = PERFMAP_FILE.lock().unwrap();
if file.is_none() {
let filename = format!("/tmp/perf-{}.map", process::id());
*file = Some(File::create(filename)?);
}
Ok(PerfMapAgent)
}
fn make_line(
writer: &mut dyn Write,
name: &str,
addr: *const u8,
len: usize,
) -> io::Result<()> {
let sanitized_name = name.replace('\n', "_").replace('\r', "_");
write!(writer, "{:x} {:x} {}\n", addr as usize, len, sanitized_name)?;
Ok(())
}
}
impl ProfilingAgent for PerfMapAgent {
fn module_load(&self, module: &CompiledModule, _dbg_image: Option<&[u8]>) {
let mut file = PERFMAP_FILE.lock().unwrap();
let file = file.as_mut().unwrap();
let mut file = BufWriter::new(file);
for (idx, func) in module.finished_functions() {
let addr = func.as_ptr();
let len = func.len();
let name = super::debug_name(module, idx);
if let Err(err) = Self::make_line(&mut file, &name, addr, len) {
eprintln!("Error when writing function info to the perf map file: {err}");
return;
}
}
for (idx, func, len) in module.trampolines() {
let (addr, len) = (func as usize as *const u8, len);
let name = format!("wasm::trampoline[{}]", idx.index());
if let Err(err) = Self::make_line(&mut file, &name, addr, len) {
eprintln!("Error when writing export trampoline info to the perf map file: {err}");
return;
}
}
if let Err(err) = file.flush() {
eprintln!("Error when flushing the perf map file buffer: {err}");
}
}
fn load_single_trampoline(
&self,
name: &str,
addr: *const u8,
size: usize,
_pid: u32,
_tid: u32,
) {
let mut file = PERFMAP_FILE.lock().unwrap();
let file = file.as_mut().unwrap();
if let Err(err) = Self::make_line(file, name, addr, size) {
eprintln!("Error when writing import trampoline info to the perf map file: {err}");
}
}
}