diff --git a/README.md b/README.md index 75ee8cc..5a6fe60 100644 --- a/README.md +++ b/README.md @@ -1 +1,9 @@ # nvtx-rs + +nvtx-rs provides Rust bindings for NVIDIA's nvtx library. + +See the generated documentation for more details and the examples for sample usage. + +## Compatibility + +The `nvtx-rs` crate requires rustc 1.70 or greater diff --git a/examples/domain.rs b/examples/domain.rs index 0c84f9c..cfd43d3 100644 --- a/examples/domain.rs +++ b/examples/domain.rs @@ -26,7 +26,7 @@ fn main() { thread::sleep(time::Duration::from_millis(10)); let yy = xx.success(); thread::sleep(time::Duration::from_millis(10)); - let zz = yy.releasing(); + let zz = yy.release(); drop(zz); thread::sleep(time::Duration::from_millis(10)); diff --git a/examples/range.rs b/examples/range.rs index fb7c8cd..0975e03 100644 --- a/examples/range.rs +++ b/examples/range.rs @@ -3,16 +3,11 @@ use std::{thread, time}; fn main() { // we must hold ranges with a proper name // _ will not work since drop() is called immediately - let _x = nvtx::Range::new( - nvtx::EventAttributesBuilder::default() - .color(nvtx::color::salmon) - .message("Start 🦀") - .build(), - ); + let _x = nvtx::LocalRange::new("Start 🦀"); thread::sleep(time::Duration::from_millis(5)); for i in 1..=10 { { - let _rng = nvtx::Range::new( + let _rng = nvtx::LocalRange::new( nvtx::EventAttributesBuilder::default() .color(nvtx::color::cornflowerblue) .message(format!("Iteration Number {}", i)) @@ -21,7 +16,7 @@ fn main() { ); for j in 1..=i { { - let _r = nvtx::Range::new( + let _r = nvtx::LocalRange::new( nvtx::EventAttributesBuilder::default() .color(nvtx::color::beige) .payload(j) diff --git a/src/category.rs b/src/category.rs index f27deb1..c7dba34 100644 --- a/src/category.rs +++ b/src/category.rs @@ -1,8 +1,7 @@ -use std::sync::atomic::{AtomicU32, Ordering}; - use crate::Str; +use std::sync::atomic::{AtomicU32, Ordering}; -/// Represents a category for use with event and range grouping. See [`crate::register_category`], [`crate::register_categories`] +/// Represents a category for use with event and range grouping. See also: [`crate::register_category`], [`crate::register_categories`] #[derive(Debug, Clone, Copy)] pub struct Category { pub(super) id: u32, @@ -10,6 +9,8 @@ pub struct Category { impl Category { /// Create a new category not affiliated with any domain + /// + /// See [`Str`] for valid conversions pub fn new(name: impl Into) -> Category { static COUNT: AtomicU32 = AtomicU32::new(0); let id: u32 = 1 + COUNT.fetch_add(1, Ordering::SeqCst); diff --git a/src/color.rs b/src/color.rs index e9fc0ee..f99634f 100644 --- a/src/color.rs +++ b/src/color.rs @@ -1,8 +1,25 @@ use crate::TypeValueEncodable; + #[cfg(feature = "color-name")] pub use color_name::colors::*; /// Represents a color in use for controlling appearance within NSight Systems +/// +/// ``` +/// // creation of a color from specific channel values: +/// let translucent_orange = nvtx::Color::new(255, 192, 0, 128); +/// +/// // creation of a color from pre-specified names: +/// #[cfg(feature = "color-name")] +/// let salmon : nvtx::Color = nvtx::color::salmon.into(); +/// +/// // modification of a color after creation: +/// +/// let opaque_orange = translucent_orange.with_alpha(255); +/// +/// #[cfg(feature = "color-name")] +/// let translucent_salmon = salmon.with_alpha(128); +/// #[derive(Debug, Clone, Copy)] pub struct Color { /// alpha channel @@ -28,26 +45,50 @@ impl From<[u8; 3]> for Color { impl Color { /// Create a new color from specified channels - pub fn new(a: u8, r: u8, g: u8, b: u8) -> Self { + /// + /// ``` + /// let nice_blue = nvtx::Color::new(0, 192, 255, 255); + /// ``` + pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self { Self { a, r, g, b } } /// Change the alpha channel for the color and yield a new color + /// + /// ``` + /// let nice_blue = nvtx::Color::new(0, 192, 255, 255); + /// let translucent_blue = nice_blue.with_alpha(128); + /// ``` pub fn with_alpha(&self, a: u8) -> Self { Color { a, ..*self } } /// Change the red channel for the color and yield a new color + /// + /// ``` + /// let dark_gray = nvtx::Color::new(32, 32, 32, 255); + /// let dark_red = dark_gray.with_red(128); + /// ``` pub fn with_red(&self, r: u8) -> Self { Color { r, ..*self } } /// Change the green channel for the color and yield a new color + /// + /// ``` + /// let dark_gray = nvtx::Color::new(32, 32, 32, 255); + /// let dark_green = dark_gray.with_green(128); + /// ``` pub fn with_green(&self, g: u8) -> Self { Color { g, ..*self } } /// Change the blue channel for the color and yield a new color + /// + /// ``` + /// let dark_gray = nvtx::Color::new(32, 32, 32, 255); + /// let dark_blue = dark_gray.with_blue(128); + /// ``` pub fn with_blue(&self, b: u8) -> Self { Color { b, ..*self } } diff --git a/src/domain.rs b/src/domain.rs index e436453..9ffa333 100644 --- a/src/domain.rs +++ b/src/domain.rs @@ -1,15 +1,3 @@ -pub use self::{ - category::Category, - event_argument::EventArgument, - event_attributes::{EventAttributes, EventAttributesBuilder}, - identifier::Identifier, - local_range::LocalRange, - message::Message, - range::Range, - registered_string::RegisteredString, - resource::Resource, -}; -pub use crate::sync; use crate::{Str, TypeValueEncodable}; use std::{ marker::PhantomData, @@ -25,6 +13,20 @@ mod message; mod range; mod registered_string; mod resource; +/// user-defined synchronization objects +pub mod sync; + +pub use self::{ + category::Category, + event_argument::EventArgument, + event_attributes::{EventAttributes, EventAttributesBuilder}, + identifier::Identifier, + local_range::LocalRange, + message::Message, + range::Range, + registered_string::RegisteredString, + resource::Resource, +}; /// Represents a domain for high-level grouping #[derive(Debug)] @@ -35,6 +37,12 @@ pub struct Domain { impl Domain { /// Register a NVTX domain + /// + /// See [`Str`] for valid conversions + /// + /// ``` + /// let domain = nvtx::Domain::new("Domain"); + /// ``` pub fn new(name: impl Into) -> Self { Domain { handle: match name.into() { @@ -46,6 +54,12 @@ impl Domain { } /// Gets a new builder instance for event attribute construction in the current domain + /// + /// ``` + /// let domain = nvtx::Domain::new("Domain"); + /// // ... + /// let builder = domain.event_attributes_builder(); + /// ``` pub fn event_attributes_builder(&self) -> EventAttributesBuilder<'_> { EventAttributesBuilder { domain: self, @@ -57,6 +71,16 @@ impl Domain { } /// Registers an immutable string within the current domain + /// + /// Returns a handle to the immutable string registered to nvtx. + /// + /// See [`Str`] for valid conversions + /// + /// ``` + /// let domain = nvtx::Domain::new("Domain"); + /// // ... + /// let my_str = domain.register_string("My immutable string"); + /// ``` pub fn register_string(&self, string: impl Into) -> RegisteredString<'_> { let handle = match string.into() { Str::Ascii(s) => unsafe { @@ -73,6 +97,16 @@ impl Domain { } /// Register many immutable strings within the current domain + /// + /// Returns an array of handles to the immutable strings registered to nvtx. + /// + /// See [`Str`] for valid conversions + /// + /// ``` + /// let domain = nvtx::Domain::new("Domain"); + /// // ... + /// let [a, b, c] = domain.register_strings(["A", "B", "C"]); + /// ``` pub fn register_strings( &self, strings: [impl Into; N], @@ -81,6 +115,16 @@ impl Domain { } /// Register a new category within the domain. Categories are used to group sets of events. + /// + /// Returns a handle to the category registered to nvtx. + /// + /// See [`Str`] for valid conversions + /// + /// ``` + /// let domain = nvtx::Domain::new("Domain"); + /// // ... + /// let cat = domain.register_category("Category"); + /// ``` pub fn register_category(&self, name: impl Into) -> Category<'_> { let id = 1 + self.registered_categories.fetch_add(1, Ordering::SeqCst); match name.into() { @@ -95,6 +139,16 @@ impl Domain { } /// Register new categories within the domain. Categories are used to group sets of events. + /// + /// Returns an array of handles to the categories registered to nvtx. + /// + /// See [`Str`] for valid conversions + /// + /// ``` + /// let domain = nvtx::Domain::new("Domain"); + /// // ... + /// let [cat_a, cat_b] = domain.register_categories(["CatA", "CatB"]); + /// ``` pub fn register_categories( &self, names: [impl Into; N], @@ -102,26 +156,71 @@ impl Domain { names.map(|name| self.register_category(name)) } - /// Marks an instantaneous event in the application. A marker can contain a text message or specify additional information using the event attributes structure. These attributes include a text message, color, category, and a payload. Each of the attributes is optional. + /// Marks an instantaneous event in the application belonging to a domain. + /// + /// A marker can contain a text message or specify additional information using the event attributes structure. These attributes include a text message, color, category, and a payload. Each of the attributes is optional. + /// + /// ``` + /// let domain = nvtx::Domain::new("Domain"); + /// // ... + /// domain.mark("Sample mark"); + /// + /// domain.mark(c"Another example"); + /// + /// domain.mark(domain.event_attributes_builder().message("Interesting example").color([255, 0, 0]).build()); + /// + /// let reg_str = domain.register_string("Registered String"); + /// domain.mark(®_str); + /// ``` pub fn mark<'a>(&'a self, arg: impl Into>) { - let attribute = match arg.into() { - EventArgument::EventAttribute(attr) => attr, - EventArgument::Ascii(s) => self.event_attributes_builder().message(s).build(), - EventArgument::Unicode(s) => self - .event_attributes_builder() - .message(Message::Unicode(s)) - .build(), + let attribute: EventAttributes<'a> = match arg.into() { + EventArgument::Attributes(attr) => attr, + EventArgument::Message(m) => m.into(), }; let encoded = attribute.encode(); unsafe { nvtx_sys::ffi::nvtxDomainMarkEx(self.handle, &encoded) } } /// Create an RAII-friendly, domain-owned range type which (1) cannot be moved across thread boundaries and (2) automatically ended when dropped. Panics on drop() if the opening level doesn't match the closing level (since it must model a perfect stack). + /// + /// ``` + /// let domain = nvtx::Domain::new("Domain"); + /// + /// // creation from Rust string + /// let range = domain.local_range("simple name"); + /// + /// // creation from C string (since 1.77) + /// let range = domain.local_range(c"simple name"); + /// + /// // creation from EventAttributes + /// let attr = domain.event_attributes_builder().payload(1).message("complex range").build(); + /// let range = domain.local_range(attr); + /// + /// // explicitly end a range + /// drop(range) + /// ``` pub fn local_range<'a>(&'a self, arg: impl Into>) -> LocalRange<'a> { LocalRange::new(arg, self) } /// Create an RAII-friendly, domain-owned range type which (1) can be moved across thread boundaries and (2) automatically ended when dropped + /// + /// ``` + /// let domain = nvtx::Domain::new("Domain"); + /// + /// // creation from a unicode string + /// let range = domain.range("simple name"); + /// + /// // creation from a c string (from rust 1.77+) + /// let range = domain.range(c"simple name"); + /// + /// // creation from EventAttributes + /// let attr = domain.event_attributes_builder().payload(1).message("complex range").build(); + /// let range = domain.range(attr); + /// + /// // explicitly end a range + /// drop(range) + /// ``` pub fn range<'a>(&'a self, arg: impl Into>) -> Range<'a> { Range::new(arg, self) } diff --git a/src/domain/event_argument.rs b/src/domain/event_argument.rs index d262bd4..8119b70 100644 --- a/src/domain/event_argument.rs +++ b/src/domain/event_argument.rs @@ -1,52 +1,29 @@ -use super::{event_attributes::EventAttributes, message::Message}; -use std::ffi::{CStr, CString}; -use widestring::WideCString; +use super::{EventAttributes, Message}; -/// Convenience wrapper for all valid argument types +/// Convenience wrapper for all valid argument types to ranges and marks +/// +/// * Any string type will be translated to [`EventArgument::Message`]. +/// * If [`EventArgument::Attributes`] is the active discriminator: +/// - Then if its held [`EventAttributes`] only specifies a message, then it's Message will be used +/// - Otherwise, the existing [`EventAttributes`] will be used for the event. #[derive(Debug, Clone)] pub enum EventArgument<'a> { - /// discriminant for an owned ASCII string - Ascii(CString), - /// discriminant for an owned Unicode string - Unicode(WideCString), - /// discriminant for a detailed Attribute - EventAttribute(EventAttributes<'a>), + /// discriminator for a Message + Message(Message<'a>), + /// discriminator for an EventAttributes + Attributes(EventAttributes<'a>), } -impl<'a> From> for EventArgument<'a> { - fn from(value: EventAttributes<'a>) -> Self { - match value { +impl<'a, T: Into>> From for EventArgument<'a> { + fn from(value: T) -> Self { + match value.into() { EventAttributes { category: None, color: None, payload: None, - message: Some(Message::Ascii(s)), - } => EventArgument::Ascii(s), - EventAttributes { - category: None, - color: None, - payload: None, - message: Some(Message::Unicode(s)), - } => EventArgument::Unicode(s), - attr => EventArgument::EventAttribute(attr), + message: Some(m), + } => EventArgument::Message(m), + attr => EventArgument::Attributes(attr), } } } - -impl From<&str> for EventArgument<'_> { - fn from(v: &str) -> Self { - Self::Unicode(WideCString::from_str(v).expect("Could not convert to wide string")) - } -} - -impl From for EventArgument<'_> { - fn from(v: CString) -> Self { - Self::Ascii(v) - } -} - -impl From<&CStr> for EventArgument<'_> { - fn from(v: &CStr) -> Self { - Self::Ascii(CString::from(v)) - } -} diff --git a/src/domain/event_attributes.rs b/src/domain/event_attributes.rs index e732ecf..a2980ea 100644 --- a/src/domain/event_attributes.rs +++ b/src/domain/event_attributes.rs @@ -1,4 +1,4 @@ -use super::{category::Category, message::Message, Domain}; +use super::{Category, Domain, Message}; use crate::{Color, Payload, TypeValueEncodable}; /// All attributes that are associated with marks and ranges @@ -45,7 +45,29 @@ impl<'a> EventAttributes<'a> { } } +impl<'a, T: Into>> From for EventAttributes<'a> { + fn from(value: T) -> Self { + EventAttributes { + category: None, + color: None, + payload: None, + message: Some(value.into()), + } + } +} + /// Builder to facilitate easier construction of [`EventAttributes`] +/// +/// ``` +/// let cat = nvtx::Category::new("Category1"); +/// +/// let attr = nvtx::EventAttributesBuilder::default() +/// .category(&cat) +/// .color([20, 192, 240]) +/// .payload(3.141592) +/// .message("Hello") +/// .build(); +/// ``` #[derive(Debug, Clone)] pub struct EventAttributesBuilder<'a> { pub(super) domain: &'a Domain, @@ -57,6 +79,15 @@ pub struct EventAttributesBuilder<'a> { impl<'a> EventAttributesBuilder<'a> { /// Update the attribute's category. An assertion will be thrown if a Category is passed in whose domain is not the same as this builder + /// + /// ``` + /// let domain = nvtx::Domain::new("Domain"); + /// let cat = domain.register_category("Category1"); + /// // ... + /// let builder = domain.event_attributes_builder(); + /// // ... + /// let builder = builder.category(&cat); + /// ``` pub fn category(mut self, category: &'a Category<'a>) -> EventAttributesBuilder<'a> { assert!( std::ptr::eq(category.domain, self.domain), @@ -66,19 +97,40 @@ impl<'a> EventAttributesBuilder<'a> { self } - /// update the attribute's color + /// Update the builder's held [`Color`]. See [`Color`] for valid conversions. + /// + /// ``` + /// let domain = nvtx::Domain::new("Domain"); + /// let builder = domain.event_attributes_builder(); + /// // ... + /// let builder = builder.color([255, 255, 255]); + /// ``` pub fn color(mut self, color: impl Into) -> EventAttributesBuilder<'a> { self.color = Some(color.into()); self } - /// update the attribute's payload + /// Update the builder's held [`Payload`]. See [`Payload`] for valid conversions. + /// + /// ``` + /// let domain = nvtx::Domain::new("Domain"); + /// let builder = domain.event_attributes_builder(); + /// // ... + /// let builder = builder.payload(3.1415926535); + /// ``` pub fn payload(mut self, payload: impl Into) -> EventAttributesBuilder<'a> { self.payload = Some(payload.into()); self } /// Update the attribute's message. An assertion will be thrown if a RegisteredString is passed in whose domain is not the same as this builder + /// + /// ``` + /// let domain = nvtx::Domain::new("Domain"); + /// let builder = domain.event_attributes_builder(); + /// // ... + /// let builder = builder.message("test"); + /// ``` pub fn message(mut self, message: impl Into>) -> EventAttributesBuilder<'a> { let msg: Message = message.into(); if let Message::Registered(r) = &msg { @@ -91,7 +143,18 @@ impl<'a> EventAttributesBuilder<'a> { self } - /// build the attribute from the builder's state + /// Construct an [`EventAttributes`] from the builder's held state + /// + /// ``` + /// let domain = nvtx::Domain::new("Domain"); + /// let cat = domain.register_category("Category1"); + /// let attr = domain.event_attributes_builder() + /// .message("Example Range") + /// .color([224, 192, 128]) + /// .category(&cat) + /// .payload(1234567) + /// .build(); + /// ``` pub fn build(self) -> EventAttributes<'a> { EventAttributes { category: self.category.copied(), diff --git a/src/domain/local_range.rs b/src/domain/local_range.rs index 27d8883..8d4c135 100644 --- a/src/domain/local_range.rs +++ b/src/domain/local_range.rs @@ -1,13 +1,9 @@ +use super::{Domain, EventArgument}; use std::marker::PhantomData; -use super::{ - event_argument::EventArgument, event_attributes::EventAttributes, message::Message, Domain, -}; - /// A RAII-like object for modeling callstack Ranges within a Domain #[derive(Debug)] pub struct LocalRange<'a> { - pub(super) level: i32, pub(super) domain: &'a Domain, // prevent Sync + Send _phantom: PhantomData<*mut i32>, @@ -15,25 +11,12 @@ pub struct LocalRange<'a> { impl<'a> LocalRange<'a> { pub(super) fn new(arg: impl Into>, domain: &'a Domain) -> LocalRange<'a> { - let argument = arg.into(); - let arg = match argument { - EventArgument::EventAttribute(attr) => attr, - EventArgument::Ascii(s) => EventAttributes { - category: None, - color: None, - payload: None, - message: Some(Message::Ascii(s)), - }, - EventArgument::Unicode(s) => EventAttributes { - category: None, - color: None, - payload: None, - message: Some(Message::Unicode(s)), - }, + let arg = match arg.into() { + EventArgument::Attributes(attr) => attr, + EventArgument::Message(m) => domain.event_attributes_builder().message(m).build(), }; - let level = unsafe { nvtx_sys::ffi::nvtxDomainRangePushEx(domain.handle, &arg.encode()) }; + unsafe { nvtx_sys::ffi::nvtxDomainRangePushEx(domain.handle, &arg.encode()) }; LocalRange { - level, domain, _phantom: PhantomData, } @@ -42,10 +25,6 @@ impl<'a> LocalRange<'a> { impl<'a> Drop for LocalRange<'a> { fn drop(&mut self) { - let end_level = unsafe { nvtx_sys::ffi::nvtxDomainRangePop(self.domain.handle) }; - assert_eq!( - self.level, end_level, - "Mismatch on levels for domain::LocalRange" - ); + unsafe { nvtx_sys::ffi::nvtxDomainRangePop(self.domain.handle) }; } } diff --git a/src/domain/message.rs b/src/domain/message.rs index ccdbad2..9542387 100644 --- a/src/domain/message.rs +++ b/src/domain/message.rs @@ -1,16 +1,20 @@ -use super::registered_string::RegisteredString; -use crate::TypeValueEncodable; -use std::ffi::{CStr, CString}; +use super::RegisteredString; +use crate::{Str, TypeValueEncodable}; +use std::ffi::CString; use widestring::WideCString; /// Represents a message for use within events and ranges +/// +/// * [`Message::Ascii`] is the discriminator for ASCII C strings +/// * [`Message::Unicode`] is the discriminator for Rust strings and wide C strings +/// * [`Message::Registered`] is the discriminator for nvtx domain-registered strings #[derive(Debug, Clone)] pub enum Message<'a> { - /// discriminant for an owned ASCII string + /// discriminator for an owned ASCII string Ascii(CString), - /// discriminant for an owned Unicode string + /// discriminator for an owned Unicode string Unicode(WideCString), - /// discriminant for a registered string belonging to a domain + /// discriminator for a registered string belonging to a domain Registered(&'a RegisteredString<'a>), } @@ -20,27 +24,12 @@ impl<'a> From<&'a RegisteredString<'a>> for Message<'a> { } } -impl From for Message<'_> { - fn from(v: String) -> Self { - Self::Unicode(WideCString::from_str(v.as_str()).expect("Could not convert to wide string")) - } -} - -impl From<&str> for Message<'_> { - fn from(v: &str) -> Self { - Self::Unicode(WideCString::from_str(v).expect("Could not convert to wide string")) - } -} - -impl From for Message<'_> { - fn from(v: CString) -> Self { - Self::Ascii(v) - } -} - -impl From<&CStr> for Message<'_> { - fn from(v: &CStr) -> Self { - Self::Ascii(CString::from(v)) +impl<'a, T: Into> From for Message<'a> { + fn from(value: T) -> Self { + match value.into() { + Str::Ascii(s) => Message::Ascii(s), + Str::Unicode(s) => Message::Unicode(s), + } } } diff --git a/src/domain/range.rs b/src/domain/range.rs index d084691..0bb53b2 100644 --- a/src/domain/range.rs +++ b/src/domain/range.rs @@ -1,6 +1,4 @@ -use super::{ - event_argument::EventArgument, event_attributes::EventAttributes, message::Message, Domain, -}; +use super::{Domain, EventArgument}; /// A RAII-like object for modeling start/end Ranges within a Domain #[derive(Debug)] @@ -11,21 +9,9 @@ pub struct Range<'a> { impl<'a> Range<'a> { pub(super) fn new(arg: impl Into>, domain: &'a Domain) -> Range<'a> { - let argument = arg.into(); - let arg = match argument { - EventArgument::EventAttribute(attr) => attr, - EventArgument::Ascii(s) => EventAttributes { - category: None, - color: None, - payload: None, - message: Some(Message::Ascii(s)), - }, - EventArgument::Unicode(s) => EventAttributes { - category: None, - color: None, - payload: None, - message: Some(Message::Unicode(s)), - }, + let arg = match arg.into() { + EventArgument::Attributes(attr) => attr, + EventArgument::Message(m) => m.into(), }; let id = unsafe { nvtx_sys::ffi::nvtxDomainRangeStartEx(domain.handle, &arg.encode()) }; Range { id, domain } diff --git a/src/sync.rs b/src/domain/sync.rs similarity index 68% rename from src/sync.rs rename to src/domain/sync.rs index ecee43b..ecb1e88 100644 --- a/src/sync.rs +++ b/src/domain/sync.rs @@ -8,6 +8,13 @@ pub struct UserSync<'a> { impl<'a> UserSync<'a> { /// Signal to tools that an attempt to acquire a user defined synchronization object. + /// + /// ``` + /// let d = nvtx::Domain::new("domain"); + /// let us = d.user_sync("custom object"); + /// // ... + /// let started = us.acquire(); + /// ``` #[must_use = "Dropping the return will violate the state machine"] pub fn acquire(self) -> UserSyncAcquireStart<'a> { unsafe { nvtx_sys::ffi::nvtxDomainSyncUserAcquireStart(self.handle) } @@ -28,6 +35,15 @@ pub struct UserSyncAcquireStart<'a> { impl<'a> UserSyncAcquireStart<'a> { /// Signal to tools of failure in acquiring a user defined synchronization object. + /// + /// ``` + /// let d = nvtx::Domain::new("domain"); + /// let us = d.user_sync("custom object"); + /// // ... + /// let started = us.acquire(); + /// // ... + /// let us2 = started.failed(); + /// ``` #[must_use = "Dropping the return will result in the Synchronization Object being destroyed"] pub fn failed(self) -> UserSync<'a> { unsafe { nvtx_sys::ffi::nvtxDomainSyncUserAcquireFailed(self.sync_object.handle) } @@ -35,6 +51,15 @@ impl<'a> UserSyncAcquireStart<'a> { } /// Signal to tools of success in acquiring a user defined synchronization object. + /// + /// ``` + /// let d = nvtx::Domain::new("domain"); + /// let us = d.user_sync("custom object"); + /// // ... + /// let started = us.acquire(); + /// // ... + /// let success = started.success(); + /// ``` #[must_use = "Dropping the return will violate the state machine"] pub fn success(self) -> UserSyncSuccess<'a> { unsafe { nvtx_sys::ffi::nvtxDomainSyncUserAcquireSuccess(self.sync_object.handle) } @@ -51,8 +76,19 @@ pub struct UserSyncSuccess<'a> { impl<'a> UserSyncSuccess<'a> { /// Signal to tools of releasing a reservation on user defined synchronization object. + /// + /// ``` + /// let d = nvtx::Domain::new("domain"); + /// let us = d.user_sync("custom object"); + /// // ... + /// let started = us.acquire(); + /// // ... + /// let success = started.success(); + /// // ... + /// let us2 = success.release(); + /// ``` #[must_use = "Dropping the return will result in the Synchronization Object being destroyed"] - pub fn releasing(self) -> UserSync<'a> { + pub fn release(self) -> UserSync<'a> { unsafe { nvtx_sys::ffi::nvtxDomainSyncUserReleasing(self.sync_object.handle) } self.sync_object } diff --git a/src/event_argument.rs b/src/event_argument.rs index 4e7bafe..b155d52 100644 --- a/src/event_argument.rs +++ b/src/event_argument.rs @@ -1,52 +1,29 @@ use crate::{EventAttributes, Message}; -use std::ffi::{CStr, CString}; -use widestring::WideCString; -/// Convenience wrapper for all valid argument types +/// Convenience wrapper for all valid argument types to ranges and marks +/// +/// * Any string type will be translated to [`EventArgument::Message`]. +/// * If [`EventArgument::Attributes`] is the active discriminator: +/// - Then if its held [`EventAttributes`] only specifies a message, then it's Message will be used +/// - Otherwise, the existing [`EventAttributes`] will be used for the event. #[derive(Debug, Clone)] pub enum EventArgument { - /// discriminant for an owned ASCII string - Ascii(CString), - /// discriminant for an owned Unicode string - Unicode(WideCString), - /// discriminant for a detailed Attribute - EventAttribute(EventAttributes), + /// discriminator for a Message + Message(Message), + /// discriminator for an EventAttributes + Attributes(EventAttributes), } -impl From for EventArgument { - fn from(value: EventAttributes) -> Self { - match value { +impl> From for EventArgument { + fn from(value: T) -> Self { + match value.into() { EventAttributes { category: None, color: None, payload: None, - message: Some(Message::Ascii(s)), - } => EventArgument::Ascii(s), - EventAttributes { - category: None, - color: None, - payload: None, - message: Some(Message::Unicode(s)), - } => EventArgument::Unicode(s), - attr => EventArgument::EventAttribute(attr), + message: Some(m), + } => EventArgument::Message(m), + attr => EventArgument::Attributes(attr), } } } - -impl From<&str> for EventArgument { - fn from(v: &str) -> Self { - Self::Unicode(WideCString::from_str(v).expect("Could not convert to wide string")) - } -} - -impl From for EventArgument { - fn from(v: CString) -> Self { - Self::Ascii(v) - } -} - -impl From<&CStr> for EventArgument { - fn from(v: &CStr) -> Self { - Self::Ascii(CString::from(v)) - } -} diff --git a/src/event_attributes.rs b/src/event_attributes.rs index a69b486..b70c105 100644 --- a/src/event_attributes.rs +++ b/src/event_attributes.rs @@ -44,7 +44,29 @@ impl EventAttributes { } } +impl> From for EventAttributes { + fn from(value: T) -> Self { + EventAttributes { + category: None, + color: None, + payload: None, + message: Some(value.into()), + } + } +} + /// Builder to facilitate easier construction of [`EventAttributes`] +/// +/// ``` +/// let cat = nvtx::Category::new("Category1"); +/// +/// let attr = nvtx::EventAttributesBuilder::default() +/// .category(&cat) +/// .color([20, 192, 240]) +/// .payload(3.141592) +/// .message("Hello") +/// .build(); +/// ``` #[derive(Debug, Clone, Default)] pub struct EventAttributesBuilder<'a> { pub(super) category: Option<&'a Category>, @@ -54,31 +76,66 @@ pub struct EventAttributesBuilder<'a> { } impl<'a> EventAttributesBuilder<'a> { - /// update the attribute's category + /// Update the builder's held [`Category`]. + /// + /// ``` + /// let cat = nvtx::Category::new("Category1"); + /// let builder = nvtx::EventAttributesBuilder::default(); + /// // ... + /// let builder = builder.category(&cat); + /// ``` pub fn category(mut self, category: &'a Category) -> EventAttributesBuilder<'a> { self.category = Some(category); self } - /// update the attribute's color + /// Update the builder's held [`Color`]. See [`Color`] for valid conversions. + /// + /// ``` + /// let builder = nvtx::EventAttributesBuilder::default(); + /// // ... + /// let builder = builder.color([255, 255, 255]); + /// ``` pub fn color(mut self, color: impl Into) -> EventAttributesBuilder<'a> { self.color = Some(color.into()); self } - /// update the attribute's payload + /// Update the builder's held [`Payload`]. See [`Payload`] for valid conversions. + /// + /// ``` + /// let builder = nvtx::EventAttributesBuilder::default(); + /// // ... + /// let builder = builder.payload(3.1415926535); + /// ``` pub fn payload(mut self, payload: impl Into) -> EventAttributesBuilder<'a> { self.payload = Some(payload.into()); self } - /// update the attribute's message + /// Update the the builder's held [`Message`]. See [`Message`] for valid conversions. + /// + /// ``` + /// let builder = nvtx::EventAttributesBuilder::default(); + /// // ... + /// let builder = builder.message("test"); + /// ``` pub fn message(mut self, message: impl Into) -> EventAttributesBuilder<'a> { self.message = Some(message.into()); self } - /// build the attribute from the builder's state + /// Construct an [`EventAttributes`] from the builder's held state + /// + /// ``` + /// let cat = nvtx::Category::new("Category1"); + /// let attr = nvtx::EventAttributesBuilder::default() + /// .message("Example Range") + /// .color([224, 192, 128]) + /// .category(&cat) + /// .payload(1234567) + /// .build(); + /// ``` pub fn build(self) -> EventAttributes { EventAttributes { category: self.category.copied(), diff --git a/src/lib.rs b/src/lib.rs index 8e2fad1..86f8197 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,26 +1,23 @@ #![deny(missing_docs)] //! crate for interfacing with NVIDIA's nvtx API - -pub use crate::{ - category::Category, - color::Color, - domain::Domain, - event_argument::EventArgument, - event_attributes::{EventAttributes, EventAttributesBuilder}, - local_range::LocalRange, - message::Message, - payload::Payload, - range::Range, - str::Str, -}; +//! +//! When not running within NSight profilers, the calls will dispatch to +//! empty method stubs, thus enabling low-overhead profiling. +//! +//! * All events are fully supported: +//! * process ranges [`crate::Range`] and [`crate::domain::Range`] +//! * thread ranges [`crate::LocalRange`] and [`crate::domain::LocalRange`] +//! * marks [`crate::mark`] and [`crate::Domain::mark`] +//! * Naming threads is fully supported (See [`crate::name_thread`] and [`crate::name_current_thread`]) +//! * Domain, category, and registered strings are fully supported. +//! * The user-defined synchronization API is implemented +//! * The user-defined resource naming API is implemented for generic types only. /// color support pub mod color; /// specialized types for use within a domain context pub mod domain; -/// user-defined synchronization objects -pub mod sync; mod category; mod event_argument; @@ -31,6 +28,19 @@ mod payload; mod range; mod str; +pub use crate::{ + category::Category, + color::Color, + domain::Domain, + event_argument::EventArgument, + event_attributes::{EventAttributes, EventAttributesBuilder}, + local_range::LocalRange, + message::Message, + payload::Payload, + range::Range, + str::Str, +}; + trait TypeValueEncodable { type Type; type Value; @@ -38,16 +48,36 @@ trait TypeValueEncodable { fn default_encoding() -> (Self::Type, Self::Value); } -/// Marks an instantaneous event in the application. A marker can contain a text message or specify additional information using the event attributes structure. These attributes include a text message, color, category, and a payload. Each of the attributes is optional. +/// Marks an instantaneous event in the application. +/// +/// A marker can contain a text message or specify additional information using the event attributes structure. These attributes include a text message, color, category, and a payload. Each of the attributes is optional. +/// ``` +/// nvtx::mark("Sample mark"); +/// +/// nvtx::mark(c"Another example"); +/// +/// nvtx::mark(nvtx::EventAttributesBuilder::default().message("Interesting example").color([255, 0, 0]).build()); +/// ``` pub fn mark(argument: impl Into) { match argument.into() { - EventArgument::Ascii(s) => unsafe { nvtx_sys::ffi::nvtxMarkA(s.as_ptr()) }, - EventArgument::Unicode(s) => unsafe { nvtx_sys::ffi::nvtxMarkW(s.as_ptr().cast()) }, - EventArgument::EventAttribute(a) => unsafe { nvtx_sys::ffi::nvtxMarkEx(&a.encode()) }, + EventArgument::Message(m) => match m { + Message::Ascii(s) => unsafe { nvtx_sys::ffi::nvtxMarkA(s.as_ptr()) }, + Message::Unicode(s) => unsafe { nvtx_sys::ffi::nvtxMarkW(s.as_ptr().cast()) }, + }, + EventArgument::Attributes(a) => unsafe { nvtx_sys::ffi::nvtxMarkEx(&a.encode()) }, } } -/// Name an active thread of the current process. If an invalid thread ID is provided or a thread ID from a different process is used the behavior of the tool is implementation dependent. +/// Name an active thread of the current process. +/// +/// If an invalid thread ID is provided or a thread ID from a different process is used the behavior of the tool is implementation dependent. +/// +/// See [`Str`] for valid conversions +/// +/// Note: getting the native TID is not necessarily simple. If you are trying to name the current thread, please use [`name_current_thread`] +/// ``` +/// nvtx::name_thread(12345, "My custom name"); +/// ``` pub fn name_thread(native_tid: u32, name: impl Into) { match name.into() { Str::Ascii(s) => unsafe { nvtx_sys::ffi::nvtxNameOsThreadA(native_tid, s.as_ptr()) }, @@ -59,6 +89,11 @@ pub fn name_thread(native_tid: u32, name: impl Into) { #[cfg(feature = "name-current-thread")] /// Name the current thread of the current process +/// +/// See [`Str`] for valid conversions +/// ``` +/// nvtx::name_current_thread("Main thread"); +/// ``` pub fn name_current_thread(name: impl Into) { let tid = gettid::gettid() as u32; match name.into() { @@ -68,11 +103,21 @@ pub fn name_current_thread(name: impl Into) { } /// Register a new category within the default (global) scope. Categories are used to group sets of events. +/// +/// See [`Str`] for valid conversions +/// ``` +/// let cat_a = nvtx::register_category("Category A"); +/// ``` pub fn register_category(name: impl Into) -> Category { Category::new(name) } /// Register many categories within the default (global) scope. Categories are used to group sets of events. +/// +/// See [`Str`] for valid conversions +/// ``` +/// let [cat_a, cat_b] = nvtx::register_categories(["Category A", "Category B"]); +/// ``` pub fn register_categories(names: [impl Into; C]) -> [Category; C] { names.map(register_category) } diff --git a/src/local_range.rs b/src/local_range.rs index 35e6454..505e3b5 100644 --- a/src/local_range.rs +++ b/src/local_range.rs @@ -1,30 +1,40 @@ use std::marker::PhantomData; -use crate::event_argument::EventArgument; +use crate::{EventArgument, Message}; /// A RAII-like object for modeling callstack Ranges #[derive(Debug)] pub struct LocalRange { - level: i32, // prevent Sync + Send _phantom: PhantomData<*mut i32>, } impl LocalRange { - /// Create an RAII-friendly range type which (1) cannot be moved across thread boundaries and (2) automatically ended when dropped. Panics on drop() if the opening level doesn't match the closing level (since it must model a perfect stack). + /// Create an RAII-friendly range type which (1) cannot be moved across thread boundaries and (2) automatically ended when dropped. + /// + /// ``` + /// // creation from Rust string + /// let range = nvtx::LocalRange::new("simple name"); + /// + /// // creation from C string (since 1.77) + /// let range = nvtx::LocalRange::new(c"simple name"); + /// + /// // creation from EventAttributes + /// let attr = nvtx::EventAttributesBuilder::default().payload(1).message("complex range").build(); + /// let range = nvtx::LocalRange::new(attr); + /// + /// // explicitly end a range + /// drop(range) + /// ``` pub fn new(arg: impl Into) -> LocalRange { - let argument = arg.into(); - let level = match &argument { - EventArgument::Ascii(s) => unsafe { nvtx_sys::ffi::nvtxRangePushA(s.as_ptr()) }, - EventArgument::Unicode(s) => unsafe { - nvtx_sys::ffi::nvtxRangePushW(s.as_ptr().cast()) - }, - EventArgument::EventAttribute(a) => unsafe { - nvtx_sys::ffi::nvtxRangePushEx(&a.encode()) + match arg.into() { + EventArgument::Message(m) => match m { + Message::Ascii(s) => unsafe { nvtx_sys::ffi::nvtxRangePushA(s.as_ptr()) }, + Message::Unicode(s) => unsafe { nvtx_sys::ffi::nvtxRangePushW(s.as_ptr().cast()) }, }, + EventArgument::Attributes(a) => unsafe { nvtx_sys::ffi::nvtxRangePushEx(&a.encode()) }, }; LocalRange { - level, _phantom: PhantomData, } } @@ -32,7 +42,6 @@ impl LocalRange { impl Drop for LocalRange { fn drop(&mut self) { - let end_level = unsafe { nvtx_sys::ffi::nvtxRangePop() }; - assert_eq!(self.level, end_level, "Mismatch on levels for LocalRange"); + unsafe { nvtx_sys::ffi::nvtxRangePop() }; } } diff --git a/src/message.rs b/src/message.rs index ab759ac..947a715 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1,37 +1,25 @@ -use crate::TypeValueEncodable; -use std::ffi::{CStr, CString}; +use crate::{Str, TypeValueEncodable}; +use std::ffi::CString; use widestring::WideCString; /// Represents a message for use within events and ranges +/// +/// * [`Message::Ascii`] is the discriminator for ASCII C strings +/// * [`Message::Unicode`] is the discriminator for Rust strings and wide C strings #[derive(Debug, Clone)] pub enum Message { - /// discriminant for an owned ASCII string + /// discriminator for an owned ASCII string Ascii(CString), - /// discriminant for an owned Unicode string + /// discriminator for an owned Unicode string Unicode(WideCString), } -impl From for Message { - fn from(v: String) -> Self { - Self::Unicode(WideCString::from_str(v.as_str()).expect("Could not convert to wide string")) - } -} - -impl From<&str> for Message { - fn from(v: &str) -> Self { - Self::Unicode(WideCString::from_str(v).expect("Could not convert to wide string")) - } -} - -impl From for Message { - fn from(v: CString) -> Self { - Self::Ascii(v) - } -} - -impl From<&CStr> for Message { - fn from(v: &CStr) -> Self { - Self::Ascii(CString::from(v)) +impl> From for Message { + fn from(value: T) -> Self { + match value.into() { + Str::Ascii(s) => Message::Ascii(s), + Str::Unicode(s) => Message::Unicode(s), + } } } diff --git a/src/payload.rs b/src/payload.rs index 5a64d26..252486d 100644 --- a/src/payload.rs +++ b/src/payload.rs @@ -1,19 +1,26 @@ use crate::TypeValueEncodable; -/// Represents a payload value for use within event attributes +/// Represents a payload value for use within [`crate::EventAttributes`] and [`crate::domain::EventAttributes`] +/// +/// * [`Payload::Float`] holds a 32-bit floating-point playload +/// * [`Payload::Double`] holds a 64-bit floating-point playload +/// * [`Payload::Int32`] holds a 32-bit integral playload +/// * [`Payload::Int64`] holds a 64-bit integral playload +/// * [`Payload::Uint32`] holds a 32-bit unsigned integral playload +/// * [`Payload::Uint64`] holds a 64-bit unsigned integral playload #[derive(Debug, Clone, Copy)] pub enum Payload { - /// the payload shall hold a 32-bit floating-point value + /// a 32-bit floating-point value Float(f32), - /// the payload shall hold a 64-bit floating-point value + /// a 64-bit floating-point value Double(f64), - /// the payload shall hold a 32-bit integral value + /// a 32-bit integral value Int32(i32), - /// the payload shall hold a 64-bit integral value + /// a 64-bit integral value Int64(i64), - /// the payload shall hold a 32-bit unsigned integral value + /// a 32-bit unsigned integral value Uint32(u32), - /// the payload shall hold a 64-bit unsigned integral value + /// a 64-bit unsigned integral value Uint64(u64), } diff --git a/src/range.rs b/src/range.rs index 834152e..64c4720 100644 --- a/src/range.rs +++ b/src/range.rs @@ -1,4 +1,4 @@ -use crate::event_argument::EventArgument; +use crate::{EventArgument, Message}; /// A RAII-like object for modeling start/end Ranges #[derive(Debug)] @@ -8,16 +8,29 @@ pub struct Range { impl Range { /// Create an RAII-friendly range type which (1) can be moved across thread boundaries and (2) automatically ended when dropped + /// + /// ``` + /// // creation from a unicode string + /// let range = nvtx::Range::new("simple name"); + /// + /// // creation from a c string (from rust 1.77+) + /// let range = nvtx::Range::new(c"simple name"); + /// + /// // creation from EventAttributes + /// let attr = nvtx::EventAttributesBuilder::default().payload(1).message("complex range").build(); + /// let range = nvtx::Range::new(attr); + /// + /// // explicitly end a range + /// drop(range) + /// ``` pub fn new(arg: impl Into) -> Range { let argument = arg.into(); let id = match &argument { - EventArgument::Ascii(s) => unsafe { nvtx_sys::ffi::nvtxRangeStartA(s.as_ptr()) }, - EventArgument::Unicode(s) => unsafe { - nvtx_sys::ffi::nvtxRangeStartW(s.as_ptr().cast()) - }, - EventArgument::EventAttribute(a) => unsafe { - nvtx_sys::ffi::nvtxRangeStartEx(&a.encode()) + EventArgument::Message(m) => match m { + Message::Ascii(s) => unsafe { nvtx_sys::ffi::nvtxRangeStartA(s.as_ptr()) }, + Message::Unicode(s) => unsafe { nvtx_sys::ffi::nvtxRangeStartW(s.as_ptr().cast()) }, }, + EventArgument::Attributes(a) => unsafe { nvtx_sys::ffi::nvtxRangeStartEx(&a.encode()) }, }; Range { id } } diff --git a/src/str.rs b/src/str.rs index 365c296..32ed25e 100644 --- a/src/str.rs +++ b/src/str.rs @@ -1,7 +1,10 @@ use std::ffi::{CStr, CString}; -use widestring::WideCString; +use widestring::{WideCStr, WideCString}; /// A convenience wrapper for various string types +/// +/// * [`Str::Ascii`] is the discriminator for C string types +/// * [`Str::Unicode`] is the discriminator for Rust string types and C wide string types #[derive(Debug, Clone)] pub enum Str { /// Represents an ASCII friendly string @@ -18,7 +21,7 @@ impl From for Str { impl From<&str> for Str { fn from(v: &str) -> Self { - Self::Unicode(WideCString::from_str(v).expect("Could not convert to wide string")) + String::from(v).into() } } @@ -30,6 +33,18 @@ impl From for Str { impl From<&CStr> for Str { fn from(v: &CStr) -> Self { - Self::Ascii(CString::from(v)) + CString::from(v).into() + } +} + +impl From for Str { + fn from(v: WideCString) -> Self { + Self::Unicode(v) + } +} + +impl From<&WideCStr> for Str { + fn from(v: &WideCStr) -> Self { + WideCString::from(v).into() } }