Skip to main content

scirs2_core/profiling/
perf_integration.rs

1//! # Linux Perf Integration for SciRS2 v0.2.0
2//!
3//! This module provides integration with Linux perf for detailed performance profiling.
4//! It enables hardware performance counter monitoring and profiling markers.
5//!
6//! # Features
7//!
8//! - **Hardware Counters**: Access CPU performance counters
9//! - **Profiling Markers**: Add markers for perf record visualization
10//! - **Cache Profiling**: Monitor cache hits/misses
11//! - **Branch Prediction**: Track branch prediction performance
12//! - **Platform-Specific**: Linux only, zero overhead on other platforms
13//!
14//! # Example
15//!
16//! ```rust,no_run
17//! use scirs2_core::profiling::perf_integration::{PerfCounter, PerfEvent};
18//!
19//! // Create a perf counter for CPU cycles
20//! let mut counter = PerfCounter::new(PerfEvent::CpuCycles)
21//!     .expect("Failed to create perf counter");
22//!
23//! counter.enable();
24//!
25//! // ... perform computation ...
26//!
27//! let cycles = counter.read().expect("Failed to read counter");
28//! println!("CPU cycles: {}", cycles);
29//! ```
30
31#[cfg(all(target_os = "linux", feature = "profiling_perf"))]
32use crate::CoreResult;
33#[cfg(all(target_os = "linux", feature = "profiling_perf"))]
34use perf_event::{events::Hardware, Builder, Counter};
35#[cfg(all(target_os = "linux", feature = "profiling_perf"))]
36use std::sync::Arc;
37
38/// Performance event types
39#[cfg(all(target_os = "linux", feature = "profiling_perf"))]
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum PerfEvent {
42    /// CPU cycles
43    CpuCycles,
44    /// Instructions executed
45    Instructions,
46    /// Cache references
47    CacheReferences,
48    /// Cache misses
49    CacheMisses,
50    /// Branch instructions
51    BranchInstructions,
52    /// Branch mispredictions
53    BranchMisses,
54    /// Bus cycles
55    BusCycles,
56    /// Stalled cycles (frontend)
57    StalledCyclesFrontend,
58    /// Stalled cycles (backend)
59    StalledCyclesBackend,
60}
61
62#[cfg(all(target_os = "linux", feature = "profiling_perf"))]
63impl PerfEvent {
64    /// Convert to perf-event Hardware type
65    fn to_hardware(self) -> Hardware {
66        match self {
67            PerfEvent::CpuCycles => Hardware::CPU_CYCLES,
68            PerfEvent::Instructions => Hardware::INSTRUCTIONS,
69            PerfEvent::CacheReferences => Hardware::CACHE_REFERENCES,
70            PerfEvent::CacheMisses => Hardware::CACHE_MISSES,
71            PerfEvent::BranchInstructions => Hardware::BRANCH_INSTRUCTIONS,
72            PerfEvent::BranchMisses => Hardware::BRANCH_MISSES,
73            PerfEvent::BusCycles => Hardware::BUS_CYCLES,
74            PerfEvent::StalledCyclesFrontend => Hardware::STALLED_CYCLES_FRONTEND,
75            PerfEvent::StalledCyclesBackend => Hardware::STALLED_CYCLES_BACKEND,
76        }
77    }
78}
79
80/// Performance counter wrapper
81#[cfg(all(target_os = "linux", feature = "profiling_perf"))]
82pub struct PerfCounter {
83    counter: Counter,
84    event: PerfEvent,
85}
86
87#[cfg(all(target_os = "linux", feature = "profiling_perf"))]
88impl PerfCounter {
89    /// Create a new performance counter
90    pub fn new(event: PerfEvent) -> CoreResult<Self> {
91        let counter = Builder::new()
92            .one_cpu(0)
93            .kind(event.to_hardware())
94            .build()
95            .map_err(|e| {
96                crate::CoreError::ConfigError(crate::error::ErrorContext::new(format!(
97                    "Failed to create perf counter: {}",
98                    e
99                )))
100            })?;
101
102        Ok(Self { counter, event })
103    }
104
105    /// Enable the counter
106    pub fn enable(&mut self) -> CoreResult<()> {
107        self.counter.enable().map_err(|e| {
108            crate::CoreError::ConfigError(crate::error::ErrorContext::new(format!(
109                "Failed to enable perf counter: {}",
110                e
111            )))
112        })?;
113        Ok(())
114    }
115
116    /// Disable the counter
117    pub fn disable(&mut self) -> CoreResult<()> {
118        self.counter.disable().map_err(|e| {
119            crate::CoreError::ConfigError(crate::error::ErrorContext::new(format!(
120                "Failed to disable perf counter: {}",
121                e
122            )))
123        })?;
124        Ok(())
125    }
126
127    /// Read the counter value
128    pub fn read(&mut self) -> CoreResult<u64> {
129        let value = self.counter.read().map_err(|e| {
130            crate::CoreError::ConfigError(crate::error::ErrorContext::new(format!(
131                "Failed to read perf counter: {}",
132                e
133            )))
134        })?;
135        Ok(value)
136    }
137
138    /// Reset the counter
139    pub fn reset(&mut self) -> CoreResult<()> {
140        self.counter.reset().map_err(|e| {
141            crate::CoreError::ConfigError(crate::error::ErrorContext::new(format!(
142                "Failed to reset perf counter: {}",
143                e
144            )))
145        })?;
146        Ok(())
147    }
148
149    /// Get the event type
150    pub fn event(&self) -> PerfEvent {
151        self.event
152    }
153}
154
155/// Performance counter group for measuring multiple events
156#[cfg(all(target_os = "linux", feature = "profiling_perf"))]
157pub struct PerfCounterGroup {
158    counters: Vec<PerfCounter>,
159}
160
161#[cfg(all(target_os = "linux", feature = "profiling_perf"))]
162impl PerfCounterGroup {
163    /// Create a new counter group
164    pub fn new(events: &[PerfEvent]) -> CoreResult<Self> {
165        let mut counters = Vec::new();
166
167        for &event in events {
168            counters.push(PerfCounter::new(event)?);
169        }
170
171        Ok(Self { counters })
172    }
173
174    /// Enable all counters
175    pub fn enable(&mut self) -> CoreResult<()> {
176        for counter in &mut self.counters {
177            counter.enable()?;
178        }
179        Ok(())
180    }
181
182    /// Disable all counters
183    pub fn disable(&mut self) -> CoreResult<()> {
184        for counter in &mut self.counters {
185            counter.disable()?;
186        }
187        Ok(())
188    }
189
190    /// Read all counter values
191    pub fn read(&mut self) -> CoreResult<Vec<(PerfEvent, u64)>> {
192        let mut results = Vec::new();
193
194        for counter in &mut self.counters {
195            let value = counter.read()?;
196            results.push((counter.event(), value));
197        }
198
199        Ok(results)
200    }
201
202    /// Reset all counters
203    pub fn reset(&mut self) -> CoreResult<()> {
204        for counter in &mut self.counters {
205            counter.reset()?;
206        }
207        Ok(())
208    }
209}
210
211/// Marker for perf record visualization
212#[cfg(all(target_os = "linux", feature = "profiling_perf"))]
213pub struct PerfMarker {
214    name: String,
215}
216
217#[cfg(all(target_os = "linux", feature = "profiling_perf"))]
218impl PerfMarker {
219    /// Create a new perf marker
220    pub fn new(name: impl Into<String>) -> Self {
221        let marker = Self { name: name.into() };
222        // In practice, this would use perf_event_open with PERF_RECORD_MISC_MARKER
223        // For now, this is a placeholder
224        marker
225    }
226
227    /// End the marker
228    pub fn end(self) {
229        // Placeholder for marker end
230    }
231}
232
233/// Macro for creating a perf marker
234#[macro_export]
235#[cfg(all(target_os = "linux", feature = "profiling_perf"))]
236macro_rules! perf_marker {
237    ($name:expr) => {
238        let _marker = $crate::profiling::perf_integration::PerfMarker::new($name);
239    };
240}
241
242/// Stub implementations when not on Linux or feature is disabled
243#[cfg(not(all(target_os = "linux", feature = "profiling_perf")))]
244use crate::CoreResult;
245
246#[cfg(not(all(target_os = "linux", feature = "profiling_perf")))]
247#[derive(Debug, Clone, Copy)]
248pub enum PerfEvent {
249    CpuCycles,
250    Instructions,
251    CacheReferences,
252    CacheMisses,
253    BranchInstructions,
254    BranchMisses,
255    BusCycles,
256    StalledCyclesFrontend,
257    StalledCyclesBackend,
258}
259
260#[cfg(not(all(target_os = "linux", feature = "profiling_perf")))]
261pub struct PerfCounter;
262
263#[cfg(not(all(target_os = "linux", feature = "profiling_perf")))]
264impl PerfCounter {
265    pub fn new(_event: PerfEvent) -> CoreResult<Self> {
266        Ok(Self)
267    }
268    pub fn enable(&mut self) -> CoreResult<()> {
269        Ok(())
270    }
271    pub fn disable(&mut self) -> CoreResult<()> {
272        Ok(())
273    }
274    pub fn read(&mut self) -> CoreResult<u64> {
275        Ok(0)
276    }
277    pub fn reset(&mut self) -> CoreResult<()> {
278        Ok(())
279    }
280}
281
282#[cfg(test)]
283#[cfg(all(target_os = "linux", feature = "profiling_perf"))]
284mod tests {
285    use super::*;
286
287    #[test]
288    fn test_perf_counter_creation() {
289        let result = PerfCounter::new(PerfEvent::CpuCycles);
290        // May fail if not running with appropriate permissions
291        if let Ok(mut counter) = result {
292            assert!(counter.enable().is_ok() || counter.enable().is_err());
293        }
294    }
295
296    #[test]
297    fn test_perf_counter_group() {
298        let events = [PerfEvent::CpuCycles, PerfEvent::Instructions];
299        let result = PerfCounterGroup::new(&events);
300
301        // May fail if not running with appropriate permissions
302        if let Ok(mut group) = result {
303            assert!(group.enable().is_ok() || group.enable().is_err());
304        }
305    }
306
307    #[test]
308    fn test_perf_marker() {
309        let marker = PerfMarker::new("test_marker");
310        marker.end();
311        // Should not panic
312    }
313}