mirror of
https://github.com/ClementTsang/bottom.git
synced 2026-08-25 04:46:40 +00:00
bug: fix cgroup v1 memory total using sentinel max as total
This commit is contained in:
@@ -4,6 +4,9 @@ use std::num::NonZeroU64;
|
|||||||
|
|
||||||
use crate::collection::{DataCollector, memory::MemData};
|
use crate::collection::{DataCollector, memory::MemData};
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
use crate::collection::linux::cgroups::CgroupMemLimit;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_usage(used: u64, total: u64) -> Option<MemData> {
|
fn get_usage(used: u64, total: u64) -> Option<MemData> {
|
||||||
NonZeroU64::new(total).map(|total_bytes| MemData {
|
NonZeroU64::new(total).map(|total_bytes| MemData {
|
||||||
@@ -12,6 +15,20 @@ fn get_usage(used: u64, total: u64) -> Option<MemData> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolves the total memory to report given an optional cgroup limit and the physical total.
|
||||||
|
///
|
||||||
|
/// cgroup v1 reports an "unlimited" limit as a very large value, which causes problems if taken
|
||||||
|
/// literally. This function caps it to the minimum of the cgroup total or the actual total to
|
||||||
|
/// avoid this problem.
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
#[inline]
|
||||||
|
fn resolve_cgroup_total(limit: Option<&CgroupMemLimit>, base_total: u64) -> u64 {
|
||||||
|
match limit {
|
||||||
|
Some(CgroupMemLimit::Bytes(bytes)) => (*bytes).min(base_total),
|
||||||
|
Some(CgroupMemLimit::Max) | None => base_total,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns memory (RAM) usage using sysinfo.
|
/// Returns memory (RAM) usage using sysinfo.
|
||||||
///
|
///
|
||||||
/// On Linux, this will take cgroup usage/limits into account.
|
/// On Linux, this will take cgroup usage/limits into account.
|
||||||
@@ -20,19 +37,13 @@ pub(crate) fn get_ram_usage(collector: &DataCollector) -> Option<MemData> {
|
|||||||
|
|
||||||
cfg_select! {
|
cfg_select! {
|
||||||
target_os = "linux" => {
|
target_os = "linux" => {
|
||||||
use crate::collection::linux::cgroups;
|
|
||||||
|
|
||||||
let base_used = sys.used_memory();
|
let base_used = sys.used_memory();
|
||||||
let base_total = sys.total_memory();
|
let base_total = sys.total_memory();
|
||||||
|
|
||||||
let (used, total) = match &collector.cgroup_memory_data.ram {
|
let (used, total) = match &collector.cgroup_memory_data.ram {
|
||||||
Some(cgroup_data) => {
|
Some(cgroup_data) => {
|
||||||
let used = cgroup_data.used_bytes;
|
let used = cgroup_data.used_bytes;
|
||||||
let total = match cgroup_data.limit {
|
let total = resolve_cgroup_total(cgroup_data.limit.as_ref(), base_total);
|
||||||
Some(cgroups::CgroupMemLimit::Bytes(bytes)) => bytes,
|
|
||||||
Some(cgroups::CgroupMemLimit::Max) => base_total,
|
|
||||||
None => base_total,
|
|
||||||
};
|
|
||||||
|
|
||||||
(used, total)
|
(used, total)
|
||||||
}
|
}
|
||||||
@@ -56,19 +67,13 @@ pub(crate) fn get_swap_usage(collector: &DataCollector) -> Option<MemData> {
|
|||||||
|
|
||||||
cfg_select! {
|
cfg_select! {
|
||||||
target_os = "linux" => {
|
target_os = "linux" => {
|
||||||
use crate::collection::linux::cgroups;
|
|
||||||
|
|
||||||
let base_used = sys.used_swap();
|
let base_used = sys.used_swap();
|
||||||
let base_total = sys.total_swap();
|
let base_total = sys.total_swap();
|
||||||
|
|
||||||
let (used, total) = match &collector.cgroup_memory_data.swap {
|
let (used, total) = match &collector.cgroup_memory_data.swap {
|
||||||
Some(cgroup_data) => {
|
Some(cgroup_data) => {
|
||||||
let used = cgroup_data.used_bytes;
|
let used = cgroup_data.used_bytes;
|
||||||
let total = match cgroup_data.limit {
|
let total = resolve_cgroup_total(cgroup_data.limit.as_ref(), base_total);
|
||||||
Some(cgroups::CgroupMemLimit::Bytes(bytes)) => bytes,
|
|
||||||
Some(cgroups::CgroupMemLimit::Max) => base_total,
|
|
||||||
None => base_total,
|
|
||||||
};
|
|
||||||
|
|
||||||
(used, total)
|
(used, total)
|
||||||
}
|
}
|
||||||
@@ -101,3 +106,39 @@ pub(crate) fn get_cache_usage(sys: &sysinfo::System) -> Option<MemData> {
|
|||||||
|
|
||||||
get_usage(mem_used, mem_total)
|
get_usage(mem_used, mem_total)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod linux_tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const BASE_TOTAL: u64 = 16 * 1024 * 1024 * 1024; // 16 GiB
|
||||||
|
|
||||||
|
/// Regression test for <https://github.com/ClementTsang/bottom/issues/2092>.
|
||||||
|
#[test]
|
||||||
|
fn cap_cgroup_v1_limits() {
|
||||||
|
let sentinel = u64::MAX;
|
||||||
|
assert_eq!(
|
||||||
|
resolve_cgroup_total(Some(&CgroupMemLimit::Bytes(sentinel)), BASE_TOTAL),
|
||||||
|
BASE_TOTAL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn legit_cgroup_limit_works() {
|
||||||
|
let limit = 4 * 1024 * 1024 * 1024; // 4 GiB
|
||||||
|
assert_eq!(
|
||||||
|
resolve_cgroup_total(Some(&CgroupMemLimit::Bytes(limit)), BASE_TOTAL),
|
||||||
|
limit
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn max_and_missing_limit_use_physical_total() {
|
||||||
|
assert_eq!(
|
||||||
|
resolve_cgroup_total(Some(&CgroupMemLimit::Max), BASE_TOTAL),
|
||||||
|
BASE_TOTAL
|
||||||
|
);
|
||||||
|
assert_eq!(resolve_cgroup_total(None, BASE_TOTAL), BASE_TOTAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user