Spaces:
Build error
Build error
pub struct Mem { | |
cgroups: Option<cgroups_mem::CgroupsMem>, | |
sysinfo: sysinfo_mem::SysinfoMem, | |
} | |
impl Mem { | |
pub fn new() -> Self { | |
Self { | |
cgroups: cgroups_mem::CgroupsMem::new(), | |
sysinfo: sysinfo_mem::SysinfoMem::new(), | |
} | |
} | |
pub fn refresh(&mut self) { | |
if let Some(cgroups) = &mut self.cgroups { | |
cgroups.refresh(); | |
} | |
self.sysinfo.refresh(); | |
} | |
pub fn total_memory_bytes(&self) -> u64 { | |
if let Some(cgroups) = &self.cgroups { | |
if let Some(memory_limit_bytes) = cgroups.memory_limit_bytes() { | |
return memory_limit_bytes; | |
} | |
} | |
self.sysinfo.total_memory_bytes() | |
} | |
pub fn available_memory_bytes(&self) -> u64 { | |
if let Some(cgroups) = &self.cgroups { | |
if let Some(memory_limit_bytes) = cgroups.memory_limit_bytes() { | |
return memory_limit_bytes.saturating_sub(cgroups.used_memory_bytes()); | |
} | |
} | |
self.sysinfo.available_memory_bytes() | |
} | |
} | |
mod cgroups_mem { | |
use cgroups_rs::{hierarchies, memory, Cgroup}; | |
use procfs::process::Process; | |
pub struct CgroupsMem { | |
mem_controller: memory::MemController, | |
memory_limit_bytes: Option<u64>, | |
used_memory_bytes: u64, | |
} | |
impl CgroupsMem { | |
pub fn new() -> Option<Self> { | |
let memory_cgroup_path = match get_current_process_memory_cgroup_path() { | |
Ok(memory_cgroup_path) => memory_cgroup_path?, | |
Err(err) => { | |
log::error!( | |
"Failed to query current process info \ | |
while initializing CgroupsMem: {err}" | |
); | |
return None; | |
} | |
}; | |
let cgroup = Cgroup::load( | |
hierarchies::auto(), | |
memory_cgroup_path.trim_start_matches('/'), | |
); | |
let mut mem = Self { | |
mem_controller: cgroup.controller_of::<memory::MemController>()?.clone(), | |
memory_limit_bytes: None, | |
used_memory_bytes: 0, | |
}; | |
mem.refresh(); | |
Some(mem) | |
} | |
pub fn refresh(&mut self) { | |
let stat = self.mem_controller.memory_stat(); | |
self.memory_limit_bytes = stat.limit_in_bytes.try_into().ok(); | |
self.used_memory_bytes = stat.usage_in_bytes; | |
} | |
pub fn memory_limit_bytes(&self) -> Option<u64> { | |
self.memory_limit_bytes | |
} | |
pub fn used_memory_bytes(&self) -> u64 { | |
self.used_memory_bytes | |
} | |
} | |
fn get_current_process_memory_cgroup_path() -> procfs::ProcResult<Option<String>> { | |
let process = Process::myself()?; | |
let cgroups = process.cgroups()?; | |
for cgroup in cgroups { | |
// TODO: Can a process belong to multiple v2 cgroups!? | |
let is_v2_cgroup = cgroup.controllers.is_empty() | |
|| cgroup | |
.controllers | |
.iter() | |
.all(|controller| controller.is_empty()); | |
// TODO: Can a process belong to multiple v1 cgroups, with some of these cgroups having the same controllers (e.g., memory)!? | |
let is_v1_memory_cgroup = cgroup | |
.controllers | |
.iter() | |
.any(|controller| controller == "memory"); | |
if is_v2_cgroup || is_v1_memory_cgroup { | |
return Ok(Some(cgroup.pathname)); | |
} | |
} | |
Ok(None) | |
} | |
} | |
mod sysinfo_mem { | |
use sysinfo::{MemoryRefreshKind, RefreshKind, System}; | |
pub struct SysinfoMem { | |
system: System, | |
} | |
impl SysinfoMem { | |
pub fn new() -> Self { | |
let system = System::new_with_specifics( | |
RefreshKind::new().with_memory(MemoryRefreshKind::everything()), | |
); | |
Self { system } | |
} | |
pub fn refresh(&mut self) { | |
self.system.refresh_memory(); | |
} | |
pub fn total_memory_bytes(&self) -> u64 { | |
self.system.total_memory() | |
} | |
pub fn available_memory_bytes(&self) -> u64 { | |
self.system.available_memory() | |
} | |
} | |
} | |