rust: add support for Intel PT regions (#140)
* rust: add `__itt*` functions in generated bindings
This change adds some bindgen-generated functions, and not just
variables, to the generated bindings. This should resolve #139 by making
the `pt_region_*` functions at least visibile in the `ittapi-sys` crate,
making it possible to add higher-level Rust versions.
We cannot generate bindings for all declared functions because these
cause link errors: they are only present in the dynamic library (`*.so`)
but not in the static library (`libittnotify.a`) that the `ittapi-sys`
crate links to. So, for these dynamically-provided functions we must
just retain the `*_ptr__3_0` data symbols and wait for them to be
resolved to functions pointers at runtime.
* rust: remove unused code
* rust: document static/dynamic linking in `README.md`
It is easy to forget how the `ittapi` system of static + dynamic linking
works (and why!). To answer the underlying question of issues like #139,
this change adds some `README.md` documentation explaining why we can't
just include all function symbols in the generated bindings.
* rust: add `Region` for marking Intel PT regions
In #139, @codecnotsupported pointed out a need for access to
`__itt_mark_pt_region_begin` and `__itt_mark_pt_region_end` for
fine-grained VTune analysis using Intel PT. This change adds a
high-level `Region` structure for easy access to these now-available
functions. Closes #139.
diff --git a/rust/ittapi-sys/Cargo.toml b/rust/ittapi-sys/Cargo.toml
index 1df3944..520f63e 100644
--- a/rust/ittapi-sys/Cargo.toml
+++ b/rust/ittapi-sys/Cargo.toml
@@ -28,5 +28,5 @@
cc = "1.0.73"
[dev-dependencies]
-bindgen = "0.68"
+bindgen = "0.69.4"
diff = "0.1"
diff --git a/rust/ittapi-sys/README.md b/rust/ittapi-sys/README.md
index 3e5f44e..15dccf1 100644
--- a/rust/ittapi-sys/README.md
+++ b/rust/ittapi-sys/README.md
@@ -26,9 +26,16 @@
```toml
[dependencies]
-ittapi-sys = "0.3"
+ittapi-sys = "*"
```
+Using the symbols in this crate can be tricky: `ittapi` consists of a static part (e.g.,
+`libittnotify.a`) linked in this crate _and_ a dynamic part (e.g., `libittnotify_collector.so`,
+VTune Profiler). The static part provides the ITT data symbols that may be subsequently resolved to
+actual implementations in the dynamic part--the data collector. This crate only provides symbols
+like `__itt_task_begin_ptr__3_0`, not `__itt_task_begin`, to avoid link errors; programs using
+`ittapi` should compile even if the dynamic part is not present. Using the [high-level Rust crate]
+avoids this complexity.
### Build
diff --git a/rust/ittapi-sys/src/freebsd/ittnotify_bindings.rs b/rust/ittapi-sys/src/freebsd/ittnotify_bindings.rs
index 1ff21d3..0ce95b3 100644
--- a/rust/ittapi-sys/src/freebsd/ittnotify_bindings.rs
+++ b/rust/ittapi-sys/src/freebsd/ittnotify_bindings.rs
@@ -69,6 +69,14 @@
extern "C" {
pub static mut __itt_pt_region_create_ptr__3_0: __itt_pt_region_create_ptr__3_0_t;
}
+extern "C" {
+ #[doc = " @brief function contains a special code pattern identified on the post-processing stage and\n marks the beginning of a code region targeted for Intel PT analysis\n @param[in] region - region id, 0 <= region < 8"]
+ pub fn __itt_mark_pt_region_begin(region: __itt_pt_region);
+}
+extern "C" {
+ #[doc = " @brief function contains a special code pattern identified on the post-processing stage and\n marks the end of a code region targeted for Intel PT analysis\n @param[in] region - region id, 0 <= region < 8"]
+ pub fn __itt_mark_pt_region_end(region: __itt_pt_region);
+}
pub type __itt_thread_set_name_ptr__3_0_t =
::std::option::Option<unsafe extern "C" fn(name: *const ::std::os::raw::c_char)>;
extern "C" {
diff --git a/rust/ittapi-sys/src/lib.rs b/rust/ittapi-sys/src/lib.rs
index ac7b2fd..24a9006 100644
--- a/rust/ittapi-sys/src/lib.rs
+++ b/rust/ittapi-sys/src/lib.rs
@@ -1,4 +1,5 @@
//! This library contains OS-specific bindings to the C `ittapi` library.
+
#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]
#![allow(unused)]
#![deny(clippy::all)]
@@ -32,11 +33,3 @@
include!("freebsd/jitprofiling_bindings.rs");
#[cfg(target_os = "openbsd")]
include!("openbsd/jitprofiling_bindings.rs");
-
-// #[link(name = "ittnotify", kind = "static")]
-// extern "C" {
-// #[link_name = "__itt_domain_create_init_3_0"]
-// pub fn __itt_domain_create_init_3_0(name: *const std::os::raw::c_char) -> *mut __itt_domain;
-// // #[link_name = "__itt_domain_create_init_3_0"]
-// // pub fn __itt_domain_create(name: *const std::os::raw::c_char) -> *mut __itt_domain;
-// }
diff --git a/rust/ittapi-sys/src/linux/ittnotify_bindings.rs b/rust/ittapi-sys/src/linux/ittnotify_bindings.rs
index e3270a0..cd069fd 100644
--- a/rust/ittapi-sys/src/linux/ittnotify_bindings.rs
+++ b/rust/ittapi-sys/src/linux/ittnotify_bindings.rs
@@ -1,4 +1,4 @@
-/* automatically generated by rust-bindgen 0.68.1 */
+/* automatically generated by rust-bindgen 0.69.4 */
pub const ITT_OS_WIN: u32 = 1;
pub const ITT_OS_LINUX: u32 = 2;
@@ -59,6 +59,14 @@
extern "C" {
pub static mut __itt_pt_region_create_ptr__3_0: __itt_pt_region_create_ptr__3_0_t;
}
+extern "C" {
+ #[doc = " @brief function contains a special code pattern identified on the post-processing stage and\n marks the beginning of a code region targeted for Intel PT analysis\n @param[in] region - region id, 0 <= region < 8"]
+ pub fn __itt_mark_pt_region_begin(region: __itt_pt_region);
+}
+extern "C" {
+ #[doc = " @brief function contains a special code pattern identified on the post-processing stage and\n marks the end of a code region targeted for Intel PT analysis\n @param[in] region - region id, 0 <= region < 8"]
+ pub fn __itt_mark_pt_region_end(region: __itt_pt_region);
+}
pub type __itt_thread_set_name_ptr__3_0_t =
::std::option::Option<unsafe extern "C" fn(name: *const ::std::os::raw::c_char)>;
extern "C" {
diff --git a/rust/ittapi-sys/src/linux/jitprofiling_bindings.rs b/rust/ittapi-sys/src/linux/jitprofiling_bindings.rs
index 29927bd..1e9d976 100644
--- a/rust/ittapi-sys/src/linux/jitprofiling_bindings.rs
+++ b/rust/ittapi-sys/src/linux/jitprofiling_bindings.rs
@@ -1,4 +1,4 @@
-/* automatically generated by rust-bindgen 0.68.1 */
+/* automatically generated by rust-bindgen 0.69.4 */
#[doc = "<\\brief Send this to shutdown the agent.\n Use NULL for event data."]
pub const iJIT_jvm_event_iJVM_EVENT_TYPE_SHUTDOWN: iJIT_jvm_event = 2;
diff --git a/rust/ittapi-sys/src/macos/ittnotify_bindings.rs b/rust/ittapi-sys/src/macos/ittnotify_bindings.rs
index bc065b2..9c1e40c 100644
--- a/rust/ittapi-sys/src/macos/ittnotify_bindings.rs
+++ b/rust/ittapi-sys/src/macos/ittnotify_bindings.rs
@@ -1,4 +1,4 @@
-/* automatically generated by rust-bindgen 0.68.1 */
+/* automatically generated by rust-bindgen 0.69.4 */
pub const ITT_OS_WIN: u32 = 1;
pub const ITT_OS_LINUX: u32 = 2;
@@ -59,6 +59,14 @@
extern "C" {
pub static mut __itt_pt_region_create_ptr__3_0: __itt_pt_region_create_ptr__3_0_t;
}
+extern "C" {
+ #[doc = " @brief function contains a special code pattern identified on the post-processing stage and\n marks the beginning of a code region targeted for Intel PT analysis\n @param[in] region - region id, 0 <= region < 8"]
+ pub fn __itt_mark_pt_region_begin(region: __itt_pt_region);
+}
+extern "C" {
+ #[doc = " @brief function contains a special code pattern identified on the post-processing stage and\n marks the end of a code region targeted for Intel PT analysis\n @param[in] region - region id, 0 <= region < 8"]
+ pub fn __itt_mark_pt_region_end(region: __itt_pt_region);
+}
pub type __itt_thread_set_name_ptr__3_0_t =
::std::option::Option<unsafe extern "C" fn(name: *const ::std::os::raw::c_char)>;
extern "C" {
diff --git a/rust/ittapi-sys/src/macos/jitprofiling_bindings.rs b/rust/ittapi-sys/src/macos/jitprofiling_bindings.rs
index 29927bd..1e9d976 100644
--- a/rust/ittapi-sys/src/macos/jitprofiling_bindings.rs
+++ b/rust/ittapi-sys/src/macos/jitprofiling_bindings.rs
@@ -1,4 +1,4 @@
-/* automatically generated by rust-bindgen 0.68.1 */
+/* automatically generated by rust-bindgen 0.69.4 */
#[doc = "<\\brief Send this to shutdown the agent.\n Use NULL for event data."]
pub const iJIT_jvm_event_iJVM_EVENT_TYPE_SHUTDOWN: iJIT_jvm_event = 2;
diff --git a/rust/ittapi-sys/src/openbsd/ittnotify_bindings.rs b/rust/ittapi-sys/src/openbsd/ittnotify_bindings.rs
index 2a0ad9f..82e3cc5 100644
--- a/rust/ittapi-sys/src/openbsd/ittnotify_bindings.rs
+++ b/rust/ittapi-sys/src/openbsd/ittnotify_bindings.rs
@@ -52,6 +52,14 @@
extern "C" {
pub static mut __itt_pt_region_create_ptr__3_0: __itt_pt_region_create_ptr__3_0_t;
}
+extern "C" {
+ #[doc = " @brief function contains a special code pattern identified on the post-processing stage and\n marks the beginning of a code region targeted for Intel PT analysis\n @param[in] region - region id, 0 <= region < 8"]
+ pub fn __itt_mark_pt_region_begin(region: __itt_pt_region);
+}
+extern "C" {
+ #[doc = " @brief function contains a special code pattern identified on the post-processing stage and\n marks the end of a code region targeted for Intel PT analysis\n @param[in] region - region id, 0 <= region < 8"]
+ pub fn __itt_mark_pt_region_end(region: __itt_pt_region);
+}
pub type __itt_thread_set_name_ptr__3_0_t =
::std::option::Option<unsafe extern "C" fn(name: *const ::std::os::raw::c_char)>;
extern "C" {
diff --git a/rust/ittapi-sys/src/windows/ittnotify_bindings.rs b/rust/ittapi-sys/src/windows/ittnotify_bindings.rs
index be4c19f..39458a7 100644
--- a/rust/ittapi-sys/src/windows/ittnotify_bindings.rs
+++ b/rust/ittapi-sys/src/windows/ittnotify_bindings.rs
@@ -1,4 +1,4 @@
-/* automatically generated by rust-bindgen 0.68.1 */
+/* automatically generated by rust-bindgen 0.69.4 */
pub const ITT_OS_WIN: u32 = 1;
pub const ITT_OS_LINUX: u32 = 2;
@@ -65,6 +65,14 @@
extern "C" {
pub static mut __itt_pt_region_createW_ptr__3_0: __itt_pt_region_createW_ptr__3_0_t;
}
+extern "C" {
+ #[doc = " @brief function contains a special code pattern identified on the post-processing stage and\n marks the beginning of a code region targeted for Intel PT analysis\n @param[in] region - region id, 0 <= region < 8"]
+ pub fn __itt_mark_pt_region_begin(region: __itt_pt_region);
+}
+extern "C" {
+ #[doc = " @brief function contains a special code pattern identified on the post-processing stage and\n marks the end of a code region targeted for Intel PT analysis\n @param[in] region - region id, 0 <= region < 8"]
+ pub fn __itt_mark_pt_region_end(region: __itt_pt_region);
+}
pub type __itt_thread_set_nameA_ptr__3_0_t =
::std::option::Option<unsafe extern "C" fn(name: *const ::std::os::raw::c_char)>;
extern "C" {
diff --git a/rust/ittapi-sys/src/windows/jitprofiling_bindings.rs b/rust/ittapi-sys/src/windows/jitprofiling_bindings.rs
index 17ce844..ad67453 100644
--- a/rust/ittapi-sys/src/windows/jitprofiling_bindings.rs
+++ b/rust/ittapi-sys/src/windows/jitprofiling_bindings.rs
@@ -1,4 +1,4 @@
-/* automatically generated by rust-bindgen 0.68.1 */
+/* automatically generated by rust-bindgen 0.69.4 */
#[doc = "<\\brief Send this to shutdown the agent.\n Use NULL for event data."]
pub const iJIT_jvm_event_iJVM_EVENT_TYPE_SHUTDOWN: iJIT_jvm_event = 2;
diff --git a/rust/ittapi-sys/tests/bindgen-up-to-date.rs b/rust/ittapi-sys/tests/bindgen-up-to-date.rs
index f16680e..7db83f0 100644
--- a/rust/ittapi-sys/tests/bindgen-up-to-date.rs
+++ b/rust/ittapi-sys/tests/bindgen-up-to-date.rs
@@ -32,6 +32,11 @@
.formatter(bindgen::Formatter::Rustfmt)
.allowlist_var("ITT.*")
.allowlist_var("__itt.*")
+ // Also, note how few functions we allow: if we generate bindings for all the declared
+ // functions, we run into linking errors. Only some functions are actually defined in
+ // `libittnotify.a` but most are provided dynamically by the dynamic collection library. See
+ // the `README.md` for more details.
+ .allowlist_function("__itt_mark_pt.*")
.header(concat(INCLUDE_PATH, "/ittnotify.h"))
.generate()
.expect("Unable to generate ittnotify bindings.")
diff --git a/rust/ittapi/src/domain.rs b/rust/ittapi/src/domain.rs
index 5c779a8..f77b08e 100644
--- a/rust/ittapi/src/domain.rs
+++ b/rust/ittapi/src/domain.rs
@@ -43,3 +43,14 @@
/// [ITT documentation]:
/// https://www.intel.com/content/www/us/en/develop/documentation/vtune-help/top/api-support/instrumentation-and-tracing-technology-apis/instrumentation-tracing-technology-api-reference/domain-api.html
unsafe impl Sync for Domain {}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ #[should_panic(expected = "unable to create a CString; does it contain a 0 byte?")]
+ fn zero_byte() {
+ let _domain = Domain::new("zero\0byte\0name");
+ }
+}
diff --git a/rust/ittapi/src/lib.rs b/rust/ittapi/src/lib.rs
index ad3ef9e..32e841e 100644
--- a/rust/ittapi/src/lib.rs
+++ b/rust/ittapi/src/lib.rs
@@ -13,6 +13,7 @@
mod domain;
mod event;
pub mod jit;
+mod region;
mod string;
mod task;
mod util;
@@ -20,5 +21,6 @@
pub use collection_control::{detach, pause, resume};
pub use domain::Domain;
pub use event::Event;
+pub use region::{MarkedRegion, Region};
pub use string::StringHandle;
pub use task::Task;
diff --git a/rust/ittapi/src/region.rs b/rust/ittapi/src/region.rs
new file mode 100644
index 0000000..df20f90
--- /dev/null
+++ b/rust/ittapi/src/region.rs
@@ -0,0 +1,94 @@
+use std::ffi::CString;
+
+/// An Intel® Processor Trace region.
+pub struct Region(ittapi_sys::__itt_pt_region);
+impl Region {
+ /// Create a new Intel PT region.
+ ///
+ /// ```
+ /// # use ittapi::Region;
+ /// let region = Region::new("test-region");
+ /// ```
+ ///
+ /// # Panics
+ ///
+ /// Panics if the domain name contains a `0` byte.
+ #[must_use]
+ pub fn new(name: &str) -> Self {
+ let c_string =
+ CString::new(name).expect("unable to create a CString; does it contain a 0 byte?");
+ #[cfg(unix)]
+ let create_fn = unsafe { ittapi_sys::__itt_pt_region_create_ptr__3_0 };
+ #[cfg(windows)]
+ let create_fn = unsafe { ittapi_sys::__itt_pt_region_createA_ptr__3_0 };
+ let region = if let Some(create_fn) = create_fn {
+ unsafe { create_fn(c_string.as_ptr()) }
+ } else {
+ // Use this value as a sentinel to indicate that the region was not created.
+ u8::MAX
+ };
+ Self(region)
+ }
+
+ /// Mark a section of code as an Intel PT region using `__itt_mark_pt_region_begin`. This can be
+ /// used for fine-grained profiling, such as [anomaly detection] (a preview feature of VTune).
+ ///
+ /// [anomaly detection]:
+ /// https://www.intel.com/content/www/us/en/docs/vtune-profiler/user-guide/2024-1/anomaly-detection-analysis.html
+ ///
+ /// ```
+ /// # use ittapi::Region;
+ /// let region = Region::new("test-region");
+ /// // Mark a region for fine-grained measurement, such as a tight loop.
+ /// for _ in 0..10 {
+ /// let _marked = region.mark();
+ /// let _ = 2 + 2;
+ /// // Marked region ends here, when dropped; use `end()` to end it explicitly.
+ /// }
+ /// ```
+ #[inline]
+ #[must_use]
+ pub fn mark(&self) -> MarkedRegion {
+ unsafe { ittapi_sys::__itt_mark_pt_region_begin(self.0) };
+ MarkedRegion(self)
+ }
+}
+
+/// A [`MarkedRegion`] is a Rust helper structure for ergonomically ending a marked region using
+/// `__itt_mark_pt_region_end`. See [`Region::mark`] for more details.
+pub struct MarkedRegion<'a>(&'a Region);
+impl<'a> MarkedRegion<'a> {
+ /// End the marked region.
+ #[inline]
+ pub fn end(self) {
+ // Do nothing; the `Drop` implementation does the work. See discussion at
+ // https://stackoverflow.com/questions/53254645.
+ }
+}
+impl Drop for MarkedRegion<'_> {
+ #[inline]
+ fn drop(&mut self) {
+ unsafe { ittapi_sys::__itt_mark_pt_region_end(self.0 .0) };
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ #[should_panic(expected = "unable to create a CString; does it contain a 0 byte?")]
+ fn zero_byte() {
+ let _region = Region::new("zero\0byte\0name");
+ }
+
+ #[test]
+ fn sanity() {
+ let region = Region::new("region");
+ for _ in 0..10 {
+ let _marked_region = region.mark();
+ // Do nothing.
+ _marked_region.end();
+ }
+ }
+}