Skip to main content

optick/
lib.rs

1//! [Optick Profiler](https://github.com/bombomby/optick) - Super Lightweight Performance Profiler
2//!
3//! ## How to use
4//!
5//! In `Cargo.toml` add:
6//!
7//! ```toml
8//! [dependencies]
9//! optick = "1.3.1"
10//! ```
11//!
12//! Example usage:
13//!
14//! ```rust
15//! fn calc(n: u32) {
16//! 	// Profile current scope (automatically extracts current function name)
17//!		// You could also specify a custom name if needed - e.g. optick::scope!("calc");
18//!     optick::event!();
19//! 
20//! 	// Attach custom data tag to the capture (i32, u32, u64, f32, str, vec3)
21//!     optick::tag!("number", n);
22//!     optick::tag!("name", "Bob");
23//!     optick::tag!("position", (10.0f32, -12.0f32, 14.0f32));
24//! 
25//!		...
26//! }
27//! 
28//! pub fn main() {
29//! 	// Start a new capture
30//!     optick::start_capture();
31//!     
32//! 	calc(42);
33//! 	
34//! 	// Stop and save current capture 
35//!     optick::stop_capture("capture_name"); // => Saves capture to {working_dir}/capture_name(date-time).opt
36//! }
37//! ```
38//! 
39//! ## GUI
40//!
41//! Use Optick GUI to open saved *.opt capture for further analysis:
42//! https://github.com/bombomby/optick/releases
43//!
44//! ## Feature flags
45//!
46//! - `enable` - this flag is used by default and enables Optick instrumentation
47//! 
48//! ## Run as Administartor to collect ETW events
49//! Optick uses ETW to collect hardware counters: switch-contexts, auto-sampling, CPU core utilization, etc.
50//! Run your app as administrator to enable the collection of ETW events:
51//! ```
52//! Start-Process cargo run -Verb runAs
53//! ```
54#![warn(missing_docs)]
55#![warn(missing_doc_code_examples)]
56
57mod optick_api;
58
59/// Instrument current scope
60/// 
61/// Note: You could override the name of the function by passing a custom name
62///
63/// Example: 
64///
65/// ```rust
66/// fn calc() {
67///     optick::event!();
68///		...
69/// }
70/// ```
71#[macro_export]
72#[cfg(all(feature = "enable"))]
73macro_rules! event {
74    () => {
75        static mut _OPTICK_EVENT_DESCRIPTION : u64 = 0;
76        let mut _optick_counter = $crate::OptickCounter { event_data: 0 };
77        unsafe {
78            if _OPTICK_EVENT_DESCRIPTION == 0 {
79                _OPTICK_EVENT_DESCRIPTION = $crate::create_description($crate::function!(), file!(), line!());
80            } 
81            _optick_counter.event_data = $crate::push_event(_OPTICK_EVENT_DESCRIPTION);
82        }
83    };
84    ($name:expr) => {
85        static mut _OPTICK_EVENT_DESCRIPTION : u64 = 0;
86        let mut _optick_counter = $crate::OptickCounter { event_data: 0 };
87        unsafe {
88            if _OPTICK_EVENT_DESCRIPTION == 0 {
89                _OPTICK_EVENT_DESCRIPTION = $crate::create_description($name, file!(), line!());
90            } 
91            _optick_counter.event_data = $crate::push_event(_OPTICK_EVENT_DESCRIPTION);
92        }
93    };
94}
95
96
97/// Attach custom data to the current scope
98/// 
99/// Note: You could override the name of the function by passing a custom name
100///
101/// Example: 
102///
103/// ```rust
104/// fn calc(number: u32, name: &str) {
105///     optick::event!();
106///     optick::tag!("number", number);
107///     optick::tag!("name", name);
108///		...
109/// }
110/// ```
111#[macro_export]
112#[cfg(all(feature = "enable"))]
113macro_rules! tag {
114    ($name:expr, $value:expr) => {{
115        use $crate::OptickTag;
116        static mut _OPTICK_EVENT_DESCRIPTION : u64 = 0;
117        let mut _optick_counter = $crate::OptickCounter { event_data: 0 };
118        unsafe {
119            if _OPTICK_EVENT_DESCRIPTION == 0 {
120                _OPTICK_EVENT_DESCRIPTION = $crate::create_description($name, file!(), line!());
121            } 
122            $value.attach(_OPTICK_EVENT_DESCRIPTION);
123        }}
124    };
125}
126
127/// Extract current function name
128#[macro_export]
129#[cfg(all(feature = "enable"))]
130macro_rules! function {
131    () => {{
132        fn f() {}
133        fn type_name_of<T>(_: T) -> &'static str {
134            std::any::type_name::<T>()
135        }
136        let name = type_name_of(f);
137        &name[..name.len() - 3]
138    }}
139}
140
141/// Scoped event
142#[cfg(all(feature = "enable"))]
143pub struct OptickCounter {
144    /// ID of the current event description
145    pub event_data : u64,
146}
147
148/// Destructor for scoped event
149#[cfg(all(feature = "enable"))]
150impl Drop for OptickCounter {
151    #[inline(always)]
152    fn drop(&mut self) {
153        if self.event_data != 0 {
154            pop_event(self.event_data);
155        }
156    }
157}
158
159/// Check whether profiler is enabled
160pub const fn enabled() -> bool {
161    cfg!(all(feature = "enable"))
162}
163
164/// Create event description
165pub fn create_description(name: &str, file: &str, line: u32) -> u64 {
166    #[cfg(all(feature = "enable"))]
167    unsafe {
168        optick_api::OptickAPI_CreateEventDescription(
169            name.as_ptr() as *const i8,
170            name.len() as u16,
171            file.as_ptr() as *const i8,
172            file.len() as u16,
173            line
174        )
175    }
176}
177
178/// Push profiling event
179pub fn push_event(_description: u64) -> u64 {
180    #[cfg(all(feature = "enable"))]
181    unsafe {
182        optick_api::OptickAPI_PushEvent(_description)
183    }
184}
185
186/// Pop profiling event
187pub fn pop_event(_event_data: u64) {
188    #[cfg(all(feature = "enable"))]
189    unsafe {
190        optick_api::OptickAPI_PopEvent(_event_data); 
191    }
192}
193
194/// Mark frame update
195///
196/// Example: 
197///
198/// ```rust
199/// fn update() {
200///     optick::next_frame();
201///		...
202/// }
203/// ```
204pub fn next_frame() {
205    #[cfg(all(feature = "enable"))]
206    unsafe {
207        static mut _OPTICK_INIT_ONCE : bool = false;
208        if _OPTICK_INIT_ONCE == false {
209            register_thread("MainThread");
210            _OPTICK_INIT_ONCE = true;
211        }
212        optick_api::OptickAPI_NextFrame();
213    }
214}
215
216/// Register thread for profiling
217///
218/// Example: 
219///
220/// ```rust
221/// optick::register_thread("Thread Name");
222/// ```
223pub fn register_thread(thread: &str) {
224    #[cfg(all(feature = "enable"))]
225    unsafe {
226        optick_api::OptickAPI_RegisterThread(
227            thread.as_ptr() as *const i8,
228            thread.len() as u16,
229        )
230    }
231}
232
233/// Start a new capture
234pub fn start_capture() {
235    #[cfg(all(feature = "enable"))]
236    {
237        register_thread("MainThread");
238        unsafe {
239            optick_api::OptickAPI_StartCapture();
240        }
241    }
242}
243
244/// Stop and save current capture to the specified path
245///
246/// Example: 
247///
248/// ```rust
249/// pub fn main() {
250///     optick::start_capture();
251/// 	calc(42);
252///     optick::stop_capture("capture_name"); // => {working_dir}/capture_name(2020-07-24.02-33-19).opt
253/// }
254/// ```
255pub fn stop_capture(path: &str) {
256    #[cfg(all(feature = "enable"))]
257    unsafe {
258        optick_api::OptickAPI_StopCapture(path.as_ptr() as *const i8, path.len() as u16);
259    }
260}
261
262/// Optick Tag
263pub trait OptickTag {
264    /// Attach tag
265    fn attach(&self, _description: u64);
266}
267
268/// Optick Tag: &str
269impl OptickTag for &str {
270    fn attach(&self, description: u64) {
271        unsafe {
272            optick_api::OptickAPI_AttachTag_String(description, (*self).as_ptr() as *const i8, (*self).len() as u16);
273        }     
274    }
275}
276
277/// Optick Tag: String
278impl OptickTag for String {
279    fn attach(&self, description: u64) {
280        unsafe {
281            optick_api::OptickAPI_AttachTag_String(description, (*self).as_ptr() as *const i8, (*self).len() as u16);
282        }     
283    }
284}
285
286/// Optick Tag: i32
287impl OptickTag for i32 {
288    fn attach(&self, description: u64) {
289        unsafe {
290            optick_api::OptickAPI_AttachTag_Int32(description, *self);
291        }     
292    }
293}
294
295/// Optick Tag: u32
296impl OptickTag for u32 {
297    fn attach(&self, description: u64) {
298        unsafe {
299            optick_api::OptickAPI_AttachTag_UInt32(description, *self);
300        }     
301    }
302}
303
304/// Optick Tag: u64
305impl OptickTag for u64 {
306    fn attach(&self, description: u64) {
307        unsafe {
308            optick_api::OptickAPI_AttachTag_UInt64(description, *self);
309        }     
310    }
311}
312
313/// Optick Tag: f32
314impl OptickTag for f32 {
315    fn attach(&self, description: u64) {
316        unsafe {
317            optick_api::OptickAPI_AttachTag_Float(description, *self);
318        }     
319    }
320}
321
322/// Optick Tag: vec3 (f32, f32, f32)
323impl OptickTag for (f32, f32, f32) {
324    fn attach(&self, description: u64) {
325        unsafe {
326            optick_api::OptickAPI_AttachTag_Point(description, (*self).0, (*self).1, (*self).2);
327        }     
328    }
329}
330
331// fn attach(n: &str, description: u64)
332// {
333//     n.attach(description);
334// }