scirs2_core/profiling/
hardware_counters.rs

1//! # Hardware Performance Counter Integration
2//!
3//! This module provides integration with hardware performance counters for detailed
4//! performance analysis including CPU cycles, cache misses, branch predictions, and more.
5
6use crate::error::{CoreError, CoreResult};
7use std::collections::HashMap;
8use std::sync::{Arc, Mutex, RwLock};
9use std::time::Instant;
10use thiserror::Error;
11
12/// Error types for hardware performance counters
13#[derive(Error, Debug)]
14pub enum HardwareCounterError {
15    /// Performance counters not available on this platform
16    #[error("Performance counters not available on this platform")]
17    NotAvailable,
18
19    /// Permission denied to access performance counters
20    #[error("Permission denied to access performance counters: {0}")]
21    PermissionDenied(String),
22
23    /// Counter not found
24    #[error("Performance counter not found: {0}")]
25    CounterNotFound(String),
26
27    /// Invalid counter configuration
28    #[error("Invalid counter configuration: {0}")]
29    InvalidConfiguration(String),
30
31    /// System error
32    #[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/// Hardware performance counter types
43#[derive(Debug, Clone, PartialEq, Eq, Hash)]
44pub enum CounterType {
45    // CPU Counters
46    /// CPU cycles
47    CpuCycles,
48    /// Instructions retired
49    Instructions,
50    /// Cache references
51    CacheReferences,
52    /// Cache misses
53    CacheMisses,
54    /// Branch instructions
55    BranchInstructions,
56    /// Branch mispredictions
57    BranchMisses,
58    /// Bus cycles
59    BusCycles,
60    /// Stalled cycles frontend
61    StalledCyclesFrontend,
62    /// Stalled cycles backend
63    StalledCyclesBackend,
64
65    // L1 Cache Counters
66    /// L1 data cache loads
67    L1DCacheLoads,
68    /// L1 data cache load misses
69    L1DCacheLoadMisses,
70    /// L1 data cache stores
71    L1DCacheStores,
72    /// L1 instruction cache loads
73    L1ICacheLoads,
74    /// L1 instruction cache load misses
75    L1ICacheLoadMisses,
76
77    // L2/L3 Cache Counters
78    /// L2 cache loads
79    L2CacheLoads,
80    /// L2 cache load misses
81    L2CacheLoadMisses,
82    /// L3 cache loads
83    L3CacheLoads,
84    /// L3 cache load misses
85    L3CacheLoadMisses,
86
87    // Memory Counters
88    /// DTLB loads
89    DtlbLoads,
90    /// DTLB load misses
91    DtlbLoadMisses,
92    /// ITLB loads
93    ItlbLoads,
94    /// ITLB load misses
95    ItlbLoadMisses,
96
97    // Power/Thermal Counters
98    /// CPU power consumption
99    CpuPower,
100    /// CPU temperature
101    CpuTemperature,
102    /// CPU frequency
103    CpuFrequency,
104
105    // Custom counter for platform-specific counters
106    Custom(String),
107}
108
109impl CounterType {
110    /// Get a human-readable description of the counter
111    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    /// Get the unit for this counter type
143    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/// Performance counter value with metadata
176#[derive(Debug, Clone)]
177pub struct CounterValue {
178    /// Counter type
179    pub countertype: CounterType,
180    /// Raw counter value
181    pub value: u64,
182    /// Timestamp when value was read
183    pub timestamp: Instant,
184    /// Whether the counter is running
185    pub enabled: bool,
186    /// Counter scaling factor (for normalized values)
187    pub scaling_factor: f64,
188}
189
190impl CounterValue {
191    /// Create a new counter value
192    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    /// Get the scaled value
203    pub fn scaled_value(&self) -> f64 {
204        self.value as f64 * self.scaling_factor
205    }
206}
207
208/// Hardware performance counter interface
209pub trait PerformanceCounter: Send + Sync {
210    /// Get available counter types on this platform
211    fn available_counters(&self) -> Vec<CounterType>;
212
213    /// Check if a counter type is available
214    fn is_available(&self, countertype: &CounterType) -> bool;
215
216    /// Start monitoring a counter
217    fn start_counter(&self, countertype: &CounterType) -> CoreResult<()>;
218
219    /// Stop monitoring a counter
220    fn stop_counter(&self, countertype: &CounterType) -> CoreResult<()>;
221
222    /// Read current value of a counter
223    fn read_counter(&self, countertype: &CounterType) -> CoreResult<CounterValue>;
224
225    /// Read multiple counters atomically
226    fn read_counters(&self, countertypes: &[CounterType]) -> CoreResult<Vec<CounterValue>>;
227
228    /// Reset a counter to zero
229    fn reset_counter(&self, countertype: &CounterType) -> CoreResult<()>;
230
231    /// Get counter overflow status
232    fn is_overflowed(&self, countertype: &CounterType) -> CoreResult<bool>;
233}
234
235/// Linux perf_event implementation
236#[cfg(target_os = "linux")]
237pub struct LinuxPerfCounter {
238    active_counters: RwLock<HashMap<CounterType, i32>>, // file descriptors
239}
240
241#[cfg(target_os = "linux")]
242impl LinuxPerfCounter {
243    /// Create a new Linux perf counter
244    pub fn new() -> Self {
245        Self {
246            active_counters: RwLock::new(HashMap::new()),
247        }
248    }
249
250    /// Convert counter type to perf event type and config
251    fn counter_to_perf_config(&self, countertype: &CounterType) -> Option<(u32, u64)> {
252        match countertype {
253            CounterType::CpuCycles => Some((0, 0)), // PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES
254            CounterType::Instructions => Some((0, 1)), // PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS
255            CounterType::CacheReferences => Some((0, 2)), // PERF_COUNT_HW_CACHE_REFERENCES
256            CounterType::CacheMisses => Some((0, 3)),  // PERF_COUNT_HW_CACHE_MISSES
257            CounterType::BranchInstructions => Some((0, 4)), // PERF_COUNT_HW_BRANCH_INSTRUCTIONS
258            CounterType::BranchMisses => Some((0, 5)), // PERF_COUNT_HW_BRANCH_MISSES
259            CounterType::BusCycles => Some((0, 6)),    // PERF_COUNT_HW_BUS_CYCLES
260            CounterType::StalledCyclesFrontend => Some((0, 7)), // PERF_COUNT_HW_STALLED_CYCLES_FRONTEND
261            CounterType::StalledCyclesBackend => Some((0, 8)), // PERF_COUNT_HW_STALLED_CYCLES_BACKEND
262            _ => None, // Not supported or requires hardware cache events
263        }
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            // In a real implementation, we would:
297            // 1. Create perf_event_attr structure
298            // 2. Call perf_event_open syscall
299            // 3. Store the file descriptor
300
301            // For now, simulate with a dummy file descriptor
302            let fd = 42; // Would be actual fd from perf_event_open
303
304            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            // In real implementation: close(fd)
317            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            // In real implementation: read() from fd
328            // For now, return a mock value
329            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            // In real implementation: ioctl(fd, PERF_EVENT_IOC_RESET, 0)
360            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            // In real implementation: check overflow bit from perf_event read
370            // For now, always return false (not overflowed)
371            Ok(false)
372        } else {
373            Err(HardwareCounterError::CounterNotFound(format!("{countertype:?}")).into())
374        }
375    }
376}
377
378/// Windows Performance Data Helper (PDH) implementation
379#[cfg(target_os = "windows")]
380pub struct WindowsPdhCounter {
381    active_counters: RwLock<HashMap<CounterType, String>>, // PDH counter paths
382}
383
384#[cfg(target_os = "windows")]
385impl WindowsPdhCounter {
386    /// Create a new Windows PDH counter
387    pub fn new() -> Self {
388        Self {
389            active_counters: RwLock::new(HashMap::new()),
390        }
391    }
392
393    /// Convert counter type to PDH counter path
394    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            // In real implementation: PDH API calls
430            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            // Mock values for Windows
451            let mock_value = match countertype {
452                CounterType::CpuCycles => 85,               // CPU usage percentage
453                CounterType::CpuFrequency => 2_400_000_000, // 2.4 GHz
454                CounterType::CpuPower => 45,                // 45 watts
455                _ => 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        // PDH counters can't be reset
474        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        // PDH counters don't typically overflow in our implementation
482        Ok(false)
483    }
484}
485
486/// macOS performance counter implementation using system profiling
487#[cfg(target_os = "macos")]
488pub struct MacOSCounter {
489    active_counters: RwLock<HashMap<CounterType, bool>>,
490}
491
492#[cfg(target_os = "macos")]
493impl MacOSCounter {
494    /// Create a new macOS counter
495    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            // Mock values for macOS
553            let mock_value = match countertype {
554                CounterType::CpuCycles => 2_000_000,
555                CounterType::Instructions => 1_000_000,
556                CounterType::CpuFrequency => 3_200_000_000, // 3.2 GHz
557                CounterType::CpuTemperature => 65,          // 65°C
558                _ => 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        // macOS counters typically can't be reset
577        Ok(())
578    }
579
580    fn is_overflowed(&self, _countertype: &CounterType) -> CoreResult<bool> {
581        // macOS hardware counters don't typically overflow in our implementation
582        Ok(false)
583    }
584}
585
586/// Hardware counter manager that provides a unified interface
587pub 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    /// Create a new hardware counter manager with platform-specific backend
596    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    /// Create platform-specific backend
608    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    /// Get available counter types
631    pub fn available_counters(&self) -> Vec<CounterType> {
632        self.backend.available_counters()
633    }
634
635    /// Start a profiling session with specific counters
636    pub fn start_session(&self, sessionname: &str, counters: Vec<CounterType>) -> CoreResult<()> {
637        // Start all requested counters
638        for counter in &counters {
639            self.backend.start_counter(counter)?;
640        }
641
642        // Register session
643        let mut sessions = self.session_counters.write().expect("Operation failed");
644        sessions.insert(sessionname.to_string(), counters);
645
646        Ok(())
647    }
648
649    /// Stop a profiling session
650    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    /// Sample all active counters
667    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        // Store in history
680        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            // Limit history size
687            if counter_history.len() > self.max_history_size {
688                counter_history.drain(0..counter_history.len() - self.max_history_size);
689            }
690        }
691
692        // Convert to HashMap
693        let result = values
694            .into_iter()
695            .map(|value| (value.countertype.clone(), value))
696            .collect();
697
698        Ok(result)
699    }
700
701    /// Get counter history
702    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    /// Calculate derived metrics
708    pub fn calculate_derived_metrics(
709        &self,
710        counters: &HashMap<CounterType, CounterValue>,
711    ) -> DerivedMetrics {
712        let mut metrics = DerivedMetrics::default();
713
714        // Instructions per cycle (IPC)
715        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        // Cache hit rate
725        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        // Branch prediction accuracy
735        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        // CPU utilization (cycles per second)
746        if let Some(cycles) = counters.get(&CounterType::CpuCycles) {
747            // Would need time delta for accurate calculation
748            metrics.cpu_utilization = cycles.value as f64 / 1_000_000.0; // Simplified
749        }
750
751        metrics
752    }
753
754    /// Generate performance report
755    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(&current_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            // Fallback with no-op backend
776            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
786/// No-op counter for unsupported platforms
787pub 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/// Derived performance metrics calculated from raw counters
824#[derive(Debug, Clone, Default)]
825pub struct DerivedMetrics {
826    /// Instructions per cycle
827    pub instructions_per_cycle: f64,
828    /// Cache hit rate (0.0 to 1.0)
829    pub cache_hit_rate: f64,
830    /// Branch prediction accuracy (0.0 to 1.0)
831    pub branch_prediction_accuracy: f64,
832    /// CPU utilization percentage
833    pub cpu_utilization: f64,
834    /// Memory bandwidth (bytes per second)
835    pub memorybandwidth: f64,
836    /// Power efficiency (instructions per watt)
837    pub power_efficiency: f64,
838}
839
840/// Performance report containing counter values and analysis
841#[derive(Debug, Clone)]
842pub struct PerformanceReport {
843    /// Session name
844    pub session_name: String,
845    /// Report timestamp
846    pub timestamp: Instant,
847    /// Raw counter values
848    pub counter_values: HashMap<CounterType, CounterValue>,
849    /// Derived metrics
850    pub derived_metrics: DerivedMetrics,
851    /// Counters that were monitored
852    pub countersmonitored: Vec<CounterType>,
853}
854
855impl PerformanceReport {
856    /// Format the report as human-readable text
857    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    /// Export report as JSON
899    pub fn to_json(&self) -> String {
900        // Simplified JSON serialization - real implementation would use serde
901        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
912/// Global hardware counter manager instance
913static GLOBAL_MANAGER: std::sync::OnceLock<Arc<Mutex<HardwareCounterManager>>> =
914    std::sync::OnceLock::new();
915
916/// Get the global hardware counter manager
917#[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
924/// Convenience functions for hardware performance monitoring
925pub mod utils {
926    use super::*;
927
928    /// Start monitoring basic CPU performance counters
929    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    /// Start monitoring cache performance
944    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    /// Get a quick performance snapshot
961    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    /// Check if hardware performance counters are available
968    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        // Should return the same instance
1052        let manager2 = global_manager();
1053        assert!(Arc::ptr_eq(&manager, &manager2));
1054    }
1055
1056    #[test]
1057    fn test_utils_functions() {
1058        // Test that utility functions don't panic
1059        let available = utils::counters_available();
1060        // Function should complete without panicking - no assertion needed
1061
1062        // Test starting monitoring (may fail on unsupported platforms)
1063        let result = utils::start_basic_cpumonitoring("test");
1064        // Either succeeds or fails with known error
1065        assert!(result.is_ok() || result.is_err());
1066    }
1067}