use core::sync::atomic::{AtomicI64, AtomicU64, Ordering};
pub struct ProcessVmStat {
vss_pages: AtomicI64,
peak_vss_pages: AtomicU64,
peak_rss_pages: AtomicU64,
}
impl ProcessVmStat {
pub const fn new() -> Self {
Self {
vss_pages: AtomicI64::new(0),
peak_vss_pages: AtomicU64::new(0),
peak_rss_pages: AtomicU64::new(0),
}
}
#[inline]
pub fn vss_pages(&self) -> u64 {
self.vss_pages.load(Ordering::Relaxed).max(0) as u64
}
#[inline]
pub fn peak_vss_pages(&self) -> u64 {
self.peak_vss_pages.load(Ordering::Relaxed)
}
#[inline]
pub fn peak_rss_pages(&self) -> u64 {
self.peak_rss_pages.load(Ordering::Relaxed)
}
#[inline]
pub(super) fn on_map(&self, pages: u64) {
let new_vss = self
.vss_pages
.fetch_add(pages as i64, Ordering::Relaxed)
.max(0) as u64
+ pages;
self.peak_vss_pages.fetch_max(new_vss, Ordering::Relaxed);
self.peak_rss_pages.fetch_max(new_vss, Ordering::Relaxed);
}
#[inline]
pub(super) fn on_unmap(&self, pages: u64) {
self.vss_pages.fetch_sub(pages as i64, Ordering::Relaxed);
}
#[inline]
pub(super) fn on_clear(&self) {
self.vss_pages.store(0, Ordering::Relaxed);
self.peak_vss_pages.store(0, Ordering::Relaxed);
self.peak_rss_pages.store(0, Ordering::Relaxed);
}
#[inline]
pub(super) fn seed_from(&self, parent: &Self) {
let vss = parent.vss_pages();
self.vss_pages.store(vss as i64, Ordering::Relaxed);
self.peak_vss_pages
.store(parent.peak_vss_pages().max(vss), Ordering::Relaxed);
self.peak_rss_pages
.store(parent.peak_rss_pages().max(vss), Ordering::Relaxed);
}
}
impl Default for ProcessVmStat {
fn default() -> Self {
Self::new()
}
}
#[cfg(axtest)]
pub(crate) fn process_vm_stat_watermarks_hold_for_test() -> bool {
let parent = ProcessVmStat::new();
parent.on_map(3);
parent.on_map(2);
parent.on_unmap(4);
let child = ProcessVmStat::new();
child.seed_from(&parent);
let inherited =
child.vss_pages() == 1 && child.peak_vss_pages() == 5 && child.peak_rss_pages() == 5;
parent.on_clear();
inherited
&& parent.vss_pages() == 0
&& parent.peak_vss_pages() == 0
&& parent.peak_rss_pages() == 0
}
#[cfg(axtest)]
pub(crate) fn process_vm_stat_edge_cases_hold_for_test() -> bool {
let stat = ProcessVmStat::new();
let init_ok = stat.vss_pages() == 0 && stat.peak_vss_pages() == 0 && stat.peak_rss_pages() == 0;
stat.on_map(10);
let after_map =
stat.vss_pages() == 10 && stat.peak_vss_pages() == 10 && stat.peak_rss_pages() == 10;
stat.on_map(5);
let after_more =
stat.vss_pages() == 15 && stat.peak_vss_pages() == 15 && stat.peak_rss_pages() == 15;
stat.on_unmap(8);
let after_unmap = stat.vss_pages() == 7
&& stat.peak_vss_pages() == 15 && stat.peak_rss_pages() == 15;
stat.on_unmap(20); let after_over = stat.vss_pages() == 0; let peaks_stable = stat.peak_vss_pages() == 15 && stat.peak_rss_pages() == 15;
let empty = ProcessVmStat::new();
let child = ProcessVmStat::new();
child.seed_from(&empty);
let from_empty =
child.vss_pages() == 0 && child.peak_vss_pages() == 0 && child.peak_rss_pages() == 0;
init_ok && after_map && after_more && after_unmap && after_over && peaks_stable && from_empty
}