bp3d_debug/profiler/
section.rs1use crate::field::FieldSet;
30use crate::util::Location;
31use std::num::NonZeroU32;
32use std::sync::OnceLock;
33use super::instant::Instant;
34
35#[repr(u8)]
36#[derive(Debug, Copy, Clone, Eq, PartialEq)]
37pub enum Level {
38 Critical = 0,
40
41 Periodic = 1,
43
44 Event = 2,
46}
47
48thread_local! {
49 static CUR_TIME: Instant = Instant::now();
50}
51
52pub struct Entered<'a, const N: usize> {
53 id: NonZeroU32,
54 start: u64,
55 fields: FieldSet<'a, N>,
56}
57
58impl<const N: usize> Drop for Entered<'_, N> {
59 fn drop(&mut self) {
60 let end = CUR_TIME.with(|v| v.elapsed().as_nanos() as _);
61 crate::engine::get().section_record(self.id, self.start, end, self.fields.as_ref());
62 }
63}
64
65pub struct Section {
66 name: &'static str,
67 location: Location,
68 level: Level,
69 parent: Option<&'static Section>,
70 id: OnceLock<NonZeroU32>,
71}
72
73impl Section {
74 pub const fn new(name: &'static str, location: Location, level: Level) -> Self {
75 Self {
76 name,
77 location,
78 level,
79 parent: None,
80 id: OnceLock::new(),
81 }
82 }
83
84 pub const fn set_parent(mut self, parent: &'static Section) -> Self {
85 self.parent = Some(parent);
86 self
87 }
88
89 pub fn name(&self) -> &'static str {
90 self.name
91 }
92
93 pub fn location(&self) -> &Location {
94 &self.location
95 }
96
97 pub fn level(&self) -> Level {
98 self.level
99 }
100
101 pub fn parent(&self) -> Option<&'static Section> {
102 self.parent
103 }
104
105 pub fn get_id(&'static self) -> &'static NonZeroU32 {
106 self.id
107 .get_or_init(|| crate::engine::get().section_register(self))
108 }
109
110 pub fn enter<'a, const N: usize>(&'static self, fields: FieldSet<'a, N>) -> Entered<'a, N> {
111 let id = self.get_id();
112 Entered {
113 id: *id,
114 start: CUR_TIME.with(|v| v.elapsed().as_nanos() as _),
115 fields,
116 }
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use crate::field::FieldSet;
123 use crate::profiler::section::{Level, Section};
124 use crate::{fields, location, profiler_section_start};
125
126 #[test]
127 fn basic() {
128 static _SECTION: Section = Section::new("api_test", location!(), Level::Event);
129 }
130
131 #[test]
132 fn api_test() {
133 static SECTION: Section = Section::new("api_test", location!(), Level::Event);
134 static _SECTION2: Section =
135 Section::new("api_test2", location!(), Level::Event).set_parent(&SECTION);
136 SECTION.enter(FieldSet::new(fields!()));
137 SECTION.enter(FieldSet::new(fields!({ test = 42 })));
138 SECTION.enter(FieldSet::new(fields!({ test = "test 123" })));
139 SECTION.enter(FieldSet::new(fields!({ test = 42.42 })));
140 SECTION.enter(FieldSet::new(fields!({test=?Level::Event})));
141 SECTION.enter(FieldSet::new(fields!({test=?Level::Event} {test2=42})));
142 let value = 32;
143 let str = "this is a test";
144 let lvl = Level::Event;
145 SECTION.enter(FieldSet::new(fields!({value} {str} {?lvl} {test = value})));
146 }
147
148 #[test]
149 fn api_test2() {
150 let value = 32;
151 let str = "this is a test";
152 let lvl = Level::Event;
153 profiler_section_start!(API_TEST, Level::Event);
154 profiler_section_start!(API2_TEST: API_TEST, Level::Event);
155 profiler_section_start!(API3_TEST_WITH_PARAMS: API2_TEST, Level::Event, {value} {str} {?lvl} {test=value});
156 }
157}