1use crate::error::{CoreError, CoreResult};
7use std::collections::HashMap;
8use std::sync::{Arc, Mutex, RwLock};
9use std::time::Instant;
10use thiserror::Error;
11
12#[derive(Error, Debug)]
14pub enum HardwareCounterError {
15 #[error("Performance counters not available on this platform")]
17 NotAvailable,
18
19 #[error("Permission denied to access performance counters: {0}")]
21 PermissionDenied(String),
22
23 #[error("Performance counter not found: {0}")]
25 CounterNotFound(String),
26
27 #[error("Invalid counter configuration: {0}")]
29 InvalidConfiguration(String),
30
31 #[error("System error: {0}")]
33 SystemError(String),
34}
35
36impl From<HardwareCounterError> for CoreError {
37 fn from(err: HardwareCounterError) -> Self {
38 CoreError::ComputationError(crate::error::ErrorContext::new(err.to_string()))
39 }
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Hash)]
44pub enum CounterType {
45 CpuCycles,
48 Instructions,
50 CacheReferences,
52 CacheMisses,
54 BranchInstructions,
56 BranchMisses,
58 BusCycles,
60 StalledCyclesFrontend,
62 StalledCyclesBackend,
64
65 L1DCacheLoads,
68 L1DCacheLoadMisses,
70 L1DCacheStores,
72 L1ICacheLoads,
74 L1ICacheLoadMisses,
76
77 L2CacheLoads,
80 L2CacheLoadMisses,
82 L3CacheLoads,
84 L3CacheLoadMisses,
86
87 DtlbLoads,
90 DtlbLoadMisses,
92 ItlbLoads,
94 ItlbLoadMisses,
96
97 CpuPower,
100 CpuTemperature,
102 CpuFrequency,
104
105 Custom(String),
107}
108
109impl CounterType {
110 pub const fn description(&self) -> &'static str {
112 match self {
113 CounterType::CpuCycles => "CPU cycles",
114 CounterType::Instructions => "Instructions retired",
115 CounterType::CacheReferences => "Cache references",
116 CounterType::CacheMisses => "Cache misses",
117 CounterType::BranchInstructions => "Branch instructions",
118 CounterType::BranchMisses => "Branch mispredictions",
119 CounterType::BusCycles => "Bus cycles",
120 CounterType::StalledCyclesFrontend => "Stalled cycles frontend",
121 CounterType::StalledCyclesBackend => "Stalled cycles backend",
122 CounterType::L1DCacheLoads => "L1 data cache loads",
123 CounterType::L1DCacheLoadMisses => "L1 data cache load misses",
124 CounterType::L1DCacheStores => "L1 data cache stores",
125 CounterType::L1ICacheLoads => "L1 instruction cache loads",
126 CounterType::L1ICacheLoadMisses => "L1 instruction cache load misses",
127 CounterType::L2CacheLoads => "L2 cache loads",
128 CounterType::L2CacheLoadMisses => "L2 cache load misses",
129 CounterType::L3CacheLoads => "L3 cache loads",
130 CounterType::L3CacheLoadMisses => "L3 cache load misses",
131 CounterType::DtlbLoads => "Data TLB loads",
132 CounterType::DtlbLoadMisses => "Data TLB load misses",
133 CounterType::ItlbLoads => "Instruction TLB loads",
134 CounterType::ItlbLoadMisses => "Instruction TLB load misses",
135 CounterType::CpuPower => "CPU power consumption",
136 CounterType::CpuTemperature => "CPU temperature",
137 CounterType::CpuFrequency => "CPU frequency",
138 CounterType::Custom(_) => "Custom counter",
139 }
140 }
141
142 pub const fn unit(&self) -> &'static str {
144 match self {
145 CounterType::CpuCycles
146 | CounterType::Instructions
147 | CounterType::CacheReferences
148 | CounterType::CacheMisses
149 | CounterType::BranchInstructions
150 | CounterType::BranchMisses
151 | CounterType::BusCycles
152 | CounterType::StalledCyclesFrontend
153 | CounterType::StalledCyclesBackend
154 | CounterType::L1DCacheLoads
155 | CounterType::L1DCacheLoadMisses
156 | CounterType::L1DCacheStores
157 | CounterType::L1ICacheLoads
158 | CounterType::L1ICacheLoadMisses
159 | CounterType::L2CacheLoads
160 | CounterType::L2CacheLoadMisses
161 | CounterType::L3CacheLoads
162 | CounterType::L3CacheLoadMisses
163 | CounterType::DtlbLoads
164 | CounterType::DtlbLoadMisses
165 | CounterType::ItlbLoads
166 | CounterType::ItlbLoadMisses => "count",
167 CounterType::CpuPower => "watts",
168 CounterType::CpuTemperature => "celsius",
169 CounterType::CpuFrequency => "hertz",
170 CounterType::Custom(_) => "unknown",
171 }
172 }
173}
174
175#[derive(Debug, Clone)]
177pub struct CounterValue {
178 pub countertype: CounterType,
180 pub value: u64,
182 pub timestamp: Instant,
184 pub enabled: bool,
186 pub scaling_factor: f64,
188}
189
190impl CounterValue {
191 pub fn new(countertype: CounterType, value: u64) -> Self {
193 Self {
194 countertype,
195 value,
196 timestamp: Instant::now(),
197 enabled: true,
198 scaling_factor: 1.0,
199 }
200 }
201
202 pub fn scaled_value(&self) -> f64 {
204 self.value as f64 * self.scaling_factor
205 }
206}
207
208pub trait PerformanceCounter: Send + Sync {
210 fn available_counters(&self) -> Vec<CounterType>;
212
213 fn is_available(&self, countertype: &CounterType) -> bool;
215
216 fn start_counter(&self, countertype: &CounterType) -> CoreResult<()>;
218
219 fn stop_counter(&self, countertype: &CounterType) -> CoreResult<()>;
221
222 fn read_counter(&self, countertype: &CounterType) -> CoreResult<CounterValue>;
224
225 fn read_counters(&self, countertypes: &[CounterType]) -> CoreResult<Vec<CounterValue>>;
227
228 fn reset_counter(&self, countertype: &CounterType) -> CoreResult<()>;
230
231 fn is_overflowed(&self, countertype: &CounterType) -> CoreResult<bool>;
233}
234
235#[cfg(target_os = "linux")]
237pub struct LinuxPerfCounter {
238 active_counters: RwLock<HashMap<CounterType, i32>>, }
240
241#[cfg(target_os = "linux")]
242impl LinuxPerfCounter {
243 pub fn new() -> Self {
245 Self {
246 active_counters: RwLock::new(HashMap::new()),
247 }
248 }
249
250 fn counter_to_perf_config(&self, countertype: &CounterType) -> Option<(u32, u64)> {
252 match countertype {
253 CounterType::CpuCycles => Some((0, 0)), CounterType::Instructions => Some((0, 1)), CounterType::CacheReferences => Some((0, 2)), CounterType::CacheMisses => Some((0, 3)), CounterType::BranchInstructions => Some((0, 4)), CounterType::BranchMisses => Some((0, 5)), CounterType::BusCycles => Some((0, 6)), CounterType::StalledCyclesFrontend => Some((0, 7)), CounterType::StalledCyclesBackend => Some((0, 8)), _ => None, }
264 }
265}
266
267#[cfg(target_os = "linux")]
268impl Default for LinuxPerfCounter {
269 fn default() -> Self {
270 Self::new()
271 }
272}
273
274#[cfg(target_os = "linux")]
275impl PerformanceCounter for LinuxPerfCounter {
276 fn available_counters(&self) -> Vec<CounterType> {
277 vec![
278 CounterType::CpuCycles,
279 CounterType::Instructions,
280 CounterType::CacheReferences,
281 CounterType::CacheMisses,
282 CounterType::BranchInstructions,
283 CounterType::BranchMisses,
284 CounterType::BusCycles,
285 CounterType::StalledCyclesFrontend,
286 CounterType::StalledCyclesBackend,
287 ]
288 }
289
290 fn is_available(&self, countertype: &CounterType) -> bool {
291 self.counter_to_perf_config(countertype).is_some()
292 }
293
294 fn start_counter(&self, countertype: &CounterType) -> CoreResult<()> {
295 if let Some(_event_type_config) = self.counter_to_perf_config(countertype) {
296 let fd = 42; let mut counters = self.active_counters.write().expect("Operation failed");
305 counters.insert(countertype.clone(), fd);
306
307 Ok(())
308 } else {
309 Err(HardwareCounterError::CounterNotFound(format!("{countertype:?}")).into())
310 }
311 }
312
313 fn stop_counter(&self, countertype: &CounterType) -> CoreResult<()> {
314 let mut counters = self.active_counters.write().expect("Operation failed");
315 if let Some(fd) = counters.remove(countertype) {
316 let _ = fd;
318 Ok(())
319 } else {
320 Err(HardwareCounterError::CounterNotFound(format!("{countertype:?}")).into())
321 }
322 }
323
324 fn read_counter(&self, countertype: &CounterType) -> CoreResult<CounterValue> {
325 let counters = self.active_counters.read().expect("Operation failed");
326 if let Some(_fd) = counters.get(countertype) {
327 let mock_value = match countertype {
330 CounterType::CpuCycles => 1_000_000,
331 CounterType::Instructions => 500_000,
332 CounterType::CacheReferences => 10_000,
333 CounterType::CacheMisses => 1_000,
334 CounterType::BranchInstructions => 100_000,
335 CounterType::BranchMisses => 5_000,
336 CounterType::BusCycles => 50_000,
337 CounterType::StalledCyclesFrontend => 10_000,
338 CounterType::StalledCyclesBackend => 20_000,
339 _ => 0,
340 };
341
342 Ok(CounterValue::new(countertype.clone(), mock_value))
343 } else {
344 Err(HardwareCounterError::CounterNotFound(format!("{countertype:?}")).into())
345 }
346 }
347
348 fn read_counters(&self, countertypes: &[CounterType]) -> CoreResult<Vec<CounterValue>> {
349 let mut results = Vec::new();
350 for countertype in countertypes {
351 results.push(self.read_counter(countertype)?);
352 }
353 Ok(results)
354 }
355
356 fn reset_counter(&self, countertype: &CounterType) -> CoreResult<()> {
357 let counters = self.active_counters.read().expect("Operation failed");
358 if counters.contains_key(countertype) {
359 Ok(())
361 } else {
362 Err(HardwareCounterError::CounterNotFound(format!("{countertype:?}")).into())
363 }
364 }
365
366 fn is_overflowed(&self, countertype: &CounterType) -> CoreResult<bool> {
367 let counters = self.active_counters.read().expect("Operation failed");
368 if counters.contains_key(countertype) {
369 Ok(false)
372 } else {
373 Err(HardwareCounterError::CounterNotFound(format!("{countertype:?}")).into())
374 }
375 }
376}
377
378#[cfg(target_os = "windows")]
380pub struct WindowsPdhCounter {
381 active_counters: RwLock<HashMap<CounterType, String>>, }
383
384#[cfg(target_os = "windows")]
385impl WindowsPdhCounter {
386 pub fn new() -> Self {
388 Self {
389 active_counters: RwLock::new(HashMap::new()),
390 }
391 }
392
393 fn counter_to_path(countertype: &CounterType) -> Option<String> {
395 match countertype {
396 CounterType::CpuCycles => Some("\\Processor(_Total)\\% Processor Time".to_string()),
397 CounterType::CpuFrequency => {
398 Some("\\Processor Information(_Total)\\Processor Frequency".to_string())
399 }
400 CounterType::CpuPower => Some("\\Power Meter(*)\\Power".to_string()),
401 _ => None,
402 }
403 }
404}
405
406#[cfg(target_os = "windows")]
407impl Default for WindowsPdhCounter {
408 fn default() -> Self {
409 Self::new()
410 }
411}
412
413#[cfg(target_os = "windows")]
414impl PerformanceCounter for WindowsPdhCounter {
415 fn available_counters(&self) -> Vec<CounterType> {
416 vec![
417 CounterType::CpuCycles,
418 CounterType::CpuFrequency,
419 CounterType::CpuPower,
420 ]
421 }
422
423 fn is_available(&self, countertype: &CounterType) -> bool {
424 Self::counter_to_path(countertype).is_some()
425 }
426
427 fn start_counter(&self, countertype: &CounterType) -> CoreResult<()> {
428 if let Some(path) = Self::counter_to_path(countertype) {
429 let mut counters = self.active_counters.write().expect("Operation failed");
431 counters.insert(countertype.clone(), path);
432 Ok(())
433 } else {
434 Err(HardwareCounterError::CounterNotFound(format!("{countertype:?}")).into())
435 }
436 }
437
438 fn stop_counter(&self, countertype: &CounterType) -> CoreResult<()> {
439 let mut counters = self.active_counters.write().expect("Operation failed");
440 if counters.remove(countertype).is_some() {
441 Ok(())
442 } else {
443 Err(HardwareCounterError::CounterNotFound(format!("{countertype:?}")).into())
444 }
445 }
446
447 fn read_counter(&self, countertype: &CounterType) -> CoreResult<CounterValue> {
448 let counters = self.active_counters.read().expect("Operation failed");
449 if counters.contains_key(countertype) {
450 let mock_value = match countertype {
452 CounterType::CpuCycles => 85, CounterType::CpuFrequency => 2_400_000_000, CounterType::CpuPower => 45, _ => 0,
456 };
457
458 Ok(CounterValue::new(countertype.clone(), mock_value))
459 } else {
460 Err(HardwareCounterError::CounterNotFound(format!("{countertype:?}")).into())
461 }
462 }
463
464 fn read_counters(&self, countertypes: &[CounterType]) -> CoreResult<Vec<CounterValue>> {
465 let mut results = Vec::new();
466 for countertype in countertypes {
467 results.push(self.read_counter(countertype)?);
468 }
469 Ok(results)
470 }
471
472 fn reset_counter(&self, countertype: &CounterType) -> CoreResult<()> {
473 Err(
475 HardwareCounterError::InvalidConfiguration("PDH counters cannot be reset".to_string())
476 .into(),
477 )
478 }
479
480 fn is_overflowed(&self, _countertype: &CounterType) -> CoreResult<bool> {
481 Ok(false)
483 }
484}
485
486#[cfg(target_os = "macos")]
488pub struct MacOSCounter {
489 active_counters: RwLock<HashMap<CounterType, bool>>,
490}
491
492#[cfg(target_os = "macos")]
493impl MacOSCounter {
494 pub fn new() -> Self {
496 Self {
497 active_counters: RwLock::new(HashMap::new()),
498 }
499 }
500}
501
502#[cfg(target_os = "macos")]
503impl Default for MacOSCounter {
504 fn default() -> Self {
505 Self::new()
506 }
507}
508
509#[cfg(target_os = "macos")]
510impl PerformanceCounter for MacOSCounter {
511 fn available_counters(&self) -> Vec<CounterType> {
512 vec![
513 CounterType::CpuCycles,
514 CounterType::Instructions,
515 CounterType::CpuFrequency,
516 CounterType::CpuTemperature,
517 ]
518 }
519
520 fn is_available(&self, countertype: &CounterType) -> bool {
521 matches!(
522 countertype,
523 CounterType::CpuCycles
524 | CounterType::Instructions
525 | CounterType::CpuFrequency
526 | CounterType::CpuTemperature
527 )
528 }
529
530 fn start_counter(&self, countertype: &CounterType) -> CoreResult<()> {
531 if self.is_available(countertype) {
532 let mut counters = self.active_counters.write().expect("Operation failed");
533 counters.insert(countertype.clone(), true);
534 Ok(())
535 } else {
536 Err(HardwareCounterError::CounterNotFound(format!("{countertype:?}")).into())
537 }
538 }
539
540 fn stop_counter(&self, countertype: &CounterType) -> CoreResult<()> {
541 let mut counters = self.active_counters.write().expect("Operation failed");
542 if counters.remove(countertype).is_some() {
543 Ok(())
544 } else {
545 Err(HardwareCounterError::CounterNotFound(format!("{countertype:?}")).into())
546 }
547 }
548
549 fn read_counter(&self, countertype: &CounterType) -> CoreResult<CounterValue> {
550 let counters = self.active_counters.read().expect("Operation failed");
551 if counters.contains_key(countertype) {
552 let mock_value = match countertype {
554 CounterType::CpuCycles => 2_000_000,
555 CounterType::Instructions => 1_000_000,
556 CounterType::CpuFrequency => 3_200_000_000, CounterType::CpuTemperature => 65, _ => 0,
559 };
560
561 Ok(CounterValue::new(countertype.clone(), mock_value))
562 } else {
563 Err(HardwareCounterError::CounterNotFound(format!("{countertype:?}")).into())
564 }
565 }
566
567 fn read_counters(&self, countertypes: &[CounterType]) -> CoreResult<Vec<CounterValue>> {
568 let mut results = Vec::new();
569 for countertype in countertypes {
570 results.push(self.read_counter(countertype)?);
571 }
572 Ok(results)
573 }
574
575 fn reset_counter(&self, countertype: &CounterType) -> CoreResult<()> {
576 Ok(())
578 }
579
580 fn is_overflowed(&self, _countertype: &CounterType) -> CoreResult<bool> {
581 Ok(false)
583 }
584}
585
586pub struct HardwareCounterManager {
588 backend: Box<dyn PerformanceCounter>,
589 session_counters: RwLock<HashMap<String, Vec<CounterType>>>,
590 counter_history: RwLock<HashMap<CounterType, Vec<CounterValue>>>,
591 max_history_size: usize,
592}
593
594impl HardwareCounterManager {
595 pub fn new() -> CoreResult<Self> {
597 let backend = Self::create_platform_backend()?;
598
599 Ok(Self {
600 backend,
601 session_counters: RwLock::new(HashMap::new()),
602 counter_history: RwLock::new(HashMap::new()),
603 max_history_size: 1000,
604 })
605 }
606
607 fn create_platform_backend() -> CoreResult<Box<dyn PerformanceCounter>> {
609 #[cfg(target_os = "linux")]
610 {
611 Ok(Box::new(LinuxPerfCounter::new()))
612 }
613
614 #[cfg(target_os = "windows")]
615 {
616 Ok(Box::new(WindowsPdhCounter::new()))
617 }
618
619 #[cfg(target_os = "macos")]
620 {
621 Ok(Box::new(MacOSCounter::new()))
622 }
623
624 #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
625 {
626 Err(HardwareCounterError::NotAvailable.into())
627 }
628 }
629
630 pub fn available_counters(&self) -> Vec<CounterType> {
632 self.backend.available_counters()
633 }
634
635 pub fn start_session(&self, sessionname: &str, counters: Vec<CounterType>) -> CoreResult<()> {
637 for counter in &counters {
639 self.backend.start_counter(counter)?;
640 }
641
642 let mut sessions = self.session_counters.write().expect("Operation failed");
644 sessions.insert(sessionname.to_string(), counters);
645
646 Ok(())
647 }
648
649 pub fn stop_session(&self, sessionname: &str) -> CoreResult<()> {
651 let mut sessions = self.session_counters.write().expect("Operation failed");
652
653 if let Some(counters) = sessions.remove(sessionname) {
654 for counter in &counters {
655 self.backend.stop_counter(counter)?;
656 }
657 Ok(())
658 } else {
659 Err(HardwareCounterError::InvalidConfiguration(format!(
660 "Session not found: {sessionname}"
661 ))
662 .into())
663 }
664 }
665
666 pub fn sample_counters(&self) -> CoreResult<HashMap<CounterType, CounterValue>> {
668 let sessions = self.session_counters.read().expect("Operation failed");
669 let active_counters: Vec<CounterType> = sessions
670 .values()
671 .flat_map(|counters| counters.iter())
672 .cloned()
673 .collect::<std::collections::HashSet<_>>()
674 .into_iter()
675 .collect();
676
677 let values = self.backend.read_counters(&active_counters)?;
678
679 let mut history = self.counter_history.write().expect("Operation failed");
681 for value in &values {
682 let counter_history = history.entry(value.countertype.clone()).or_default();
683
684 counter_history.push(value.clone());
685
686 if counter_history.len() > self.max_history_size {
688 counter_history.drain(0..counter_history.len() - self.max_history_size);
689 }
690 }
691
692 let result = values
694 .into_iter()
695 .map(|value| (value.countertype.clone(), value))
696 .collect();
697
698 Ok(result)
699 }
700
701 pub fn get_counter_history(&self, countertype: &CounterType) -> Vec<CounterValue> {
703 let history = self.counter_history.read().expect("Operation failed");
704 history.get(countertype).cloned().unwrap_or_default()
705 }
706
707 pub fn calculate_derived_metrics(
709 &self,
710 counters: &HashMap<CounterType, CounterValue>,
711 ) -> DerivedMetrics {
712 let mut metrics = DerivedMetrics::default();
713
714 if let (Some(instructions), Some(cycles)) = (
716 counters.get(&CounterType::Instructions),
717 counters.get(&CounterType::CpuCycles),
718 ) {
719 if cycles.value > 0 {
720 metrics.instructions_per_cycle = instructions.value as f64 / cycles.value as f64;
721 }
722 }
723
724 if let (Some(references), Some(misses)) = (
726 counters.get(&CounterType::CacheReferences),
727 counters.get(&CounterType::CacheMisses),
728 ) {
729 if references.value > 0 {
730 metrics.cache_hit_rate = 1.0 - (misses.value as f64 / references.value as f64);
731 }
732 }
733
734 if let (Some(instructions), Some(misses)) = (
736 counters.get(&CounterType::BranchInstructions),
737 counters.get(&CounterType::BranchMisses),
738 ) {
739 if instructions.value > 0 {
740 metrics.branch_prediction_accuracy =
741 1.0 - (misses.value as f64 / instructions.value as f64);
742 }
743 }
744
745 if let Some(cycles) = counters.get(&CounterType::CpuCycles) {
747 metrics.cpu_utilization = cycles.value as f64 / 1_000_000.0; }
750
751 metrics
752 }
753
754 pub fn generate_report(&self, sessionname: &str) -> PerformanceReport {
756 let sessions = self.session_counters.read().expect("Operation failed");
757 let counters = sessions.get(sessionname).cloned().unwrap_or_default();
758
759 let current_values = self.sample_counters().unwrap_or_default();
760 let derived_metrics = self.calculate_derived_metrics(¤t_values);
761
762 PerformanceReport {
763 session_name: sessionname.to_string(),
764 timestamp: Instant::now(),
765 counter_values: current_values,
766 derived_metrics,
767 countersmonitored: counters,
768 }
769 }
770}
771
772impl Default for HardwareCounterManager {
773 fn default() -> Self {
774 Self::new().unwrap_or_else(|_| {
775 Self {
777 backend: Box::new(NoOpCounter),
778 session_counters: RwLock::new(HashMap::new()),
779 counter_history: RwLock::new(HashMap::new()),
780 max_history_size: 1000,
781 }
782 })
783 }
784}
785
786pub struct NoOpCounter;
788
789impl PerformanceCounter for NoOpCounter {
790 fn available_counters(&self) -> Vec<CounterType> {
791 Vec::new()
792 }
793
794 fn is_available(&self, _countertype: &CounterType) -> bool {
795 false
796 }
797
798 fn start_counter(&self, _countertype: &CounterType) -> CoreResult<()> {
799 Err(HardwareCounterError::NotAvailable.into())
800 }
801
802 fn stop_counter(&self, _countertype: &CounterType) -> CoreResult<()> {
803 Err(HardwareCounterError::NotAvailable.into())
804 }
805
806 fn read_counter(&self, _countertype: &CounterType) -> CoreResult<CounterValue> {
807 Err(HardwareCounterError::NotAvailable.into())
808 }
809
810 fn read_counters(&self, _countertypes: &[CounterType]) -> CoreResult<Vec<CounterValue>> {
811 Err(HardwareCounterError::NotAvailable.into())
812 }
813
814 fn reset_counter(&self, _countertype: &CounterType) -> CoreResult<()> {
815 Err(HardwareCounterError::NotAvailable.into())
816 }
817
818 fn is_overflowed(&self, _countertype: &CounterType) -> CoreResult<bool> {
819 Err(HardwareCounterError::NotAvailable.into())
820 }
821}
822
823#[derive(Debug, Clone, Default)]
825pub struct DerivedMetrics {
826 pub instructions_per_cycle: f64,
828 pub cache_hit_rate: f64,
830 pub branch_prediction_accuracy: f64,
832 pub cpu_utilization: f64,
834 pub memorybandwidth: f64,
836 pub power_efficiency: f64,
838}
839
840#[derive(Debug, Clone)]
842pub struct PerformanceReport {
843 pub session_name: String,
845 pub timestamp: Instant,
847 pub counter_values: HashMap<CounterType, CounterValue>,
849 pub derived_metrics: DerivedMetrics,
851 pub countersmonitored: Vec<CounterType>,
853}
854
855impl PerformanceReport {
856 pub fn formattext(&self) -> String {
858 let mut output = String::new();
859
860 output.push_str(&format!(
861 "Performance Report: {session_name}\n",
862 session_name = self.session_name
863 ));
864 output.push_str(&format!("Timestamp: {:?}\n\n", self.timestamp));
865
866 output.push_str("Raw Counters:\n");
867 for (countertype, value) in &self.counter_values {
868 output.push_str(&format!(
869 " {}: {} {}\n",
870 countertype.description(),
871 value.scaled_value(),
872 countertype.unit()
873 ));
874 }
875
876 output.push_str("\nDerived Metrics:\n");
877 let metrics = &self.derived_metrics;
878 output.push_str(&format!(
879 " Instructions per Cycle: {:.2}\n",
880 metrics.instructions_per_cycle
881 ));
882 output.push_str(&format!(
883 " Cache Hit Rate: {:.2}%\n",
884 metrics.cache_hit_rate * 100.0
885 ));
886 output.push_str(&format!(
887 " Branch Prediction Accuracy: {:.2}%\n",
888 metrics.branch_prediction_accuracy * 100.0
889 ));
890 output.push_str(&format!(
891 " CPU Utilization: {:.2}%\n",
892 metrics.cpu_utilization
893 ));
894
895 output
896 }
897
898 pub fn to_json(&self) -> String {
900 format!(
902 r#"{{"session":"{}","timestamp":"{}","metrics":{{"ipc":{:.2},"cache_hit_rate":{:.2},"branch_accuracy":{:.2}}}}}"#,
903 self.session_name,
904 self.timestamp.elapsed().as_secs(),
905 self.derived_metrics.instructions_per_cycle,
906 self.derived_metrics.cache_hit_rate,
907 self.derived_metrics.branch_prediction_accuracy
908 )
909 }
910}
911
912static GLOBAL_MANAGER: std::sync::OnceLock<Arc<Mutex<HardwareCounterManager>>> =
914 std::sync::OnceLock::new();
915
916#[allow(dead_code)]
918pub fn global_manager() -> Arc<Mutex<HardwareCounterManager>> {
919 GLOBAL_MANAGER
920 .get_or_init(|| Arc::new(Mutex::new(HardwareCounterManager::default())))
921 .clone()
922}
923
924pub mod utils {
926 use super::*;
927
928 pub fn start_basic_cpumonitoring(sessionname: &str) -> CoreResult<()> {
930 let manager = global_manager();
931 let manager = manager.lock().expect("Operation failed");
932
933 let counters = vec![
934 CounterType::CpuCycles,
935 CounterType::Instructions,
936 CounterType::CacheReferences,
937 CounterType::CacheMisses,
938 ];
939
940 manager.start_session(sessionname, counters)
941 }
942
943 pub fn start_cachemonitoring(sessionname: &str) -> CoreResult<()> {
945 let manager = global_manager();
946 let manager = manager.lock().expect("Operation failed");
947
948 let counters = vec![
949 CounterType::L1DCacheLoads,
950 CounterType::L1DCacheLoadMisses,
951 CounterType::L2CacheLoads,
952 CounterType::L2CacheLoadMisses,
953 CounterType::L3CacheLoads,
954 CounterType::L3CacheLoadMisses,
955 ];
956
957 manager.start_session(sessionname, counters)
958 }
959
960 pub fn get_performance_snapshot() -> CoreResult<HashMap<CounterType, CounterValue>> {
962 let manager = global_manager();
963 let manager = manager.lock().expect("Operation failed");
964 manager.sample_counters()
965 }
966
967 pub fn counters_available() -> bool {
969 let manager = global_manager();
970 let manager = manager.lock().expect("Operation failed");
971 !manager.available_counters().is_empty()
972 }
973}
974
975#[cfg(test)]
976mod tests {
977 use super::*;
978
979 #[test]
980 fn test_countertype_properties() {
981 let counter = CounterType::CpuCycles;
982 assert_eq!(counter.description(), "CPU cycles");
983 assert_eq!(counter.unit(), "count");
984
985 let custom = CounterType::Custom("test".to_string());
986 assert_eq!(custom.description(), "Custom counter");
987 assert_eq!(custom.unit(), "unknown");
988 }
989
990 #[test]
991 fn test_counter_value() {
992 let counter = CounterType::Instructions;
993 let value = CounterValue::new(counter.clone(), 1000);
994
995 assert_eq!(value.countertype, counter);
996 assert_eq!(value.value, 1000);
997 assert_eq!(value.scaled_value(), 1000.0);
998 assert!(value.enabled);
999 }
1000
1001 #[test]
1002 fn test_derived_metrics() {
1003 let metrics = DerivedMetrics {
1004 instructions_per_cycle: 2.5,
1005 cache_hit_rate: 0.95,
1006 branch_prediction_accuracy: 0.98,
1007 ..Default::default()
1008 };
1009
1010 assert_eq!(metrics.instructions_per_cycle, 2.5);
1011 assert_eq!(metrics.cache_hit_rate, 0.95);
1012 assert_eq!(metrics.branch_prediction_accuracy, 0.98);
1013 }
1014
1015 #[test]
1016 fn test_performance_report() {
1017 let mut counter_values = HashMap::new();
1018 counter_values.insert(
1019 CounterType::CpuCycles,
1020 CounterValue::new(CounterType::CpuCycles, 1000000),
1021 );
1022
1023 let report = PerformanceReport {
1024 session_name: "test_session".to_string(),
1025 timestamp: Instant::now(),
1026 counter_values,
1027 derived_metrics: DerivedMetrics::default(),
1028 countersmonitored: vec![CounterType::CpuCycles],
1029 };
1030
1031 let text = report.formattext();
1032 assert!(text.contains("Performance Report: test_session"));
1033 assert!(text.contains("CPU cycles"));
1034
1035 let json = report.to_json();
1036 assert!(json.contains("test_session"));
1037 }
1038
1039 #[test]
1040 fn test_no_op_counter() {
1041 let counter = NoOpCounter;
1042 assert!(counter.available_counters().is_empty());
1043 assert!(!counter.is_available(&CounterType::CpuCycles));
1044 assert!(counter.start_counter(&CounterType::CpuCycles).is_err());
1045 }
1046
1047 #[test]
1048 fn test_global_manager() {
1049 let manager = global_manager();
1050
1051 let manager2 = global_manager();
1053 assert!(Arc::ptr_eq(&manager, &manager2));
1054 }
1055
1056 #[test]
1057 fn test_utils_functions() {
1058 let available = utils::counters_available();
1060 let result = utils::start_basic_cpumonitoring("test");
1064 assert!(result.is_ok() || result.is_err());
1066 }
1067}