1use std::time::{Duration, Instant};
2
3pub struct PlatformMemoryInfo {
5 last_stats: Option<MemoryStats>,
7 collection_interval: Duration,
9 last_collection: Option<Instant>,
11 platform_context: MemoryContext,
13}
14
15#[derive(Debug, Clone)]
17pub struct MemoryStats {
18 pub virtual_memory: VirtualMemoryStats,
20 pub physical_memory: PhysicalMemoryStats,
22 pub process_memory: ProcessMemoryStats,
24 pub system_memory: SystemMemoryStats,
26 pub pressure_indicators: PressureIndicators,
28 pub timestamp: Instant,
30}
31
32#[derive(Debug, Clone)]
34pub struct VirtualMemoryStats {
35 pub total_virtual: u64,
37 pub available_virtual: u64,
39 pub used_virtual: u64,
41 pub reserved: u64,
43 pub committed: u64,
45}
46
47#[derive(Debug, Clone)]
49pub struct PhysicalMemoryStats {
50 pub total_physical: u64,
52 pub available_physical: u64,
54 pub used_physical: u64,
56 pub cached: u64,
58 pub buffers: u64,
60 pub swap: SwapStats,
62}
63
64#[derive(Debug, Clone)]
66pub struct SwapStats {
67 pub total_swap: u64,
69 pub used_swap: u64,
71 pub available_swap: u64,
73 pub swap_in_rate: f64,
75 pub swap_out_rate: f64,
77}
78
79#[derive(Debug, Clone)]
81pub struct ProcessMemoryStats {
82 pub virtual_size: u64,
84 pub resident_size: u64,
86 pub shared_size: u64,
88 pub private_size: u64,
90 pub heap_size: u64,
92 pub stack_size: u64,
94 pub mapped_files: u64,
96 pub peak_usage: u64,
98}
99
100#[derive(Debug, Clone)]
102pub struct SystemMemoryStats {
103 pub allocation_count: u64,
105 pub deallocation_count: u64,
107 pub active_allocations: u64,
109 pub total_allocated: u64,
111 pub total_deallocated: u64,
113 pub fragmentation_level: f64,
115 pub large_pages: LargePageStats,
117}
118
119#[derive(Debug, Clone)]
121pub struct LargePageStats {
122 pub supported: bool,
124 pub total_large_pages: u64,
126 pub used_large_pages: u64,
128 pub page_size: u64,
130}
131
132#[derive(Debug, Clone)]
134pub struct PressureIndicators {
135 pub pressure_level: PressureLevel,
137 pub low_memory: bool,
139 pub swapping_active: bool,
141 pub allocation_failure_rate: f64,
143 pub gc_pressure: Option<f64>,
145}
146
147#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
149pub enum PressureLevel {
150 Normal,
152 Moderate,
154 High,
156 Critical,
158}
159
160#[derive(Debug, Clone)]
162pub struct SystemInfo {
163 pub os_name: String,
165 pub os_version: String,
167 pub architecture: String,
169 pub cpu_cores: u32,
171 pub cpu_cache: CpuCacheInfo,
173 pub page_size: u64,
175 pub large_page_size: Option<u64>,
177 pub mmu_info: MmuInfo,
179}
180
181#[derive(Debug, Clone)]
183pub struct CpuCacheInfo {
184 pub l1_cache_size: u64,
186 pub l2_cache_size: u64,
188 pub l3_cache_size: Option<u64>,
190 pub cache_line_size: u64,
192}
193
194#[derive(Debug, Clone)]
196pub struct MmuInfo {
197 pub virtual_address_bits: u32,
199 pub physical_address_bits: u32,
201 pub aslr_enabled: bool,
203 pub nx_bit_supported: bool,
205}
206
207#[derive(Debug)]
209struct MemoryContext {
210 initialized: bool,
212
213 #[cfg(target_os = "linux")]
214 linux_context: LinuxMemoryContext,
215
216 #[cfg(target_os = "windows")]
217 windows_context: WindowsMemoryContext,
218
219 #[cfg(target_os = "macos")]
220 macos_context: MacOSMemoryContext,
221}
222
223#[cfg(target_os = "linux")]
224#[derive(Debug)]
225struct LinuxMemoryContext {
226 proc_meminfo_available: bool,
228 proc_status_available: bool,
230 proc_maps_available: bool,
232}
233
234#[cfg(target_os = "windows")]
235#[derive(Debug)]
236struct WindowsMemoryContext {
237 global_memory_api_available: bool,
239 process_memory_api_available: bool,
241 virtual_query_available: bool,
243}
244
245#[cfg(target_os = "macos")]
246#[derive(Debug)]
247struct MacOSMemoryContext {
248 vm_stat_available: bool,
250 task_info_available: bool,
252 mach_api_available: bool,
254}
255
256impl PlatformMemoryInfo {
257 pub fn new() -> Self {
259 Self {
260 last_stats: None,
261 collection_interval: Duration::from_secs(1),
262 last_collection: None,
263 platform_context: MemoryContext::new(),
264 }
265 }
266
267 pub fn initialize(&mut self) -> Result<(), MemoryError> {
269 #[cfg(target_os = "linux")]
270 {
271 self.initialize_linux()
272 }
273
274 #[cfg(target_os = "windows")]
275 {
276 self.initialize_windows()
277 }
278
279 #[cfg(target_os = "macos")]
280 {
281 self.initialize_macos()
282 }
283
284 #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
285 {
286 Err(MemoryError::UnsupportedPlatform)
287 }
288 }
289
290 pub fn collect_stats(&mut self) -> Result<MemoryStats, MemoryError> {
292 if !self.platform_context.initialized {
293 return Err(MemoryError::NotInitialized);
294 }
295
296 let now = Instant::now();
297
298 if let Some(last) = self.last_collection {
300 if now.duration_since(last) < self.collection_interval {
301 if let Some(ref stats) = self.last_stats {
302 return Ok(stats.clone());
303 }
304 }
305 }
306
307 let stats = self.perform_collection()?;
308 self.last_stats = Some(stats.clone());
309 self.last_collection = Some(now);
310
311 Ok(stats)
312 }
313
314 pub fn get_system_info(&self) -> Result<SystemInfo, MemoryError> {
316 if !self.platform_context.initialized {
317 return Err(MemoryError::NotInitialized);
318 }
319
320 #[cfg(target_os = "linux")]
321 return self.get_linux_system_info();
322
323 #[cfg(target_os = "windows")]
324 return self.get_windows_system_info();
325
326 #[cfg(target_os = "macos")]
327 return self.get_macos_system_info();
328
329 #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
330 Err(MemoryError::UnsupportedPlatform)
331 }
332
333 pub fn set_collection_interval(&mut self, interval: Duration) {
335 self.collection_interval = interval;
336 }
337
338 pub fn get_last_stats(&self) -> Option<&MemoryStats> {
340 self.last_stats.as_ref()
341 }
342
343 fn perform_collection(&self) -> Result<MemoryStats, MemoryError> {
344 #[cfg(target_os = "linux")]
345 return self.collect_linux_stats();
346
347 #[cfg(target_os = "windows")]
348 return self.collect_windows_stats();
349
350 #[cfg(target_os = "macos")]
351 return self.collect_macos_stats();
352
353 #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
354 Err(MemoryError::UnsupportedPlatform)
355 }
356
357 #[cfg(target_os = "linux")]
358 fn initialize_linux(&mut self) -> Result<(), MemoryError> {
359 self.platform_context.linux_context.proc_meminfo_available =
361 std::path::Path::new("/proc/meminfo").exists();
362 self.platform_context.linux_context.proc_status_available =
363 std::path::Path::new("/proc/self/status").exists();
364 self.platform_context.linux_context.proc_maps_available =
365 std::path::Path::new("/proc/self/maps").exists();
366
367 self.platform_context.initialized = true;
368 Ok(())
369 }
370
371 #[cfg(target_os = "windows")]
372 fn initialize_windows(&mut self) -> Result<(), MemoryError> {
373 self.platform_context
375 .windows_context
376 .global_memory_api_available = true; self.platform_context
378 .windows_context
379 .process_memory_api_available = true; self.platform_context
381 .windows_context
382 .virtual_query_available = true; self.platform_context.initialized = true;
385 Ok(())
386 }
387
388 #[cfg(target_os = "macos")]
389 fn initialize_macos(&mut self) -> Result<(), MemoryError> {
390 self.platform_context.macos_context.vm_stat_available = true; self.platform_context.macos_context.task_info_available = true; self.platform_context.macos_context.mach_api_available = true; self.platform_context.initialized = true;
396 Ok(())
397 }
398
399 #[cfg(target_os = "linux")]
400 fn collect_linux_stats(&self) -> Result<MemoryStats, MemoryError> {
401 Ok(MemoryStats {
404 virtual_memory: VirtualMemoryStats {
405 total_virtual: 1_099_511_627_776, available_virtual: 549_755_813_888, used_virtual: 549_755_813_888,
408 reserved: 274_877_906_944, committed: 274_877_906_944,
410 },
411 physical_memory: PhysicalMemoryStats {
412 total_physical: 17_179_869_184, available_physical: 8_589_934_592, used_physical: 8_589_934_592,
415 cached: 4_294_967_296, buffers: 1_073_741_824, swap: SwapStats {
418 total_swap: 8_589_934_592, used_swap: 1_073_741_824, available_swap: 7_516_192_768,
421 swap_in_rate: 0.0,
422 swap_out_rate: 0.0,
423 },
424 },
425 process_memory: ProcessMemoryStats {
426 virtual_size: 1_073_741_824, resident_size: 536_870_912, shared_size: 134_217_728, private_size: 402_653_184, heap_size: 268_435_456, stack_size: 8_388_608, mapped_files: 134_217_728, peak_usage: 1_073_741_824,
434 },
435 system_memory: SystemMemoryStats {
436 allocation_count: 1_000_000,
437 deallocation_count: 950_000,
438 active_allocations: 50_000,
439 total_allocated: 10_737_418_240, total_deallocated: 9_663_676_416, fragmentation_level: 0.15,
442 large_pages: LargePageStats {
443 supported: true,
444 total_large_pages: 2_097_152, used_large_pages: 0,
446 page_size: 2_097_152,
447 },
448 },
449 pressure_indicators: PressureIndicators {
450 pressure_level: PressureLevel::Normal,
451 low_memory: false,
452 swapping_active: false,
453 allocation_failure_rate: 0.001,
454 gc_pressure: None,
455 },
456 timestamp: Instant::now(),
457 })
458 }
459
460 #[cfg(target_os = "windows")]
461 fn collect_windows_stats(&self) -> Result<MemoryStats, MemoryError> {
462 Ok(MemoryStats {
465 virtual_memory: VirtualMemoryStats {
466 total_virtual: 140_737_488_355_328, available_virtual: 70_368_744_177_664, used_virtual: 70_368_744_177_664,
469 reserved: 35_184_372_088_832, committed: 35_184_372_088_832,
471 },
472 physical_memory: PhysicalMemoryStats {
473 total_physical: 34_359_738_368, available_physical: 17_179_869_184, used_physical: 17_179_869_184,
476 cached: 8_589_934_592, buffers: 2_147_483_648, swap: SwapStats {
479 total_swap: 17_179_869_184, used_swap: 2_147_483_648, available_swap: 15_032_385_536,
482 swap_in_rate: 0.0,
483 swap_out_rate: 0.0,
484 },
485 },
486 process_memory: ProcessMemoryStats {
487 virtual_size: 2_147_483_648, resident_size: 1_073_741_824, shared_size: 268_435_456, private_size: 805_306_368, heap_size: 536_870_912, stack_size: 16_777_216, mapped_files: 268_435_456, peak_usage: 2_147_483_648,
495 },
496 system_memory: SystemMemoryStats {
497 allocation_count: 2_000_000,
498 deallocation_count: 1_900_000,
499 active_allocations: 100_000,
500 total_allocated: 21_474_836_480, total_deallocated: 19_327_352_832, fragmentation_level: 0.12,
503 large_pages: LargePageStats {
504 supported: true,
505 total_large_pages: 2_097_152, used_large_pages: 0,
507 page_size: 2_097_152,
508 },
509 },
510 pressure_indicators: PressureIndicators {
511 pressure_level: PressureLevel::Normal,
512 low_memory: false,
513 swapping_active: false,
514 allocation_failure_rate: 0.0005,
515 gc_pressure: None,
516 },
517 timestamp: Instant::now(),
518 })
519 }
520
521 #[cfg(target_os = "macos")]
522 fn collect_macos_stats(&self) -> Result<MemoryStats, MemoryError> {
523 Ok(MemoryStats {
526 virtual_memory: VirtualMemoryStats {
527 total_virtual: 1_099_511_627_776, available_virtual: 549_755_813_888, used_virtual: 549_755_813_888,
530 reserved: 274_877_906_944, committed: 274_877_906_944,
532 },
533 physical_memory: PhysicalMemoryStats {
534 total_physical: 68_719_476_736, available_physical: 34_359_738_368, used_physical: 34_359_738_368,
537 cached: 17_179_869_184, buffers: 4_294_967_296, swap: SwapStats {
540 total_swap: 34_359_738_368, used_swap: 4_294_967_296, available_swap: 30_064_771_072,
543 swap_in_rate: 0.0,
544 swap_out_rate: 0.0,
545 },
546 },
547 process_memory: ProcessMemoryStats {
548 virtual_size: 4_294_967_296, resident_size: 2_147_483_648, shared_size: 536_870_912, private_size: 1_610_612_736, heap_size: 1_073_741_824, stack_size: 33_554_432, mapped_files: 536_870_912, peak_usage: 4_294_967_296,
556 },
557 system_memory: SystemMemoryStats {
558 allocation_count: 1_500_000,
559 deallocation_count: 1_425_000,
560 active_allocations: 75_000,
561 total_allocated: 16_106_127_360, total_deallocated: 14_495_514_624, fragmentation_level: 0.10,
564 large_pages: LargePageStats {
565 supported: false,
566 total_large_pages: 0,
567 used_large_pages: 0,
568 page_size: 0,
569 },
570 },
571 pressure_indicators: PressureIndicators {
572 pressure_level: PressureLevel::Normal,
573 low_memory: false,
574 swapping_active: false,
575 allocation_failure_rate: 0.0002,
576 gc_pressure: None,
577 },
578 timestamp: Instant::now(),
579 })
580 }
581
582 #[cfg(target_os = "linux")]
583 fn get_linux_system_info(&self) -> Result<SystemInfo, MemoryError> {
584 Ok(SystemInfo {
585 os_name: "Linux".to_string(),
586 os_version: "5.15.0".to_string(),
587 architecture: "x86_64".to_string(),
588 cpu_cores: 8,
589 cpu_cache: CpuCacheInfo {
590 l1_cache_size: 32768, l2_cache_size: 262144, l3_cache_size: Some(8388608), cache_line_size: 64,
594 },
595 page_size: 4096,
596 large_page_size: Some(2097152), mmu_info: MmuInfo {
598 virtual_address_bits: 48,
599 physical_address_bits: 40,
600 aslr_enabled: true,
601 nx_bit_supported: true,
602 },
603 })
604 }
605
606 #[cfg(target_os = "windows")]
607 fn get_windows_system_info(&self) -> Result<SystemInfo, MemoryError> {
608 Ok(SystemInfo {
609 os_name: "Windows".to_string(),
610 os_version: "10.0.19045".to_string(),
611 architecture: "x86_64".to_string(),
612 cpu_cores: 16,
613 cpu_cache: CpuCacheInfo {
614 l1_cache_size: 32768, l2_cache_size: 524288, l3_cache_size: Some(16777216), cache_line_size: 64,
618 },
619 page_size: 4096,
620 large_page_size: Some(2097152), mmu_info: MmuInfo {
622 virtual_address_bits: 48,
623 physical_address_bits: 40,
624 aslr_enabled: true,
625 nx_bit_supported: true,
626 },
627 })
628 }
629
630 #[cfg(target_os = "macos")]
631 fn get_macos_system_info(&self) -> Result<SystemInfo, MemoryError> {
632 Ok(SystemInfo {
633 os_name: "macOS".to_string(),
634 os_version: "14.1.0".to_string(),
635 architecture: "arm64".to_string(),
636 cpu_cores: 12,
637 cpu_cache: CpuCacheInfo {
638 l1_cache_size: 65536, l2_cache_size: 4194304, l3_cache_size: None, cache_line_size: 128,
642 },
643 page_size: 16384, large_page_size: None, mmu_info: MmuInfo {
646 virtual_address_bits: 48,
647 physical_address_bits: 40,
648 aslr_enabled: true,
649 nx_bit_supported: true,
650 },
651 })
652 }
653}
654
655impl MemoryContext {
656 fn new() -> Self {
657 Self {
658 initialized: false,
659 #[cfg(target_os = "linux")]
660 linux_context: LinuxMemoryContext {
661 proc_meminfo_available: false,
662 proc_status_available: false,
663 proc_maps_available: false,
664 },
665 #[cfg(target_os = "windows")]
666 windows_context: WindowsMemoryContext {
667 global_memory_api_available: false,
668 process_memory_api_available: false,
669 virtual_query_available: false,
670 },
671 #[cfg(target_os = "macos")]
672 macos_context: MacOSMemoryContext {
673 vm_stat_available: false,
674 task_info_available: false,
675 mach_api_available: false,
676 },
677 }
678 }
679}
680
681#[derive(Debug, Clone, PartialEq)]
683pub enum MemoryError {
684 UnsupportedPlatform,
686 NotInitialized,
688 PermissionDenied,
690 SystemError(String),
692 ParseError(String),
694 IoError(String),
696}
697
698impl Default for PlatformMemoryInfo {
699 fn default() -> Self {
700 Self::new()
701 }
702}
703
704impl std::fmt::Display for MemoryError {
705 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
706 match self {
707 MemoryError::UnsupportedPlatform => {
708 write!(f, "Platform not supported for memory info collection")
709 }
710 MemoryError::NotInitialized => write!(f, "Memory info collector not initialized"),
711 MemoryError::PermissionDenied => write!(f, "Permission denied for memory info access"),
712 MemoryError::SystemError(msg) => write!(f, "System error: {}", msg),
713 MemoryError::ParseError(msg) => write!(f, "Parse error: {}", msg),
714 MemoryError::IoError(msg) => write!(f, "I/O error: {}", msg),
715 }
716 }
717}
718
719impl std::error::Error for MemoryError {}
720
721#[cfg(test)]
722mod tests {
723 use super::*;
724
725 #[test]
726 fn test_memory_info_creation() {
727 let info = PlatformMemoryInfo::new();
728 assert!(!info.platform_context.initialized);
729 assert!(info.last_stats.is_none());
730 }
731
732 #[test]
733 fn test_initialization() {
734 let mut info = PlatformMemoryInfo::new();
735 let result = info.initialize();
736
737 #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
738 assert!(result.is_ok());
739
740 #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
741 assert_eq!(result, Err(MemoryError::UnsupportedPlatform));
742 }
743
744 #[test]
745 fn test_stats_collection() {
746 let mut info = PlatformMemoryInfo::new();
747 let _ = info.initialize();
748
749 let result = info.collect_stats();
750
751 #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
752 {
753 if info.platform_context.initialized {
754 assert!(result.is_ok());
755 let stats = result.expect("Stats should be collected");
756 assert!(stats.physical_memory.total_physical > 0);
757 assert!(stats.virtual_memory.total_virtual > 0);
758 }
759 }
760 }
761
762 #[test]
763 fn test_system_info() {
764 let mut info = PlatformMemoryInfo::new();
765 let _ = info.initialize();
766
767 let result = info.get_system_info();
768
769 #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
770 {
771 if info.platform_context.initialized {
772 assert!(result.is_ok());
773 let sys_info = result.expect("System info should be available");
774 assert!(!sys_info.os_name.is_empty());
775 assert!(sys_info.cpu_cores > 0);
776 assert!(sys_info.page_size > 0);
777 }
778 }
779 }
780
781 #[test]
782 fn test_pressure_level_ordering() {
783 assert!(PressureLevel::Critical > PressureLevel::High);
784 assert!(PressureLevel::High > PressureLevel::Moderate);
785 assert!(PressureLevel::Moderate > PressureLevel::Normal);
786 }
787
788 #[test]
789 fn test_collection_interval() {
790 let mut info = PlatformMemoryInfo::new();
791 info.set_collection_interval(Duration::from_millis(500));
792 assert_eq!(info.collection_interval, Duration::from_millis(500));
793 }
794}