use std::sync::OnceLock; use alligator_console::{ConsoleLogger, ConsoleMessage}; use chrono::{DateTime, Utc}; use scopeguard::ScopeGuard; static GLOBAL_PROFILER: OnceLock = OnceLock::new(); struct Profiler { logger: ConsoleLogger, start_time: DateTime, } impl Profiler { fn new(logger: ConsoleLogger) -> Self { Self { logger, start_time: Utc::now(), } } fn current_timestamp(&self) -> i64 { Utc::now() .signed_duration_since(DateTime::UNIX_EPOCH) .num_microseconds() .unwrap_or(i64::MAX) } fn finish_frame(&self) { self.logger.send(ConsoleMessage::FrameTime { timestamp: self.current_timestamp(), }) } fn start_scope(&self, scope_name: String) { self.logger.send(ConsoleMessage::ScopeStart { scope_name, timestamp: self.current_timestamp(), }) } fn end_scope(&self) { self.logger.send(ConsoleMessage::ScopeEnd { timestamp: self.current_timestamp(), }) } } pub fn set_profiler(logger: ConsoleLogger) { GLOBAL_PROFILER.get_or_init(|| Profiler::new(logger)); } pub fn finish_frame() { GLOBAL_PROFILER.get().unwrap().finish_frame(); } pub fn start_scope(scope_name: impl AsRef) { GLOBAL_PROFILER .get() .unwrap() .start_scope(scope_name.as_ref().to_string()); } pub fn end_scope() { GLOBAL_PROFILER.get().unwrap().end_scope(); } pub fn profile_scope(scope_name: impl AsRef) -> ScopeGuard<(), impl FnOnce(())> { start_scope(scope_name); scopeguard::guard((), |_| end_scope()) } #[macro_export] macro_rules! scope { ($scope_name: expr) => { let _profiling_scope = $crate::profile_scope($scope_name); }; () => { let _profiling_scope = $crate::profile_scope("unnamed scope"); }; } #[macro_export] macro_rules! function_name { () => {{ fn f() {} fn type_name_of(_: T) -> &'static str { std::any::type_name::() } type_name_of(f) .split("::") .filter(|&part| part != "f") .collect::>() .join("::") }}; } #[macro_export] macro_rules! profile_function { () => { $crate::scope!($crate::function_name!()); }; }