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. 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 { 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 { 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