Skip to main content

scirs2_core/memory/metrics/
profiler.rs

1//! Memory profiler for real-time memory monitoring and analysis
2//!
3//! This module provides a comprehensive memory profiler that combines
4//! real-time monitoring with advanced analytics and automated reporting.
5
6use std::sync::{Arc, Mutex, RwLock};
7use std::thread;
8use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
9
10use crate::memory::metrics::{
11    analytics::{LeakDetectionConfig, MemoryAnalytics},
12    collector::MemoryMetricsCollector,
13    MemoryEvent, MemoryReport,
14};
15
16#[cfg(test)]
17use crate::memory::metrics::MemoryEventType;
18
19#[cfg(feature = "memory_metrics")]
20#[cfg(feature = "serialization")]
21use serde::{Deserialize, Serialize};
22
23/// Memory profiler configuration
24#[derive(Debug, Clone)]
25pub struct MemoryProfilerConfig {
26    /// Whether the profiler is enabled
27    pub enabled: bool,
28    /// Profiling interval for periodic reporting
29    pub profiling_interval: Duration,
30    /// Whether to automatically detect memory leaks
31    pub auto_leak_detection: bool,
32    /// Whether to generate optimization recommendations
33    pub auto_recommendations: bool,
34    /// Whether to save profiling results to file
35    pub save_to_file: bool,
36    /// File path for saving results (if save_to_file is true)
37    pub output_file_path: Option<String>,
38    /// Maximum number of profiling reports to keep in memory
39    pub max_reports_in_memory: usize,
40    /// Whether to enable call stack capture
41    pub capture_call_stacks: bool,
42}
43
44impl Default for MemoryProfilerConfig {
45    fn default() -> Self {
46        Self {
47            enabled: true,
48            profiling_interval: Duration::from_secs(30),
49            auto_leak_detection: true,
50            auto_recommendations: true,
51            save_to_file: false,
52            output_file_path: None,
53            max_reports_in_memory: 100,
54            capture_call_stacks: cfg!(feature = "memory_call_stack"),
55        }
56    }
57}
58
59/// Profiling session information
60#[derive(Debug, Clone)]
61#[cfg_attr(
62    feature = "memory_metrics",
63    derive(serde::Serialize, serde::Deserialize)
64)]
65pub struct ProfilingSession {
66    /// Session identifier
67    pub id: String,
68    /// Session start time (timestamp in microseconds since epoch)
69    pub start_time_micros: u64,
70    /// Session duration in microseconds
71    pub duration_micros: u64,
72    /// Number of memory events recorded
73    pub event_count: usize,
74    /// Number of components tracked
75    pub component_count: usize,
76    /// Peak memory usage during session
77    pub peak_memory_usage: usize,
78    /// Whether any leaks were detected
79    pub leaks_detected: bool,
80}
81
82/// Memory profiling result containing comprehensive analysis
83#[derive(Debug, Clone)]
84#[cfg_attr(feature = "memory_metrics", derive(Serialize, Deserialize))]
85pub struct ProfilingResult {
86    /// Session information
87    pub session: ProfilingSession,
88    /// Basic memory report
89    pub memory_report: MemoryReport,
90    /// Leak detection results
91    pub leak_results: Vec<crate::memory::metrics::analytics::LeakDetectionResult>,
92    /// Pattern analysis results
93    pub pattern_analysis: Vec<crate::memory::metrics::analytics::MemoryPatternAnalysis>,
94    /// Performance impact analysis
95    pub performance_impact: PerformanceImpactAnalysis,
96    /// Summary and recommendations
97    pub summary: ProfilingSummary,
98}
99
100/// Performance impact analysis
101#[derive(Debug, Clone)]
102#[cfg_attr(feature = "memory_metrics", derive(Serialize, Deserialize))]
103pub struct PerformanceImpactAnalysis {
104    /// Total time spent in memory allocation operations
105    pub total_allocation_time: Duration,
106    /// Average allocation time
107    pub avg_allocation_time: Duration,
108    /// Number of potential performance bottlenecks
109    pub performance_bottlenecks: usize,
110    /// Memory bandwidth utilization estimate
111    pub memorybandwidth_utilization: f64,
112    /// Cache miss estimate
113    pub cache_miss_estimate: f64,
114}
115
116/// Profiling summary with key insights
117#[derive(Debug, Clone)]
118#[cfg_attr(feature = "memory_metrics", derive(Serialize, Deserialize))]
119pub struct ProfilingSummary {
120    /// Overall memory health score (0.0 to 1.0)
121    pub health_score: f64,
122    /// Key insights discovered
123    pub key_insights: Vec<String>,
124    /// Priority recommendations
125    pub priority_recommendations: Vec<String>,
126    /// Risk assessment
127    pub risk_assessment: RiskAssessment,
128}
129
130/// Risk assessment for memory usage
131#[derive(Debug, Clone)]
132#[cfg_attr(feature = "memory_metrics", derive(Serialize, Deserialize))]
133pub enum RiskAssessment {
134    /// Low risk - memory usage is healthy
135    Low,
136    /// Medium risk - some issues detected but manageable
137    Medium { issues: Vec<String> },
138    /// High risk - critical issues that need immediate attention
139    High { critical_issues: Vec<String> },
140}
141
142/// Memory profiler for comprehensive memory analysis
143pub struct MemoryProfiler {
144    /// Configuration
145    config: MemoryProfilerConfig,
146    /// Memory metrics collector
147    collector: Arc<MemoryMetricsCollector>,
148    /// Memory analytics engine
149    analytics: Arc<Mutex<MemoryAnalytics>>,
150    /// Profiling results history
151    results_history: Arc<RwLock<Vec<ProfilingResult>>>,
152    /// Current session information
153    current_session: Arc<Mutex<Option<ProfilingSession>>>,
154    /// Background thread handle
155    background_thread: Option<thread::JoinHandle<()>>,
156}
157
158impl MemoryProfiler {
159    /// Create a new memory profiler
160    pub fn new(config: MemoryProfilerConfig) -> Self {
161        let collector = Arc::new(MemoryMetricsCollector::new(
162            crate::memory::metrics::MemoryMetricsConfig::default(),
163        ));
164
165        let analytics = Arc::new(Mutex::new(MemoryAnalytics::new(
166            LeakDetectionConfig::default(),
167        )));
168
169        let results_history = Arc::new(RwLock::new(Vec::new()));
170        let current_session = Arc::new(Mutex::new(None));
171
172        let mut profiler = Self {
173            config,
174            collector,
175            analytics,
176            results_history,
177            current_session,
178            background_thread: None,
179        };
180
181        // Start background profiling if enabled
182        if profiler.config.enabled {
183            profiler.start_background_profiling();
184        }
185
186        profiler
187    }
188
189    /// Start a new profiling session
190    pub fn start_session(&self, sessionid: Option<String>) -> String {
191        let sessionid = sessionid.unwrap_or_else(|| {
192            let timestamp = SystemTime::now()
193                .duration_since(UNIX_EPOCH)
194                .unwrap_or_default()
195                .as_secs();
196            format!("{timestamp}")
197        });
198
199        let now = SystemTime::now();
200        let start_time_micros = now
201            .duration_since(UNIX_EPOCH)
202            .unwrap_or_default()
203            .as_micros() as u64;
204
205        let session = ProfilingSession {
206            id: sessionid.clone(),
207            start_time_micros,
208            duration_micros: 0,
209            event_count: 0,
210            component_count: 0,
211            peak_memory_usage: 0,
212            leaks_detected: false,
213        };
214
215        {
216            let mut current = self.current_session.lock().expect("Operation failed");
217            *current = Some(session);
218        }
219
220        // Reset collector and analytics
221        self.collector.reset();
222        self.analytics.lock().expect("Operation failed").clear();
223
224        sessionid
225    }
226
227    /// End the current profiling session and generate results
228    pub fn end_session(&self) -> Option<ProfilingResult> {
229        let session = {
230            let mut current = self.current_session.lock().expect("Operation failed");
231            current.take()?
232        };
233
234        // Update session with final metrics
235        let memory_report = self.collector.generate_report();
236        let analytics = self.analytics.lock().expect("Operation failed");
237        let leak_results = analytics.get_leak_detection_results();
238        let pattern_analysis = analytics.get_pattern_analysis_results();
239
240        let now_micros = SystemTime::now()
241            .duration_since(UNIX_EPOCH)
242            .unwrap_or_default()
243            .as_micros() as u64;
244
245        let duration_micros = now_micros.saturating_sub(session.start_time_micros);
246
247        let updated_session = ProfilingSession {
248            duration_micros,
249            event_count: memory_report.total_allocation_count,
250            component_count: memory_report.component_stats.len(),
251            peak_memory_usage: memory_report.total_peak_usage,
252            leaks_detected: leak_results.iter().any(|r| r.leak_detected),
253            ..session
254        };
255
256        let performance_impact = self.analyze_performance_impact(&memory_report, &pattern_analysis);
257        let summary =
258            self.generate_profiling_summary(&memory_report, &leak_results, &pattern_analysis);
259
260        let result = ProfilingResult {
261            session: updated_session,
262            memory_report,
263            leak_results,
264            pattern_analysis,
265            performance_impact,
266            summary,
267        };
268
269        // Store result in history
270        {
271            let mut history = self.results_history.write().expect("Operation failed");
272            history.push(result.clone());
273
274            // Limit history size
275            while history.len() > self.config.max_reports_in_memory {
276                history.remove(0);
277            }
278        }
279
280        // Save to file if configured
281        if self.config.save_to_file {
282            if let Some(path) = &self.config.output_file_path {
283                let _ = self.save_result_to_file(&result, path);
284            }
285        }
286
287        Some(result)
288    }
289
290    /// Record a memory event for profiling
291    pub fn record_event(&self, event: MemoryEvent) {
292        // Record in collector
293        self.collector.record_event(event.clone());
294
295        // Record in analytics
296        self.analytics
297            .lock()
298            .expect("Operation failed")
299            .record_event(event);
300    }
301
302    /// Start background profiling thread
303    fn start_background_profiling(&mut self) {
304        if self.background_thread.is_some() {
305            return; // Already running
306        }
307
308        let interval = self.config.profiling_interval;
309        let collector = Arc::clone(&self.collector);
310        let analytics = Arc::clone(&self.analytics);
311        let results_history = Arc::clone(&self.results_history);
312        let current_session = Arc::clone(&self.current_session);
313        let config = self.config.clone();
314
315        let handle = thread::spawn(move || {
316            let mut last_report_time = Instant::now();
317
318            loop {
319                thread::sleep(Duration::from_secs(1));
320
321                if last_report_time.elapsed() >= interval {
322                    // Generate periodic report
323                    let memory_report = collector.generate_report();
324                    let analytics_guard = analytics.lock().expect("Operation failed");
325                    let leak_results = analytics_guard.get_leak_detection_results();
326                    let pattern_analysis = analytics_guard.get_pattern_analysis_results();
327                    drop(analytics_guard);
328
329                    // Check for critical issues
330                    let critical_leaks = leak_results
331                        .iter()
332                        .any(|r| r.leak_detected && r.confidence > 0.8);
333                    let high_memory_usage = memory_report.total_current_usage > 1024 * 1024 * 1024; // 1 GB
334
335                    if critical_leaks || high_memory_usage {
336                        // Log critical issues
337                        eprintln!("MEMORY PROFILER WARNING: Critical memory issues detected!");
338                        if critical_leaks {
339                            eprintln!("  - Memory leaks detected in components");
340                        }
341                        if high_memory_usage {
342                            eprintln!(
343                                "  - High memory usage: {} MB",
344                                memory_report.total_current_usage / (1024 * 1024)
345                            );
346                        }
347                    }
348
349                    last_report_time = Instant::now();
350                }
351
352                // Check if we should continue running
353                if !config.enabled {
354                    break;
355                }
356            }
357        });
358
359        self.background_thread = Some(handle);
360    }
361
362    /// Analyze performance impact based on memory patterns
363    fn analyze_performance_impact(
364        &self,
365        memory_report: &MemoryReport,
366        pattern_analysis: &[crate::memory::metrics::analytics::MemoryPatternAnalysis],
367    ) -> PerformanceImpactAnalysis {
368        // Calculate performance metrics based on allocation patterns
369        let total_allocations = memory_report.total_allocation_count;
370        let total_duration = memory_report.duration;
371
372        // Estimate allocation time (this would be more accurate with actual timing data)
373        let avg_allocation_time = if total_allocations > 0 {
374            Duration::from_nanos(100) // Estimate: 100ns per allocation
375        } else {
376            Duration::from_nanos(0)
377        };
378
379        let total_allocation_time = avg_allocation_time * total_allocations as u32;
380
381        // Count performance bottlenecks
382        let performance_bottlenecks = pattern_analysis
383            .iter()
384            .map(|analysis| {
385                analysis.potential_issues
386                    .iter()
387                    .filter(|issue| matches!(
388                        issue,
389                        crate::memory::metrics::analytics::MemoryIssue::HighAllocationFrequency { .. }
390                    ))
391                    .count()
392            })
393            .sum();
394
395        // Estimate memory bandwidth utilization (simplified)
396        let bytes_per_second = if total_duration.as_secs() > 0 {
397            memory_report.total_allocated_bytes as f64 / total_duration.as_secs_f64()
398        } else {
399            0.0
400        };
401
402        // Assume peak memory bandwidth of 100 GB/s (modern systems)
403        let memorybandwidth_utilization =
404            (bytes_per_second / (100.0 * 1024.0 * 1024.0 * 1024.0)).min(1.0);
405
406        // Estimate cache miss rate based on allocation patterns
407        let cache_miss_estimate = pattern_analysis
408            .iter()
409            .map(|analysis| analysis.efficiency.fragmentation_estimate)
410            .sum::<f64>()
411            / pattern_analysis.len().max(1) as f64;
412
413        PerformanceImpactAnalysis {
414            total_allocation_time,
415            avg_allocation_time,
416            performance_bottlenecks,
417            memorybandwidth_utilization,
418            cache_miss_estimate,
419        }
420    }
421
422    /// Generate profiling summary with insights and recommendations
423    fn generate_profiling_summary(
424        &self,
425        memory_report: &MemoryReport,
426        leak_results: &[crate::memory::metrics::analytics::LeakDetectionResult],
427        pattern_analysis: &[crate::memory::metrics::analytics::MemoryPatternAnalysis],
428    ) -> ProfilingSummary {
429        let mut health_score = 1.0;
430        let mut key_insights = Vec::new();
431        let mut priority_recommendations = Vec::new();
432        let mut risk_issues = Vec::new();
433
434        // Analyze memory health
435        let total_memory_mb = memory_report.total_current_usage / (1024 * 1024);
436        if total_memory_mb > 1000 {
437            health_score -= 0.2;
438            key_insights.push(format!("High memory usage detected: {total_memory_mb} MB"));
439            priority_recommendations
440                .push("Consider implementing memory optimization strategies".to_string());
441        }
442
443        // Check for memory leaks
444        let critical_leaks = leak_results
445            .iter()
446            .filter(|r| r.leak_detected && r.confidence > 0.8)
447            .count();
448        if critical_leaks > 0 {
449            health_score -= 0.3 * critical_leaks as f64;
450            key_insights.push(format!("{critical_leaks} potential memory leaks detected"));
451            priority_recommendations
452                .push("Investigate and fix memory leaks immediately".to_string());
453            risk_issues.push(format!("{critical_leaks} critical memory leaks"));
454        }
455
456        // Check allocation efficiency
457        let avg_reuse_ratio = pattern_analysis
458            .iter()
459            .map(|p| p.efficiency.reuse_ratio)
460            .sum::<f64>()
461            / pattern_analysis.len().max(1) as f64;
462
463        if avg_reuse_ratio > 5.0 {
464            health_score -= 0.1;
465            key_insights.push("Low memory reuse efficiency detected".to_string());
466            priority_recommendations
467                .push("Implement buffer pooling to improve memory reuse".to_string());
468        }
469
470        // Check allocation frequency
471        let high_frequency_components = pattern_analysis
472            .iter()
473            .filter(|p| p.efficiency.allocation_frequency > 100.0)
474            .count();
475
476        if high_frequency_components > 0 {
477            health_score -= 0.1;
478            key_insights.push(format!(
479                "{high_frequency_components} components with high allocation frequency"
480            ));
481            priority_recommendations
482                .push("Consider batching allocations for better performance".to_string());
483        }
484
485        // Determine risk assessment
486        let risk_assessment = if health_score > 0.8 {
487            RiskAssessment::Low
488        } else if health_score > 0.5 {
489            RiskAssessment::Medium {
490                issues: key_insights.clone(),
491            }
492        } else {
493            RiskAssessment::High {
494                critical_issues: risk_issues,
495            }
496        };
497
498        // Add general insights
499        if memory_report.component_stats.len() > 10 {
500            key_insights
501                .push("Large number of components tracked - consider consolidation".to_string());
502        }
503
504        if memory_report.duration.as_secs() < 60 {
505            key_insights.push(
506                "Short profiling duration - longer sessions provide better insights".to_string(),
507            );
508        }
509
510        ProfilingSummary {
511            health_score: health_score.max(0.0),
512            key_insights,
513            priority_recommendations,
514            risk_assessment,
515        }
516    }
517
518    /// Save profiling result to file
519    fn save_result_to_file(
520        &self,
521        result: &ProfilingResult,
522        file_path: &str,
523    ) -> Result<(), Box<dyn std::error::Error>> {
524        #[cfg(feature = "memory_metrics")]
525        {
526            let json = serde_json::to_string_pretty(result)?;
527            std::fs::write(file_path, json)?;
528        }
529
530        #[cfg(not(feature = "memory_metrics"))]
531        {
532            // Just write a simple summary
533            let summary = format!(
534                "Memory Profiling Session: {}\nDuration: {} micros\nPeak Usage: {} bytes\nLeaks Detected: {}\n",
535                result.session.id,
536                result.session.duration_micros,
537                result.session.peak_memory_usage,
538                result.session.leaks_detected
539            );
540            std::fs::write(file_path, summary)?;
541        }
542
543        Ok(())
544    }
545
546    /// Get profiling results history
547    pub fn get_results_history(&self) -> Vec<ProfilingResult> {
548        self.results_history
549            .read()
550            .expect("Operation failed")
551            .clone()
552    }
553
554    /// Get current session information
555    pub fn get_current_session(&self) -> Option<ProfilingSession> {
556        self.current_session
557            .lock()
558            .expect("Operation failed")
559            .clone()
560    }
561
562    /// Generate a quick health check report
563    pub fn health_check(&self) -> ProfilingSummary {
564        let memory_report = self.collector.generate_report();
565        let analytics = self.analytics.lock().expect("Operation failed");
566        let leak_results = analytics.get_leak_detection_results();
567        let pattern_analysis = analytics.get_pattern_analysis_results();
568        drop(analytics);
569
570        self.generate_profiling_summary(&memory_report, &leak_results, &pattern_analysis)
571    }
572
573    /// Clear all profiling data
574    pub fn clear_all_data(&self) {
575        self.collector.reset();
576        self.analytics.lock().expect("Operation failed").clear();
577        self.results_history
578            .write()
579            .expect("Operation failed")
580            .clear();
581        *self.current_session.lock().expect("Operation failed") = None;
582    }
583}
584
585impl Default for MemoryProfiler {
586    fn default() -> Self {
587        Self::new(MemoryProfilerConfig::default())
588    }
589}
590
591#[cfg(test)]
592mod tests {
593    use super::*;
594
595    #[test]
596    fn test_memory_profiler_creation() {
597        let profiler = MemoryProfiler::new(MemoryProfilerConfig::default());
598        assert!(profiler.get_current_session().is_none());
599        assert!(profiler.get_results_history().is_empty());
600    }
601
602    #[test]
603    fn test_profiling_session_lifecycle() {
604        let profiler = MemoryProfiler::new(MemoryProfilerConfig {
605            enabled: false, // Disable background thread for testing
606            ..Default::default()
607        });
608
609        // Start session
610        let sessionid = profiler.start_session(Some("test_session".to_string()));
611        assert_eq!(sessionid, "test_session");
612        assert!(profiler.get_current_session().is_some());
613
614        // Record some events
615        let event = MemoryEvent::new(MemoryEventType::Allocation, "TestComponent", 1024, 0x1000);
616        profiler.record_event(event);
617
618        // End session
619        std::thread::sleep(Duration::from_millis(10)); // Ensure some duration
620        let result = profiler.end_session();
621        assert!(result.is_some());
622
623        let result = result.expect("Operation failed");
624        assert_eq!(result.session.id, "test_session");
625        assert!(result.session.duration_micros > 0);
626        assert!(profiler.get_current_session().is_none());
627    }
628
629    #[test]
630    fn test_health_check() {
631        let profiler = MemoryProfiler::new(MemoryProfilerConfig {
632            enabled: false,
633            ..Default::default()
634        });
635
636        let health = profiler.health_check();
637        assert!(health.health_score >= 0.0 && health.health_score <= 1.0);
638    }
639
640    #[test]
641    fn test_performance_impact_analysis() {
642        let profiler = MemoryProfiler::new(MemoryProfilerConfig {
643            enabled: false,
644            ..Default::default()
645        });
646
647        let memory_report = crate::memory::metrics::MemoryReport {
648            total_current_usage: 1024,
649            total_peak_usage: 2048,
650            total_allocation_count: 10,
651            total_allocated_bytes: 4096,
652            component_stats: std::collections::HashMap::new(),
653            duration: Duration::from_secs(60),
654        };
655
656        let pattern_analysis = Vec::new();
657        let impact = profiler.analyze_performance_impact(&memory_report, &pattern_analysis);
658
659        assert_eq!(impact.avg_allocation_time, Duration::from_nanos(100));
660        assert_eq!(impact.performance_bottlenecks, 0);
661    }
662}