Support vtune profiling of trampolines too (#3687)

* Provide helpers for demangling function names

* Profile trampolines in vtune too

* get rid of mapping

* avoid code duplication with jitdump_linux

* maintain previous default display name for wasm functions

* no dash, grrr

* Remove unused profiling error type
This commit is contained in:
Benjamin Bouvier
2022-01-19 16:49:23 +01:00
committed by GitHub
parent 2afd6900f4
commit 2649d2352c
13 changed files with 191 additions and 141 deletions

View File

@@ -9,10 +9,10 @@
//! ### Profile
//!
//! ```ignore
//! amplxe-cl -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 --vtune test.wasm
//! ```
//!
//! Note: `amplxe-cl` is a command-line tool for VTune which must [be
//! Note: `vtune` is a command-line tool for VTune which must [be
//! installed](https://www.intel.com/content/www/us/en/developer/tools/oneapi/vtune-profiler.html#standalone)
//! for this to work.
@@ -20,10 +20,9 @@ use crate::{CompiledModule, ProfilingAgent};
use anyhow::Result;
use core::ptr;
use ittapi_rs::*;
use std::collections::HashMap;
use std::ffi::CString;
use std::sync::{atomic, Mutex};
use wasmtime_environ::DefinedFuncIndex;
use wasmtime_environ::EntityRef;
/// Interface for driving the ittapi for VTune support
pub struct VTuneAgent {
@@ -34,19 +33,13 @@ pub struct VTuneAgent {
/// Interface for driving vtune
#[derive(Clone, Debug, Default)]
struct State {
/// Unique identifier for the jitted function
method_id: HashMap<(usize, DefinedFuncIndex), u32>,
}
struct State;
impl VTuneAgent {
/// Intialize a VTuneAgent and write out the header
/// Initialize a VTuneAgent.
pub fn new() -> Result<Self> {
let state = State {
method_id: HashMap::new(),
};
Ok(VTuneAgent {
state: Mutex::new(state),
state: Mutex::new(State),
})
}
}
@@ -58,24 +51,15 @@ impl Drop for VTuneAgent {
}
impl State {
/// Return the unique method ID for use with the ittapi
pub fn get_method_id(&mut self, module_id: usize, func_idx: DefinedFuncIndex) -> u32 {
let method_id: u32;
unsafe {
method_id = iJIT_GetNewMethodID();
}
assert_eq!(
self.method_id.insert((module_id, func_idx), method_id),
None
);
method_id
/// Return a method ID for use with the ittapi.
fn get_method_id(&self) -> u32 {
unsafe { iJIT_GetNewMethodID() }
}
/// Load module
pub fn event_load(
/// Notify vtune about a newly tracked code region.
fn event_load(
&mut self,
method_id: u32,
filename: &str,
module_name: &str,
method_name: &str,
addr: *const u8,
@@ -94,13 +78,16 @@ impl State {
class_file_name: CString::new(module_name)
.expect("CString::new failed")
.into_raw(),
source_file_name: CString::new(filename)
source_file_name: CString::new("<unknown wasm filename>")
.expect("CString::new failed")
.into_raw(),
};
let jmethod_ptr = &mut jmethod as *mut _ as *mut _;
unsafe {
println!("EventLoad: NotifyEvent Called {}", method_id);
log::trace!(
"NotifyEvent: method load (single method with id {})",
method_id
);
let _ret = iJIT_NotifyEvent(
iJIT_jvm_event_iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED,
jmethod_ptr as *mut ::std::os::raw::c_void,
@@ -111,7 +98,7 @@ impl State {
/// Shutdown module
fn event_shutdown(&mut self) -> () {
unsafe {
println!("Drop was called!!!!!!\n");
log::trace!("NotifyEvent shutdown (whole module)");
let _ret = iJIT_NotifyEvent(iJIT_jvm_event_iJVM_EVENT_TYPE_SHUTDOWN, ptr::null_mut());
}
}
@@ -121,8 +108,11 @@ impl ProfilingAgent for VTuneAgent {
fn module_load(&self, module: &CompiledModule, dbg_image: Option<&[u8]>) {
self.state.lock().unwrap().module_load(module, dbg_image);
}
fn trampoline_load(&self, _file: &object::File<'_>) {
// TODO: needs an implementation
fn load_single_trampoline(&self, name: &str, addr: *const u8, size: usize, pid: u32, tid: u32) {
self.state
.lock()
.unwrap()
.load_single_trampoline(name, addr, size, pid, tid);
}
}
@@ -132,29 +122,52 @@ impl State {
static MODULE_ID: atomic::AtomicUsize = atomic::AtomicUsize::new(0);
let global_module_id = MODULE_ID.fetch_add(1, atomic::Ordering::SeqCst);
let module_name = module
.module()
.name
.as_ref()
.cloned()
.unwrap_or_else(|| format!("wasm_module_{}", global_module_id));
for (idx, func) in module.finished_functions() {
let (addr, len) = unsafe { ((*func).as_ptr() as *const u8, (*func).len()) };
let default_filename = "wasm_file";
let default_module_name = String::from("wasm_module");
let module_name = module
.module()
.name
.as_ref()
.unwrap_or(&default_module_name);
let method_name = super::debug_name(module.module(), idx);
let method_id = self.get_method_id(global_module_id, idx);
println!(
"Event Load: ({}) {:?}::{:?} Addr:{:?}\n",
method_id, module_name, method_name, addr
);
self.event_load(
let method_id = self.get_method_id();
log::trace!(
"new function ({}) {:?}::{:?} @ {:?}\n",
method_id,
default_filename,
module_name,
&method_name,
addr,
len,
method_name,
addr
);
self.event_load(method_id, &module_name, &method_name, addr, len);
}
// Note: these are the trampolines into exported functions.
for (idx, func, len) in module.trampolines() {
let idx = idx.index();
let (addr, len) = (func as usize as *const u8, len);
let method_name = format!("wasm::trampoline[{}]", idx,);
let method_id = self.get_method_id();
log::trace!(
"new trampoline ({}) for exported signature {} @ {:?}\n",
method_id,
idx,
addr
);
self.event_load(method_id, &module_name, &method_name, addr, len);
}
}
fn load_single_trampoline(
&mut self,
name: &str,
addr: *const u8,
size: usize,
_pid: u32,
_tid: u32,
) {
let method_id = self.get_method_id();
self.event_load(method_id, "wasm trampoline for Func::new", name, addr, size);
}
}