use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ActiveAllocation {
pub ptr: usize,
pub size: usize,
pub allocated_at: u64,
pub var_name: Option<String>,
pub type_name: Option<String>,
pub thread_id: u64,
pub call_stack_hash: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct MemoryStats {
pub total_allocations: usize,
pub total_reallocations: usize,
pub total_deallocations: usize,
pub unmatched_deallocations: usize,
pub active_allocations: usize,
pub total_allocated: usize,
pub total_deallocated: usize,
pub current_memory: usize,
pub peak_memory: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ThreadMemoryStats {
pub thread_id: u64,
pub allocation_count: usize,
pub total_allocated: usize,
pub total_deallocated: usize,
pub current_memory: usize,
pub peak_memory: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct MemorySnapshot {
pub timestamp: u64,
pub stats: MemoryStats,
pub active_allocations: HashMap<usize, ActiveAllocation>,
pub thread_stats: HashMap<u64, ThreadMemoryStats>,
}
impl MemorySnapshot {
pub fn new() -> Self {
Self {
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos() as u64,
stats: MemoryStats::default(),
active_allocations: HashMap::new(),
thread_stats: HashMap::new(),
}
}
pub fn from_allocation_infos(
allocations: Vec<crate::capture::backends::core_types::AllocationInfo>,
) -> Self {
let mut snapshot = Self::new();
let mut thread_stats: HashMap<u64, ThreadMemoryStats> = HashMap::new();
let mut current_memory: usize = 0;
for alloc in allocations {
let thread_id = alloc.thread_id;
let active_alloc = ActiveAllocation {
ptr: alloc.ptr,
size: alloc.size,
allocated_at: alloc.allocated_at_ns,
var_name: alloc.var_name,
type_name: alloc.type_name,
thread_id,
call_stack_hash: None,
};
current_memory += alloc.size;
snapshot.stats.total_allocations += 1;
snapshot.stats.total_allocated += alloc.size;
let thread_stat = thread_stats
.entry(thread_id)
.or_insert_with(|| ThreadMemoryStats {
thread_id,
allocation_count: 0,
total_allocated: 0,
total_deallocated: 0,
current_memory: 0,
peak_memory: 0,
});
thread_stat.allocation_count += 1;
thread_stat.total_allocated += alloc.size;
thread_stat.current_memory += alloc.size;
if thread_stat.current_memory > thread_stat.peak_memory {
thread_stat.peak_memory = thread_stat.current_memory;
}
snapshot.active_allocations.insert(alloc.ptr, active_alloc);
}
snapshot.stats.current_memory = current_memory;
snapshot.stats.peak_memory = 0; snapshot.stats.active_allocations = snapshot.active_allocations.len();
snapshot.thread_stats = thread_stats;
snapshot
}
pub fn active_count(&self) -> usize {
self.active_allocations.len()
}
pub fn current_memory(&self) -> usize {
self.stats.current_memory
}
pub fn peak_memory(&self) -> usize {
self.stats.peak_memory
}
}