wasmtime/profiling_agent/
perfmap.rs

1use crate::prelude::*;
2use crate::profiling_agent::ProfilingAgent;
3use std::fs::OpenOptions;
4use std::io::{self, Write};
5use std::process;
6use std::{fs::File, sync::Mutex};
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
12struct PerfMapAgent;
13
14/// Initialize a JitDumpAgent and write out the header.
15pub fn new() -> Result<Box<dyn ProfilingAgent>> {
16    let mut file = PERFMAP_FILE.lock().unwrap();
17    if file.is_none() {
18        let filename = format!("/tmp/perf-{}.map", process::id());
19
20        // Open the file specifically in append mode to handle the case where
21        // multiple engines in the same process are all writing to this file.
22        *file = Some(
23            OpenOptions::new()
24                .append(true)
25                .write(true)
26                .create(true)
27                .open(&filename)?,
28        );
29    }
30    Ok(Box::new(PerfMapAgent))
31}
32
33impl PerfMapAgent {
34    fn make_line(writer: &mut File, name: &str, code: &[u8]) -> io::Result<()> {
35        // Format is documented here: https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/jit-interface.txt
36        // Try our best to sanitize the name, since wasm allows for any utf8 string in there.
37        let sanitized_name = name.replace('\n', "_").replace('\r', "_");
38        let line = format!("{:p} {:x} {sanitized_name}\n", code.as_ptr(), code.len());
39
40        // To handle multiple concurrent engines in the same process writing to
41        // this file an attempt is made to issue a single `write` syscall with
42        // all of the contents. This would mean, though, that partial writes
43        // would be an error. In lieu of returning an error the `write_all`
44        // helper is used instead which may result in a corrupt file if there
45        // are other engines writing to the file at the same time.
46        //
47        // For more discussion of the tradeoffs here see the `perf_jitdump.rs`
48        // file which has to deal with the same problem.
49        writer.write_all(line.as_bytes())?;
50
51        Ok(())
52    }
53}
54
55impl ProfilingAgent for PerfMapAgent {
56    fn register_function(&self, name: &str, code: &[u8]) {
57        let mut file = PERFMAP_FILE.lock().unwrap();
58        let file = file.as_mut().unwrap();
59        if let Err(err) = Self::make_line(file, name, code) {
60            eprintln!("Error when writing import trampoline info to the perf map file: {err}");
61        }
62    }
63}