#![deny(missing_docs)]
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg))]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(test, allow(clippy::expect_used, clippy::unwrap_used))]
#![deny(unsafe_op_in_unsafe_fn)]
#[cfg(feature = "alloc")]
extern crate alloc;
mod error;
pub use error::NvtxError;
#[cfg(feature = "alloc")]
mod category;
#[cfg(feature = "alloc")]
pub use category::Category;
#[cfg(feature = "alloc")]
mod common;
#[cfg(all(test, feature = "std"))]
pub use common::test_utils;
pub mod color;
pub type Color = color::Color;
#[cfg(all(feature = "alloc", feature = "cuda"))]
mod cuda;
#[cfg(all(feature = "alloc", feature = "cuda"))]
pub use cuda::*;
#[cfg(all(feature = "alloc", feature = "cuda_runtime"))]
mod cuda_runtime;
#[cfg(all(feature = "alloc", feature = "cuda_runtime"))]
pub use cuda_runtime::*;
#[cfg(feature = "alloc")]
pub mod domain;
#[cfg(feature = "alloc")]
pub type Domain = domain::Domain;
#[cfg(feature = "alloc")]
pub type EventArgument = crate::common::GenericEventArgument<Message, EventAttributes>;
#[cfg(feature = "alloc")]
pub type EventAttributes = crate::common::GenericEventAttributes<Category, Message>;
#[cfg(feature = "alloc")]
impl EventAttributes {
pub fn builder() -> crate::common::GenericEventAttributesBuilder<Category, Message> {
crate::common::GenericEventAttributesBuilder::default()
}
}
#[cfg(feature = "alloc")]
pub type Message = crate::common::GenericMessage<()>;
#[cfg(feature = "alloc")]
fn strip_registered_message_for_global_context(arg: EventArgument) -> EventArgument {
match arg {
EventArgument::Attributes(mut attr) => {
if matches!(attr.message, Some(Message::Registered(()))) {
attr.message = None;
}
EventArgument::Attributes(attr)
}
EventArgument::Message(Message::Registered(())) => {
EventArgument::Attributes(EventAttributes {
category: None,
color: None,
message: None,
payload: None,
})
}
arg @ EventArgument::Message(_) => arg,
}
}
pub mod native_types;
mod payload;
pub use payload::Payload;
#[cfg(feature = "alloc")]
mod ranges;
#[cfg(feature = "alloc")]
pub use ranges::{LocalRange, Range};
#[cfg(feature = "alloc")]
mod str;
#[cfg(feature = "alloc")]
pub use crate::str::{Str, StrError};
#[cfg(all(feature = "tracing", feature = "std"))]
pub mod tracing;
pub use nvtx_sys as sys;
pub trait TypeValueEncodable {
type Type;
type Value;
fn encode(&self) -> (Self::Type, Self::Value);
fn default_encoding() -> (Self::Type, Self::Value);
}
pub fn mark_ascii(message: &core::ffi::CStr) {
nvtx_sys::mark_ascii(message);
}
pub fn mark_unicode(message: &widestring::WideCStr) {
nvtx_sys::mark_unicode(message);
}
#[cfg(feature = "alloc")]
pub fn mark(argument: impl Into<EventArgument>) {
let argument = argument.into();
match try_mark(argument.clone()) {
Ok(()) => {}
Err(error) => {
debug_assert!(false, "{error}");
let _ = try_mark(strip_registered_message_for_global_context(argument));
}
}
}
#[cfg(feature = "alloc")]
pub fn try_mark(argument: impl Into<EventArgument>) -> Result<(), NvtxError> {
match argument.into() {
EventArgument::Message(Message::Ascii(s)) => mark_ascii(&s),
EventArgument::Message(Message::Unicode(s)) => mark_unicode(&s),
EventArgument::Message(Message::Registered(())) => {
return Err(NvtxError::RegisteredStringInGlobalContext);
}
EventArgument::Attributes(a) => {
if matches!(a.message, Some(Message::Registered(()))) {
return Err(NvtxError::RegisteredStringInGlobalContext);
}
nvtx_sys::mark_ex(&a.encode());
}
}
Ok(())
}
#[cfg(feature = "alloc")]
pub fn name_thread(native_tid: u32, name: impl Into<Str>) {
match name.into() {
Str::Ascii(s) => name_thread_ascii(native_tid, &s),
Str::Unicode(s) => name_thread_unicode(native_tid, &s),
}
}
pub fn name_thread_ascii(native_tid: u32, name: &core::ffi::CStr) {
nvtx_sys::name_os_thread_ascii(native_tid, name);
}
pub fn name_thread_unicode(native_tid: u32, name: &widestring::WideCStr) {
nvtx_sys::name_os_thread_unicode(native_tid, name);
}
#[cfg(all(feature = "name-current-thread", feature = "std"))]
pub fn name_current_thread(name: impl Into<Str>) {
let raw_tid = gettid::gettid();
let Ok(native_tid) = u32::try_from(raw_tid) else {
debug_assert!(false, "OS thread id {raw_tid} does not fit into u32");
return;
};
name_thread(native_tid, name);
}
#[cfg(feature = "alloc")]
pub fn register_category(name: impl Into<Str>) -> Category {
Category::new(name)
}
#[cfg(feature = "alloc")]
pub fn register_categories<const C: usize>(names: [impl Into<Str>; C]) -> [Category; C] {
names.map(register_category)
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;
use crate::common::TestUtils;
use std::ffi::CString;
use widestring::WideCString;
fn lossy_str(value: &str) -> Str {
Str::from_str_lossy(value)
}
#[test]
fn test_message_ascii() {
let cstr = CString::new("hello").unwrap();
let m = Message::Ascii(cstr.clone());
assert!(matches!(m, Message::Ascii(s) if s == cstr));
}
#[test]
fn test_message_unicode() {
let s = "hello";
let wstr = WideCString::from_str(s).unwrap();
let m = Message::Unicode(wstr.clone());
assert!(matches!(m, Message::Unicode(s) if s == wstr));
}
#[test]
fn test_encode_ascii() {
let cstr = CString::new("hello").unwrap();
let m = Message::Ascii(cstr.clone());
TestUtils::assert_message_ascii_encoding(&m, "hello");
}
#[test]
fn test_encode_unicode() {
let s = "hello";
let wstr = WideCString::from_str(s).unwrap();
let m = Message::Unicode(wstr.clone());
TestUtils::assert_message_unicode_encoding(&m, "hello");
}
#[test]
fn register_category_test() {
let cat1 = crate::register_category(lossy_str("category 1"));
let cat2 = crate::register_category(lossy_str("category 2"));
assert_ne!(cat1, cat2);
}
#[test]
fn register_categories() {
let [cat1, cat2] =
crate::register_categories([lossy_str("category 1"), lossy_str("category 2")]);
assert_ne!(cat1, cat2);
}
#[test]
fn test_builder_color() {
let builder = EventAttributes::builder();
let color = Color::new(0x11, 0x22, 0x44, 0x88);
let attr = builder.color(color).build();
assert!(matches!(attr.color, Some(c) if c == color));
}
#[test]
fn test_builder_category() {
let builder = EventAttributes::builder();
let cat = register_category(lossy_str("cat"));
let attr = builder.category(cat).build();
assert!(matches!(attr.category, Some(c) if c == cat));
}
#[test]
fn test_builder_payload() {
let attr = EventAttributes::builder().payload(1_i32).build();
assert!(matches!(attr.payload, Some(Payload::Int32(i)) if i == 1_i32));
let attr = EventAttributes::builder().payload(2_u32).build();
assert!(matches!(attr.payload, Some(Payload::Uint32(i)) if i == 2_u32));
let attr = EventAttributes::builder().payload(1_i64).build();
assert!(matches!(attr.payload, Some(Payload::Int64(i)) if i == 1_i64));
let attr = EventAttributes::builder().payload(2_u64).build();
assert!(matches!(attr.payload, Some(Payload::Uint64(i)) if i == 2_u64));
let attr = EventAttributes::builder().payload(1.0_f32).build();
assert!(matches!(attr.payload, Some(Payload::Float(i)) if i == 1.0_f32));
let attr = EventAttributes::builder().payload(2.0_f64).build();
assert!(matches!(attr.payload, Some(Payload::Double(i)) if i == 2.0_f64));
}
#[test]
fn test_builder_message() {
let builder = EventAttributes::builder();
let string = "This is a message";
let attr = builder.message(lossy_str(string)).build();
assert!(
matches!(attr.message, Some(Message::Unicode(s)) if s.to_string().unwrap() == string)
);
let builder = EventAttributes::builder();
let cstring = CString::new("This is a message").unwrap();
let attr = builder.message(cstring.clone()).build();
assert!(matches!(attr.message, Some(Message::Ascii(s)) if s == cstring));
}
#[test]
fn test_try_mark_rejects_registered_message() {
let arg = EventArgument::Message(Message::Registered(()));
assert!(matches!(
try_mark(arg),
Err(NvtxError::RegisteredStringInGlobalContext)
));
}
#[test]
fn test_try_mark_rejects_registered_message_in_attributes() {
let attr = EventAttributes::builder()
.message(Message::Registered(()))
.build();
assert!(matches!(
try_mark(attr),
Err(NvtxError::RegisteredStringInGlobalContext)
));
}
}