use crate::profiler::{EventId, EventType};
use crate::{Profiler, ProfilerEvent, ProfilerStats};
use torsh_core::error::Result;
#[cfg(feature = "std")]
use std::sync::{Arc, Mutex};
#[cfg(feature = "std")]
#[allow(unused_imports)]
use std::time::{Duration, Instant};
#[cfg(not(feature = "std"))]
use alloc::sync::Arc;
#[cfg(not(feature = "std"))]
use spin::Mutex;
#[cfg(not(feature = "std"))]
use alloc::{string::String, vec::Vec};
#[derive(Debug)]
pub struct CpuProfiler {
events: Arc<Mutex<Vec<ProfilerEvent>>>,
stats: Arc<Mutex<ProfilerStats>>,
enabled: Arc<Mutex<bool>>,
next_event_id: Arc<Mutex<u64>>,
}
impl CpuProfiler {
pub fn new() -> Self {
Self {
events: Arc::new(Mutex::new(Vec::new())),
stats: Arc::new(Mutex::new(ProfilerStats::default())),
enabled: Arc::new(Mutex::new(false)),
next_event_id: Arc::new(Mutex::new(1)),
}
}
fn next_id(&self) -> EventId {
let mut id = self
.next_event_id
.lock()
.expect("lock should not be poisoned");
let event_id = EventId(*id);
*id += 1;
event_id
}
}
impl Profiler for CpuProfiler {
fn start(&mut self) -> Result<()> {
let mut enabled = self.enabled.lock().expect("lock should not be poisoned");
*enabled = true;
let mut events = self.events.lock().expect("lock should not be poisoned");
events.clear();
let mut stats = self.stats.lock().expect("lock should not be poisoned");
*stats = ProfilerStats::default();
Ok(())
}
fn stop(&mut self) -> Result<()> {
let mut enabled = self.enabled.lock().expect("lock should not be poisoned");
*enabled = false;
Ok(())
}
fn is_enabled(&self) -> bool {
*self.enabled.lock().expect("lock should not be poisoned")
}
fn begin_event(&mut self, name: &str) -> Result<EventId> {
if !self.is_enabled() {
return Ok(EventId(0));
}
let event_id = self.next_id();
let event = ProfilerEvent::new(
event_id,
name.to_string(),
EventType::Custom(name.to_string()),
);
let mut events = self.events.lock().expect("lock should not be poisoned");
events.push(event);
Ok(event_id)
}
fn end_event(&mut self, event_id: EventId) -> Result<()> {
if !self.is_enabled() || event_id.0 == 0 {
return Ok(());
}
let mut events = self.events.lock().expect("lock should not be poisoned");
if let Some(event) = events.iter_mut().find(|e| e.id == event_id) {
event.finish();
}
Ok(())
}
fn marker(&mut self, name: &str) -> Result<()> {
if !self.is_enabled() {
return Ok(());
}
let event_id = self.next_id();
let mut event = ProfilerEvent::new(event_id, name.to_string(), EventType::Marker);
event.finish();
let mut events = self.events.lock().expect("lock should not be poisoned");
events.push(event);
Ok(())
}
fn stats(&self) -> ProfilerStats {
self.stats
.lock()
.expect("lock should not be poisoned")
.clone()
}
fn events(&self) -> &[ProfilerEvent] {
&[]
}
fn clear(&mut self) {
let mut events = self.events.lock().expect("lock should not be poisoned");
events.clear();
let mut stats = self.stats.lock().expect("lock should not be poisoned");
*stats = ProfilerStats::default();
}
fn report(&self) -> String {
let events = self.events.lock().expect("lock should not be poisoned");
let stats = self.stats.lock().expect("lock should not be poisoned");
let mut report = String::new();
report.push_str("=== CPU Profiler Report ===\n");
report.push_str(&format!("Total Events: {}\n", stats.total_events));
report.push_str(&format!(
"Total Time: {:.2}ms\n",
stats.total_time.as_secs_f64() * 1000.0
));
report.push_str("\n=== Events ===\n");
for event in events.iter() {
if let Some(duration) = event.duration() {
report.push_str(&format!(
"{}: {:.2}μs\n",
event.name,
duration.as_secs_f64() * 1_000_000.0
));
} else {
report.push_str(&format!("{}: (marker)\n", event.name));
}
}
report
}
}
impl Default for CpuProfiler {
fn default() -> Self {
Self::new()
}
}
impl Clone for CpuProfiler {
fn clone(&self) -> Self {
Self {
events: Arc::clone(&self.events),
stats: Arc::clone(&self.stats),
enabled: Arc::clone(&self.enabled),
next_event_id: Arc::clone(&self.next_event_id),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cpu_profiler_basic() {
let mut profiler = CpuProfiler::new();
assert!(!profiler.is_enabled());
profiler.start().expect("start operation should succeed");
assert!(profiler.is_enabled());
let event_id = profiler
.begin_event("test_event")
.expect("event begin should succeed");
profiler
.end_event(event_id)
.expect("event end should succeed");
profiler
.marker("test_marker")
.expect("marker operation should succeed");
profiler.stop().expect("stop should succeed");
assert!(!profiler.is_enabled());
let report = profiler.report();
assert!(report.contains("test_event"));
assert!(report.contains("test_marker"));
}
#[test]
fn test_cpu_profiler_disabled() {
let mut profiler = CpuProfiler::new();
let event_id = profiler
.begin_event("test")
.expect("event begin should succeed");
assert_eq!(event_id.0, 0);
profiler
.end_event(event_id)
.expect("event end should succeed");
profiler
.marker("test_marker")
.expect("marker operation should succeed");
let report = profiler.report();
assert!(!report.contains("test"));
}
#[test]
fn test_cpu_profiler_clear() {
let mut profiler = CpuProfiler::new();
profiler.start().expect("start operation should succeed");
profiler
.begin_event("test")
.expect("event begin should succeed");
profiler.clear();
let report = profiler.report();
assert!(!report.contains("test"));
}
}