use std::fs;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use crate::MemoryStats;
#[cfg(not(feature = "always_use_statm"))]
const SMAPS: &str = "/proc/self/smaps";
const STATM: &str = "/proc/self/statm";
#[cfg(not(feature = "always_use_statm"))]
static SMAPS_CHECKED: AtomicBool = AtomicBool::new(false);
#[cfg(not(feature = "always_use_statm"))]
static SMAPS_EXIST: AtomicBool = AtomicBool::new(false);
static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0);
pub fn memory_stats() -> Option<MemoryStats> {
#[cfg(feature = "always_use_statm")]
load_page_size()?;
#[cfg(not(feature = "always_use_statm"))]
if let Ok(false) = SMAPS_CHECKED.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) {
let smaps_exist = fs::metadata(SMAPS).is_ok();
if !smaps_exist {
load_page_size()?;
}
SMAPS_EXIST.store(smaps_exist, Ordering::Relaxed);
}
#[cfg(not(feature = "always_use_statm"))]
if SMAPS_EXIST.load(Ordering::Relaxed) {
return match fs::read_to_string(SMAPS) {
Ok(smap_info) => {
let mut total_size_kb: usize = 0;
let mut total_rss_kb: usize = 0;
for line in smap_info.lines() {
if let Some(rest) = line.strip_prefix("Size:") {
total_size_kb += scan_int(rest).0;
} else if let Some(rest) = line.strip_prefix("Rss:") {
total_rss_kb += scan_int(rest).0;
}
}
Some(MemoryStats {
physical_mem: total_rss_kb << 10,
virtual_mem: total_size_kb << 10,
})
}
Err(_) => None,
};
}
match fs::read_to_string(STATM) {
Ok(statm_info) => {
let page_size = PAGE_SIZE.load(Ordering::Relaxed);
let (total_size_pages, idx) = scan_int(&statm_info);
let (total_rss_pages, _) = scan_int(&statm_info[idx..]);
Some(MemoryStats {
physical_mem: total_rss_pages * page_size,
virtual_mem: total_size_pages * page_size,
})
}
Err(_) => None,
}
}
fn load_page_size() -> Option<()> {
if PAGE_SIZE.load(Ordering::Relaxed) == 0 {
let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
if page_size == -1 {
return None;
} else {
PAGE_SIZE.store(page_size as usize, Ordering::Relaxed);
}
}
Some(())
}
fn scan_int(string: &str) -> (usize, usize) {
let mut out = 0;
let mut idx = 0;
let mut chars = string.chars().peekable();
while let Some(' ') = chars.next_if_eq(&' ') {
idx += 1;
}
for n in chars {
idx += 1;
if n.is_ascii_digit() {
out *= 10;
out += n as usize - '0' as usize;
} else {
break;
}
}
(out, idx)
}