bp3d_debug/trace/
span.rs

1// Copyright (c) 2024, BlockProject 3D
2//
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without modification,
6// are permitted provided that the following conditions are met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//     * Redistributions in binary form must reproduce the above copyright notice,
11//       this list of conditions and the following disclaimer in the documentation
12//       and/or other materials provided with the distribution.
13//     * Neither the name of BlockProject 3D nor the names of its contributors
14//       may be used to endorse or promote products derived from this software
15//       without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29use crate::field::Field;
30use crate::util::Location;
31use std::num::{NonZeroU32, NonZeroU64};
32use std::sync::OnceLock;
33
34#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
35#[repr(transparent)]
36pub struct Id(NonZeroU64);
37
38impl Id {
39    pub fn new(callsite: NonZeroU32, instance: NonZeroU32) -> Self {
40        Self(unsafe {
41            NonZeroU64::new_unchecked((callsite.get() as u64) << 32 | instance.get() as u64)
42        })
43    }
44
45    pub fn from_raw(id: NonZeroU64) -> Self {
46        Self(id)
47    }
48
49    pub fn into_raw(self) -> NonZeroU64 {
50        self.0
51    }
52
53    pub fn get_callsite(&self) -> NonZeroU32 {
54        unsafe { NonZeroU32::new_unchecked((self.0.get() >> 32) as u32) }
55    }
56
57    pub fn get_instance(&self) -> NonZeroU32 {
58        unsafe { NonZeroU32::new_unchecked(self.0.get() as u32) }
59    }
60}
61
62pub struct Callsite {
63    name: &'static str,
64    location: Location,
65    id: OnceLock<NonZeroU32>,
66}
67
68impl Callsite {
69    pub const fn new(name: &'static str, location: Location) -> Self {
70        Self {
71            name,
72            location,
73            id: OnceLock::new(),
74        }
75    }
76
77    pub fn location(&self) -> &Location {
78        &self.location
79    }
80
81    pub fn name(&self) -> &'static str {
82        self.name
83    }
84
85    pub fn get_id(&'static self) -> &'static NonZeroU32 {
86        self.id
87            .get_or_init(|| crate::engine::get().register_callsite(self))
88    }
89}
90
91pub struct Entered {
92    id: Id,
93}
94
95impl Drop for Entered {
96    fn drop(&mut self) {
97        crate::engine::get().span_exit(self.id);
98    }
99}
100
101pub struct Span {
102    id: Id,
103}
104
105impl Span {
106    pub fn with_fields(callsite: &'static Callsite, fields: &[Field]) -> Self {
107        let callsite = *callsite.get_id();
108        let instance = crate::engine::get().span_create(callsite, fields);
109        Self {
110            id: Id::new(callsite, instance),
111        }
112    }
113
114    pub fn new(callsite: &'static Callsite) -> Self {
115        let callsite = *callsite.get_id();
116        let instance = crate::engine::get().span_create(callsite, &[]);
117        Self {
118            id: Id::new(callsite, instance),
119        }
120    }
121
122    pub fn record(&self, fields: &[Field]) {
123        crate::engine::get().span_record(self.id, fields);
124    }
125
126    pub fn enter(&self) -> Entered {
127        Entered { id: self.id }
128    }
129}
130
131impl Drop for Span {
132    fn drop(&mut self) {
133        crate::engine::get().span_destroy(self.id);
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use crate::profiler::section::Level;
140    use crate::{fields, span};
141
142    #[test]
143    fn api_test() {
144        let value = 32;
145        let str = "this is a test";
146        let lvl = Level::Event;
147        let _span = span!(API_TEST);
148        let span = span!(API_TEST2, {value} {str} {?lvl} {test=value});
149        span.record(fields!({ test2 = str }).as_ref());
150        let _entered = span.enter();
151    }
152}