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}