use anyhow::Context;
use std::{ffi::CString, os, ptr};
#[derive(Default)]
pub struct Jit {
shutdown_complete: bool,
}
impl Jit {
#[allow(clippy::unused_self)]
pub fn get_method_id(&self) -> MethodId {
MethodId(unsafe { ittapi_sys::iJIT_GetNewMethodID() })
}
#[allow(clippy::unused_self)]
pub fn notify_event(&self, mut event: EventType) -> anyhow::Result<()> {
let tag = event.tag();
let data = event.data();
log::trace!("notify_event: tag={:?}", tag);
let res = unsafe { ittapi_sys::iJIT_NotifyEvent(tag, data) };
if res == 1 {
Ok(())
} else {
anyhow::bail!("error when notifying event")
}
}
pub fn load_method(&self, builder: MethodLoadBuilder) -> anyhow::Result<()> {
let method_id = self.get_method_id();
let method_load = builder.build(method_id)?;
self.notify_event(method_load)
}
pub fn shutdown(&mut self) -> anyhow::Result<()> {
let res = self.notify_event(EventType::Shutdown);
if res.is_ok() {
self.shutdown_complete = true;
}
res
}
}
impl Drop for Jit {
fn drop(&mut self) {
if !self.shutdown_complete {
if let Err(err) = self.shutdown() {
log::error!("Error when shutting down VTune: {}", err);
}
}
}
}
pub enum EventType {
MethodLoadFinished(MethodLoad),
Shutdown,
}
impl EventType {
fn tag(&self) -> ittapi_sys::iJIT_jvm_event {
match self {
EventType::MethodLoadFinished(_) => {
ittapi_sys::iJIT_jvm_event_iJVM_EVENT_TYPE_METHOD_INLINE_LOAD_FINISHED
}
EventType::Shutdown => ittapi_sys::iJIT_jvm_event_iJVM_EVENT_TYPE_SHUTDOWN,
}
}
fn data(&mut self) -> *mut os::raw::c_void {
match self {
EventType::MethodLoadFinished(method_load) => ptr::addr_of_mut!(method_load.0).cast(),
EventType::Shutdown => ptr::null_mut(),
}
}
}
#[derive(Clone, Copy)]
pub struct MethodId(u32);
pub struct MethodLoad(ittapi_sys::_iJIT_Method_Load);
pub struct MethodLoadBuilder {
method_name: String,
addr: *const u8,
len: usize,
class_file_name: Option<String>,
source_file_name: Option<String>,
}
impl MethodLoadBuilder {
pub fn new(method_name: String, addr: *const u8, len: usize) -> Self {
Self {
method_name,
addr,
len,
class_file_name: None,
source_file_name: None,
}
}
pub fn class_file_name(mut self, class_file_name: String) -> Self {
self.class_file_name = Some(class_file_name);
self
}
pub fn source_file_name(mut self, source_file_name: String) -> Self {
self.source_file_name = Some(source_file_name);
self
}
pub fn build(self, method_id: MethodId) -> anyhow::Result<EventType> {
Ok(EventType::MethodLoadFinished(MethodLoad(
ittapi_sys::_iJIT_Method_Load {
method_id: method_id.0,
method_name: CString::new(self.method_name)
.context("CString::new failed")?
.into_raw(),
method_load_address: self.addr as *mut os::raw::c_void,
method_size: self.len.try_into().expect("cannot fit length into 32 bits"),
line_number_size: 0,
line_number_table: ptr::null_mut(),
class_id: 0, class_file_name: CString::new(
self.class_file_name
.as_deref()
.unwrap_or("<unknown class file name>"),
)
.context("CString::new failed")?
.into_raw(),
source_file_name: CString::new(
self.source_file_name
.as_deref()
.unwrap_or("<unknown source file name>"),
)
.context("CString::new failed")?
.into_raw(),
},
)))
}
}