1use crate::core::types::{
7 AllocationInfo, ConcurrencyAnalysis, FragmentationAnalysis, ScopeLifecycleMetrics,
8 SystemLibraryStats,
9};
10use serde::{Deserialize, Serialize};
11use std::collections::VecDeque;
12
13#[derive(Debug, Clone)]
15pub struct BoundedStatsConfig {
16 pub max_recent_allocations: usize,
18 pub max_historical_summaries: usize,
20 pub enable_auto_cleanup: bool,
22 pub cleanup_threshold: f32,
24}
25
26impl Default for BoundedStatsConfig {
27 fn default() -> Self {
28 Self {
29 max_recent_allocations: 10_000, max_historical_summaries: 1_000, enable_auto_cleanup: true,
32 cleanup_threshold: 0.9, }
34 }
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct AllocationSummary {
40 pub ptr: usize,
41 pub size: usize,
42 pub timestamp_alloc: u64,
43 pub timestamp_dealloc: Option<u64>,
44 pub type_name: Option<String>,
45 pub var_name: Option<String>,
46 pub is_leaked: bool,
47 pub lifetime_ms: Option<u64>,
48}
49
50impl From<&AllocationInfo> for AllocationSummary {
51 fn from(alloc: &AllocationInfo) -> Self {
52 Self {
53 ptr: alloc.ptr,
54 size: alloc.size,
55 timestamp_alloc: alloc.timestamp_alloc,
56 timestamp_dealloc: alloc.timestamp_dealloc,
57 type_name: alloc.type_name.clone(),
58 var_name: alloc.var_name.clone(),
59 is_leaked: alloc.is_leaked,
60 lifetime_ms: alloc.lifetime_ms,
61 }
62 }
63}
64
65#[derive(Debug, Clone, Serialize)]
67pub struct BoundedMemoryStats {
68 #[serde(skip)]
70 pub config: BoundedStatsConfig,
71
72 pub total_allocations: usize,
74 pub total_allocated: usize,
75 pub active_allocations: usize,
76 pub active_memory: usize,
77 pub peak_allocations: usize,
78 pub peak_memory: usize,
79 pub total_deallocations: usize,
80 pub total_deallocated: usize,
81 pub leaked_allocations: usize,
82 pub leaked_memory: usize,
83
84 pub fragmentation_analysis: FragmentationAnalysis,
86 pub lifecycle_stats: ScopeLifecycleMetrics,
87 pub system_library_stats: SystemLibraryStats,
88 pub concurrency_analysis: ConcurrencyAnalysis,
89
90 pub recent_allocations: VecDeque<AllocationSummary>,
92 pub historical_summaries: VecDeque<HistoricalSummary>,
93
94 pub cleanup_count: u32,
96 pub last_cleanup_timestamp: Option<u64>,
97 pub total_allocations_processed: u64,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct HistoricalSummary {
103 pub timestamp: u64,
104 pub total_allocations: usize,
105 pub total_memory: usize,
106 pub active_allocations: usize,
107 pub active_memory: usize,
108 pub allocation_rate: f64, pub memory_growth_rate: f64, }
111
112impl BoundedMemoryStats {
113 pub fn new() -> Self {
115 Self::with_config(BoundedStatsConfig::default())
116 }
117
118 pub fn with_config(config: BoundedStatsConfig) -> Self {
120 Self {
121 config,
122 total_allocations: 0,
123 total_allocated: 0,
124 active_allocations: 0,
125 active_memory: 0,
126 peak_allocations: 0,
127 peak_memory: 0,
128 total_deallocations: 0,
129 total_deallocated: 0,
130 leaked_allocations: 0,
131 leaked_memory: 0,
132 fragmentation_analysis: FragmentationAnalysis::default(),
133 lifecycle_stats: ScopeLifecycleMetrics {
134 scope_name: "global".to_string(),
135 variable_count: 0,
136 average_lifetime_ms: 0.0,
137 total_memory_usage: 0,
138 peak_memory_usage: 0,
139 allocation_frequency: 0.0,
140 deallocation_efficiency: 0.0,
141 completed_allocations: 0,
142 memory_growth_events: 0,
143 peak_concurrent_variables: 0,
144 memory_efficiency_ratio: 0.0,
145 ownership_transfer_events: 0,
146 fragmentation_score: 0.0,
147 instant_allocations: 0,
148 short_term_allocations: 0,
149 medium_term_allocations: 0,
150 long_term_allocations: 0,
151 suspected_leaks: 0,
152 risk_distribution: crate::core::types::RiskDistribution::default(),
153 scope_metrics: Vec::new(),
154 type_lifecycle_patterns: Vec::new(),
155 },
156 system_library_stats: SystemLibraryStats::default(),
157 concurrency_analysis: ConcurrencyAnalysis::default(),
158 recent_allocations: VecDeque::with_capacity(1000),
159 historical_summaries: VecDeque::with_capacity(100),
160 cleanup_count: 0,
161 last_cleanup_timestamp: None,
162 total_allocations_processed: 0,
163 }
164 }
165
166 pub fn add_allocation(&mut self, alloc: &AllocationInfo) {
168 self.total_allocations += 1;
173 self.total_allocated += alloc.size;
174 self.total_allocations_processed += 1;
175
176 if alloc.var_name.is_some() {
179 self.active_allocations += 1;
180 self.active_memory += alloc.size;
181
182 if self.active_allocations > self.peak_allocations {
184 self.peak_allocations = self.active_allocations;
185 }
186 if self.active_memory > self.peak_memory {
187 self.peak_memory = self.active_memory;
188 }
189
190 let summary = AllocationSummary::from(alloc);
192 self.add_allocation_summary(summary);
193 }
194
195 if self.config.enable_auto_cleanup {
197 self.check_and_cleanup();
198 }
199 }
200
201 pub fn update_active_allocation_status(
204 &mut self,
205 alloc: &AllocationInfo,
206 old_var_name_is_none: bool,
207 ) {
208 if old_var_name_is_none && alloc.var_name.is_some() {
212 self.active_allocations += 1;
213 self.active_memory += alloc.size;
214
215 if self.active_allocations > self.peak_allocations {
217 self.peak_allocations = self.active_allocations;
218 }
219 if self.active_memory > self.peak_memory {
220 self.peak_memory = self.active_memory;
221 }
222
223 let summary = AllocationSummary::from(alloc);
225 self.add_allocation_summary(summary);
226 } else if alloc.var_name.is_some() {
227 if let Some(summary) = self
229 .recent_allocations
230 .iter_mut()
231 .find(|s| s.ptr == alloc.ptr)
232 {
233 summary.var_name = alloc.var_name.clone();
234 summary.type_name = alloc.type_name.clone();
235 summary.size = alloc.size; }
237 }
238
239 if self.config.enable_auto_cleanup {
241 self.check_and_cleanup();
242 }
243 }
244
245 pub fn record_deallocation(&mut self, ptr: usize, size: usize) {
247 self.total_deallocations += 1;
248 self.total_deallocated += size;
249
250 let current_timestamp = self.get_current_timestamp();
253 if let Some(summary) = self.recent_allocations.iter_mut().find(|s| s.ptr == ptr) {
254 self.active_allocations = self.active_allocations.saturating_sub(1);
256 self.active_memory = self.active_memory.saturating_sub(size);
257
258 summary.timestamp_dealloc = Some(current_timestamp);
260 if let Some(alloc_time) = summary.timestamp_dealloc {
261 summary.lifetime_ms = Some((alloc_time - summary.timestamp_alloc) / 1_000_000);
262 }
263 }
264 }
266
267 pub fn record_leak(&mut self, size: usize) {
269 self.leaked_allocations += 1;
270 self.leaked_memory += size;
271 }
272
273 fn add_allocation_summary(&mut self, summary: AllocationSummary) {
275 if self.recent_allocations.len() >= self.config.max_recent_allocations {
277 if let Some(old_summary) = self.recent_allocations.pop_front() {
279 self.maybe_create_historical_summary(&old_summary);
281 }
282 }
283
284 self.recent_allocations.push_back(summary);
285 }
286
287 fn maybe_create_historical_summary(&mut self, _removed_summary: &AllocationSummary) {
289 if self.total_allocations % 1000 == 0 {
291 let summary = HistoricalSummary {
292 timestamp: self.get_current_timestamp(),
293 total_allocations: self.total_allocations,
294 total_memory: self.total_allocated,
295 active_allocations: self.active_allocations,
296 active_memory: self.active_memory,
297 allocation_rate: self.calculate_allocation_rate(),
298 memory_growth_rate: self.calculate_memory_growth_rate(),
299 };
300
301 if self.historical_summaries.len() >= self.config.max_historical_summaries {
303 self.historical_summaries.pop_front();
304 }
305 self.historical_summaries.push_back(summary);
306 }
307 }
308
309 fn check_and_cleanup(&mut self) {
311 let recent_threshold =
312 (self.config.max_recent_allocations as f32 * self.config.cleanup_threshold) as usize;
313 let historical_threshold =
314 (self.config.max_historical_summaries as f32 * self.config.cleanup_threshold) as usize;
315
316 let mut cleaned = false;
317
318 if self.recent_allocations.len() >= recent_threshold {
320 let remove_count = self.recent_allocations.len() / 4; for _ in 0..remove_count {
322 if let Some(old_summary) = self.recent_allocations.pop_front() {
323 self.maybe_create_historical_summary(&old_summary);
324 }
325 }
326 cleaned = true;
327 }
328
329 if self.historical_summaries.len() >= historical_threshold {
331 let remove_count = self.historical_summaries.len() / 4; for _ in 0..remove_count {
333 self.historical_summaries.pop_front();
334 }
335 cleaned = true;
336 }
337
338 if cleaned {
339 self.cleanup_count += 1;
340 self.last_cleanup_timestamp = Some(self.get_current_timestamp());
341 }
342 }
343
344 fn get_current_timestamp(&self) -> u64 {
346 std::time::SystemTime::now()
347 .duration_since(std::time::UNIX_EPOCH)
348 .unwrap_or_default()
349 .as_nanos() as u64
350 }
351
352 fn calculate_allocation_rate(&self) -> f64 {
354 if let Some(oldest) = self.recent_allocations.front() {
355 let time_span = self.get_current_timestamp() - oldest.timestamp_alloc;
356 if time_span > 0 {
357 let seconds = time_span as f64 / 1_000_000_000.0;
358 return self.recent_allocations.len() as f64 / seconds;
359 }
360 }
361 0.0
362 }
363
364 fn calculate_memory_growth_rate(&self) -> f64 {
366 if let Some(oldest) = self.historical_summaries.front() {
367 let time_span = self.get_current_timestamp() - oldest.timestamp;
368 if time_span > 0 {
369 let seconds = time_span as f64 / 1_000_000_000.0;
370 let memory_growth = self.active_memory as i64 - oldest.active_memory as i64;
371 return memory_growth as f64 / seconds;
372 }
373 }
374 0.0
375 }
376
377 pub fn get_memory_usage(&self) -> MemoryUsageStats {
379 let recent_allocations_size =
380 self.recent_allocations.len() * std::mem::size_of::<AllocationSummary>();
381 let historical_summaries_size =
382 self.historical_summaries.len() * std::mem::size_of::<HistoricalSummary>();
383 let base_size = std::mem::size_of::<Self>();
384
385 MemoryUsageStats {
386 total_size: base_size + recent_allocations_size + historical_summaries_size,
387 recent_allocations_size,
388 historical_summaries_size,
389 base_size,
390 recent_allocations_count: self.recent_allocations.len(),
391 historical_summaries_count: self.historical_summaries.len(),
392 }
393 }
394
395 pub fn force_cleanup(&mut self) {
397 let old_cleanup_threshold = self.config.cleanup_threshold;
398 self.config.cleanup_threshold = 0.5; self.check_and_cleanup();
400 self.config.cleanup_threshold = old_cleanup_threshold;
401 }
402
403 pub fn get_all_allocations(&self) -> Vec<AllocationInfo> {
405 self.recent_allocations
406 .iter()
407 .map(|summary| {
408 AllocationInfo {
410 ptr: summary.ptr,
411 size: summary.size,
412 var_name: summary.var_name.clone(),
413 type_name: summary.type_name.clone(),
414 scope_name: Some("tracked".to_string()),
415 timestamp_alloc: summary.timestamp_alloc,
416 timestamp_dealloc: summary.timestamp_dealloc,
417 thread_id: "main".to_string(), borrow_count: 0,
419 stack_trace: None,
420 is_leaked: summary.is_leaked,
421 lifetime_ms: summary.lifetime_ms,
422 borrow_info: None,
423 clone_info: None,
424 ownership_history_available: false,
425 smart_pointer_info: None,
427 memory_layout: None,
428 generic_info: None,
429 dynamic_type_info: None,
430 runtime_state: None,
431 stack_allocation: None,
432 temporary_object: None,
433 fragmentation_analysis: None,
434 generic_instantiation: None,
435 type_relationships: None,
436 type_usage: None,
437 function_call_tracking: None,
438 lifecycle_tracking: None,
439 access_tracking: None,
440 drop_chain_analysis: None,
441 }
442 })
443 .collect()
444 }
445}
446
447impl Default for BoundedMemoryStats {
448 fn default() -> Self {
449 Self::new()
450 }
451}
452
453#[derive(Debug, Clone, Serialize)]
455pub struct MemoryUsageStats {
456 pub total_size: usize,
457 pub recent_allocations_size: usize,
458 pub historical_summaries_size: usize,
459 pub base_size: usize,
460 pub recent_allocations_count: usize,
461 pub historical_summaries_count: usize,
462}
463
464pub struct AllocationHistoryManager {
466 config: BoundedStatsConfig,
468 history: VecDeque<AllocationInfo>,
470 cleanup_count: u32,
472 last_cleanup_timestamp: Option<u64>,
473}
474
475impl AllocationHistoryManager {
476 pub fn new() -> Self {
478 Self::with_config(BoundedStatsConfig::default())
479 }
480
481 pub fn with_config(config: BoundedStatsConfig) -> Self {
483 Self {
484 config,
485 history: VecDeque::with_capacity(1000),
486 cleanup_count: 0,
487 last_cleanup_timestamp: None,
488 }
489 }
490
491 pub fn add_allocation(&mut self, alloc: AllocationInfo) {
493 if self.history.len() >= self.config.max_recent_allocations {
495 let remove_count = self.history.len() / 4; for _ in 0..remove_count {
497 self.history.pop_front();
498 }
499 self.cleanup_count += 1;
500 self.last_cleanup_timestamp = Some(
501 std::time::SystemTime::now()
502 .duration_since(std::time::UNIX_EPOCH)
503 .unwrap_or_default()
504 .as_nanos() as u64,
505 );
506 }
507
508 self.history.push_back(alloc);
509 }
510
511 pub fn get_history(&self) -> &VecDeque<AllocationInfo> {
513 &self.history
514 }
515
516 pub fn get_history_vec(&self) -> Vec<AllocationInfo> {
518 self.history.iter().cloned().collect()
519 }
520
521 pub fn clear(&mut self) {
523 self.history.clear();
524 }
525
526 pub fn get_memory_usage(&self) -> usize {
528 std::mem::size_of::<Self>() + self.history.len() * std::mem::size_of::<AllocationInfo>()
529 }
530}
531
532impl Default for AllocationHistoryManager {
533 fn default() -> Self {
534 Self::new()
535 }
536}
537
538#[cfg(test)]
539mod tests {
540 use super::*;
541 use crate::core::types::AllocationInfo;
542
543 fn create_test_allocation(id: usize) -> AllocationInfo {
544 AllocationInfo {
545 ptr: 0x1000 + id,
546 size: 64 + (id % 100),
547 var_name: Some(format!("var_{id}")),
548 type_name: Some("TestType".to_string()),
549 scope_name: Some("test".to_string()),
550 timestamp_alloc: id as u64 * 1000,
551 timestamp_dealloc: None,
552 thread_id: "main".to_string(),
553 borrow_count: 0,
554 stack_trace: None,
555 is_leaked: false,
556 lifetime_ms: None,
557 borrow_info: None,
558 clone_info: None,
559 ownership_history_available: false,
560 smart_pointer_info: None,
561 memory_layout: None,
562 generic_info: None,
563 dynamic_type_info: None,
564 runtime_state: None,
565 stack_allocation: None,
566 temporary_object: None,
567 fragmentation_analysis: None,
568 generic_instantiation: None,
569 type_relationships: None,
570 type_usage: None,
571 function_call_tracking: None,
572 lifecycle_tracking: None,
573 access_tracking: None,
574 drop_chain_analysis: None,
575 }
576 }
577
578 #[test]
579 fn test_bounded_memory_stats_no_overflow() {
580 let mut stats = BoundedMemoryStats::with_config(BoundedStatsConfig {
581 max_recent_allocations: 100,
582 max_historical_summaries: 10,
583 enable_auto_cleanup: true,
584 cleanup_threshold: 0.9,
585 });
586
587 for i in 0..150 {
589 let alloc = create_test_allocation(i);
590 stats.add_allocation(&alloc);
591 }
592
593 assert!(stats.recent_allocations.len() <= 100);
595 assert_eq!(stats.total_allocations, 150);
596 assert!(stats.cleanup_count > 0);
597 }
598
599 #[test]
600 fn test_allocation_history_manager_bounds() {
601 let mut manager = AllocationHistoryManager::with_config(BoundedStatsConfig {
602 max_recent_allocations: 50,
603 ..Default::default()
604 });
605
606 for i in 0..100 {
608 let alloc = create_test_allocation(i);
609 manager.add_allocation(alloc);
610 }
611
612 assert!(manager.history.len() <= 50);
614 assert!(manager.cleanup_count > 0);
615 }
616
617 #[test]
618 fn test_memory_usage_calculation() {
619 let stats = BoundedMemoryStats::new();
620 let usage = stats.get_memory_usage();
621
622 assert!(usage.total_size > 0);
623 assert_eq!(usage.recent_allocations_count, 0);
624 assert_eq!(usage.historical_summaries_count, 0);
625 }
626
627 #[test]
628 fn test_deallocation_tracking() {
629 let mut stats = BoundedMemoryStats::new();
630 let alloc = create_test_allocation(1);
631
632 stats.add_allocation(&alloc);
633 assert_eq!(stats.active_allocations, 1);
634 assert_eq!(stats.active_memory, alloc.size);
635
636 stats.record_deallocation(alloc.ptr, alloc.size);
637 assert_eq!(stats.active_allocations, 0);
638 assert_eq!(stats.active_memory, 0);
639 assert_eq!(stats.total_deallocations, 1);
640 }
641
642 #[test]
643 fn test_leak_tracking() {
644 let mut stats = BoundedMemoryStats::new();
645
646 stats.record_leak(1024);
647 assert_eq!(stats.leaked_allocations, 1);
648 assert_eq!(stats.leaked_memory, 1024);
649 }
650}