sklears_core/
system_info.rs1use crate::error::{Result, SklearsError};
7
8#[derive(Debug, Clone)]
10pub struct SystemMemory {
11 pub total: u64,
13 pub available: u64,
15 pub used: u64,
17}
18
19pub fn system_memory() -> Result<SystemMemory> {
28 system_memory_impl()
29}
30
31pub fn process_rss_bytes() -> Option<u64> {
35 process_rss_impl()
36}
37
38#[cfg(target_os = "linux")]
41fn system_memory_impl() -> Result<SystemMemory> {
42 let content = std::fs::read_to_string("/proc/meminfo")
43 .map_err(|e| SklearsError::InvalidOperation(format!("cannot read /proc/meminfo: {}", e)))?;
44
45 let mut mem_total: Option<u64> = None;
46 let mut mem_available: Option<u64> = None;
47
48 for line in content.lines() {
49 if let Some(rest) = line.strip_prefix("MemTotal:") {
50 mem_total = Some(parse_kb_line(rest)?);
51 } else if let Some(rest) = line.strip_prefix("MemAvailable:") {
52 mem_available = Some(parse_kb_line(rest)?);
53 }
54 if mem_total.is_some() && mem_available.is_some() {
55 break;
56 }
57 }
58
59 let total = mem_total.ok_or_else(|| {
60 SklearsError::InvalidOperation("MemTotal not found in /proc/meminfo".to_string())
61 })?;
62 let available = mem_available.ok_or_else(|| {
63 SklearsError::InvalidOperation("MemAvailable not found in /proc/meminfo".to_string())
64 })?;
65 let used = total.saturating_sub(available);
66
67 Ok(SystemMemory {
68 total,
69 available,
70 used,
71 })
72}
73
74#[cfg(target_os = "linux")]
75fn parse_kb_line(rest: &str) -> Result<u64> {
76 let trimmed = rest.trim();
78 let kb_str = trimmed
79 .split_whitespace()
80 .next()
81 .ok_or_else(|| SklearsError::InvalidOperation("empty /proc/meminfo value".to_string()))?;
82 let kb: u64 = kb_str.parse().map_err(|_| {
83 SklearsError::InvalidOperation(format!("cannot parse /proc/meminfo value: {}", kb_str))
84 })?;
85 Ok(kb * 1024)
86}
87
88#[cfg(target_os = "linux")]
89fn process_rss_impl() -> Option<u64> {
90 let content = std::fs::read_to_string("/proc/self/statm").ok()?;
92 let resident_pages: u64 = content.split_whitespace().nth(1)?.parse().ok()?;
93 let page_size = page_size_bytes()?;
94 Some(resident_pages * page_size)
95}
96
97#[cfg(all(
100 target_family = "unix",
101 not(target_os = "linux"),
102 not(target_os = "macos")
103))]
104fn system_memory_impl() -> Result<SystemMemory> {
105 let total = unix_sysconf_bytes(libc::_SC_PHYS_PAGES).ok_or_else(|| {
106 SklearsError::InvalidOperation("sysconf(_SC_PHYS_PAGES) returned unavailable".to_string())
107 })?;
108 let available = unix_sysconf_bytes(libc::_SC_AVPHYS_PAGES).ok_or_else(|| {
109 SklearsError::InvalidOperation("sysconf(_SC_AVPHYS_PAGES) returned unavailable".to_string())
110 })?;
111 let used = total.saturating_sub(available);
112 Ok(SystemMemory {
113 total,
114 available,
115 used,
116 })
117}
118
119#[cfg(all(
120 target_family = "unix",
121 not(target_os = "linux"),
122 not(target_os = "macos")
123))]
124fn unix_sysconf_bytes(name: libc::c_int) -> Option<u64> {
125 let pages = unsafe { libc::sysconf(name) };
127 if pages < 0 {
128 return None;
129 }
130 let page_size = page_size_bytes()?;
131 Some(pages as u64 * page_size)
132}
133
134#[cfg(all(
135 target_family = "unix",
136 not(target_os = "linux"),
137 not(target_os = "macos")
138))]
139fn process_rss_impl() -> Option<u64> {
140 let mut usage: libc::rusage = unsafe { std::mem::zeroed() };
142 let ret = unsafe { libc::getrusage(libc::RUSAGE_SELF, &mut usage) };
144 if ret != 0 {
145 return None;
146 }
147 let rss = usage.ru_maxrss as u64 * 1024; Some(rss)
149}
150
151#[cfg(target_os = "macos")]
157extern "C" {
158 fn mach_host_self() -> libc::mach_port_t;
159}
160
161#[cfg(target_os = "macos")]
162fn system_memory_impl() -> Result<SystemMemory> {
163 let total = macos_total_memory().ok_or_else(|| {
164 SklearsError::InvalidOperation("sysctl hw.memsize failed on macOS".to_string())
165 })?;
166 let available = macos_available_memory().ok_or_else(|| {
167 SklearsError::InvalidOperation("host_statistics64 failed on macOS".to_string())
168 })?;
169 let used = total.saturating_sub(available);
170 Ok(SystemMemory {
171 total,
172 available,
173 used,
174 })
175}
176
177#[cfg(target_os = "macos")]
178fn macos_total_memory() -> Option<u64> {
179 let mut value: u64 = 0;
180 let mut len = std::mem::size_of::<u64>();
181 let ret = unsafe {
183 libc::sysctlbyname(
184 c"hw.memsize".as_ptr(),
185 &mut value as *mut u64 as *mut libc::c_void,
186 &mut len,
187 std::ptr::null_mut(),
188 0,
189 )
190 };
191 if ret == 0 {
192 Some(value)
193 } else {
194 None
195 }
196}
197
198#[cfg(target_os = "macos")]
199fn macos_available_memory() -> Option<u64> {
200 let page_size = page_size_bytes()?;
201 let mut vm_stats: libc::vm_statistics64 = unsafe { std::mem::zeroed() };
202 let mut count: libc::mach_msg_type_number_t = (std::mem::size_of::<libc::vm_statistics64>()
203 / std::mem::size_of::<libc::integer_t>())
204 as libc::mach_msg_type_number_t;
205 let ret = unsafe {
207 libc::host_statistics64(
208 mach_host_self(),
209 libc::HOST_VM_INFO64 as libc::host_flavor_t,
210 &mut vm_stats as *mut _ as *mut libc::integer_t,
211 &mut count,
212 )
213 };
214 if ret != libc::KERN_SUCCESS {
215 return None;
216 }
217 let free_pages = vm_stats.free_count as u64 + vm_stats.inactive_count as u64;
218 Some(free_pages * page_size)
219}
220
221#[cfg(target_os = "macos")]
222fn process_rss_impl() -> Option<u64> {
223 let mut usage: libc::rusage = unsafe { std::mem::zeroed() };
224 let ret = unsafe { libc::getrusage(libc::RUSAGE_SELF, &mut usage) };
226 if ret != 0 {
227 return None;
228 }
229 Some(usage.ru_maxrss as u64)
231}
232
233#[cfg(target_os = "windows")]
236fn system_memory_impl() -> Result<SystemMemory> {
237 use winapi::um::processthreadsapi::GetCurrentProcess;
238 use winapi::um::psapi::{GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS};
239 use winapi::um::sysinfoapi::{GlobalMemoryStatusEx, MEMORYSTATUSEX};
240
241 let mut mem_status: MEMORYSTATUSEX = unsafe { std::mem::zeroed() };
242 mem_status.dwLength = std::mem::size_of::<MEMORYSTATUSEX>() as u32;
243
244 let ok = unsafe { GlobalMemoryStatusEx(&mut mem_status) };
246 if ok == 0 {
247 return Err(SklearsError::InvalidOperation(
248 "GlobalMemoryStatusEx failed".to_string(),
249 ));
250 }
251
252 let total = mem_status.ullTotalPhys;
253 let available = mem_status.ullAvailPhys;
254 let used = total.saturating_sub(available);
255
256 Ok(SystemMemory {
257 total,
258 available,
259 used,
260 })
261}
262
263#[cfg(target_os = "windows")]
264fn process_rss_impl() -> Option<u64> {
265 use winapi::um::processthreadsapi::GetCurrentProcess;
266 use winapi::um::psapi::{GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS};
267
268 let mut pmc: PROCESS_MEMORY_COUNTERS = unsafe { std::mem::zeroed() };
269 let size = std::mem::size_of::<PROCESS_MEMORY_COUNTERS>() as u32;
270 let ok = unsafe { GetProcessMemoryInfo(GetCurrentProcess(), &mut pmc, size) };
272 if ok == 0 {
273 return None;
274 }
275 Some(pmc.WorkingSetSize as u64)
276}
277
278#[cfg(not(any(target_family = "unix", target_os = "windows")))]
281fn system_memory_impl() -> Result<SystemMemory> {
282 Err(SklearsError::NotImplemented(
283 "system_memory() is not implemented on this platform".to_string(),
284 ))
285}
286
287#[cfg(not(any(target_family = "unix", target_os = "windows")))]
288fn process_rss_impl() -> Option<u64> {
289 None
290}
291
292#[cfg(target_family = "unix")]
296fn page_size_bytes() -> Option<u64> {
297 let ps = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
299 if ps <= 0 {
300 None
301 } else {
302 Some(ps as u64)
303 }
304}