Skip to content

Commit

Permalink
Adds perf jitdump support
Browse files Browse the repository at this point in the history
Patch adds support for the perf jitdump file specification.
With this patch it should be possible to see profile data for code
generated and maped at runtime. Specifically the patch adds support
for the JIT_CODE_LOAD and the JIT_DEBUG_INFO record as described in
the specification. Dumping jitfiles is enabled with the --jitdump
flag. When the -g flag is also used there is an attempt to dump file
and line number information where this option would be most useful
when the WASM file already includes DWARF debug information.

The generation of the jitdump files has been tested on only a few wasm
files. This patch is expected to be useful/serviceable where currently
there is no means for jit profiling, but future patches may benefit
line mapping and add support for additional jitdump record types.

Usage Example:
Record
  sudo perf record -k 1 -e instructions:u target/debug/wasmtime -g
  --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
  • Loading branch information
jlb6740 committed Feb 12, 2020
1 parent d7c9a90 commit 4441788
Show file tree
Hide file tree
Showing 15 changed files with 1,215 additions and 108 deletions.
388 changes: 287 additions & 101 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ wasmtime-environ = { path = "crates/environ" }
wasmtime-interface-types = { path = "crates/interface-types" }
wasmtime-jit = { path = "crates/jit" }
wasmtime-obj = { path = "crates/obj" }
wasmtime-profiling = { path = "crates/profiling" }
wasmtime-wast = { path = "crates/wast" }
wasmtime-wasi = { path = "crates/wasi" }
wasi-common = { path = "crates/wasi-common" }
Expand Down Expand Up @@ -74,6 +75,7 @@ lightbeam = [
"wasmtime-wast/lightbeam",
"wasmtime/lightbeam",
]
profiling = ["wasmtime-jit/profiling", "wasmtime/profiling", "wasmtime-profiling/profiling"]
test_programs = ["test-programs/test_programs"]

[badges]
Expand Down
2 changes: 2 additions & 0 deletions crates/api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ edition = "2018"
wasmtime-runtime = { path = "../runtime", version = "0.9.0" }
wasmtime-environ = { path = "../environ", version = "0.9.0" }
wasmtime-jit = { path = "../jit", version = "0.9.0" }
wasmtime-profiling = { path = "../profiling" }
wasmparser = "0.51.1"
target-lexicon = { version = "0.10.0", default-features = false }
anyhow = "1.0.19"
Expand Down Expand Up @@ -44,3 +45,4 @@ default = ['wat']
# to cranelift. Requires Nightly Rust currently, and this is not enabled by
# default.
lightbeam = ["wasmtime-jit/lightbeam"]
profiling = []
1 change: 1 addition & 0 deletions crates/api/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ impl Module {
&mut store.compiler_mut(),
binary,
store.engine().config().debug_info,
store.engine().config().profiler.as_ref(),
)?;

let names = Arc::new(Names {
Expand Down
19 changes: 18 additions & 1 deletion crates/api/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ use std::cell::RefCell;
use std::fmt;
use std::path::Path;
use std::rc::Rc;
use std::sync::Arc;
use std::sync::{Arc, Mutex};
use wasmparser::{OperatorValidatorConfig, ValidatingParserConfig};
use wasmtime_environ::settings::{self, Configurable};
use wasmtime_environ::CacheConfig;
use wasmtime_jit::{native, CompilationStrategy, Compiler};
use wasmtime_profiling::{JitDumpAgent, ProfilingAgent, ProfilingStrategy};

// Runtime Environment

Expand All @@ -25,6 +26,7 @@ pub struct Config {
pub(crate) debug_info: bool,
pub(crate) strategy: CompilationStrategy,
pub(crate) cache_config: CacheConfig,
pub(crate) profiler: Option<Arc<Mutex<Box<dyn ProfilingAgent + Send>>>>,
}

impl Config {
Expand Down Expand Up @@ -58,6 +60,7 @@ impl Config {
flags,
strategy: CompilationStrategy::Auto,
cache_config: CacheConfig::new_cache_disabled(),
profiler: None,
}
}

Expand Down Expand Up @@ -212,6 +215,20 @@ impl Config {
Ok(self)
}

/// Creates a default profiler based on the profiling strategy choosen
///
/// Profiler creation calls the type's default initializer where the purpose is
/// really just to put in place the type used for profiling.
pub fn profiler(&mut self, profile: ProfilingStrategy) -> Result<&mut Self> {
match profile {
ProfilingStrategy::JitDumpProfiler => {
self.profiler = { Some(Arc::new(Mutex::new(Box::new(JitDumpAgent::default())))) }
}
_ => self.profiler = { None },
};
Ok(self)
}

/// Configures whether the debug verifier of Cranelift is enabled or not.
///
/// When Cranelift is used as a code generation backend this will configure
Expand Down
2 changes: 2 additions & 0 deletions crates/jit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ cranelift-frontend = "0.58.0"
wasmtime-environ = { path = "../environ", version = "0.9.0" }
wasmtime-runtime = { path = "../runtime", version = "0.9.0" }
wasmtime-debug = { path = "../debug", version = "0.9.0" }
wasmtime-profiling = { path = "../profiling" }
region = "2.0.0"
thiserror = "1.0.4"
target-lexicon = { version = "0.10.0", default-features = false }
Expand All @@ -32,6 +33,7 @@ winapi = { version = "0.3.7", features = ["winnt", "impl-default"] }

[features]
lightbeam = ["wasmtime-environ/lightbeam"]
profiling = ["wasmtime-profiling/profiling"]

[badges]
maintenance = { status = "actively-developed" }
19 changes: 19 additions & 0 deletions crates/jit/src/code_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use region;
use std::mem::ManuallyDrop;
use std::{cmp, mem};
use wasmtime_environ::{Compilation, CompiledFunction};
use wasmtime_profiling::ProfilingAgent;
use wasmtime_runtime::{Mmap, VMFunctionBody};

struct CodeMemoryEntry {
Expand Down Expand Up @@ -230,4 +231,22 @@ impl CodeMemory {

Ok(())
}

/// Calls the module_load for a given ProfilerAgent. Includes
/// all memory address and length for the given module.
/// TODO: Properly handle the possibilities of multiple mmapped regions
/// which may, amongst other things, influence being more specific about
/// the module name.
pub fn profiler_module_load(
&mut self,
profiler: &mut Box<dyn ProfilingAgent + Send>,
module_name: &str,
dbg_image: Option<&[u8]>,
) -> () {
for CodeMemoryEntry { mmap: m, table: _t } in &mut self.entries {
if m.len() > 0 {
profiler.module_load(module_name, m.as_ptr(), m.len(), dbg_image);
}
}
}
}
11 changes: 11 additions & 0 deletions crates/jit/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use wasmtime_environ::{
Compiler as _C, FunctionBodyData, Module, ModuleMemoryOffset, ModuleVmctxInfo, Relocations,
Traps, Tunables, VMOffsets,
};
use wasmtime_profiling::ProfilingAgent;
use wasmtime_runtime::{
InstantiationError, SignatureRegistry, TrapRegistration, TrapRegistry, VMFunctionBody,
};
Expand Down Expand Up @@ -240,6 +241,16 @@ impl Compiler {
self.code_memory.publish();
}

pub(crate) fn profiler_module_load(
&mut self,
profiler: &mut Box<dyn ProfilingAgent + Send>,
module_name: &str,
dbg_image: Option<&[u8]>,
) -> () {
self.code_memory
.profiler_module_load(profiler, module_name, dbg_image);
}

/// Shared signature registry.
pub fn signatures(&self) -> &SignatureRegistry {
&self.signatures
Expand Down
30 changes: 26 additions & 4 deletions crates/jit/src/instantiate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ use crate::link::link_module;
use crate::resolver::Resolver;
use std::io::Write;
use std::rc::Rc;
use std::sync::Arc;
use std::sync::{Arc, Mutex};
use thiserror::Error;
use wasmtime_debug::read_debuginfo;
use wasmtime_environ::entity::{BoxedSlice, PrimaryMap};
use wasmtime_environ::wasm::{DefinedFuncIndex, SignatureIndex};
use wasmtime_environ::{
CompileError, DataInitializer, DataInitializerLocation, Module, ModuleEnvironment,
};
use wasmtime_profiling::ProfilingAgent;
use wasmtime_runtime::{
GdbJitImageRegistration, InstanceHandle, InstantiationError, TrapRegistration, VMFunctionBody,
VMSharedSignatureIndex,
Expand Down Expand Up @@ -61,6 +62,7 @@ impl<'data> RawCompiledModule<'data> {
compiler: &mut Compiler,
data: &'data [u8],
debug_info: bool,
profiler: Option<&Arc<Mutex<Box<dyn ProfilingAgent + Send>>>>,
) -> Result<Self, SetupError> {
let environ = ModuleEnvironment::new(compiler.frontend_config(), compiler.tunables());

Expand Down Expand Up @@ -103,6 +105,23 @@ impl<'data> RawCompiledModule<'data> {
// Make all code compiled thus far executable.
compiler.publish_compiled_code();

// Initialize profiler and load the wasm module
let _: Option<Box<dyn ProfilingAgent>> = match profiler {
Some(_) => {
let region_name = String::from("wasm_module");
let mut profiler_new = profiler.unwrap().lock().unwrap().clone();
profiler_new.init().ok();
match &dbg_image {
Some(dbg) => {
compiler.profiler_module_load(&mut profiler_new, &region_name, Some(&dbg))
}
_ => compiler.profiler_module_load(&mut profiler_new, &region_name, None),
};
Some(profiler_new)
}
_ => None,
};

let dbg_jit_registration = if let Some(img) = dbg_image {
let mut bytes = Vec::new();
bytes.write_all(&img).expect("all written");
Expand Down Expand Up @@ -139,8 +158,9 @@ impl CompiledModule {
compiler: &mut Compiler,
data: &'data [u8],
debug_info: bool,
profiler: Option<&Arc<Mutex<Box<dyn ProfilingAgent + Send>>>>,
) -> Result<Self, SetupError> {
let raw = RawCompiledModule::<'data>::new(compiler, data, debug_info)?;
let raw = RawCompiledModule::<'data>::new(compiler, data, debug_info, profiler)?;

Ok(Self::from_parts(
raw.module,
Expand Down Expand Up @@ -246,7 +266,7 @@ impl OwnedDataInitializer {

/// Create a new wasm instance by compiling the wasm module in `data` and instatiating it.
///
/// This is equivalent to createing a `CompiledModule` and calling `instantiate()` on it,
/// This is equivalent to creating a `CompiledModule` and calling `instantiate()` on it,
/// but avoids creating an intermediate copy of the data initializers.
///
/// # Unsafety
Expand All @@ -258,7 +278,9 @@ pub unsafe fn instantiate(
data: &[u8],
resolver: &mut dyn Resolver,
debug_info: bool,
profiler: Option<&Arc<Mutex<Box<dyn ProfilingAgent + Send>>>>,
) -> Result<InstanceHandle, SetupError> {
let instance = CompiledModule::new(compiler, data, debug_info)?.instantiate(resolver)?;
let instance =
CompiledModule::new(compiler, data, debug_info, profiler)?.instantiate(resolver)?;
Ok(instance)
}
27 changes: 27 additions & 0 deletions crates/profiling/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "wasmtime-profiling"
version = "0.1.0"
authors = ["The Wasmtime Project Developers"]
description = "Runtime library support for Wasmtime"
license = "Apache-2.0 WITH LLVM-exception"
categories = ["wasm"]
keywords = ["webassembly", "wasm"]
repository = "https://github.com/bytecodealliance/wasmtime"
readme = "README.md"
edition = "2018"

[dependencies]
libc = { version = "0.2.60", default-features = false }
goblin = "0.0.24"
serde = { version = "1.0.99", features = ["derive"] }
scroll = "0.9.2"
gimli = "0.19.0"
object = "0.12.0"
target-lexicon = "0.4.0"
lazy_static = "1.4"

[badges]
maintenance = { status = "actively-developed" }

[features]
profiling = []
Loading

0 comments on commit 4441788

Please sign in to comment.