ruvector_memopt/windows/
memory.rs1use sysinfo::System;
4use tracing::{info, warn};
5use std::time::Instant;
6
7#[derive(Debug, Clone)]
8pub struct MemoryStatus {
9 pub total_physical_mb: f64,
10 pub available_physical_mb: f64,
11 pub memory_load_percent: u32,
12 pub total_page_file_mb: f64,
13 pub available_page_file_mb: f64,
14 pub total_virtual_mb: f64,
15 pub available_virtual_mb: f64,
16}
17
18impl MemoryStatus {
19 pub fn used_physical_mb(&self) -> f64 { self.total_physical_mb - self.available_physical_mb }
20 pub fn is_high_pressure(&self) -> bool { self.memory_load_percent > 80 }
21 pub fn is_critical(&self) -> bool { self.memory_load_percent > 95 }
22}
23
24#[derive(Debug, Clone)]
25pub struct OptimizationResult {
26 pub freed_mb: f64,
27 pub before_available_mb: f64,
28 pub after_available_mb: f64,
29 pub processes_trimmed: usize,
30 pub duration_ms: u64,
31}
32
33pub struct WindowsMemoryOptimizer {
34 has_admin: bool,
35}
36
37impl WindowsMemoryOptimizer {
38 pub fn new() -> Self {
39 let has_admin = Self::check_admin();
40 if !has_admin { warn!("Running without admin - limited optimization"); }
41 else { info!("Running with admin privileges - full optimization available"); }
42 Self { has_admin }
43 }
44
45 fn check_admin() -> bool {
46 #[cfg(windows)]
47 {
48 std::process::Command::new("net").args(["session"])
49 .stdout(std::process::Stdio::null())
50 .stderr(std::process::Stdio::null())
51 .status().map(|s| s.success()).unwrap_or(false)
52 }
53 #[cfg(not(windows))]
54 { false }
55 }
56
57 pub fn get_memory_status() -> Result<MemoryStatus, String> {
58 let mut sys = System::new();
59 sys.refresh_memory();
60 let total = sys.total_memory() as f64 / 1024.0 / 1024.0;
61 let avail = sys.available_memory() as f64 / 1024.0 / 1024.0;
62 let load = if total > 0.0 { (((total - avail) / total) * 100.0) as u32 } else { 0 };
63 Ok(MemoryStatus {
64 total_physical_mb: total, available_physical_mb: avail, memory_load_percent: load,
65 total_page_file_mb: total * 1.5, available_page_file_mb: avail,
66 total_virtual_mb: total * 2.0, available_virtual_mb: avail * 2.0,
67 })
68 }
69
70 #[cfg(windows)]
71 pub fn trim_process_working_set(pid: u32) -> Result<u64, String> {
72 use windows::Win32::Foundation::CloseHandle;
73 use windows::Win32::System::Threading::{OpenProcess, PROCESS_SET_QUOTA, PROCESS_QUERY_INFORMATION};
74 use windows::Win32::System::Memory::SetProcessWorkingSetSizeEx;
75 use windows::Win32::System::ProcessStatus::{GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS};
76 use std::mem::size_of;
77
78 unsafe {
79 let handle = match OpenProcess(PROCESS_SET_QUOTA | PROCESS_QUERY_INFORMATION, false, pid) {
80 Ok(h) => h,
81 Err(_) => return Ok(0),
82 };
83
84 let mut mem_counters = PROCESS_MEMORY_COUNTERS::default();
85 mem_counters.cb = size_of::<PROCESS_MEMORY_COUNTERS>() as u32;
86 let before_ws = if GetProcessMemoryInfo(handle, &mut mem_counters, size_of::<PROCESS_MEMORY_COUNTERS>() as u32).is_ok() {
87 mem_counters.WorkingSetSize
88 } else { 0 };
89
90 let _ = SetProcessWorkingSetSizeEx(handle, usize::MAX, usize::MAX,
91 windows::Win32::System::Memory::SETPROCESSWORKINGSETSIZEEX_FLAGS(0));
92
93 let after_ws = if GetProcessMemoryInfo(handle, &mut mem_counters, size_of::<PROCESS_MEMORY_COUNTERS>() as u32).is_ok() {
94 mem_counters.WorkingSetSize
95 } else { before_ws };
96
97 let _ = CloseHandle(handle);
98 Ok(before_ws.saturating_sub(after_ws) as u64)
99 }
100 }
101
102 #[cfg(not(windows))]
103 pub fn trim_process_working_set(_pid: u32) -> Result<u64, String> { Ok(0) }
104
105 pub fn optimize(&self, aggressive: bool) -> Result<OptimizationResult, String> {
106 let start = Instant::now();
107 let before = Self::get_memory_status()?;
108 let mut trimmed = 0usize;
109 let mut total_freed: u64 = 0;
110
111 if let Ok(procs) = super::process::list_processes() {
112 for pid in procs.iter().take(150) {
113 match Self::trim_process_working_set(*pid) {
114 Ok(freed) => {
115 if freed > 0 {
116 total_freed += freed;
117 trimmed += 1;
118 }
119 }
120 Err(_) => {}
121 }
122 }
123 }
124
125 std::thread::sleep(std::time::Duration::from_millis(100));
127
128 let after = Self::get_memory_status()?;
129 let measured_freed = after.available_physical_mb - before.available_physical_mb;
130 let calculated_freed = total_freed as f64 / 1024.0 / 1024.0;
131 let freed_mb = measured_freed.max(calculated_freed).max(0.0);
132
133 info!("Optimized: trimmed {} processes, freed {:.1} MB in {}ms",
134 trimmed, freed_mb, start.elapsed().as_millis());
135
136 Ok(OptimizationResult {
137 freed_mb,
138 before_available_mb: before.available_physical_mb,
139 after_available_mb: after.available_physical_mb,
140 processes_trimmed: trimmed,
141 duration_ms: start.elapsed().as_millis() as u64,
142 })
143 }
144
145 pub fn has_admin_privileges(&self) -> bool { self.has_admin }
146}
147
148impl Default for WindowsMemoryOptimizer { fn default() -> Self { Self::new() } }