Add support for generating perf maps for simple perf profiling (#6030)
* Add support for generating perf maps for simple perf profiling * add missing enum entry in C code * bugfix: use hexa when printing the code region's length too (thanks bjorn3!) * sanitize file name + use bufwriter * introduce --profile CLI flag for wasmtime * Update doc and doc comments for new --profile option * remove redundant FromStr import * Apply review feedback: make_line receives a Write impl, report errors * fix tests? * better docs
This commit is contained in:
@@ -11,6 +11,16 @@ cfg_if::cfg_if! {
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_os = "linux")] {
|
||||
#[path = "profiling/perfmap_linux.rs"]
|
||||
mod perfmap;
|
||||
} else {
|
||||
#[path = "profiling/perfmap_disabled.rs"]
|
||||
mod perfmap;
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
// Note: VTune support is disabled on windows mingw because the ittapi crate doesn't compile
|
||||
// there; see also https://github.com/bytecodealliance/wasmtime/pull/4003 for rationale.
|
||||
@@ -24,6 +34,7 @@ cfg_if::cfg_if! {
|
||||
}
|
||||
|
||||
pub use jitdump::JitDumpAgent;
|
||||
pub use perfmap::PerfMapAgent;
|
||||
pub use vtune::VTuneAgent;
|
||||
|
||||
/// Common interface for profiling tools.
|
||||
|
||||
@@ -8,7 +8,7 @@ pub struct JitDumpAgent {
|
||||
}
|
||||
|
||||
impl JitDumpAgent {
|
||||
/// Intialize a JitDumpAgent and write out the header
|
||||
/// Intialize a dummy JitDumpAgent that will fail upon instantiation.
|
||||
pub fn new() -> Result<Self> {
|
||||
if cfg!(feature = "jitdump") {
|
||||
bail!("jitdump is not supported on this platform");
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//!
|
||||
//! Usage Example:
|
||||
//! Record
|
||||
//! sudo perf record -k 1 -e instructions:u target/debug/wasmtime -g --jitdump test.wasm
|
||||
//! sudo perf record -k 1 -e instructions:u target/debug/wasmtime -g --profile=jitdump test.wasm
|
||||
//! Combine
|
||||
//! sudo perf inject -v -j -i perf.data -o perf.jit.data
|
||||
//! Report
|
||||
|
||||
28
crates/jit/src/profiling/perfmap_disabled.rs
Normal file
28
crates/jit/src/profiling/perfmap_disabled.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use crate::{CompiledModule, ProfilingAgent};
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
/// Interface for driving the creation of jitdump files
|
||||
#[derive(Debug)]
|
||||
pub struct PerfMapAgent {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl PerfMapAgent {
|
||||
/// Intialize a dummy PerfMapAgent that will fail upon instantiation.
|
||||
pub fn new() -> Result<Self> {
|
||||
bail!("perfmap support not supported on this platform");
|
||||
}
|
||||
}
|
||||
|
||||
impl ProfilingAgent for PerfMapAgent {
|
||||
fn module_load(&self, _module: &CompiledModule, _dbg_image: Option<&[u8]>) {}
|
||||
fn load_single_trampoline(
|
||||
&self,
|
||||
_name: &str,
|
||||
_addr: *const u8,
|
||||
_size: usize,
|
||||
__pid: u32,
|
||||
_tid: u32,
|
||||
) {
|
||||
}
|
||||
}
|
||||
85
crates/jit/src/profiling/perfmap_linux.rs
Normal file
85
crates/jit/src/profiling/perfmap_linux.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
use crate::{CompiledModule, ProfilingAgent};
|
||||
use anyhow::Result;
|
||||
use std::io::{self, BufWriter, Write};
|
||||
use std::process;
|
||||
use std::{fs::File, sync::Mutex};
|
||||
use wasmtime_environ::EntityRef as _;
|
||||
|
||||
/// Process-wide perf map file. Perf only reads a unique file per process.
|
||||
static PERFMAP_FILE: Mutex<Option<File>> = Mutex::new(None);
|
||||
|
||||
/// Interface for driving the creation of jitdump files
|
||||
pub struct PerfMapAgent;
|
||||
|
||||
impl PerfMapAgent {
|
||||
/// Intialize a JitDumpAgent and write out the header.
|
||||
pub fn new() -> Result<Self> {
|
||||
let mut file = PERFMAP_FILE.lock().unwrap();
|
||||
if file.is_none() {
|
||||
let filename = format!("/tmp/perf-{}.map", process::id());
|
||||
*file = Some(File::create(filename)?);
|
||||
}
|
||||
Ok(PerfMapAgent)
|
||||
}
|
||||
|
||||
fn make_line(
|
||||
writer: &mut dyn Write,
|
||||
name: &str,
|
||||
addr: *const u8,
|
||||
len: usize,
|
||||
) -> io::Result<()> {
|
||||
// Format is documented here: https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/jit-interface.txt
|
||||
// Try our best to sanitize the name, since wasm allows for any utf8 string in there.
|
||||
let sanitized_name = name.replace('\n', "_").replace('\r', "_");
|
||||
write!(writer, "{:x} {:x} {}\n", addr as usize, len, sanitized_name)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ProfilingAgent for PerfMapAgent {
|
||||
/// Sent when a method is compiled and loaded into memory by the VM.
|
||||
fn module_load(&self, module: &CompiledModule, _dbg_image: Option<&[u8]>) {
|
||||
let mut file = PERFMAP_FILE.lock().unwrap();
|
||||
let file = file.as_mut().unwrap();
|
||||
let mut file = BufWriter::new(file);
|
||||
|
||||
for (idx, func) in module.finished_functions() {
|
||||
let addr = func.as_ptr();
|
||||
let len = func.len();
|
||||
let name = super::debug_name(module, idx);
|
||||
if let Err(err) = Self::make_line(&mut file, &name, addr, len) {
|
||||
eprintln!("Error when writing function info to the perf map file: {err}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Note: these are the trampolines into exported functions.
|
||||
for (idx, func, len) in module.trampolines() {
|
||||
let (addr, len) = (func as usize as *const u8, len);
|
||||
let name = format!("wasm::trampoline[{}]", idx.index());
|
||||
if let Err(err) = Self::make_line(&mut file, &name, addr, len) {
|
||||
eprintln!("Error when writing export trampoline info to the perf map file: {err}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(err) = file.flush() {
|
||||
eprintln!("Error when flushing the perf map file buffer: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
fn load_single_trampoline(
|
||||
&self,
|
||||
name: &str,
|
||||
addr: *const u8,
|
||||
size: usize,
|
||||
_pid: u32,
|
||||
_tid: u32,
|
||||
) {
|
||||
let mut file = PERFMAP_FILE.lock().unwrap();
|
||||
let file = file.as_mut().unwrap();
|
||||
if let Err(err) = Self::make_line(file, name, addr, size) {
|
||||
eprintln!("Error when writing import trampoline info to the perf map file: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
//! Adds support for profiling JIT-ed code using VTune. By default, VTune
|
||||
//! support is built in to Wasmtime (configure with the `vtune` feature flag).
|
||||
//! To enable it at runtime, use the `--vtune` CLI flag.
|
||||
//! To enable it at runtime, use the `--profile=vtune` CLI flag.
|
||||
//!
|
||||
//! ### Profile
|
||||
//!
|
||||
//! ```ignore
|
||||
//! vtune -run-pass-thru=--no-altstack -v -collect hotspots target/debug/wasmtime --vtune test.wasm
|
||||
//! vtune -run-pass-thru=--no-altstack -v -collect hotspots target/debug/wasmtime --profile=vtune test.wasm
|
||||
//! ```
|
||||
//!
|
||||
//! Note: `vtune` is a command-line tool for VTune which must [be
|
||||
|
||||
@@ -8,7 +8,7 @@ pub struct VTuneAgent {
|
||||
}
|
||||
|
||||
impl VTuneAgent {
|
||||
/// Intialize a VTuneAgent and write out the header
|
||||
/// Intialize a dummy VTuneAgent that will fail upon instantiation.
|
||||
pub fn new() -> Result<Self> {
|
||||
if cfg!(feature = "vtune") {
|
||||
bail!("VTune is not supported on this platform.");
|
||||
|
||||
Reference in New Issue
Block a user