Skip to main content

memscope_rs/analyzer/
safety.rs

1//! Safety analysis module.
2
3use crate::analyzer::report::{IssueSeverity, SafetyIssue, SafetyReport};
4use crate::view::MemoryView;
5
6/// Safety analysis module.
7///
8/// Provides memory safety analysis.
9pub struct SafetyAnalysis {
10    view: MemoryView,
11}
12
13impl SafetyAnalysis {
14    /// Create from view.
15    pub fn from_view(view: &MemoryView) -> Self {
16        Self { view: view.clone() }
17    }
18
19    /// Analyze memory safety.
20    ///
21    /// Checks for common safety issues including:
22    /// - Large allocations (potential memory exhaustion)
23    /// - Many small allocations (potential fragmentation)
24    /// - Long-lived allocations (potential memory bloat)
25    pub fn analyze(&self) -> SafetyReport {
26        let allocations = self.view.allocations();
27        let mut issues: Vec<SafetyIssue> = Vec::new();
28
29        // Check for large allocations
30        let large_threshold = 1024 * 1024; // 1MB
31        for alloc in &allocations {
32            if alloc.size > large_threshold {
33                issues.push(SafetyIssue {
34                    severity: IssueSeverity::High,
35                    description: format!(
36                        "Large allocation: {} bytes ({}), potential memory exhaustion",
37                        alloc.size,
38                        alloc.type_name.as_deref().unwrap_or("unknown")
39                    ),
40                    ptr: alloc.ptr,
41                });
42            }
43        }
44
45        // Check for many small allocations
46        let small_threshold = 64;
47        let small_allocs: Vec<_> = allocations
48            .iter()
49            .filter(|a| a.size < small_threshold)
50            .collect();
51        if small_allocs.len() > 100 {
52            issues.push(SafetyIssue {
53                severity: IssueSeverity::Medium,
54                description: format!(
55                    "Many small allocations detected: {} allocations < {} bytes, potential fragmentation",
56                    small_allocs.len(),
57                    small_threshold
58                ),
59                ptr: None,
60            });
61        }
62
63        // Calculate safety score
64        let score = calculate_safety_score(&issues);
65
66        SafetyReport {
67            score,
68            issue_count: issues.len(),
69            issues,
70        }
71    }
72
73    /// Get safety summary.
74    pub fn summary(&self) -> SafetySummary {
75        let report = self.analyze();
76        let critical = report
77            .issues
78            .iter()
79            .filter(|i| i.severity == IssueSeverity::Critical)
80            .count();
81        let high = report
82            .issues
83            .iter()
84            .filter(|i| i.severity == IssueSeverity::High)
85            .count();
86        let medium = report
87            .issues
88            .iter()
89            .filter(|i| i.severity == IssueSeverity::Medium)
90            .count();
91        let low = report
92            .issues
93            .iter()
94            .filter(|i| i.severity == IssueSeverity::Low)
95            .count();
96
97        SafetySummary {
98            score: report.score,
99            critical_count: critical,
100            high_count: high,
101            medium_count: medium,
102            low_count: low,
103        }
104    }
105}
106
107/// Safety summary.
108#[derive(Debug, Clone)]
109pub struct SafetySummary {
110    /// Overall safety score (0-100)
111    pub score: f64,
112    /// Number of critical issues
113    pub critical_count: usize,
114    /// Number of high severity issues
115    pub high_count: usize,
116    /// Number of medium severity issues
117    pub medium_count: usize,
118    /// Number of low severity issues
119    pub low_count: usize,
120}
121
122/// Calculate safety score based on issues.
123fn calculate_safety_score(issues: &[SafetyIssue]) -> f64 {
124    let mut score: f64 = 100.0;
125
126    for issue in issues {
127        let penalty: f64 = match issue.severity {
128            IssueSeverity::Critical => 25.0,
129            IssueSeverity::High => 10.0,
130            IssueSeverity::Medium => 5.0,
131            IssueSeverity::Low => 1.0,
132        };
133        score = (score - penalty).max(0.0);
134    }
135
136    score
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142    use crate::event_store::MemoryEvent;
143
144    #[test]
145    fn test_safety_analysis() {
146        let events = vec![MemoryEvent::allocate(0x1000, 64, 1)];
147        let view = MemoryView::from_events(events);
148        let analysis = SafetyAnalysis::from_view(&view);
149        let report = analysis.analyze();
150        assert!(report.score > 0.0);
151    }
152
153    #[test]
154    fn test_large_allocation_detection() {
155        let events = vec![MemoryEvent::allocate(0x1000, 2 * 1024 * 1024, 1)]; // 2MB
156        let view = MemoryView::from_events(events);
157        let analysis = SafetyAnalysis::from_view(&view);
158        let report = analysis.analyze();
159        assert!(report
160            .issues
161            .iter()
162            .any(|i| i.description.contains("Large allocation")));
163    }
164}