-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor Wasmtime's profiling support (#6361)
* Reorganize profiling-related code This commit is a bit of reorganization around profiling-related code in Wasmtime with the aim of eventually simplifying it a bit more. The changes here are: * All exposed agents are removed and instead only constructor functions returning trait objects are now exposed. * All `*_disabled.rs` files and modules are removed in favor of a function that returns a result (less trait impls). * All `*_linux.rs` files where renamed to just `*.rs`. (less files in play) * The `pid` and `tid` arguments were removed as they were only used by the jitdump profiler and now that manages it internally. * Registering an entire ELF image is now part of the trait rather than buried within the trampoline code in Wasmtime. * Remove DWARF support from jitdump In general Wasmtime's support for DWARF is not great so this is rarely used and at least in my experience this hasn't been necessary to get good information from perf. This commit removes the processing here which while probably useful is probably not necessary and otherwise makes the jidump agent a bit of an odd-one-out relative among the other agents. * Remove now no-longer-needed `dbg_image` argument * Only grab the jitdump lock once-per-module Refactor slightly to account for this. * Fill in the `tid` argument to jitump This has been the same as `self.pid` for quite some time but with `rustix` it's pretty easy to get access to the current thread id. * Merge module/trampoline registration for profilers Add a second argument to registration of an entire module for custom names to get functions named correctly, and otherwise profilers now only need to look at individual functions. * Fixup vtune support * Delete no-longer-needed accessors Closes #6328 * Remove unused import * Fix a portability issue with u32-vs-i32
- Loading branch information
1 parent
cfb506b
commit 4c38a18
Showing
16 changed files
with
224 additions
and
845 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,74 +1,108 @@ | ||
use crate::{demangling::demangle_function_name_or_index, CompiledModule}; | ||
use wasmtime_environ::{DefinedFuncIndex, EntityRef}; | ||
#![allow(missing_docs)] | ||
|
||
use crate::CodeMemory; | ||
#[allow(unused_imports)] | ||
use anyhow::{bail, Result}; | ||
|
||
cfg_if::cfg_if! { | ||
if #[cfg(all(feature = "jitdump", target_os = "linux"))] { | ||
#[path = "profiling/jitdump_linux.rs"] | ||
mod jitdump; | ||
pub use jitdump::new as new_jitdump; | ||
} else { | ||
#[path = "profiling/jitdump_disabled.rs"] | ||
mod jitdump; | ||
pub fn new_jitdump() -> Result<Box<dyn ProfilingAgent>> { | ||
if cfg!(feature = "jitdump") { | ||
bail!("jitdump is not supported on this platform"); | ||
} else { | ||
bail!("jitdump support disabled at compile time"); | ||
} | ||
} | ||
} | ||
} | ||
|
||
cfg_if::cfg_if! { | ||
if #[cfg(target_os = "linux")] { | ||
#[path = "profiling/perfmap_linux.rs"] | ||
mod perfmap; | ||
pub use perfmap::new as new_perfmap; | ||
} else { | ||
#[path = "profiling/perfmap_disabled.rs"] | ||
mod perfmap; | ||
pub fn new_perfmap() -> Result<Box<dyn ProfilingAgent>> { | ||
bail!("perfmap support not supported on this platform"); | ||
} | ||
} | ||
} | ||
|
||
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. | ||
if #[cfg(all(feature = "vtune", target_arch = "x86_64", not(all(target_os = "windows", target_env = "gnu"))))] { | ||
#[path = "profiling/vtune.rs"] | ||
mod vtune; | ||
pub use vtune::new as new_vtune; | ||
} else { | ||
#[path = "profiling/vtune_disabled.rs"] | ||
mod vtune; | ||
pub fn new_vtune() -> Result<Box<dyn ProfilingAgent>> { | ||
if cfg!(feature = "vtune") { | ||
bail!("VTune is not supported on this platform."); | ||
} else { | ||
bail!("VTune support disabled at compile time."); | ||
} | ||
} | ||
} | ||
} | ||
|
||
pub use jitdump::JitDumpAgent; | ||
pub use perfmap::PerfMapAgent; | ||
pub use vtune::VTuneAgent; | ||
|
||
/// Common interface for profiling tools. | ||
pub trait ProfilingAgent: Send + Sync + 'static { | ||
/// Notify the profiler of a new module loaded into memory | ||
fn module_load(&self, module: &CompiledModule, dbg_image: Option<&[u8]>); | ||
fn register_function(&self, name: &str, addr: *const u8, size: usize); | ||
|
||
/// Notify the profiler about a single dynamically-generated trampoline (for host function) | ||
/// that is being loaded now.` | ||
fn load_single_trampoline(&self, name: &str, addr: *const u8, size: usize, pid: u32, tid: u32); | ||
} | ||
fn register_module(&self, code: &CodeMemory, custom_name: &dyn Fn(usize) -> Option<String>) { | ||
use object::{File, Object as _, ObjectSection, ObjectSymbol, SectionKind, SymbolKind}; | ||
|
||
/// Default agent for unsupported profiling build. | ||
#[derive(Debug, Default, Clone, Copy)] | ||
pub struct NullProfilerAgent; | ||
let image = match File::parse(&code.mmap()[..]) { | ||
Ok(image) => image, | ||
Err(_) => return, | ||
}; | ||
|
||
impl ProfilingAgent for NullProfilerAgent { | ||
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, | ||
) { | ||
let text_base = match image.sections().find(|s| s.kind() == SectionKind::Text) { | ||
Some(section) => match section.data() { | ||
Ok(data) => data.as_ptr() as usize, | ||
Err(_) => return, | ||
}, | ||
None => return, | ||
}; | ||
|
||
for sym in image.symbols() { | ||
if !sym.is_definition() { | ||
continue; | ||
} | ||
if sym.kind() != SymbolKind::Text { | ||
continue; | ||
} | ||
let address = sym.address(); | ||
let size = sym.size(); | ||
if address == 0 || size == 0 { | ||
continue; | ||
} | ||
if let Ok(name) = sym.name() { | ||
let addr = text_base + address as usize; | ||
let owned; | ||
let name = match custom_name(address as usize) { | ||
Some(name) => { | ||
owned = name; | ||
&owned | ||
} | ||
None => name, | ||
}; | ||
self.register_function(name, addr as *const u8, size as usize); | ||
} | ||
} | ||
} | ||
} | ||
|
||
#[allow(dead_code)] | ||
fn debug_name(module: &CompiledModule, index: DefinedFuncIndex) -> String { | ||
let index = module.module().func_index(index); | ||
let mut debug_name = String::new(); | ||
demangle_function_name_or_index(&mut debug_name, module.func_name(index), index.index()) | ||
.unwrap(); | ||
debug_name | ||
pub fn new_null() -> Box<dyn ProfilingAgent> { | ||
Box::new(NullProfilerAgent) | ||
} | ||
|
||
#[derive(Debug, Default, Clone, Copy)] | ||
struct NullProfilerAgent; | ||
|
||
impl ProfilingAgent for NullProfilerAgent { | ||
fn register_function(&self, _name: &str, _addr: *const u8, _size: usize) {} | ||
fn register_module(&self, _code: &CodeMemory, _custom_name: &dyn Fn(usize) -> Option<String>) {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
//! Support for jitdump files which can be used by perf for profiling jitted code. | ||
//! Spec definitions for the output format is as described here: | ||
//! <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/perf/Documentation/jitdump-specification.txt> | ||
//! | ||
//! Usage Example: | ||
//! Record | ||
//! 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 | ||
//! sudo perf report -i perf.jit.data -F+period,srcline | ||
//! Note: For descriptive results, the WASM file being executed should contain dwarf debug data | ||
use crate::profiling::ProfilingAgent; | ||
use anyhow::Result; | ||
use std::process; | ||
use std::sync::Mutex; | ||
use target_lexicon::Architecture; | ||
use wasmtime_jit_debug::perf_jitdump::*; | ||
|
||
use object::elf; | ||
|
||
/// Interface for driving the creation of jitdump files | ||
struct JitDumpAgent { | ||
pid: u32, | ||
} | ||
|
||
/// Process-wide JIT dump file. Perf only accepts a unique file per process, in the injection step. | ||
static JITDUMP_FILE: Mutex<Option<JitDumpFile>> = Mutex::new(None); | ||
|
||
/// Intialize a JitDumpAgent and write out the header. | ||
pub fn new() -> Result<Box<dyn ProfilingAgent>> { | ||
let mut jitdump_file = JITDUMP_FILE.lock().unwrap(); | ||
|
||
if jitdump_file.is_none() { | ||
let filename = format!("./jit-{}.dump", process::id()); | ||
let e_machine = match target_lexicon::HOST.architecture { | ||
Architecture::X86_64 => elf::EM_X86_64 as u32, | ||
Architecture::X86_32(_) => elf::EM_386 as u32, | ||
Architecture::Arm(_) => elf::EM_ARM as u32, | ||
Architecture::Aarch64(_) => elf::EM_AARCH64 as u32, | ||
Architecture::S390x => elf::EM_S390 as u32, | ||
_ => unimplemented!("unrecognized architecture"), | ||
}; | ||
*jitdump_file = Some(JitDumpFile::new(filename, e_machine)?); | ||
} | ||
|
||
Ok(Box::new(JitDumpAgent { | ||
pid: std::process::id(), | ||
})) | ||
} | ||
|
||
impl ProfilingAgent for JitDumpAgent { | ||
fn register_function(&self, name: &str, addr: *const u8, size: usize) { | ||
let mut jitdump_file = JITDUMP_FILE.lock().unwrap(); | ||
let jitdump_file = jitdump_file.as_mut().unwrap(); | ||
let timestamp = jitdump_file.get_time_stamp(); | ||
#[allow(trivial_numeric_casts)] | ||
let tid = rustix::thread::gettid().as_raw_nonzero().get() as u32; | ||
if let Err(err) = | ||
jitdump_file.dump_code_load_record(&name, addr, size, timestamp, self.pid, tid) | ||
{ | ||
println!("Jitdump: write_code_load_failed_record failed: {:?}\n", err); | ||
} | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.