use crate::{
common::{
event_attributes::GenericEventAttributesBuilder, CategoryEncodable, GenericEventArgument,
GenericEventAttributes,
},
Color, NvtxError, Payload, Str, TypeValueEncodable,
};
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::collections::BTreeMap as StringMap;
use alloc::string::{String, ToString};
use core::{
marker::PhantomData,
sync::atomic::{AtomicU32, Ordering},
};
#[cfg(all(feature = "alloc", not(feature = "std")))]
use spin::Mutex;
#[cfg(feature = "std")]
use std::collections::HashMap as StringMap;
#[cfg(feature = "std")]
use std::sync::Mutex;
mod category;
pub use category::Category;
pub type EventArgument<'a> = GenericEventArgument<Message<'a>, EventAttributes<'a>>;
pub type EventAttributes<'a> = GenericEventAttributes<Category<'a>, Message<'a>>;
#[derive(Clone)]
pub struct EventAttributesBuilder<'a> {
pub(super) domain: &'a Domain,
pub(super) inner: GenericEventAttributesBuilder<Category<'a>, Message<'a>>,
}
impl<'a> EventAttributesBuilder<'a> {
fn category_in_builder_domain(&self, category: Category<'a>) -> Category<'a> {
if core::ptr::eq(category.domain(), self.domain) {
category
} else {
Category::new(category.encode_id(), self.domain)
}
}
fn resolve_registered_message(
&self,
message: impl Into<Message<'a>>,
) -> Result<RegisteredString<'a>, NvtxError> {
let msg = match message.into() {
Message::Ascii(s) => self.domain.register_string(s),
Message::Unicode(s) => self.domain.register_string(s),
Message::Registered(r) => r,
};
if !core::ptr::eq(msg.domain(), self.domain) {
return Err(NvtxError::DomainMismatch);
}
Ok(msg)
}
#[must_use]
pub fn category(mut self, category: Category<'a>) -> EventAttributesBuilder<'a> {
if core::ptr::eq(category.domain(), self.domain) {
self.inner = self.inner.category(category);
} else {
debug_assert!(false, "{:?}", NvtxError::DomainMismatch);
let category = self.category_in_builder_domain(category);
self.inner = self.inner.category(category);
}
self
}
pub fn try_category(
mut self,
category: Category<'a>,
) -> Result<EventAttributesBuilder<'a>, NvtxError> {
if !core::ptr::eq(category.domain(), self.domain) {
return Err(NvtxError::DomainMismatch);
}
self.inner = self.inner.category(category);
Ok(self)
}
#[must_use]
pub fn category_name(mut self, name: impl Into<Str>) -> EventAttributesBuilder<'a> {
let category = self.domain.register_category(name);
self.inner = self.inner.category(category);
self
}
#[must_use]
pub fn color(mut self, color: impl Into<Color>) -> EventAttributesBuilder<'a> {
self.inner = self.inner.color(color);
self
}
#[must_use]
pub fn payload(mut self, payload: impl Into<Payload>) -> EventAttributesBuilder<'a> {
self.inner = self.inner.payload(payload);
self
}
#[must_use]
pub fn message(mut self, message: impl Into<Message<'a>>) -> EventAttributesBuilder<'a> {
match self.resolve_registered_message(message) {
Ok(msg) => {
self.inner = self.inner.message(Message::Registered(msg));
}
Err(error) => {
debug_assert!(false, "{error}");
self.inner = self.inner.clear_message();
}
}
self
}
pub fn try_message(
mut self,
message: impl Into<Message<'a>>,
) -> Result<EventAttributesBuilder<'a>, NvtxError> {
let msg = self.resolve_registered_message(message)?;
self.inner = self.inner.message(Message::Registered(msg));
Ok(self)
}
#[must_use]
pub fn build(self) -> EventAttributes<'a> {
self.inner.build()
}
}
mod identifier;
#[cfg(feature = "cuda")]
pub use self::identifier::CudaIdentifier;
#[cfg(feature = "cuda_runtime")]
pub use self::identifier::CudaRuntimeIdentifier;
#[cfg(target_family = "unix")]
pub use self::identifier::PThreadIdentifier;
pub use identifier::{GenericIdentifier, Identifier};
mod ranges;
pub use ranges::{LocalRange, Range};
mod registered_string;
pub use registered_string::RegisteredString;
pub type Message<'a> = crate::common::GenericMessage<RegisteredString<'a>>;
mod resource;
pub use resource::Resource;
pub mod sync;
#[derive(Debug)]
pub struct Domain {
handle: nvtx_sys::DomainHandle,
registered_strings: AtomicU32,
registered_categories: AtomicU32,
strings: Mutex<StringMap<String, (nvtx_sys::StringHandle, u32)>>,
categories: Mutex<StringMap<String, u32>>,
}
#[cfg(feature = "std")]
fn lock_unpoison<T>(mutex: &Mutex<T>) -> std::sync::MutexGuard<'_, T> {
mutex
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
#[cfg(all(feature = "alloc", not(feature = "std")))]
fn lock_unpoison<T>(mutex: &Mutex<T>) -> spin::MutexGuard<'_, T> {
mutex.lock()
}
impl Domain {
pub fn new(name: impl Into<Str>) -> Self {
Domain {
handle: match name.into() {
Str::Ascii(s) => nvtx_sys::domain_create_ascii(&s),
Str::Unicode(s) => nvtx_sys::domain_create_unicode(&s),
},
registered_strings: AtomicU32::new(0),
registered_categories: AtomicU32::new(0),
strings: Mutex::new(StringMap::default()),
categories: Mutex::new(StringMap::default()),
}
}
pub fn event_attributes_builder(&self) -> EventAttributesBuilder<'_> {
EventAttributesBuilder {
domain: self,
inner: GenericEventAttributesBuilder::default(),
}
}
pub fn register_string(&self, string: impl Into<Str>) -> RegisteredString<'_> {
let into_string: Str = string.into();
let owned_string = match &into_string {
Str::Ascii(s) => {
let mut key = String::from("Ascii:");
key.push_str(&s.to_string_lossy());
key
}
Str::Unicode(s) => {
let mut key = String::from("Unicode:");
key.push_str(&s.to_string_lossy());
key
}
};
let (handle, uid) = *lock_unpoison(&self.strings)
.entry(owned_string)
.or_insert_with(|| {
let id = 1 + self.registered_strings.fetch_add(1, Ordering::SeqCst);
let handle = match into_string {
Str::Ascii(s) => nvtx_sys::domain_register_string_ascii(self.handle, &s),
Str::Unicode(s) => nvtx_sys::domain_register_string_unicode(self.handle, &s),
};
(handle, id)
});
RegisteredString::new(handle, uid, self)
}
pub fn register_strings<const N: usize>(
&self,
strings: [impl Into<Str>; N],
) -> [RegisteredString<'_>; N] {
strings.map(|string| self.register_string(string))
}
pub fn register_category(&self, name: impl Into<Str>) -> Category<'_> {
let into_name: Str = name.into();
let owned_name = match &into_name {
Str::Ascii(s) => s.to_string_lossy().to_string(),
Str::Unicode(s) => s.to_string_lossy().clone(),
};
let id = *lock_unpoison(&self.categories)
.entry(owned_name)
.or_insert_with(|| {
let id = 1 + self.registered_categories.fetch_add(1, Ordering::SeqCst);
match into_name {
Str::Ascii(s) => nvtx_sys::domain_name_category_ascii(self.handle, id, &s),
Str::Unicode(s) => nvtx_sys::domain_name_category_unicode(self.handle, id, &s),
}
id
});
Category::new(id, self)
}
pub fn register_categories<const N: usize>(
&self,
names: [impl Into<Str>; N],
) -> [Category<'_>; N] {
names.map(|name| self.register_category(name))
}
fn validate_event_argument<'a>(&'a self, arg: &EventArgument<'a>) -> Result<(), NvtxError> {
match arg {
EventArgument::Attributes(attr) => {
if let Some(category) = &attr.category {
if !core::ptr::eq(category.domain(), self) {
return Err(NvtxError::DomainMismatch);
}
}
if let Some(Message::Registered(reg_str)) = &attr.message {
if !core::ptr::eq(reg_str.domain(), self) {
return Err(NvtxError::DomainMismatch);
}
}
}
EventArgument::Message(Message::Registered(reg_str)) => {
if !core::ptr::eq(reg_str.domain(), self) {
return Err(NvtxError::DomainMismatch);
}
}
EventArgument::Message(_) => {}
}
Ok(())
}
fn strip_foreign_domain_values<'a>(&'a self, arg: EventArgument<'a>) -> EventArgument<'a> {
match arg {
EventArgument::Attributes(mut attr) => {
if attr
.category
.as_ref()
.is_some_and(|category| !core::ptr::eq(category.domain(), self))
{
attr.category = None;
}
if attr.message.as_ref().is_some_and(|message| {
matches!(message, Message::Registered(reg_str) if !core::ptr::eq(reg_str.domain(), self))
}) {
attr.message = None;
}
EventArgument::Attributes(attr)
}
EventArgument::Message(Message::Registered(reg_str))
if !core::ptr::eq(reg_str.domain(), self) =>
{
EventArgument::Attributes(EventAttributes {
category: None,
color: None,
message: None,
payload: None,
})
}
arg @ EventArgument::Message(_) => arg,
}
}
fn mark_event_argument<'a>(&'a self, event_arg: EventArgument<'a>) {
match event_arg {
EventArgument::Attributes(attr) => {
let encoded = attr.encode();
nvtx_sys::domain_mark_ex(self.handle, &encoded);
}
EventArgument::Message(m) => {
let attr = EventAttributes {
category: None,
color: None,
message: Some(m),
payload: None,
};
let encoded = attr.encode();
nvtx_sys::domain_mark_ex(self.handle, &encoded);
}
}
}
pub fn mark<'a>(&'a self, arg: impl Into<EventArgument<'a>>) {
let event_arg = arg.into();
match self.validate_event_argument(&event_arg) {
Ok(()) => self.mark_event_argument(event_arg),
Err(error) => {
debug_assert!(false, "{error}");
self.mark_event_argument(self.strip_foreign_domain_values(event_arg));
}
}
}
pub fn try_mark<'a>(&'a self, arg: impl Into<EventArgument<'a>>) -> Result<(), NvtxError> {
let event_arg = arg.into();
self.validate_event_argument(&event_arg)?;
self.mark_event_argument(event_arg);
Ok(())
}
pub fn local_range<'a>(&'a self, arg: impl Into<EventArgument<'a>>) -> LocalRange<'a> {
let event_arg = arg.into();
match self.validate_event_argument(&event_arg) {
Ok(()) => LocalRange::new(event_arg, self),
Err(error) => {
debug_assert!(false, "{error}");
LocalRange::new(self.strip_foreign_domain_values(event_arg), self)
}
}
}
pub fn try_local_range<'a>(
&'a self,
arg: impl Into<EventArgument<'a>>,
) -> Result<LocalRange<'a>, NvtxError> {
let event_arg = arg.into();
self.validate_event_argument(&event_arg)?;
Ok(LocalRange::new(event_arg, self))
}
pub fn range<'a>(&'a self, arg: impl Into<EventArgument<'a>>) -> Range<'a> {
let event_arg = arg.into();
match self.validate_event_argument(&event_arg) {
Ok(()) => Range::new(event_arg, self),
Err(error) => {
debug_assert!(false, "{error}");
Range::new(self.strip_foreign_domain_values(event_arg), self)
}
}
}
pub fn try_range<'a>(
&'a self,
arg: impl Into<EventArgument<'a>>,
) -> Result<Range<'a>, NvtxError> {
let event_arg = arg.into();
self.validate_event_argument(&event_arg)?;
Ok(Range::new(event_arg, self))
}
pub(super) fn range_start<'a>(&self, arg: impl Into<EventArgument<'a>>) -> u64 {
let arg = match arg.into() {
EventArgument::Attributes(attr) => attr,
EventArgument::Message(m) => EventAttributes {
category: None,
color: None,
message: Some(m),
payload: None,
},
};
nvtx_sys::domain_range_start_ex(self.handle, &arg.encode())
}
pub(super) fn range_end(&self, range_id: u64) {
nvtx_sys::domain_range_end(self.handle, range_id);
}
pub fn name_resource<'a>(
&'a self,
identifier: impl Into<Identifier>,
name: impl Into<Message<'a>>,
) -> Resource<'a> {
let materialized_identifier: Identifier = identifier.into();
let materialized_name: Message = name.into();
let (msg_type, msg_value) = materialized_name.encode();
let (id_type, id_value) = materialized_identifier.encode();
let attrs = nvtx_sys::ResourceAttributes {
version: nvtx_sys::NVTX_VERSION as u16,
size: nvtx_sys::NVTX_RESOURCE_ATTRIBUTES_SIZE as u16,
identifierType: id_type as i32,
identifier: id_value,
messageType: i32::from(msg_type),
message: msg_value,
};
Resource {
handle: nvtx_sys::domain_resource_create(self.handle, attrs),
_lifetime: PhantomData,
}
}
pub fn user_sync<'a>(&'a self, name: impl Into<Message<'a>>) -> sync::UserSync<'a> {
let message = name.into();
let (msg_type, msg_value) = message.encode();
let attrs = nvtx_sys::SyncUserAttributes {
version: nvtx_sys::NVTX_VERSION as u16,
size: nvtx_sys::NVTX_SYNC_USER_ATTRIBUTES_SIZE as u16,
messageType: i32::from(msg_type),
message: msg_value,
};
let handle = nvtx_sys::domain_syncuser_create(self.handle, attrs);
sync::UserSync {
handle,
_lifetime: PhantomData,
}
}
}
impl Drop for Domain {
fn drop(&mut self) {
nvtx_sys::domain_destroy(self.handle);
}
}
#[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_register_string() {
let d = Domain::new(lossy_str("d"));
TestUtils::assert_domain_string_registration(&d, "hello");
let s1 = d.register_string(lossy_str("hello"));
let s2 = d.register_string(lossy_str("hello2"));
assert_ne!(s1, s2);
}
#[test]
fn test_register_strings() {
let d = Domain::new(lossy_str("d"));
let [s1, s2, s3] =
d.register_strings([lossy_str("hello"), lossy_str("hello2"), lossy_str("hello")]);
assert_eq!(s1, s3);
assert_ne!(s1, s2);
}
#[test]
fn test_register_category() {
let d = Domain::new(lossy_str("d"));
TestUtils::assert_domain_category_registration(&d, "hello");
let c1 = d.register_category(lossy_str("hello"));
let c2 = d.register_category(lossy_str("hello2"));
assert_ne!(c1, c2);
}
#[test]
fn test_register_categories() {
let d = Domain::new(lossy_str("d"));
let [c1, c2, c3] =
d.register_categories([lossy_str("hello"), lossy_str("hello2"), lossy_str("hello")]);
assert_eq!(c1, c3);
assert_ne!(c1, c2);
}
#[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_message_registered() {
let d = Domain::new(lossy_str("d"));
let reg = d.register_string(lossy_str("test"));
let m = Message::Registered(reg);
assert!(matches!(m, Message::Registered(s) if s == reg));
}
#[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 test_encode_registered() {
let domain = Domain::new(lossy_str("d"));
let registered_message = domain.register_string(lossy_str("test"));
let message = Message::Registered(registered_message);
let (message_type, message_value) = message.encode();
assert_eq!(
message_type,
nvtx_sys::MessageType::NVTX_MESSAGE_TYPE_REGISTERED
);
unsafe {
assert!(
matches!(message_value, nvtx_sys::MessageValue{ registered: registered_handle } if registered_handle == nvtx_sys::ffi::nvtxStringHandle_t::from(registered_message.handle()))
);
}
}
#[test]
fn test_builder_color() {
let d = Domain::new(lossy_str("d"));
let builder = d.event_attributes_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 d = Domain::new(lossy_str("d"));
let cat = d.register_category(lossy_str("cat"));
let builder = d.event_attributes_builder();
let attr = builder.category(cat).build();
assert!(matches!(attr.category, Some(c) if c == cat));
}
#[test]
fn test_builder_category_rebinds_foreign_category_id_for_best_effort_use() {
let d1 = Domain::new(lossy_str("Domain1"));
let c1 = d1.register_category(lossy_str("category"));
let d2 = Domain::new(lossy_str("Domain2"));
let rebound = d2.event_attributes_builder().category_in_builder_domain(c1);
assert_eq!(rebound.encode_id(), c1.encode_id());
assert!(core::ptr::eq(rebound.domain(), &d2));
}
#[test]
fn test_builder_category_name() {
let d = Domain::new(lossy_str("d"));
let builder = d.event_attributes_builder();
let attr = builder.category_name(lossy_str("cat")).build();
let cat = d.register_category(lossy_str("cat"));
assert!(matches!(attr.category, Some(c) if c == cat));
}
#[test]
fn test_builder_payload() {
let d = Domain::new(lossy_str("d"));
let builder = d.event_attributes_builder();
let attr = builder.clone().payload(1_i32).build();
assert!(matches!(attr.payload, Some(Payload::Int32(i)) if i == 1_i32));
let attr = builder.clone().payload(2_u32).build();
assert!(matches!(attr.payload, Some(Payload::Uint32(i)) if i == 2_u32));
let attr = builder.clone().payload(1_i64).build();
assert!(matches!(attr.payload, Some(Payload::Int64(i)) if i == 1_i64));
let attr = builder.clone().payload(2_u64).build();
assert!(matches!(attr.payload, Some(Payload::Uint64(i)) if i == 2_u64));
let attr = builder.clone().payload(1.0_f32).build();
assert!(matches!(attr.payload, Some(Payload::Float(i)) if i == 1.0_f32));
let attr = builder.clone().payload(2.0_f64).build();
assert!(matches!(attr.payload, Some(Payload::Double(i)) if i == 2.0_f64));
}
#[test]
fn test_builder_message() {
let d = Domain::new(lossy_str("d"));
let builder = d.event_attributes_builder();
let attr = builder.message(lossy_str("This is a message")).build();
let registered = d.register_string(lossy_str("This is a message"));
assert!(matches!(attr.message, Some(Message::Registered(r)) if r == registered));
}
#[test]
fn test_unowned_category_try_mark_error() {
let d1 = Domain::new(lossy_str("Domain1"));
let c1 = d1.register_category(lossy_str("category"));
let d2 = Domain::new(lossy_str("Domain2"));
let attr = d1.event_attributes_builder().category(c1).build();
assert!(matches!(d2.try_mark(attr), Err(NvtxError::DomainMismatch)));
}
#[test]
fn test_unowned_category_mark_strips_foreign_category() {
let d1 = Domain::new(lossy_str("Domain1"));
let c1 = d1.register_category(lossy_str("category"));
let d2 = Domain::new(lossy_str("Domain2"));
let attr = EventAttributes {
category: Some(c1),
color: None,
message: Some(Message::from(lossy_str("message"))),
payload: Some(Payload::Int32(1)),
};
let EventArgument::Attributes(sanitized) =
d2.strip_foreign_domain_values(EventArgument::Attributes(attr))
else {
unreachable!("expected sanitized attributes");
};
assert!(sanitized.category.is_none());
assert!(matches!(sanitized.message, Some(Message::Unicode(_))));
assert!(matches!(sanitized.payload, Some(Payload::Int32(1))));
}
#[test]
fn test_unowned_category_try_builder_error() {
let d1 = Domain::new(lossy_str("Domain1"));
let c1 = d1.register_category(lossy_str("category"));
let d2 = Domain::new(lossy_str("Domain2"));
assert!(matches!(
d2.event_attributes_builder().try_category(c1),
Err(NvtxError::DomainMismatch)
));
}
#[test]
fn test_unowned_category_try_range_error() {
let d1 = Domain::new(lossy_str("Domain1"));
let c1 = d1.register_category(lossy_str("category"));
let d2 = Domain::new(lossy_str("Domain2"));
let attr = d1.event_attributes_builder().category(c1).build();
assert!(matches!(d2.try_range(attr), Err(NvtxError::DomainMismatch)));
}
#[test]
fn test_unowned_category_range_can_start_from_sanitized_attributes() {
let d1 = Domain::new(lossy_str("Domain1"));
let c1 = d1.register_category(lossy_str("category"));
let d2 = Domain::new(lossy_str("Domain2"));
let attr = EventAttributes {
category: Some(c1),
color: None,
message: Some(Message::from(lossy_str("message"))),
payload: Some(Payload::Int32(1)),
};
let event_arg = d2.strip_foreign_domain_values(EventArgument::Attributes(attr));
let range = Range::new(event_arg, &d2);
assert!(range.id.is_some());
}
#[test]
fn test_unowned_category_try_local_range_error() {
let d1 = Domain::new(lossy_str("Domain1"));
let c1 = d1.register_category(lossy_str("category"));
let d2 = Domain::new(lossy_str("Domain2"));
let attr = d1.event_attributes_builder().category(c1).build();
assert!(matches!(
d2.try_local_range(attr),
Err(NvtxError::DomainMismatch)
));
}
#[test]
fn test_unowned_category_local_range_can_start_from_sanitized_attributes() {
let d1 = Domain::new(lossy_str("Domain1"));
let c1 = d1.register_category(lossy_str("category"));
let d2 = Domain::new(lossy_str("Domain2"));
let attr = EventAttributes {
category: Some(c1),
color: None,
message: Some(Message::from(lossy_str("message"))),
payload: Some(Payload::Int32(1)),
};
let event_arg = d2.strip_foreign_domain_values(EventArgument::Attributes(attr));
let range = LocalRange::new(event_arg, &d2);
assert!(range.active);
}
#[test]
fn test_unowned_string_try_mark_error() {
let d1 = Domain::new(lossy_str("Domain1"));
let s1 = d1.register_string(lossy_str("test string"));
let d2 = Domain::new(lossy_str("Domain2"));
let attr = d1.event_attributes_builder().message(s1).build();
assert!(matches!(d2.try_mark(attr), Err(NvtxError::DomainMismatch)));
}
#[test]
fn test_unowned_string_mark_strips_foreign_registered_message() {
let d1 = Domain::new(lossy_str("Domain1"));
let s1 = d1.register_string(lossy_str("test string"));
let d2 = Domain::new(lossy_str("Domain2"));
let EventArgument::Attributes(sanitized) =
d2.strip_foreign_domain_values(EventArgument::Message(Message::Registered(s1)))
else {
unreachable!("expected sanitized attributes");
};
assert!(sanitized.category.is_none());
assert!(sanitized.message.is_none());
assert!(sanitized.payload.is_none());
}
#[test]
fn test_unowned_string_try_builder_error() {
let d1 = Domain::new(lossy_str("Domain1"));
let s1 = d1.register_string(lossy_str("test string"));
let d2 = Domain::new(lossy_str("Domain2"));
assert!(matches!(
d2.event_attributes_builder().try_message(s1),
Err(NvtxError::DomainMismatch)
));
}
#[test]
fn test_unowned_string_try_range_error() {
let d1 = Domain::new(lossy_str("Domain1"));
let s1 = d1.register_string(lossy_str("test string"));
let d2 = Domain::new(lossy_str("Domain2"));
let attr = d1.event_attributes_builder().message(s1).build();
assert!(matches!(d2.try_range(attr), Err(NvtxError::DomainMismatch)));
}
#[test]
fn test_unowned_string_try_local_range_error() {
let d1 = Domain::new(lossy_str("Domain1"));
let s1 = d1.register_string(lossy_str("test string"));
let d2 = Domain::new(lossy_str("Domain2"));
let attr = d1.event_attributes_builder().message(s1).build();
assert!(matches!(
d2.try_local_range(attr),
Err(NvtxError::DomainMismatch)
));
}
#[test]
fn test_simple_domain_validation() {
let d1 = Domain::new(lossy_str("Domain1"));
let s1 = d1.register_string(lossy_str("test string"));
let d2 = Domain::new(lossy_str("Domain2"));
let attr = d1.event_attributes_builder().message(s1).build();
assert!(matches!(d2.try_mark(attr), Err(NvtxError::DomainMismatch)));
}
}