memscope_rs/
types.rs

1//! Core types and error handling for the memscope-rs library.
2
3use serde::{Deserialize, Serialize};
4
5/// Error type for memory tracking operations
6#[derive(Debug, thiserror::Error)]
7pub enum TrackingError {
8    /// Failed to acquire a lock
9    #[error("Failed to acquire lock: {0}")]
10    LockError(String),
11
12    /// Invalid pointer for association
13    #[error("Invalid pointer association: {ptr:?}")]
14    InvalidPointer {
15        /// The invalid pointer address
16        ptr: usize,
17    },
18
19    /// Allocation tracking is disabled
20    #[error("Allocation tracking disabled")]
21    TrackingDisabled,
22
23    /// Memory corruption detected
24    #[error("Memory corruption detected")]
25    MemoryCorruption,
26
27    /// Serialization error
28    #[error("Serialization error: {0}")]
29    SerializationError(String),
30
31    /// IO error during export
32    #[error("IO error: {0}")]
33    IoError(#[from] std::io::Error),
34}
35
36/// Result type for tracking operations
37pub type TrackingResult<T> = Result<T, TrackingError>;
38
39/// Enhanced information about a memory allocation with lifecycle tracking
40#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct AllocationInfo {
42    /// Memory address of the allocation
43    pub ptr: usize,
44    /// Size of the allocation in bytes
45    pub size: usize,
46    /// Timestamp when the allocation occurred (milliseconds since UNIX_EPOCH)
47    pub timestamp_alloc: u128,
48    /// Timestamp when the deallocation occurred (if applicable)
49    pub timestamp_dealloc: Option<u128>,
50    /// Optional name of the variable associated with this allocation
51    pub var_name: Option<String>,
52    /// Optional type name of the variable associated with this allocation
53    pub type_name: Option<String>,
54    /// Thread ID where the allocation occurred
55    pub thread_id: String,
56    /// Backtrace information (if available)
57    #[cfg(feature = "backtrace")]
58    pub backtrace: Option<Vec<String>>,
59
60    // Enhanced lifecycle tracking fields
61    /// Peak memory size reached during lifetime (for growable types)
62    pub peak_size: Option<usize>,
63    /// Number of memory growth events (reallocations)
64    pub growth_events: usize,
65    /// Scope identifier where this allocation occurred
66    pub scope_name: Option<String>,
67    /// Ownership pattern for this allocation
68    pub ownership_pattern: Option<OwnershipPattern>,
69    /// Risk classification for this allocation
70    pub risk_level: Option<RiskLevel>,
71    /// Memory efficiency score (useful_bytes / allocated_bytes)
72    pub efficiency_score: Option<f64>,
73    /// Borrowing events count (how many times this was borrowed)
74    pub borrow_count: usize,
75    /// Mutable borrowing events count
76    pub mut_borrow_count: usize,
77    /// Ownership transfer events
78    pub transfer_count: usize,
79    /// Custom metadata tags
80    pub metadata_tags: Vec<String>,
81}
82
83impl AllocationInfo {
84    /// Create a new allocation info with enhanced lifecycle tracking
85    pub fn new(ptr: usize, size: usize) -> Self {
86        let timestamp = std::time::SystemTime::now()
87            .duration_since(std::time::UNIX_EPOCH)
88            .unwrap_or_default()
89            .as_millis();
90
91        let thread_id = format!("{:?}", std::thread::current().id());
92
93        Self {
94            ptr,
95            size,
96            timestamp_alloc: timestamp,
97            timestamp_dealloc: None,
98            var_name: None,
99            type_name: None,
100            thread_id,
101            #[cfg(feature = "backtrace")]
102            backtrace: None,
103
104            // Initialize enhanced lifecycle fields
105            peak_size: Some(size), // Initially same as size
106            growth_events: 0,
107            scope_name: None,
108            ownership_pattern: None,
109            risk_level: None,
110            efficiency_score: Some(1.0), // Initially 100% efficient
111            borrow_count: 0,
112            mut_borrow_count: 0,
113            transfer_count: 0,
114            metadata_tags: Vec::new(),
115        }
116    }
117
118    /// Mark this allocation as deallocated
119    pub fn mark_deallocated(&mut self) {
120        let timestamp = std::time::SystemTime::now()
121            .duration_since(std::time::UNIX_EPOCH)
122            .unwrap_or_default()
123            .as_millis();
124
125        self.timestamp_dealloc = Some(timestamp);
126    }
127
128    /// Check if this allocation is still active
129    pub fn is_active(&self) -> bool {
130        self.timestamp_dealloc.is_none()
131    }
132
133    /// Get the lifetime of this allocation in milliseconds
134    pub fn lifetime_ms(&self) -> Option<u128> {
135        self.timestamp_dealloc
136            .map(|dealloc| dealloc - self.timestamp_alloc)
137    }
138
139    /// Record a memory growth event (reallocation)
140    pub fn record_growth(&mut self, new_size: usize) {
141        self.growth_events += 1;
142        if let Some(peak) = self.peak_size {
143            self.peak_size = Some(peak.max(new_size));
144        } else {
145            self.peak_size = Some(new_size);
146        }
147
148        // Update efficiency score
149        if let Some(peak) = self.peak_size {
150            self.efficiency_score = Some(self.size as f64 / peak as f64);
151        }
152    }
153
154    /// Record a borrowing event
155    pub fn record_borrow(&mut self, is_mutable: bool) {
156        if is_mutable {
157            self.mut_borrow_count += 1;
158        } else {
159            self.borrow_count += 1;
160        }
161    }
162
163    /// Record an ownership transfer
164    pub fn record_transfer(&mut self) {
165        self.transfer_count += 1;
166    }
167
168    /// Add a metadata tag
169    pub fn add_metadata_tag(&mut self, tag: String) {
170        if !self.metadata_tags.contains(&tag) {
171            self.metadata_tags.push(tag);
172        }
173    }
174
175    /// Calculate memory growth factor
176    pub fn memory_growth_factor(&self) -> f64 {
177        if let Some(peak) = self.peak_size {
178            peak as f64 / self.size.max(1) as f64
179        } else {
180            1.0
181        }
182    }
183
184    /// Classify the risk level of this allocation
185    pub fn classify_risk(&mut self) {
186        let growth_factor = self.memory_growth_factor();
187        let lifetime = self.lifetime_ms().unwrap_or(0) as f64;
188
189        self.risk_level = Some(if self.size > 1024 * 1024 || growth_factor > 3.0 {
190            RiskLevel::Critical
191        } else if self.size > 1024 || growth_factor > 2.0 || lifetime > 10000.0 {
192            RiskLevel::High
193        } else if self.size > 256 || growth_factor > 1.5 || lifetime > 1000.0 {
194            RiskLevel::Medium
195        } else {
196            RiskLevel::Low
197        });
198    }
199
200    /// Determine ownership pattern based on type
201    pub fn determine_ownership_pattern(&mut self) {
202        if let Some(type_name) = &self.type_name {
203            self.ownership_pattern =
204                Some(if type_name.contains("Rc") || type_name.contains("Arc") {
205                    OwnershipPattern::Shared
206                } else if type_name.starts_with('&') {
207                    OwnershipPattern::Borrowed
208                } else if self.transfer_count > 0 && self.borrow_count > 0 {
209                    OwnershipPattern::Mixed
210                } else {
211                    OwnershipPattern::Owned
212                });
213        }
214    }
215}
216
217/// Memory usage statistics
218#[derive(Debug, Clone, Serialize, Deserialize, Default)]
219pub struct MemoryStats {
220    /// Total number of allocations tracked
221    pub total_allocations: usize,
222    /// Total number of deallocations tracked
223    pub total_deallocations: usize,
224    /// Total bytes allocated
225    pub total_allocated: usize,
226    /// Total bytes deallocated
227    pub total_deallocated: usize,
228    /// Current number of active allocations
229    pub active_allocations: usize,
230    /// Current bytes in active allocations
231    pub active_memory: usize,
232    /// Peak number of active allocations
233    pub peak_allocations: usize,
234    /// Peak memory usage in bytes
235    pub peak_memory: usize,
236    /// Lifecycle statistics
237    pub lifecycle_stats: LifecycleStats,
238}
239
240/// Memory usage by type
241#[derive(Debug, Clone, Serialize, Deserialize)]
242pub struct TypeMemoryUsage {
243    /// The name of the data type
244    pub type_name: String,
245    /// Total size in bytes for this type
246    pub total_size: usize,
247    /// Number of allocations for this type
248    pub allocation_count: usize,
249}
250
251/// Allocation hotspot information
252#[derive(Debug, Clone, Serialize, Deserialize)]
253pub struct HotspotInfo {
254    /// Location identifier (could be function name, file:line, etc.)
255    pub location: String,
256    /// Number of allocations from this location
257    pub count: usize,
258    /// Total size of allocations from this location
259    pub total_size: usize,
260    /// Average allocation size
261    pub average_size: f64,
262}
263
264/// Enhanced lifecycle statistics for memory allocations per lifecycle.md specification
265#[derive(Debug, Clone, Serialize, Deserialize, Default)]
266pub struct LifecycleStats {
267    /// Number of completed allocations (with deallocation timestamps)
268    pub completed_allocations: usize,
269    /// Average lifetime in milliseconds
270    pub average_lifetime_ms: f64,
271    /// Median lifetime in milliseconds
272    pub median_lifetime_ms: f64,
273    /// Lifecycle percentiles
274    pub lifetime_percentiles: LifecyclePercentiles,
275    /// Shortest lifetime in milliseconds
276    pub min_lifetime_ms: u128,
277    /// Longest lifetime in milliseconds
278    pub max_lifetime_ms: u128,
279    /// Number of instant allocations (< 1ms)
280    pub instant_allocations: usize,
281    /// Number of short-term allocations (1ms - 100ms)
282    pub short_term_allocations: usize,
283    /// Number of medium-term allocations (100ms - 1s)
284    pub medium_term_allocations: usize,
285    /// Number of long-term allocations (> 1s)
286    pub long_term_allocations: usize,
287    /// Number of suspected memory leaks (active > 10s)
288    pub suspected_leaks: usize,
289
290    // Enhanced metrics per lifecycle.md
291    /// Memory growth events (reallocations, expansions)
292    pub memory_growth_events: usize,
293    /// Peak concurrent variables at any point in time
294    pub peak_concurrent_variables: usize,
295    /// Memory efficiency ratio (useful_memory / total_allocated)
296    pub memory_efficiency_ratio: f64,
297    /// Ownership transfer events detected
298    pub ownership_transfer_events: usize,
299    /// Borrowing relationship violations
300    pub borrowing_violations: usize,
301    /// Memory fragmentation score (0.0 = perfect, 1.0 = highly fragmented)
302    pub fragmentation_score: f64,
303    /// Risk classification distribution
304    pub risk_distribution: RiskDistribution,
305    /// Scope-based lifecycle metrics
306    pub scope_metrics: Vec<ScopeLifecycleMetrics>,
307    /// Type-specific lifecycle patterns
308    pub type_lifecycle_patterns: Vec<TypeLifecyclePattern>,
309}
310
311/// Lifecycle percentile statistics
312#[derive(Debug, Clone, Serialize, Deserialize, Default)]
313pub struct LifecyclePercentiles {
314    /// 50th percentile (median)
315    pub p50: f64,
316    /// 90th percentile
317    pub p90: f64,
318    /// 95th percentile
319    pub p95: f64,
320    /// 99th percentile
321    pub p99: f64,
322}
323
324/// Lifecycle statistics by type
325#[derive(Debug, Clone, Serialize, Deserialize)]
326pub struct TypeLifecycleStats {
327    /// Type name
328    pub type_name: String,
329    /// Average lifetime for this type
330    pub average_lifetime_ms: f64,
331    /// Number of allocations for this type
332    pub allocation_count: usize,
333    /// Lifecycle category
334    pub category: LifecycleCategory,
335}
336
337/// Categories for lifecycle duration
338#[derive(Debug, Clone, Serialize, Deserialize)]
339pub enum LifecycleCategory {
340    /// Very short-lived (< 1ms)
341    Instant,
342    /// Short-lived (1ms - 100ms)
343    ShortTerm,
344    /// Medium-lived (100ms - 1s)
345    MediumTerm,
346    /// Long-lived (> 1s)
347    LongTerm,
348}
349
350/// Risk classification distribution for memory allocations
351#[derive(Debug, Clone, Serialize, Deserialize, Default)]
352pub struct RiskDistribution {
353    /// High memory risk allocations (large size or high growth)
354    pub high_memory_risk: usize,
355    /// Potential growth risk allocations
356    pub potential_growth_risk: usize,
357    /// Short lifecycle risk allocations
358    pub short_lifecycle_risk: usize,
359    /// Low risk allocations
360    pub low_risk: usize,
361    /// Memory leak risk allocations (long-lived without deallocation)
362    pub leak_risk: usize,
363}
364
365/// Scope-based lifecycle metrics
366#[derive(Debug, Clone, Serialize, Deserialize)]
367pub struct ScopeLifecycleMetrics {
368    /// Scope identifier (function name, block, etc.)
369    pub scope_name: String,
370    /// Number of variables in this scope
371    pub variable_count: usize,
372    /// Average lifetime of variables in this scope
373    pub avg_lifetime_ms: f64,
374    /// Total memory usage in this scope
375    pub total_memory_bytes: usize,
376    /// Peak concurrent variables in this scope
377    pub peak_concurrent_vars: usize,
378    /// Scope efficiency score (0.0 = poor, 1.0 = excellent)
379    pub efficiency_score: f64,
380}
381
382/// Type-specific lifecycle patterns
383#[derive(Debug, Clone, Serialize, Deserialize)]
384pub struct TypeLifecyclePattern {
385    /// Type name (String, Vec, Box, etc.)
386    pub type_name: String,
387    /// Average allocation count per variable of this type
388    pub avg_allocations_per_var: f64,
389    /// Memory growth factor (peak_size / initial_size)
390    pub memory_growth_factor: f64,
391    /// Typical lifetime range for this type
392    pub typical_lifetime_range: (u64, u64), // (min_ms, max_ms)
393    /// Ownership pattern (owned, borrowed, shared)
394    pub ownership_pattern: OwnershipPattern,
395    /// Risk level for this type
396    pub risk_level: RiskLevel,
397}
398
399/// Ownership patterns for variables
400#[derive(Debug, Clone, Serialize, Deserialize)]
401pub enum OwnershipPattern {
402    /// Exclusively owned (Box, Vec, String)
403    Owned,
404    /// Shared ownership (Rc, Arc)
405    Shared,
406    /// Borrowed references (&T, &mut T)
407    Borrowed,
408    /// Mixed ownership patterns
409    Mixed,
410}
411
412/// Risk levels for memory allocations
413#[derive(Debug, Clone, Serialize, Deserialize)]
414pub enum RiskLevel {
415    /// Low risk - small, predictable allocations
416    Low,
417    /// Medium risk - moderate size or some growth
418    Medium,
419    /// High risk - large allocations or significant growth
420    High,
421    /// Critical risk - potential memory leaks or excessive growth
422    Critical,
423}