use crate::core::error::MemScopeError;
use crate::snapshot::ActiveAllocation;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalysisReport {
pub stats: MemoryStatsReport,
pub leaks: LeakReport,
pub cycles: CycleReport,
pub metrics: MetricsReport,
}
impl AnalysisReport {
pub fn has_issues(&self) -> bool {
self.leaks.has_leaks() || self.cycles.has_cycles()
}
pub fn summary(&self) -> String {
format!(
"Analysis Report:\n Allocations: {}\n Total Bytes: {}\n Leaks: {}\n Cycles: {}",
self.stats.allocation_count,
self.stats.total_bytes,
self.leaks.leak_count,
self.cycles.cycle_count
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryStatsReport {
pub allocation_count: usize,
pub total_bytes: usize,
pub peak_bytes: usize,
pub thread_count: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LeakReport {
pub leak_count: usize,
pub total_leaked_bytes: usize,
pub leaked_allocations: Vec<LeakInfo>,
}
impl LeakReport {
pub fn has_leaks(&self) -> bool {
self.leak_count > 0
}
pub fn empty() -> Self {
Self {
leak_count: 0,
total_leaked_bytes: 0,
leaked_allocations: vec![],
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LeakInfo {
pub ptr: usize,
pub size: usize,
pub var_name: Option<String>,
pub type_name: Option<String>,
pub thread_id: u64,
}
impl From<&ActiveAllocation> for LeakInfo {
fn from(alloc: &ActiveAllocation) -> Self {
Self {
ptr: alloc.ptr.unwrap_or(0),
size: alloc.size,
var_name: alloc.var_name.clone(),
type_name: alloc.type_name.clone(),
thread_id: alloc.thread_id,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CycleReport {
pub cycle_count: usize,
pub cycles: Vec<CycleInfo>,
}
impl CycleReport {
pub fn has_cycles(&self) -> bool {
self.cycle_count > 0
}
pub fn empty() -> Self {
Self {
cycle_count: 0,
cycles: vec![],
}
}
pub fn from_graph<T>(_graph: &T) -> Self {
Self::empty()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CycleInfo {
pub nodes: Vec<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct MetricsReport {
pub allocation_count: usize,
pub total_bytes: usize,
pub peak_bytes: usize,
pub thread_count: usize,
pub by_type: HashMap<String, TypeMetric>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct TypeMetric {
pub count: usize,
pub total_bytes: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UafReport {
pub uaf_count: usize,
pub issues: Vec<UafInfo>,
}
impl UafReport {
pub fn empty() -> Self {
Self {
uaf_count: 0,
issues: vec![],
}
}
pub fn from_events(_events: &[crate::event_store::MemoryEvent]) -> Self {
Self::empty()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UafInfo {
pub ptr: usize,
pub deallocated_at: u64,
pub accessed_at: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SafetyReport {
pub score: f64,
pub issue_count: usize,
pub issues: Vec<SafetyIssue>,
}
impl SafetyReport {
pub fn from_allocations(_allocations: &[&ActiveAllocation]) -> Self {
Self {
score: 100.0,
issue_count: 0,
issues: vec![],
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SafetyIssue {
pub severity: IssueSeverity,
pub description: String,
pub ptr: Option<usize>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum IssueSeverity {
Low,
Medium,
High,
Critical,
}
#[allow(dead_code)]
pub type ExportError = MemScopeError;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_leak_report() {
let report = LeakReport::empty();
assert!(!report.has_leaks());
assert_eq!(report.leak_count, 0);
}
#[test]
fn test_empty_cycle_report() {
let report = CycleReport::empty();
assert!(!report.has_cycles());
assert_eq!(report.cycle_count, 0);
}
#[test]
fn test_analysis_report_summary() {
let report = AnalysisReport {
stats: MemoryStatsReport {
allocation_count: 10,
total_bytes: 1000,
peak_bytes: 500,
thread_count: 2,
},
leaks: LeakReport::empty(),
cycles: CycleReport::empty(),
metrics: MetricsReport::default(),
};
let summary = report.summary();
assert!(summary.contains("Allocations: 10"));
assert!(summary.contains("Total Bytes: 1000"));
}
}