Skip to main content

memscope_rs/snapshot/
types.rs

1//! Snapshot types - Data structures for memory snapshots
2//!
3//! This module defines the core data structures used by the
4//! SnapshotEngine for representing memory snapshots.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9/// Active allocation information in a snapshot
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
11pub struct ActiveAllocation {
12    /// Memory pointer address
13    pub ptr: usize,
14    /// Allocation size in bytes
15    pub size: usize,
16    /// Timestamp when this allocation was made
17    pub allocated_at: u64,
18    /// Optional variable name
19    pub var_name: Option<String>,
20    /// Optional type name
21    pub type_name: Option<String>,
22    /// Thread ID that made this allocation
23    pub thread_id: u64,
24    /// Optional call stack hash for clone detection
25    pub call_stack_hash: Option<u64>,
26}
27
28/// Memory statistics for a snapshot
29#[derive(Debug, Clone, Serialize, Deserialize, Default)]
30pub struct MemoryStats {
31    /// Total number of allocations in the snapshot
32    pub total_allocations: usize,
33    /// Total number of reallocations
34    pub total_reallocations: usize,
35    /// Total number of deallocations
36    pub total_deallocations: usize,
37    /// Number of deallocations without matching allocations
38    pub unmatched_deallocations: usize,
39    /// Number of currently active allocations
40    pub active_allocations: usize,
41    /// Total bytes allocated
42    pub total_allocated: usize,
43    /// Total bytes deallocated
44    pub total_deallocated: usize,
45    /// Currently used memory (sum of active allocations)
46    pub current_memory: usize,
47    /// Peak memory usage observed
48    pub peak_memory: usize,
49}
50
51/// Thread-specific memory statistics
52#[derive(Debug, Clone, Serialize, Deserialize, Default)]
53pub struct ThreadMemoryStats {
54    /// Thread ID
55    pub thread_id: u64,
56    /// Number of allocations by this thread
57    pub allocation_count: usize,
58    /// Total bytes allocated by this thread
59    pub total_allocated: usize,
60    /// Total bytes deallocated by this thread
61    pub total_deallocated: usize,
62    /// Current memory usage by this thread
63    pub current_memory: usize,
64    /// Peak memory usage by this thread
65    pub peak_memory: usize,
66}
67
68/// Memory snapshot - a point-in-time view of memory usage
69#[derive(Debug, Clone, Serialize, Deserialize, Default)]
70pub struct MemorySnapshot {
71    /// Timestamp when this snapshot was taken
72    pub timestamp: u64,
73    /// Overall memory statistics
74    pub stats: MemoryStats,
75    /// Active allocations (ptr -> allocation info)
76    pub active_allocations: HashMap<usize, ActiveAllocation>,
77    /// Per-thread statistics
78    pub thread_stats: HashMap<u64, ThreadMemoryStats>,
79}
80
81impl MemorySnapshot {
82    /// Create a new empty snapshot
83    pub fn new() -> Self {
84        Self {
85            timestamp: std::time::SystemTime::now()
86                .duration_since(std::time::UNIX_EPOCH)
87                .unwrap_or_default()
88                .as_nanos() as u64,
89            stats: MemoryStats::default(),
90            active_allocations: HashMap::new(),
91            thread_stats: HashMap::new(),
92        }
93    }
94
95    /// Build a MemorySnapshot from a list of AllocationInfo (capture module type)
96    pub fn from_allocation_infos(
97        allocations: Vec<crate::capture::backends::core_types::AllocationInfo>,
98    ) -> Self {
99        let mut snapshot = Self::new();
100        let mut thread_stats: HashMap<u64, ThreadMemoryStats> = HashMap::new();
101        let mut current_memory: usize = 0;
102
103        for alloc in allocations {
104            let thread_id = alloc.thread_id;
105
106            let active_alloc = ActiveAllocation {
107                ptr: alloc.ptr,
108                size: alloc.size,
109                allocated_at: alloc.allocated_at_ns,
110                var_name: alloc.var_name,
111                type_name: alloc.type_name,
112                thread_id,
113                call_stack_hash: None,
114            };
115
116            current_memory += alloc.size;
117
118            snapshot.stats.total_allocations += 1;
119            snapshot.stats.total_allocated += alloc.size;
120
121            let thread_stat = thread_stats
122                .entry(thread_id)
123                .or_insert_with(|| ThreadMemoryStats {
124                    thread_id,
125                    allocation_count: 0,
126                    total_allocated: 0,
127                    total_deallocated: 0,
128                    current_memory: 0,
129                    peak_memory: 0,
130                });
131
132            thread_stat.allocation_count += 1;
133            thread_stat.total_allocated += alloc.size;
134            thread_stat.current_memory += alloc.size;
135            if thread_stat.current_memory > thread_stat.peak_memory {
136                thread_stat.peak_memory = thread_stat.current_memory;
137            }
138
139            snapshot.active_allocations.insert(alloc.ptr, active_alloc);
140        }
141
142        snapshot.stats.current_memory = current_memory;
143        snapshot.stats.peak_memory = 0; // Cannot determine peak from current allocations only
144        snapshot.stats.active_allocations = snapshot.active_allocations.len();
145        snapshot.thread_stats = thread_stats;
146
147        snapshot
148    }
149
150    /// Get the number of active allocations
151    pub fn active_count(&self) -> usize {
152        self.active_allocations.len()
153    }
154
155    /// Get the current memory usage
156    pub fn current_memory(&self) -> usize {
157        self.stats.current_memory
158    }
159
160    /// Get the peak memory usage
161    pub fn peak_memory(&self) -> usize {
162        self.stats.peak_memory
163    }
164}