concinnity_engine/app/
sysmem.rs1pub(crate) fn total_physical_bytes() -> Option<u64> {
15 imp::total_physical_bytes()
16}
17
18pub fn process_resident_bytes() -> Option<u64> {
21 imp::process_resident_bytes()
22}
23
24#[cfg(target_os = "macos")]
25mod imp {
26 pub(super) fn total_physical_bytes() -> Option<u64> {
29 let mut value: u64 = 0;
30 let mut len = std::mem::size_of::<u64>();
31 let name = c"hw.memsize";
32 let rc = unsafe {
35 libc::sysctlbyname(
36 name.as_ptr(),
37 &mut value as *mut u64 as *mut libc::c_void,
38 &mut len,
39 std::ptr::null_mut(),
40 0,
41 )
42 };
43 (rc == 0 && value > 0).then_some(value)
44 }
45
46 #[expect(
50 deprecated,
51 reason = "mach_task_self_ is a stable fundamental symbol, kept over pulling in mach2 for one static"
52 )]
53 pub(super) fn process_resident_bytes() -> Option<u64> {
54 let mut info: libc::mach_task_basic_info = unsafe { std::mem::zeroed() };
57 let mut count = (std::mem::size_of::<libc::mach_task_basic_info>()
58 / std::mem::size_of::<libc::natural_t>())
59 as libc::mach_msg_type_number_t;
60 let rc = unsafe {
64 libc::task_info(
65 libc::mach_task_self_,
66 libc::MACH_TASK_BASIC_INFO,
67 &mut info as *mut _ as libc::task_info_t,
68 &mut count,
69 )
70 };
71 (rc == libc::KERN_SUCCESS).then_some(info.resident_size)
72 }
73}
74
75#[cfg(target_os = "linux")]
76mod imp {
77 pub(super) fn total_physical_bytes() -> Option<u64> {
80 let meminfo = std::fs::read_to_string("/proc/meminfo").ok()?;
81 for line in meminfo.lines() {
82 if let Some(rest) = line.strip_prefix("MemTotal:") {
83 let kb: u64 = rest.split_whitespace().next()?.parse().ok()?;
84 return Some(kb * 1024);
85 }
86 }
87 None
88 }
89
90 pub(super) fn process_resident_bytes() -> Option<u64> {
91 let statm = std::fs::read_to_string("/proc/self/statm").ok()?;
92 let resident_pages: u64 = statm.split_whitespace().nth(1)?.parse().ok()?;
93 let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
95 (page_size > 0).then(|| resident_pages * page_size as u64)
96 }
97}
98
99#[cfg(target_os = "windows")]
100mod imp {
101 use windows::Win32::System::ProcessStatus::{GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS};
102 use windows::Win32::System::SystemInformation::{GlobalMemoryStatusEx, MEMORYSTATUSEX};
103 use windows::Win32::System::Threading::GetCurrentProcess;
104
105 pub(super) fn total_physical_bytes() -> Option<u64> {
106 let mut status = MEMORYSTATUSEX {
107 dwLength: std::mem::size_of::<MEMORYSTATUSEX>() as u32,
108 ..Default::default()
109 };
110 unsafe { GlobalMemoryStatusEx(&mut status) }.ok()?;
112 (status.ullTotalPhys > 0).then_some(status.ullTotalPhys)
113 }
114
115 pub(super) fn process_resident_bytes() -> Option<u64> {
116 let mut counters = PROCESS_MEMORY_COUNTERS::default();
117 let ok = unsafe {
119 GetProcessMemoryInfo(
120 GetCurrentProcess(),
121 &mut counters,
122 std::mem::size_of::<PROCESS_MEMORY_COUNTERS>() as u32,
123 )
124 };
125 ok.ok().map(|()| counters.WorkingSetSize as u64)
126 }
127}
128
129#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
130mod imp {
131 pub(super) fn total_physical_bytes() -> Option<u64> {
132 None
133 }
134 pub(super) fn process_resident_bytes() -> Option<u64> {
135 None
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
148 fn queries_return_plausible_values() {
149 if cfg!(any(
150 target_os = "macos",
151 target_os = "linux",
152 target_os = "windows"
153 )) {
154 let total = total_physical_bytes().expect("total RAM query works on this platform");
155 assert!(
156 total >= 256 * 1024 * 1024,
157 "implausibly small total RAM: {total}"
158 );
159
160 let rss = process_resident_bytes().expect("RSS query works on this platform");
161 assert!(rss > 0, "process resident size should be positive");
162 assert!(rss <= total, "RSS {rss} exceeds total RAM {total}");
163 }
164 }
165}