mirror of
https://github.com/ClementTsang/bottom.git
synced 2026-09-13 06:05:44 +00:00
bug: fix memory collection using cgroup v1 sentinel max as total (#2093)
Fixes a bug with cgroups v1, where the "total" if there isn't a limit set may be some crazy high number which would be interpreted as many terabytes large. This is obviously incorrect, so we should not use that as the total, and instead, use the min of the actual total and the cgroups value (so if a lower cgroup v1 value is set, we use that instead).
This commit is contained in:
@@ -4,6 +4,9 @@ use std::num::NonZeroU64;
|
||||
|
||||
use crate::collection::{DataCollector, memory::MemData};
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use crate::collection::linux::cgroups::CgroupMemLimit;
|
||||
|
||||
#[inline]
|
||||
fn get_usage(used: u64, total: u64) -> Option<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.
|
||||
///
|
||||
/// 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! {
|
||||
target_os = "linux" => {
|
||||
use crate::collection::linux::cgroups;
|
||||
|
||||
let base_used = sys.used_memory();
|
||||
let base_total = sys.total_memory();
|
||||
|
||||
let (used, total) = match &collector.cgroup_memory_data.ram {
|
||||
Some(cgroup_data) => {
|
||||
let used = cgroup_data.used_bytes;
|
||||
let total = match cgroup_data.limit {
|
||||
Some(cgroups::CgroupMemLimit::Bytes(bytes)) => bytes,
|
||||
Some(cgroups::CgroupMemLimit::Max) => base_total,
|
||||
None => base_total,
|
||||
};
|
||||
let total = resolve_cgroup_total(cgroup_data.limit.as_ref(), base_total);
|
||||
|
||||
(used, total)
|
||||
}
|
||||
@@ -56,19 +67,13 @@ pub(crate) fn get_swap_usage(collector: &DataCollector) -> Option<MemData> {
|
||||
|
||||
cfg_select! {
|
||||
target_os = "linux" => {
|
||||
use crate::collection::linux::cgroups;
|
||||
|
||||
let base_used = sys.used_swap();
|
||||
let base_total = sys.total_swap();
|
||||
|
||||
let (used, total) = match &collector.cgroup_memory_data.swap {
|
||||
Some(cgroup_data) => {
|
||||
let used = cgroup_data.used_bytes;
|
||||
let total = match cgroup_data.limit {
|
||||
Some(cgroups::CgroupMemLimit::Bytes(bytes)) => bytes,
|
||||
Some(cgroups::CgroupMemLimit::Max) => base_total,
|
||||
None => base_total,
|
||||
};
|
||||
let total = resolve_cgroup_total(cgroup_data.limit.as_ref(), base_total);
|
||||
|
||||
(used, total)
|
||||
}
|
||||
@@ -101,3 +106,39 @@ pub(crate) fn get_cache_usage(sys: &sysinfo::System) -> Option<MemData> {
|
||||
|
||||
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