wasmtime_jit/profiling/
perfmap_linux.rs

1use crate::{CompiledModule, ProfilingAgent};
2use anyhow::Result;
3use std::io::{self, BufWriter, Write};
4use std::process;
5use std::{fs::File, sync::Mutex};
6use wasmtime_environ::EntityRef as _;
7
8/// Process-wide perf map file. Perf only reads a unique file per process.
9static PERFMAP_FILE: Mutex<Option<File>> = Mutex::new(None);
10
11/// Interface for driving the creation of jitdump files
12pub struct PerfMapAgent;
13
14impl PerfMapAgent {
15    /// Intialize a JitDumpAgent and write out the header.
16    pub fn new() -> Result<Self> {
17        let mut file = PERFMAP_FILE.lock().unwrap();
18        if file.is_none() {
19            let filename = format!("/tmp/perf-{}.map", process::id());
20            *file = Some(File::create(filename)?);
21        }
22        Ok(PerfMapAgent)
23    }
24
25    fn make_line(
26        writer: &mut dyn Write,
27        name: &str,
28        addr: *const u8,
29        len: usize,
30    ) -> io::Result<()> {
31        // Format is documented here: https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/jit-interface.txt
32        // Try our best to sanitize the name, since wasm allows for any utf8 string in there.
33        let sanitized_name = name.replace('\n', "_").replace('\r', "_");
34        write!(writer, "{:x} {:x} {}\n", addr as usize, len, sanitized_name)?;
35        Ok(())
36    }
37}
38
39impl ProfilingAgent for PerfMapAgent {
40    /// Sent when a method is compiled and loaded into memory by the VM.
41    fn module_load(&self, module: &CompiledModule, _dbg_image: Option<&[u8]>) {
42        let mut file = PERFMAP_FILE.lock().unwrap();
43        let file = file.as_mut().unwrap();
44        let mut file = BufWriter::new(file);
45
46        for (idx, func) in module.finished_functions() {
47            let addr = func.as_ptr();
48            let len = func.len();
49            let name = super::debug_name(module, idx);
50            if let Err(err) = Self::make_line(&mut file, &name, addr, len) {
51                eprintln!("Error when writing function info to the perf map file: {err}");
52                return;
53            }
54        }
55
56        // Note: these are the trampolines into exported functions.
57        for (idx, func, len) in module.trampolines() {
58            let (addr, len) = (func as usize as *const u8, len);
59            let name = format!("wasm::trampoline[{}]", idx.index());
60            if let Err(err) = Self::make_line(&mut file, &name, addr, len) {
61                eprintln!("Error when writing export trampoline info to the perf map file: {err}");
62                return;
63            }
64        }
65
66        if let Err(err) = file.flush() {
67            eprintln!("Error when flushing the perf map file buffer: {err}");
68        }
69    }
70
71    fn load_single_trampoline(
72        &self,
73        name: &str,
74        addr: *const u8,
75        size: usize,
76        _pid: u32,
77        _tid: u32,
78    ) {
79        let mut file = PERFMAP_FILE.lock().unwrap();
80        let file = file.as_mut().unwrap();
81        if let Err(err) = Self::make_line(file, name, addr, size) {
82            eprintln!("Error when writing import trampoline info to the perf map file: {err}");
83        }
84    }
85}