scribe_scanner/
performance.rs

1//! Performance instrumentation and monitoring for the scanning system.
2//!
3//! This module provides comprehensive performance monitoring, profiling, and
4//! metrics collection to track scanning performance, identify bottlenecks,
5//! and guide optimization efforts.
6
7use fxhash::FxHashMap;
8use once_cell::sync::Lazy;
9use parking_lot::{Mutex, RwLock};
10use serde::{Deserialize, Serialize};
11use std::collections::VecDeque;
12use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
13use std::sync::Arc;
14use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
15
16/// Global performance monitor instance
17pub static PERF_MONITOR: Lazy<PerformanceMonitor> = Lazy::new(PerformanceMonitor::new);
18
19/// Comprehensive performance monitoring system
20#[derive(Debug)]
21pub struct PerformanceMonitor {
22    /// Real-time metrics
23    real_time: Arc<RealTimeMetrics>,
24    /// Historical performance data
25    history: Arc<RwLock<PerformanceHistory>>,
26    /// System resource tracking
27    system_tracker: Arc<Mutex<SystemResourceTracker>>,
28    /// Performance profiles for different operations
29    profiles: Arc<RwLock<FxHashMap<String, OperationProfile>>>,
30    /// Configuration
31    config: MonitoringConfig,
32}
33
34/// Real-time performance metrics (atomic counters)
35#[derive(Debug)]
36pub struct RealTimeMetrics {
37    // File processing counters
38    pub files_processed: AtomicU64,
39    pub files_filtered: AtomicU64,
40    pub files_cached: AtomicU64,
41    pub files_failed: AtomicU64,
42    pub bytes_processed: AtomicU64,
43    pub bytes_read: AtomicU64,
44
45    // Timing counters (microseconds)
46    pub total_scan_time_us: AtomicU64,
47    pub io_time_us: AtomicU64,
48    pub cpu_time_us: AtomicU64,
49    pub git_time_us: AtomicU64,
50    pub filter_time_us: AtomicU64,
51    pub parallel_time_us: AtomicU64,
52
53    // Memory tracking
54    pub peak_memory_bytes: AtomicU64,
55    pub current_memory_bytes: AtomicU64,
56    pub memory_allocations: AtomicU64,
57
58    // Concurrency metrics
59    pub active_threads: AtomicUsize,
60    pub peak_threads: AtomicUsize,
61    pub context_switches: AtomicU64,
62
63    // Cache metrics
64    pub cache_hits: AtomicU64,
65    pub cache_misses: AtomicU64,
66    pub cache_evictions: AtomicU64,
67
68    // Error counters
69    pub io_errors: AtomicU64,
70    pub git_errors: AtomicU64,
71    pub parsing_errors: AtomicU64,
72    pub other_errors: AtomicU64,
73}
74
75/// Historical performance data
76#[derive(Debug)]
77struct PerformanceHistory {
78    /// Time-series performance snapshots
79    snapshots: VecDeque<PerformanceSnapshot>,
80    /// Maximum snapshots to keep
81    max_snapshots: usize,
82    /// Aggregated statistics
83    aggregated: AggregatedStats,
84}
85
86/// Performance snapshot at a point in time
87#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct PerformanceSnapshot {
89    pub timestamp: u64,
90    pub files_per_second: f64,
91    pub bytes_per_second: f64,
92    pub memory_usage_mb: f64,
93    pub cpu_utilization: f64,
94    pub io_wait_percentage: f64,
95    pub cache_hit_rate: f64,
96    pub error_rate: f64,
97    pub active_threads: usize,
98    pub queue_depth: usize,
99}
100
101/// Aggregated performance statistics
102#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct AggregatedStats {
104    pub avg_throughput_fps: f64, // files per second
105    pub p50_latency_ms: f64,
106    pub p95_latency_ms: f64,
107    pub p99_latency_ms: f64,
108    pub max_memory_mb: f64,
109    pub avg_memory_mb: f64,
110    pub total_files_processed: u64,
111    pub total_bytes_processed: u64,
112    pub total_runtime_seconds: f64,
113    pub cache_efficiency: f64,
114    pub error_percentage: f64,
115}
116
117/// System resource tracking
118#[derive(Debug)]
119struct SystemResourceTracker {
120    /// CPU usage samples
121    cpu_samples: VecDeque<CpuSample>,
122    /// Memory usage samples
123    memory_samples: VecDeque<MemorySample>,
124    /// I/O statistics
125    io_stats: IoStats,
126    /// Last sample time
127    last_sample_time: Instant,
128}
129
130/// CPU usage sample
131#[derive(Debug, Clone)]
132struct CpuSample {
133    timestamp: Instant,
134    user_time: Duration,
135    system_time: Duration,
136    idle_time: Duration,
137}
138
139/// Memory usage sample
140#[derive(Debug, Clone)]
141struct MemorySample {
142    timestamp: Instant,
143    rss_bytes: u64,       // Resident Set Size
144    vms_bytes: u64,       // Virtual Memory Size
145    heap_bytes: u64,      // Heap usage
146    available_bytes: u64, // Available system memory
147}
148
149/// I/O statistics
150#[derive(Debug, Default)]
151struct IoStats {
152    pub reads_completed: u64,
153    pub writes_completed: u64,
154    pub bytes_read: u64,
155    pub bytes_written: u64,
156    pub read_time_ms: u64,
157    pub write_time_ms: u64,
158}
159
160/// Operation-specific performance profile
161#[derive(Debug, Clone, Serialize, Deserialize)]
162pub struct OperationProfile {
163    pub operation_name: String,
164    pub call_count: u64,
165    pub total_time_us: u64,
166    pub min_time_us: u64,
167    pub max_time_us: u64,
168    pub avg_time_us: u64,
169    pub p95_time_us: u64,
170    pub success_count: u64,
171    pub error_count: u64,
172    pub bytes_processed: u64,
173    pub last_updated: u64,
174}
175
176/// Configuration for performance monitoring
177#[derive(Debug, Clone)]
178pub struct MonitoringConfig {
179    /// Enable detailed profiling
180    pub enable_profiling: bool,
181    /// Sample interval for system metrics (milliseconds)
182    pub sample_interval_ms: u64,
183    /// Maximum history snapshots to keep
184    pub max_history_snapshots: usize,
185    /// Performance report interval (seconds)
186    pub report_interval_secs: u64,
187    /// Enable memory tracking
188    pub track_memory: bool,
189    /// Enable I/O tracking
190    pub track_io: bool,
191    /// Enable per-operation profiling
192    pub profile_operations: bool,
193}
194
195/// Performance timing guard for automatic measurement
196#[derive(Debug)]
197pub struct PerfTimer {
198    start_time: Instant,
199    operation_name: String,
200    bytes_hint: Option<u64>,
201}
202
203/// Macro for easy performance timing
204#[macro_export]
205macro_rules! perf_timer {
206    ($operation:expr) => {
207        $crate::performance::PerfTimer::start($operation)
208    };
209    ($operation:expr, $bytes:expr) => {
210        $crate::performance::PerfTimer::start_with_bytes($operation, $bytes)
211    };
212}
213
214impl Default for MonitoringConfig {
215    fn default() -> Self {
216        Self {
217            enable_profiling: true,
218            sample_interval_ms: 1000,
219            max_history_snapshots: 3600, // 1 hour at 1s intervals
220            report_interval_secs: 30,
221            track_memory: true,
222            track_io: true,
223            profile_operations: true,
224        }
225    }
226}
227
228impl PerformanceMonitor {
229    /// Create a new performance monitor
230    pub fn new() -> Self {
231        Self {
232            real_time: Arc::new(RealTimeMetrics::new()),
233            history: Arc::new(RwLock::new(PerformanceHistory::new(3600))),
234            system_tracker: Arc::new(Mutex::new(SystemResourceTracker::new())),
235            profiles: Arc::new(RwLock::new(FxHashMap::default())),
236            config: MonitoringConfig::default(),
237        }
238    }
239
240    /// Start performance monitoring
241    pub fn start_monitoring(&self) {
242        if !self.config.enable_profiling {
243            return;
244        }
245
246        let real_time = Arc::clone(&self.real_time);
247        let history = Arc::clone(&self.history);
248        let system_tracker = Arc::clone(&self.system_tracker);
249        let config = self.config.clone();
250
251        // Spawn monitoring task
252        tokio::spawn(async move {
253            let mut interval =
254                tokio::time::interval(Duration::from_millis(config.sample_interval_ms));
255
256            loop {
257                interval.tick().await;
258
259                // Sample system metrics
260                let mut tracker = system_tracker.lock();
261                tracker.sample_system_metrics();
262                drop(tracker);
263
264                // Create performance snapshot
265                let snapshot = Self::create_snapshot(&real_time, &system_tracker);
266
267                // Add to history
268                let mut hist = history.write();
269                hist.add_snapshot(snapshot);
270            }
271        });
272
273        log::info!("Performance monitoring started");
274    }
275
276    /// Record file processing
277    pub fn record_file_processed(&self, bytes: u64, duration: Duration) {
278        self.real_time
279            .files_processed
280            .fetch_add(1, Ordering::Relaxed);
281        self.real_time
282            .bytes_processed
283            .fetch_add(bytes, Ordering::Relaxed);
284        self.real_time
285            .total_scan_time_us
286            .fetch_add(duration.as_micros() as u64, Ordering::Relaxed);
287    }
288
289    /// Record file filtered
290    pub fn record_file_filtered(&self) {
291        self.real_time
292            .files_filtered
293            .fetch_add(1, Ordering::Relaxed);
294    }
295
296    /// Record file cached
297    pub fn record_file_cached(&self) {
298        self.real_time.files_cached.fetch_add(1, Ordering::Relaxed);
299        self.real_time.cache_hits.fetch_add(1, Ordering::Relaxed);
300    }
301
302    /// Record file failed
303    pub fn record_file_failed(&self) {
304        self.real_time.files_failed.fetch_add(1, Ordering::Relaxed);
305    }
306
307    /// Record I/O operation
308    pub fn record_io_operation(&self, bytes: u64, duration: Duration) {
309        self.real_time
310            .bytes_read
311            .fetch_add(bytes, Ordering::Relaxed);
312        self.real_time
313            .io_time_us
314            .fetch_add(duration.as_micros() as u64, Ordering::Relaxed);
315    }
316
317    /// Record git operation
318    pub fn record_git_operation(&self, duration: Duration) {
319        self.real_time
320            .git_time_us
321            .fetch_add(duration.as_micros() as u64, Ordering::Relaxed);
322    }
323
324    /// Record cache miss
325    pub fn record_cache_miss(&self) {
326        self.real_time.cache_misses.fetch_add(1, Ordering::Relaxed);
327    }
328
329    /// Record error
330    pub fn record_error(&self, error_type: ErrorType) {
331        match error_type {
332            ErrorType::Io => self.real_time.io_errors.fetch_add(1, Ordering::Relaxed),
333            ErrorType::Git => self.real_time.git_errors.fetch_add(1, Ordering::Relaxed),
334            ErrorType::Parsing => self
335                .real_time
336                .parsing_errors
337                .fetch_add(1, Ordering::Relaxed),
338            ErrorType::Other => self.real_time.other_errors.fetch_add(1, Ordering::Relaxed),
339        };
340    }
341
342    /// Update memory usage
343    pub fn update_memory_usage(&self, bytes: u64) {
344        self.real_time
345            .current_memory_bytes
346            .store(bytes, Ordering::Relaxed);
347
348        // Update peak
349        self.real_time
350            .peak_memory_bytes
351            .fetch_max(bytes, Ordering::Relaxed);
352    }
353
354    /// Update thread count
355    pub fn update_thread_count(&self, count: usize) {
356        self.real_time
357            .active_threads
358            .store(count, Ordering::Relaxed);
359
360        // Update peak
361        let mut peak = self.real_time.peak_threads.load(Ordering::Relaxed);
362        while peak < count {
363            match self.real_time.peak_threads.compare_exchange_weak(
364                peak,
365                count,
366                Ordering::Relaxed,
367                Ordering::Relaxed,
368            ) {
369                Ok(_) => break,
370                Err(x) => peak = x,
371            }
372        }
373    }
374
375    /// Profile an operation
376    pub fn profile_operation(&self, name: &str, duration: Duration, bytes: u64, success: bool) {
377        if !self.config.profile_operations {
378            return;
379        }
380
381        let mut profiles = self.profiles.write();
382        let profile = profiles
383            .entry(name.to_string())
384            .or_insert_with(|| OperationProfile::new(name));
385
386        profile.record(duration, bytes, success);
387    }
388
389    /// Get current performance snapshot
390    pub fn get_current_snapshot(&self) -> PerformanceSnapshot {
391        Self::create_snapshot(&self.real_time, &self.system_tracker)
392    }
393
394    /// Get aggregated performance statistics
395    pub fn get_aggregated_stats(&self) -> AggregatedStats {
396        let history = self.history.read();
397        history.aggregated.clone()
398    }
399
400    /// Get operation profiles
401    pub fn get_operation_profiles(&self) -> FxHashMap<String, OperationProfile> {
402        self.profiles.read().clone()
403    }
404
405    /// Generate performance report
406    pub fn generate_report(&self) -> PerformanceReport {
407        let snapshot = self.get_current_snapshot();
408        let aggregated = self.get_aggregated_stats();
409        let profiles = self.get_operation_profiles();
410
411        PerformanceReport {
412            current: snapshot,
413            aggregated,
414            top_operations: Self::get_top_operations(&profiles, 10),
415            bottlenecks: self.identify_bottlenecks(),
416            recommendations: self.generate_recommendations(),
417            timestamp: SystemTime::now()
418                .duration_since(UNIX_EPOCH)
419                .unwrap()
420                .as_secs(),
421        }
422    }
423
424    /// Reset all metrics
425    pub fn reset_metrics(&self) {
426        // Reset real-time metrics
427        self.real_time.reset();
428
429        // Clear history
430        let mut history = self.history.write();
431        history.snapshots.clear();
432        history.aggregated = AggregatedStats::default();
433
434        // Clear profiles
435        let mut profiles = self.profiles.write();
436        profiles.clear();
437
438        log::info!("Performance metrics reset");
439    }
440
441    /// Create performance snapshot
442    fn create_snapshot(
443        real_time: &RealTimeMetrics,
444        system_tracker: &Arc<Mutex<SystemResourceTracker>>,
445    ) -> PerformanceSnapshot {
446        let tracker = system_tracker.lock();
447
448        let files_processed = real_time.files_processed.load(Ordering::Relaxed);
449        let bytes_processed = real_time.bytes_processed.load(Ordering::Relaxed);
450        let scan_time_us = real_time.total_scan_time_us.load(Ordering::Relaxed);
451        let cache_hits = real_time.cache_hits.load(Ordering::Relaxed);
452        let cache_misses = real_time.cache_misses.load(Ordering::Relaxed);
453
454        let files_per_second = if scan_time_us > 0 {
455            files_processed as f64 / (scan_time_us as f64 / 1_000_000.0)
456        } else {
457            0.0
458        };
459
460        let bytes_per_second = if scan_time_us > 0 {
461            bytes_processed as f64 / (scan_time_us as f64 / 1_000_000.0)
462        } else {
463            0.0
464        };
465
466        let cache_hit_rate = if cache_hits + cache_misses > 0 {
467            cache_hits as f64 / (cache_hits + cache_misses) as f64
468        } else {
469            0.0
470        };
471
472        PerformanceSnapshot {
473            timestamp: SystemTime::now()
474                .duration_since(UNIX_EPOCH)
475                .unwrap()
476                .as_secs(),
477            files_per_second,
478            bytes_per_second,
479            memory_usage_mb: real_time.current_memory_bytes.load(Ordering::Relaxed) as f64
480                / (1024.0 * 1024.0),
481            cpu_utilization: tracker.get_cpu_utilization(),
482            io_wait_percentage: tracker.get_io_wait_percentage(),
483            cache_hit_rate,
484            error_rate: 0.0, // Calculate based on total operations
485            active_threads: real_time.active_threads.load(Ordering::Relaxed),
486            queue_depth: 0, // Would need queue monitoring
487        }
488    }
489
490    /// Identify performance bottlenecks
491    fn identify_bottlenecks(&self) -> Vec<String> {
492        let mut bottlenecks = Vec::new();
493        let snapshot = self.get_current_snapshot();
494
495        if snapshot.cpu_utilization > 80.0 {
496            bottlenecks.push("High CPU utilization".to_string());
497        }
498
499        if snapshot.io_wait_percentage > 20.0 {
500            bottlenecks.push("High I/O wait time".to_string());
501        }
502
503        if snapshot.cache_hit_rate < 0.5 {
504            bottlenecks.push("Low cache hit rate".to_string());
505        }
506
507        if snapshot.memory_usage_mb > 1000.0 {
508            bottlenecks.push("High memory usage".to_string());
509        }
510
511        bottlenecks
512    }
513
514    /// Generate performance recommendations
515    fn generate_recommendations(&self) -> Vec<String> {
516        let mut recommendations = Vec::new();
517        let bottlenecks = self.identify_bottlenecks();
518
519        for bottleneck in &bottlenecks {
520            match bottleneck.as_str() {
521                "High CPU utilization" => {
522                    recommendations.push(
523                        "Consider reducing parallelism or optimizing CPU-intensive operations"
524                            .to_string(),
525                    );
526                }
527                "High I/O wait time" => {
528                    recommendations.push(
529                        "Consider using faster storage or implementing better I/O batching"
530                            .to_string(),
531                    );
532                }
533                "Low cache hit rate" => {
534                    recommendations.push(
535                        "Increase cache size or improve cache warming strategies".to_string(),
536                    );
537                }
538                "High memory usage" => {
539                    recommendations.push(
540                        "Consider reducing batch sizes or implementing memory streaming"
541                            .to_string(),
542                    );
543                }
544                _ => {}
545            }
546        }
547
548        recommendations
549    }
550
551    /// Get top operations by various metrics
552    fn get_top_operations(
553        profiles: &FxHashMap<String, OperationProfile>,
554        limit: usize,
555    ) -> Vec<OperationProfile> {
556        let mut ops: Vec<_> = profiles.values().cloned().collect();
557        ops.sort_by(|a, b| b.total_time_us.cmp(&a.total_time_us));
558        ops.into_iter().take(limit).collect()
559    }
560}
561
562/// Error types for performance monitoring
563#[derive(Debug, Clone, Copy)]
564pub enum ErrorType {
565    Io,
566    Git,
567    Parsing,
568    Other,
569}
570
571/// Complete performance report
572#[derive(Debug, Serialize, Deserialize)]
573pub struct PerformanceReport {
574    pub current: PerformanceSnapshot,
575    pub aggregated: AggregatedStats,
576    pub top_operations: Vec<OperationProfile>,
577    pub bottlenecks: Vec<String>,
578    pub recommendations: Vec<String>,
579    pub timestamp: u64,
580}
581
582impl RealTimeMetrics {
583    fn new() -> Self {
584        Self {
585            files_processed: AtomicU64::new(0),
586            files_filtered: AtomicU64::new(0),
587            files_cached: AtomicU64::new(0),
588            files_failed: AtomicU64::new(0),
589            bytes_processed: AtomicU64::new(0),
590            bytes_read: AtomicU64::new(0),
591            total_scan_time_us: AtomicU64::new(0),
592            io_time_us: AtomicU64::new(0),
593            cpu_time_us: AtomicU64::new(0),
594            git_time_us: AtomicU64::new(0),
595            filter_time_us: AtomicU64::new(0),
596            parallel_time_us: AtomicU64::new(0),
597            peak_memory_bytes: AtomicU64::new(0),
598            current_memory_bytes: AtomicU64::new(0),
599            memory_allocations: AtomicU64::new(0),
600            active_threads: AtomicUsize::new(0),
601            peak_threads: AtomicUsize::new(0),
602            context_switches: AtomicU64::new(0),
603            cache_hits: AtomicU64::new(0),
604            cache_misses: AtomicU64::new(0),
605            cache_evictions: AtomicU64::new(0),
606            io_errors: AtomicU64::new(0),
607            git_errors: AtomicU64::new(0),
608            parsing_errors: AtomicU64::new(0),
609            other_errors: AtomicU64::new(0),
610        }
611    }
612
613    fn reset(&self) {
614        // Reset all atomic counters
615        self.files_processed.store(0, Ordering::Relaxed);
616        self.files_filtered.store(0, Ordering::Relaxed);
617        self.files_cached.store(0, Ordering::Relaxed);
618        self.files_failed.store(0, Ordering::Relaxed);
619        self.bytes_processed.store(0, Ordering::Relaxed);
620        self.bytes_read.store(0, Ordering::Relaxed);
621        self.total_scan_time_us.store(0, Ordering::Relaxed);
622        self.io_time_us.store(0, Ordering::Relaxed);
623        self.cpu_time_us.store(0, Ordering::Relaxed);
624        self.git_time_us.store(0, Ordering::Relaxed);
625        self.filter_time_us.store(0, Ordering::Relaxed);
626        self.parallel_time_us.store(0, Ordering::Relaxed);
627        self.peak_memory_bytes.store(0, Ordering::Relaxed);
628        self.current_memory_bytes.store(0, Ordering::Relaxed);
629        self.memory_allocations.store(0, Ordering::Relaxed);
630        self.active_threads.store(0, Ordering::Relaxed);
631        self.peak_threads.store(0, Ordering::Relaxed);
632        self.context_switches.store(0, Ordering::Relaxed);
633        self.cache_hits.store(0, Ordering::Relaxed);
634        self.cache_misses.store(0, Ordering::Relaxed);
635        self.cache_evictions.store(0, Ordering::Relaxed);
636        self.io_errors.store(0, Ordering::Relaxed);
637        self.git_errors.store(0, Ordering::Relaxed);
638        self.parsing_errors.store(0, Ordering::Relaxed);
639        self.other_errors.store(0, Ordering::Relaxed);
640    }
641}
642
643impl PerformanceHistory {
644    fn new(max_snapshots: usize) -> Self {
645        Self {
646            snapshots: VecDeque::with_capacity(max_snapshots),
647            max_snapshots,
648            aggregated: AggregatedStats::default(),
649        }
650    }
651
652    fn add_snapshot(&mut self, snapshot: PerformanceSnapshot) {
653        if self.snapshots.len() >= self.max_snapshots {
654            self.snapshots.pop_front();
655        }
656        self.snapshots.push_back(snapshot);
657        self.update_aggregated_stats();
658    }
659
660    fn update_aggregated_stats(&mut self) {
661        if self.snapshots.is_empty() {
662            return;
663        }
664
665        let mut throughputs: Vec<f64> = self.snapshots.iter().map(|s| s.files_per_second).collect();
666        throughputs.sort_by(|a, b| a.partial_cmp(b).unwrap());
667
668        self.aggregated.avg_throughput_fps =
669            throughputs.iter().sum::<f64>() / throughputs.len() as f64;
670
671        // Calculate percentiles (simplified)
672        if !throughputs.is_empty() {
673            self.aggregated.p50_latency_ms = throughputs[throughputs.len() / 2];
674            self.aggregated.p95_latency_ms = throughputs[(throughputs.len() * 95) / 100];
675            self.aggregated.p99_latency_ms = throughputs[(throughputs.len() * 99) / 100];
676        }
677
678        self.aggregated.max_memory_mb = self
679            .snapshots
680            .iter()
681            .map(|s| s.memory_usage_mb)
682            .fold(0.0, f64::max);
683
684        self.aggregated.avg_memory_mb = self
685            .snapshots
686            .iter()
687            .map(|s| s.memory_usage_mb)
688            .sum::<f64>()
689            / self.snapshots.len() as f64;
690    }
691}
692
693impl SystemResourceTracker {
694    fn new() -> Self {
695        Self {
696            cpu_samples: VecDeque::with_capacity(60), // 1 minute of samples
697            memory_samples: VecDeque::with_capacity(60),
698            io_stats: IoStats::default(),
699            last_sample_time: Instant::now(),
700        }
701    }
702
703    fn sample_system_metrics(&mut self) {
704        let now = Instant::now();
705
706        // Sample CPU
707        if let Some(cpu_sample) = self.sample_cpu() {
708            self.cpu_samples.push_back(cpu_sample);
709            if self.cpu_samples.len() > 60 {
710                self.cpu_samples.pop_front();
711            }
712        }
713
714        // Sample memory
715        if let Some(memory_sample) = self.sample_memory() {
716            self.memory_samples.push_back(memory_sample);
717            if self.memory_samples.len() > 60 {
718                self.memory_samples.pop_front();
719            }
720        }
721
722        self.last_sample_time = now;
723    }
724
725    fn sample_cpu(&self) -> Option<CpuSample> {
726        // Platform-specific CPU sampling
727        #[cfg(unix)]
728        {
729            self.sample_cpu_unix()
730        }
731        #[cfg(not(unix))]
732        {
733            None // Fallback for non-Unix platforms
734        }
735    }
736
737    #[cfg(unix)]
738    fn sample_cpu_unix(&self) -> Option<CpuSample> {
739        use std::fs;
740
741        if let Ok(contents) = fs::read_to_string("/proc/stat") {
742            let line = contents.lines().next()?;
743            let parts: Vec<&str> = line.split_whitespace().collect();
744
745            if parts.len() >= 4 && parts[0] == "cpu" {
746                let user: u64 = parts[1].parse().ok()?;
747                let system: u64 = parts[3].parse().ok()?;
748                let idle: u64 = parts[4].parse().ok()?;
749
750                return Some(CpuSample {
751                    timestamp: Instant::now(),
752                    user_time: Duration::from_secs(user / 100), // Convert jiffies
753                    system_time: Duration::from_secs(system / 100),
754                    idle_time: Duration::from_secs(idle / 100),
755                });
756            }
757        }
758
759        None
760    }
761
762    fn sample_memory(&self) -> Option<MemorySample> {
763        // Platform-specific memory sampling
764        #[cfg(unix)]
765        {
766            self.sample_memory_unix()
767        }
768        #[cfg(not(unix))]
769        {
770            None // Fallback
771        }
772    }
773
774    #[cfg(unix)]
775    fn sample_memory_unix(&self) -> Option<MemorySample> {
776        use std::fs;
777
778        // Read process memory info
779        if let Ok(contents) = fs::read_to_string("/proc/self/status") {
780            let mut rss_kb = 0u64;
781            let mut vms_kb = 0u64;
782
783            for line in contents.lines() {
784                if line.starts_with("VmRSS:") {
785                    if let Some(value) = line.split_whitespace().nth(1) {
786                        rss_kb = value.parse().unwrap_or(0);
787                    }
788                } else if line.starts_with("VmSize:") {
789                    if let Some(value) = line.split_whitespace().nth(1) {
790                        vms_kb = value.parse().unwrap_or(0);
791                    }
792                }
793            }
794
795            return Some(MemorySample {
796                timestamp: Instant::now(),
797                rss_bytes: rss_kb * 1024,
798                vms_bytes: vms_kb * 1024,
799                heap_bytes: 0,      // Would need heap profiler integration
800                available_bytes: 0, // Would need system memory info
801            });
802        }
803
804        None
805    }
806
807    fn get_cpu_utilization(&self) -> f64 {
808        if self.cpu_samples.len() < 2 {
809            return 0.0;
810        }
811
812        let recent = &self.cpu_samples[self.cpu_samples.len() - 1];
813        let previous = &self.cpu_samples[self.cpu_samples.len() - 2];
814
815        let total_time = recent.user_time + recent.system_time + recent.idle_time;
816        let prev_total_time = previous.user_time + previous.system_time + previous.idle_time;
817
818        let delta_total = total_time.saturating_sub(prev_total_time);
819        let delta_idle = recent.idle_time.saturating_sub(previous.idle_time);
820
821        if delta_total.as_secs_f64() > 0.0 {
822            100.0 * (1.0 - delta_idle.as_secs_f64() / delta_total.as_secs_f64())
823        } else {
824            0.0
825        }
826    }
827
828    fn get_io_wait_percentage(&self) -> f64 {
829        // Simplified I/O wait calculation
830        10.0 // Placeholder
831    }
832}
833
834impl OperationProfile {
835    fn new(name: &str) -> Self {
836        Self {
837            operation_name: name.to_string(),
838            call_count: 0,
839            total_time_us: 0,
840            min_time_us: u64::MAX,
841            max_time_us: 0,
842            avg_time_us: 0,
843            p95_time_us: 0,
844            success_count: 0,
845            error_count: 0,
846            bytes_processed: 0,
847            last_updated: SystemTime::now()
848                .duration_since(UNIX_EPOCH)
849                .unwrap()
850                .as_secs(),
851        }
852    }
853
854    fn record(&mut self, duration: Duration, bytes: u64, success: bool) {
855        let time_us = duration.as_micros() as u64;
856
857        self.call_count += 1;
858        self.total_time_us += time_us;
859        self.min_time_us = self.min_time_us.min(time_us);
860        self.max_time_us = self.max_time_us.max(time_us);
861        self.avg_time_us = self.total_time_us / self.call_count;
862        self.bytes_processed += bytes;
863
864        if success {
865            self.success_count += 1;
866        } else {
867            self.error_count += 1;
868        }
869
870        self.last_updated = SystemTime::now()
871            .duration_since(UNIX_EPOCH)
872            .unwrap()
873            .as_secs();
874    }
875}
876
877impl PerfTimer {
878    /// Start timing an operation
879    pub fn start(operation_name: &str) -> Self {
880        Self {
881            start_time: Instant::now(),
882            operation_name: operation_name.to_string(),
883            bytes_hint: None,
884        }
885    }
886
887    /// Start timing with bytes hint
888    pub fn start_with_bytes(operation_name: &str, bytes: u64) -> Self {
889        Self {
890            start_time: Instant::now(),
891            operation_name: operation_name.to_string(),
892            bytes_hint: Some(bytes),
893        }
894    }
895
896    /// Finish timing and record success
897    pub fn finish_success(self) {
898        let duration = self.start_time.elapsed();
899        let bytes = self.bytes_hint.unwrap_or(0);
900
901        PERF_MONITOR.profile_operation(&self.operation_name, duration, bytes, true);
902    }
903
904    /// Finish timing and record error
905    pub fn finish_error(self) {
906        let duration = self.start_time.elapsed();
907        let bytes = self.bytes_hint.unwrap_or(0);
908
909        PERF_MONITOR.profile_operation(&self.operation_name, duration, bytes, false);
910    }
911}
912
913impl Drop for PerfTimer {
914    fn drop(&mut self) {
915        // Auto-record on drop (assume success)
916        let duration = self.start_time.elapsed();
917        let bytes = self.bytes_hint.unwrap_or(0);
918
919        PERF_MONITOR.profile_operation(&self.operation_name, duration, bytes, true);
920    }
921}
922
923impl Default for AggregatedStats {
924    fn default() -> Self {
925        Self {
926            avg_throughput_fps: 0.0,
927            p50_latency_ms: 0.0,
928            p95_latency_ms: 0.0,
929            p99_latency_ms: 0.0,
930            max_memory_mb: 0.0,
931            avg_memory_mb: 0.0,
932            total_files_processed: 0,
933            total_bytes_processed: 0,
934            total_runtime_seconds: 0.0,
935            cache_efficiency: 0.0,
936            error_percentage: 0.0,
937        }
938    }
939}
940
941#[cfg(test)]
942mod tests {
943    use super::*;
944    use std::thread;
945
946    #[test]
947    fn test_performance_monitor_creation() {
948        let monitor = PerformanceMonitor::new();
949        let snapshot = monitor.get_current_snapshot();
950
951        assert_eq!(snapshot.files_per_second, 0.0);
952        assert_eq!(snapshot.cache_hit_rate, 0.0);
953    }
954
955    #[test]
956    fn test_real_time_metrics() {
957        let monitor = PerformanceMonitor::new();
958
959        // Record some operations
960        monitor.record_file_processed(1024, Duration::from_millis(10));
961        monitor.record_file_cached();
962        monitor.record_cache_miss();
963
964        let snapshot = monitor.get_current_snapshot();
965        assert!(snapshot.files_per_second > 0.0);
966    }
967
968    #[test]
969    fn test_perf_timer() {
970        let _timer = PerfTimer::start("test_operation");
971        thread::sleep(Duration::from_millis(1));
972        // Timer will auto-record on drop
973    }
974
975    #[test]
976    fn test_perf_timer_macro() {
977        let _timer = perf_timer!("macro_test");
978        thread::sleep(Duration::from_millis(1));
979
980        let _timer2 = perf_timer!("macro_test_with_bytes", 1024);
981        thread::sleep(Duration::from_millis(1));
982    }
983
984    #[test]
985    fn test_operation_profile() {
986        let mut profile = OperationProfile::new("test_op");
987
988        profile.record(Duration::from_millis(10), 1024, true);
989        profile.record(Duration::from_millis(20), 2048, false);
990
991        assert_eq!(profile.call_count, 2);
992        assert_eq!(profile.success_count, 1);
993        assert_eq!(profile.error_count, 1);
994        assert_eq!(profile.bytes_processed, 3072);
995    }
996
997    #[test]
998    fn test_error_recording() {
999        let monitor = PerformanceMonitor::new();
1000
1001        monitor.record_error(ErrorType::Io);
1002        monitor.record_error(ErrorType::Git);
1003        monitor.record_error(ErrorType::Parsing);
1004
1005        assert_eq!(monitor.real_time.io_errors.load(Ordering::Relaxed), 1);
1006        assert_eq!(monitor.real_time.git_errors.load(Ordering::Relaxed), 1);
1007        assert_eq!(monitor.real_time.parsing_errors.load(Ordering::Relaxed), 1);
1008    }
1009
1010    #[test]
1011    fn test_memory_tracking() {
1012        let monitor = PerformanceMonitor::new();
1013
1014        monitor.update_memory_usage(1024 * 1024); // 1MB
1015        monitor.update_memory_usage(2048 * 1024); // 2MB
1016        monitor.update_memory_usage(512 * 1024); // 512KB
1017
1018        assert_eq!(
1019            monitor
1020                .real_time
1021                .current_memory_bytes
1022                .load(Ordering::Relaxed),
1023            512 * 1024
1024        );
1025        assert_eq!(
1026            monitor.real_time.peak_memory_bytes.load(Ordering::Relaxed),
1027            2048 * 1024
1028        );
1029    }
1030
1031    #[test]
1032    fn test_performance_report() {
1033        let monitor = PerformanceMonitor::new();
1034
1035        // Record some data
1036        monitor.record_file_processed(1024, Duration::from_millis(10));
1037        monitor.record_file_cached();
1038        monitor.update_memory_usage(1024 * 1024);
1039
1040        let report = monitor.generate_report();
1041
1042        assert!(report.timestamp > 0);
1043        assert!(report.current.files_per_second >= 0.0);
1044    }
1045
1046    #[test]
1047    fn test_bottleneck_identification() {
1048        let monitor = PerformanceMonitor::new();
1049
1050        // Simulate high memory usage
1051        monitor.update_memory_usage(2000 * 1024 * 1024); // 2GB
1052
1053        let bottlenecks = monitor.identify_bottlenecks();
1054        assert!(bottlenecks.iter().any(|b| b.contains("memory")));
1055    }
1056
1057    #[test]
1058    fn test_metrics_reset() {
1059        let monitor = PerformanceMonitor::new();
1060
1061        // Record some data
1062        monitor.record_file_processed(1024, Duration::from_millis(10));
1063        monitor.record_cache_miss();
1064
1065        // Verify data exists
1066        assert!(monitor.real_time.files_processed.load(Ordering::Relaxed) > 0);
1067
1068        // Reset and verify
1069        monitor.reset_metrics();
1070        assert_eq!(monitor.real_time.files_processed.load(Ordering::Relaxed), 0);
1071        assert_eq!(monitor.real_time.cache_misses.load(Ordering::Relaxed), 0);
1072    }
1073}