1 use crate::prelude::*;
2 use crate::profiling_agent::ProfilingAgent;
3 use std::fs::OpenOptions;
4 use std::io::{self, Write};
5 use std::process;
6 use std::{fs::File, sync::Mutex};
7 
8 /// Process-wide perf map file. Perf only reads a unique file per process.
9 static PERFMAP_FILE: Mutex<Option<File>> = Mutex::new(None);
10 
11 /// Interface for driving the creation of jitdump files
12 struct PerfMapAgent;
13 
14 /// Initialize a JitDumpAgent and write out the header.
new() -> Result<Box<dyn ProfilingAgent>>15 pub 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 
33 impl PerfMapAgent {
make_line(writer: &mut File, name: &str, code: &[u8]) -> io::Result<()>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 
55 impl ProfilingAgent for PerfMapAgent {
register_function(&self, name: &str, code: &[u8])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 }
64