Skip to main content

memscope_rs/analyzer/
report.rs

1//! Analysis report types.
2//!
3//! This module defines all report types returned by analysis operations.
4
5use crate::core::error::MemScopeError;
6use crate::snapshot::ActiveAllocation;
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10/// Full analysis report.
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct AnalysisReport {
13    /// Memory statistics
14    pub stats: MemoryStatsReport,
15    /// Leak detection results
16    pub leaks: LeakReport,
17    /// Cycle detection results
18    pub cycles: CycleReport,
19    /// Metrics summary
20    pub metrics: MetricsReport,
21}
22
23impl AnalysisReport {
24    /// Check if any issues were found.
25    pub fn has_issues(&self) -> bool {
26        self.leaks.has_leaks() || self.cycles.has_cycles()
27    }
28
29    /// Get summary string.
30    pub fn summary(&self) -> String {
31        format!(
32            "Analysis Report:\n  Allocations: {}\n  Total Bytes: {}\n  Leaks: {}\n  Cycles: {}",
33            self.stats.allocation_count,
34            self.stats.total_bytes,
35            self.leaks.leak_count,
36            self.cycles.cycle_count
37        )
38    }
39}
40
41/// Memory statistics report.
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct MemoryStatsReport {
44    /// Total number of allocations
45    pub allocation_count: usize,
46    /// Total bytes allocated
47    pub total_bytes: usize,
48    /// Peak memory usage
49    pub peak_bytes: usize,
50    /// Number of threads
51    pub thread_count: usize,
52}
53
54/// Memory leak report.
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct LeakReport {
57    /// Number of leaked allocations
58    pub leak_count: usize,
59    /// Total bytes leaked
60    pub total_leaked_bytes: usize,
61    /// Details of leaked allocations
62    pub leaked_allocations: Vec<LeakInfo>,
63}
64
65impl LeakReport {
66    /// Check if any leaks were detected.
67    pub fn has_leaks(&self) -> bool {
68        self.leak_count > 0
69    }
70
71    /// Create empty report.
72    pub fn empty() -> Self {
73        Self {
74            leak_count: 0,
75            total_leaked_bytes: 0,
76            leaked_allocations: vec![],
77        }
78    }
79}
80
81/// Information about a leaked allocation.
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct LeakInfo {
84    /// Memory pointer
85    pub ptr: usize,
86    /// Allocation size
87    pub size: usize,
88    /// Variable name if available
89    pub var_name: Option<String>,
90    /// Type name if available
91    pub type_name: Option<String>,
92    /// Thread ID
93    pub thread_id: u64,
94}
95
96impl From<&ActiveAllocation> for LeakInfo {
97    fn from(alloc: &ActiveAllocation) -> Self {
98        Self {
99            ptr: alloc.ptr.unwrap_or(0),
100            size: alloc.size,
101            var_name: alloc.var_name.clone(),
102            type_name: alloc.type_name.clone(),
103            thread_id: alloc.thread_id,
104        }
105    }
106}
107
108/// Cycle detection report.
109#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct CycleReport {
111    /// Number of cycles detected
112    pub cycle_count: usize,
113    /// Details of detected cycles
114    pub cycles: Vec<CycleInfo>,
115}
116
117impl CycleReport {
118    /// Check if any cycles were detected.
119    pub fn has_cycles(&self) -> bool {
120        self.cycle_count > 0
121    }
122
123    /// Create empty report.
124    pub fn empty() -> Self {
125        Self {
126            cycle_count: 0,
127            cycles: vec![],
128        }
129    }
130
131    /// Create from graph (placeholder for now).
132    pub fn from_graph<T>(_graph: &T) -> Self {
133        Self::empty()
134    }
135}
136
137/// Information about a reference cycle.
138#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct CycleInfo {
140    /// Node IDs in the cycle
141    pub nodes: Vec<u64>,
142}
143
144/// Metrics report.
145#[derive(Debug, Clone, Serialize, Deserialize, Default)]
146pub struct MetricsReport {
147    /// Total allocation count
148    pub allocation_count: usize,
149    /// Total bytes
150    pub total_bytes: usize,
151    /// Peak bytes
152    pub peak_bytes: usize,
153    /// Thread count
154    pub thread_count: usize,
155    /// Allocations by type
156    pub by_type: HashMap<String, TypeMetric>,
157}
158
159/// Metrics for a specific type.
160#[derive(Debug, Clone, Serialize, Deserialize, Default)]
161pub struct TypeMetric {
162    /// Number of allocations
163    pub count: usize,
164    /// Total bytes
165    pub total_bytes: usize,
166}
167
168/// UAF (Use-After-Free) detection report.
169#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct UafReport {
171    /// Number of UAF issues detected
172    pub uaf_count: usize,
173    /// Details of UAF issues
174    pub issues: Vec<UafInfo>,
175}
176
177impl UafReport {
178    /// Create empty report.
179    pub fn empty() -> Self {
180        Self {
181            uaf_count: 0,
182            issues: vec![],
183        }
184    }
185
186    /// Create from events (placeholder for now).
187    pub fn from_events(_events: &[crate::event_store::MemoryEvent]) -> Self {
188        Self::empty()
189    }
190}
191
192/// Information about a UAF issue.
193#[derive(Debug, Clone, Serialize, Deserialize)]
194pub struct UafInfo {
195    /// Memory pointer
196    pub ptr: usize,
197    /// Deallocation timestamp
198    pub deallocated_at: u64,
199    /// Access timestamp
200    pub accessed_at: u64,
201}
202
203/// Safety analysis report.
204#[derive(Debug, Clone, Serialize, Deserialize)]
205pub struct SafetyReport {
206    /// Overall safety score (0-100)
207    pub score: f64,
208    /// Number of issues
209    pub issue_count: usize,
210    /// Issues found
211    pub issues: Vec<SafetyIssue>,
212}
213
214impl SafetyReport {
215    /// Create from allocations (placeholder for now).
216    pub fn from_allocations(_allocations: &[&ActiveAllocation]) -> Self {
217        Self {
218            score: 100.0,
219            issue_count: 0,
220            issues: vec![],
221        }
222    }
223}
224
225/// A safety issue.
226#[derive(Debug, Clone, Serialize, Deserialize)]
227pub struct SafetyIssue {
228    /// Issue severity
229    pub severity: IssueSeverity,
230    /// Issue description
231    pub description: String,
232    /// Related pointer
233    pub ptr: Option<usize>,
234}
235
236/// Issue severity level.
237#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
238pub enum IssueSeverity {
239    /// Low severity
240    Low,
241    /// Medium severity
242    Medium,
243    /// High severity
244    High,
245    /// Critical severity
246    Critical,
247}
248
249/// Export error type alias using project's MemScopeError.
250#[allow(dead_code)]
251pub type ExportError = MemScopeError;
252
253#[cfg(test)]
254mod tests {
255    use super::*;
256
257    #[test]
258    fn test_empty_leak_report() {
259        let report = LeakReport::empty();
260        assert!(!report.has_leaks());
261        assert_eq!(report.leak_count, 0);
262    }
263
264    #[test]
265    fn test_empty_cycle_report() {
266        let report = CycleReport::empty();
267        assert!(!report.has_cycles());
268        assert_eq!(report.cycle_count, 0);
269    }
270
271    #[test]
272    fn test_analysis_report_summary() {
273        let report = AnalysisReport {
274            stats: MemoryStatsReport {
275                allocation_count: 10,
276                total_bytes: 1000,
277                peak_bytes: 500,
278                thread_count: 2,
279            },
280            leaks: LeakReport::empty(),
281            cycles: CycleReport::empty(),
282            metrics: MetricsReport::default(),
283        };
284        let summary = report.summary();
285        assert!(summary.contains("Allocations: 10"));
286        assert!(summary.contains("Total Bytes: 1000"));
287    }
288}