ittapi/jit.rs
1//! The JIT (Just-In-Time) Profiling API provides functionality to report information about
2//! just-in-time generated code that can be used by performance tools. The [Jit] Rust structure is a
3//! high-level view of a subset of the full functionality available. See the [JIT Profiling API] for
4//! more information.
5//!
6//! [JIT Profiling API]:
7/// https://www.intel.com/content/www/us/en/develop/documentation/vtune-help/top/api-support/jit-profiling-api.html
8use anyhow::Context;
9use std::{ffi::CString, os, ptr};
10
11/// Register JIT-compiled methods with a performance tool (e.g., VTune). This structure assumes
12/// single-threaded access; if your program may be multi-threaded, make sure to guard multi-threaded
13/// access with a mutex.
14#[derive(Default)]
15pub struct Jit {
16 shutdown_complete: bool,
17}
18
19impl Jit {
20 /// Returns a new `MethodId` for use in `MethodLoad` events.
21 #[allow(clippy::unused_self)]
22 #[must_use]
23 pub fn get_method_id(&self) -> MethodId {
24 MethodId(unsafe { ittapi_sys::iJIT_GetNewMethodID() })
25 }
26
27 /// Notifies any `EventType` to VTune.
28 ///
29 /// # Errors
30 ///
31 /// May fail if the underlying call to the ITT library fails.
32 #[allow(clippy::unused_self)]
33 pub fn notify_event(&self, mut event: EventType) -> anyhow::Result<()> {
34 let tag = event.tag();
35 let data = event.data();
36 log::trace!("notify_event: tag={:?}", tag);
37 let res = unsafe { ittapi_sys::iJIT_NotifyEvent(tag, data) };
38 match event {
39 EventType::MethodLoadFinished(_) => {
40 // Documentation of the `iJIT_NotifyEvent` says that the return code is undefined
41 // for this particular event, so we can't distinguish failures from successes.
42 // Hope for the best.
43 Ok(())
44 }
45 EventType::Shutdown => {
46 if res == 1 {
47 Ok(())
48 } else {
49 anyhow::bail!("error when notifying event with tag {tag}: return code = {res}");
50 }
51 }
52 }
53 }
54
55 // High-level helpers.
56
57 /// Notifies VTune that a new function described by the `MethodLoadBuilder` has been jitted.
58 ///
59 /// # Errors
60 ///
61 /// May fail if the builder has invalid data or if the ITT library fails to notify the method
62 /// load event.
63 pub fn load_method(&self, builder: MethodLoadBuilder) -> anyhow::Result<()> {
64 let method_id = self.get_method_id();
65 let method_load = builder.build(method_id)?;
66 self.notify_event(method_load)
67 }
68
69 /// Notifies VTune that profiling is being shut down.
70 ///
71 /// # Errors
72 ///
73 /// May fail if the ITT library fails to notify the shutdown event.
74 pub fn shutdown(&mut self) -> anyhow::Result<()> {
75 if self.shutdown_complete {
76 return Ok(());
77 }
78 let res = self.notify_event(EventType::Shutdown);
79 if res.is_ok() {
80 self.shutdown_complete = true;
81 }
82 res
83 }
84}
85
86impl Drop for Jit {
87 fn drop(&mut self) {
88 if !self.shutdown_complete {
89 // There's not much we can do when an error happens here.
90 if let Err(err) = self.shutdown() {
91 log::error!("Error when shutting down VTune: {}", err);
92 }
93 }
94 }
95}
96
97/// Type of event to be dispatched through ittapi's JIT event API.
98pub enum EventType {
99 /// Send this event after a JITted method has been loaded into memory, and possibly JIT
100 /// compiled, but before the code is executed.
101 MethodLoadFinished(MethodLoad),
102
103 /// Send this notification to terminate profiling.
104 Shutdown,
105}
106
107impl EventType {
108 /// Returns the C event type to be used when notifying this event to VTune.
109 fn tag(&self) -> ittapi_sys::iJIT_jvm_event {
110 match self {
111 EventType::MethodLoadFinished(_) => {
112 ittapi_sys::iJIT_jvm_event_iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED
113 }
114 EventType::Shutdown => ittapi_sys::iJIT_jvm_event_iJVM_EVENT_TYPE_SHUTDOWN,
115 }
116 }
117
118 /// Returns a raw C pointer to the event-specific data that must be used when notifying this
119 /// event to VTune.
120 fn data(&mut self) -> *mut os::raw::c_void {
121 match self {
122 EventType::MethodLoadFinished(method_load) => ptr::addr_of_mut!(method_load.0).cast(),
123 EventType::Shutdown => ptr::null_mut(),
124 }
125 }
126}
127
128/// Newtype wrapper for a method id returned by ittapi's `iJIT_GetNewMethodID`, as returned by
129/// `VtuneState::get_method_id` in the high-level API.
130#[derive(Clone, Copy)]
131pub struct MethodId(u32);
132
133/// Newtype wrapper for a JIT method load.
134pub struct MethodLoad(ittapi_sys::_iJIT_Method_Load);
135
136/// Multi-step constructor using the builder pattern for a `MethodLoad` event.
137pub struct MethodLoadBuilder {
138 method_name: String,
139 addr: *const u8,
140 len: usize,
141 class_file_name: Option<String>,
142 source_file_name: Option<String>,
143}
144
145impl MethodLoadBuilder {
146 /// Creates a new `MethodLoadBuilder` from scratch.
147 ///
148 /// `addr` is the pointer to the start of the code region, `len` is the size of this code
149 /// region in bytes.
150 #[must_use]
151 pub fn new(method_name: String, addr: *const u8, len: usize) -> Self {
152 Self {
153 method_name,
154 addr,
155 len,
156 class_file_name: None,
157 source_file_name: None,
158 }
159 }
160
161 /// Attache a class file.
162 #[must_use]
163 pub fn class_file_name(mut self, class_file_name: String) -> Self {
164 self.class_file_name = Some(class_file_name);
165 self
166 }
167
168 /// Attach a source file.
169 #[must_use]
170 pub fn source_file_name(mut self, source_file_name: String) -> Self {
171 self.source_file_name = Some(source_file_name);
172 self
173 }
174
175 /// Build a "method load" event for the given `method_id`.
176 ///
177 /// # Errors
178 ///
179 /// May fail if the various names passed to this builder are not valid C strings.
180 pub fn build(self, method_id: MethodId) -> anyhow::Result<EventType> {
181 Ok(EventType::MethodLoadFinished(MethodLoad(
182 ittapi_sys::_iJIT_Method_Load {
183 method_id: method_id.0,
184 method_name: CString::new(self.method_name)
185 .context("CString::new failed")?
186 .into_raw(),
187 method_load_address: self.addr as *mut os::raw::c_void,
188 method_size: self.len.try_into().expect("cannot fit length into 32 bits"),
189 line_number_size: 0,
190 line_number_table: ptr::null_mut(),
191 class_id: 0, // Field officially obsolete in Intel's doc.
192 class_file_name: CString::new(
193 self.class_file_name
194 .as_deref()
195 .unwrap_or("<unknown class file name>"),
196 )
197 .context("CString::new failed")?
198 .into_raw(),
199 source_file_name: CString::new(
200 self.source_file_name
201 .as_deref()
202 .unwrap_or("<unknown source file name>"),
203 )
204 .context("CString::new failed")?
205 .into_raw(),
206 },
207 )))
208 }
209}