boa/
profiler.rs

1#![allow(missing_copy_implementations, missing_debug_implementations)]
2
3#[cfg(feature = "profiler")]
4use measureme::{EventId, Profiler, TimingGuard};
5#[cfg(feature = "profiler")]
6use once_cell::sync::OnceCell;
7use std::fmt::{self, Debug};
8#[cfg(feature = "profiler")]
9use std::{
10    path::Path,
11    thread::{current, ThreadId},
12};
13
14#[cfg(feature = "profiler")]
15pub struct BoaProfiler {
16    profiler: Profiler,
17}
18
19/// This static instance should never be public, and its only access should be done through the `global()` and `drop()` methods
20/// This is because `get_or_init` manages synchronisation and the case of an empty value
21#[cfg(feature = "profiler")]
22static mut INSTANCE: OnceCell<BoaProfiler> = OnceCell::new();
23
24#[cfg(feature = "profiler")]
25impl BoaProfiler {
26    pub fn start_event(&self, label: &str, category: &str) -> TimingGuard<'_> {
27        let kind = self.profiler.alloc_string(category);
28        let id = EventId::from_label(self.profiler.alloc_string(label));
29        let thread_id = Self::thread_id_to_u32(current().id());
30        self.profiler
31            .start_recording_interval_event(kind, id, thread_id)
32    }
33
34    pub fn default() -> BoaProfiler {
35        let profiler = Profiler::new(Path::new("./my_trace")).unwrap();
36        BoaProfiler { profiler }
37    }
38
39    pub fn global() -> &'static BoaProfiler {
40        unsafe { INSTANCE.get_or_init(Self::default) }
41    }
42
43    pub fn drop(&self) {
44        // In order to drop the INSTANCE we need to get ownership of it, which isn't possible on a static unless you make it a mutable static
45        // mutating statics is unsafe, so we need to wrap it as so.
46        // This is actually safe though because init and drop are only called at the beginning and end of the application
47        unsafe {
48            INSTANCE
49                .take()
50                .expect("Could not take back profiler instance");
51        }
52    }
53
54    // Sadly we need to use the unsafe method until this is resolved:
55    // https://github.com/rust-lang/rust/issues/67939
56    // Once `as_64()` is in stable we can do this:
57    // https://github.com/rust-lang/rust/pull/68531/commits/ea42b1c5b85f649728e3a3b334489bac6dce890a
58    // Until then our options are: use rust-nightly or use unsafe {}
59    fn thread_id_to_u32(tid: ThreadId) -> u32 {
60        unsafe { std::mem::transmute::<ThreadId, u64>(tid) as u32 }
61    }
62}
63
64impl Debug for BoaProfiler {
65    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66        Debug::fmt("no debug implemented", f)
67    }
68}
69
70#[cfg(not(feature = "profiler"))]
71pub struct BoaProfiler;
72
73#[allow(clippy::unused_unit)]
74#[cfg(not(feature = "profiler"))]
75impl BoaProfiler {
76    pub fn start_event(&self, _label: &str, _category: &str) -> () {
77        ()
78    }
79
80    pub fn drop(&self) {
81        ()
82    }
83
84    pub fn global() -> BoaProfiler {
85        BoaProfiler
86    }
87}