scirs2_core/memory/metrics/
profiler.rs1use 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#[derive(Debug, Clone)]
25pub struct MemoryProfilerConfig {
26 pub enabled: bool,
28 pub profiling_interval: Duration,
30 pub auto_leak_detection: bool,
32 pub auto_recommendations: bool,
34 pub save_to_file: bool,
36 pub output_file_path: Option<String>,
38 pub max_reports_in_memory: usize,
40 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#[derive(Debug, Clone)]
61#[cfg_attr(
62 feature = "memory_metrics",
63 derive(serde::Serialize, serde::Deserialize)
64)]
65pub struct ProfilingSession {
66 pub id: String,
68 pub start_time_micros: u64,
70 pub duration_micros: u64,
72 pub event_count: usize,
74 pub component_count: usize,
76 pub peak_memory_usage: usize,
78 pub leaks_detected: bool,
80}
81
82#[derive(Debug, Clone)]
84#[cfg_attr(feature = "memory_metrics", derive(Serialize, Deserialize))]
85pub struct ProfilingResult {
86 pub session: ProfilingSession,
88 pub memory_report: MemoryReport,
90 pub leak_results: Vec<crate::memory::metrics::analytics::LeakDetectionResult>,
92 pub pattern_analysis: Vec<crate::memory::metrics::analytics::MemoryPatternAnalysis>,
94 pub performance_impact: PerformanceImpactAnalysis,
96 pub summary: ProfilingSummary,
98}
99
100#[derive(Debug, Clone)]
102#[cfg_attr(feature = "memory_metrics", derive(Serialize, Deserialize))]
103pub struct PerformanceImpactAnalysis {
104 pub total_allocation_time: Duration,
106 pub avg_allocation_time: Duration,
108 pub performance_bottlenecks: usize,
110 pub memorybandwidth_utilization: f64,
112 pub cache_miss_estimate: f64,
114}
115
116#[derive(Debug, Clone)]
118#[cfg_attr(feature = "memory_metrics", derive(Serialize, Deserialize))]
119pub struct ProfilingSummary {
120 pub health_score: f64,
122 pub key_insights: Vec<String>,
124 pub priority_recommendations: Vec<String>,
126 pub risk_assessment: RiskAssessment,
128}
129
130#[derive(Debug, Clone)]
132#[cfg_attr(feature = "memory_metrics", derive(Serialize, Deserialize))]
133pub enum RiskAssessment {
134 Low,
136 Medium { issues: Vec<String> },
138 High { critical_issues: Vec<String> },
140}
141
142pub struct MemoryProfiler {
144 config: MemoryProfilerConfig,
146 collector: Arc<MemoryMetricsCollector>,
148 analytics: Arc<Mutex<MemoryAnalytics>>,
150 results_history: Arc<RwLock<Vec<ProfilingResult>>>,
152 current_session: Arc<Mutex<Option<ProfilingSession>>>,
154 background_thread: Option<thread::JoinHandle<()>>,
156}
157
158impl MemoryProfiler {
159 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 if profiler.config.enabled {
183 profiler.start_background_profiling();
184 }
185
186 profiler
187 }
188
189 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 self.collector.reset();
222 self.analytics.lock().expect("Operation failed").clear();
223
224 sessionid
225 }
226
227 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 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 {
271 let mut history = self.results_history.write().expect("Operation failed");
272 history.push(result.clone());
273
274 while history.len() > self.config.max_reports_in_memory {
276 history.remove(0);
277 }
278 }
279
280 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 pub fn record_event(&self, event: MemoryEvent) {
292 self.collector.record_event(event.clone());
294
295 self.analytics
297 .lock()
298 .expect("Operation failed")
299 .record_event(event);
300 }
301
302 fn start_background_profiling(&mut self) {
304 if self.background_thread.is_some() {
305 return; }
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 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 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; if critical_leaks || high_memory_usage {
336 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 if !config.enabled {
354 break;
355 }
356 }
357 });
358
359 self.background_thread = Some(handle);
360 }
361
362 fn analyze_performance_impact(
364 &self,
365 memory_report: &MemoryReport,
366 pattern_analysis: &[crate::memory::metrics::analytics::MemoryPatternAnalysis],
367 ) -> PerformanceImpactAnalysis {
368 let total_allocations = memory_report.total_allocation_count;
370 let total_duration = memory_report.duration;
371
372 let avg_allocation_time = if total_allocations > 0 {
374 Duration::from_nanos(100) } else {
376 Duration::from_nanos(0)
377 };
378
379 let total_allocation_time = avg_allocation_time * total_allocations as u32;
380
381 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 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 let memorybandwidth_utilization =
404 (bytes_per_second / (100.0 * 1024.0 * 1024.0 * 1024.0)).min(1.0);
405
406 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 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 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 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 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 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 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 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 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 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 pub fn get_results_history(&self) -> Vec<ProfilingResult> {
548 self.results_history
549 .read()
550 .expect("Operation failed")
551 .clone()
552 }
553
554 pub fn get_current_session(&self) -> Option<ProfilingSession> {
556 self.current_session
557 .lock()
558 .expect("Operation failed")
559 .clone()
560 }
561
562 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 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, ..Default::default()
607 });
608
609 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 let event = MemoryEvent::new(MemoryEventType::Allocation, "TestComponent", 1024, 0x1000);
616 profiler.record_event(event);
617
618 std::thread::sleep(Duration::from_millis(10)); 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}