summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--below/Cargo.toml6
-rw-r--r--below/below_derive/Cargo.toml2
-rw-r--r--below/btrfs/Cargo.toml2
-rw-r--r--below/cgroupfs/Cargo.toml2
-rw-r--r--below/cgroupfs/src/lib.rs12
-rw-r--r--below/cgroupfs/src/types.rs9
-rw-r--r--below/common/Cargo.toml2
-rw-r--r--below/config/Cargo.toml2
-rw-r--r--below/config/src/lib.rs2
-rw-r--r--below/dump/Cargo.toml2
-rw-r--r--below/dump/src/test.rs9
-rw-r--r--below/ethtool/Cargo.toml4
-rw-r--r--below/gpu_stats/Cargo.toml2
-rw-r--r--below/model/Cargo.toml2
-rw-r--r--below/model/src/cgroup.rs17
-rw-r--r--below/model/src/collector.rs9
-rw-r--r--below/model/src/common_field_ids.rs31
-rw-r--r--below/model/src/sample.rs2
-rw-r--r--below/model/src/sample_model.rs26
-rw-r--r--below/model/src/system.rs65
-rw-r--r--below/procfs/Cargo.toml2
-rw-r--r--below/procfs/src/lib.rs76
-rw-r--r--below/procfs/src/test.rs69
-rw-r--r--below/procfs/src/types.rs28
-rw-r--r--below/render/Cargo.toml2
-rw-r--r--below/render/src/default_configs.rs81
-rw-r--r--below/resctrlfs/Cargo.toml2
-rw-r--r--below/src/main.rs2
-rw-r--r--below/store/Cargo.toml2
-rw-r--r--below/tc/Cargo.toml2
-rw-r--r--below/view/Cargo.toml2
31 files changed, 456 insertions, 20 deletions
diff --git a/below/Cargo.toml b/below/Cargo.toml
index 8ecfe567..b8634b92 100644
--- a/below/Cargo.toml
+++ b/below/Cargo.toml
@@ -3,7 +3,7 @@
[package]
name = "below"
version = "0.8.1"
-authors = ["Daniel Xu <dlxu@fb.com>", "Facebook"]
+authors = ["Meta Platforms, Inc. and affiliates"]
edition = "2021"
description = "An interactive tool to view and record historical system data"
readme = "../README.md"
@@ -29,7 +29,7 @@ config = { package = "below-config", version = "0.8.1", path = "config" }
cursive = { version = "0.20.0", features = ["crossterm-backend"], default-features = false }
dump = { package = "below-dump", version = "0.8.1", path = "dump" }
indicatif = { version = "0.17.6", features = ["improved_unicode", "rayon", "tokio"] }
-libbpf-rs = { version = "0.23", default-features = false }
+libbpf-rs = { version = "0.23.1", default-features = false }
libc = "0.2.139"
model = { package = "below-model", version = "0.8.1", path = "model" }
once_cell = "1.12"
@@ -52,7 +52,7 @@ maplit = "1.0"
portpicker = "0.1.1"
[build-dependencies]
-libbpf-cargo = { version = "0.23", default-features = false }
+libbpf-cargo = { version = "0.23.1", default-features = false }
[features]
default = ["libbpf-cargo/default", "libbpf-rs/default"]
diff --git a/below/below_derive/Cargo.toml b/below/below_derive/Cargo.toml
index dae99d08..59e4c791 100644
--- a/below/below_derive/Cargo.toml
+++ b/below/below_derive/Cargo.toml
@@ -3,7 +3,7 @@
[package]
name = "below_derive"
version = "0.8.1"
-authors = ["Daniel Xu <dlxu@fb.com>", "Facebook"]
+authors = ["Meta Platforms, Inc. and affiliates"]
edition = "2021"
description = "Proc macros for below"
repository = "https://github.com/facebookincubator/below"
diff --git a/below/btrfs/Cargo.toml b/below/btrfs/Cargo.toml
index 90f2ad6f..329f511e 100644
--- a/below/btrfs/Cargo.toml
+++ b/below/btrfs/Cargo.toml
@@ -3,7 +3,7 @@
[package]
name = "below-btrfs"
version = "0.8.1"
-authors = ["Daniel Xu <dlxu@fb.com>", "Facebook"]
+authors = ["Meta Platforms, Inc. and affiliates"]
edition = "2021"
description = "A crate for reading btrfs"
readme = "README"
diff --git a/below/cgroupfs/Cargo.toml b/below/cgroupfs/Cargo.toml
index 2a2308e8..67745de5 100644
--- a/below/cgroupfs/Cargo.toml
+++ b/below/cgroupfs/Cargo.toml
@@ -3,7 +3,7 @@
[package]
name = "cgroupfs"
version = "0.8.1"
-authors = ["Daniel Xu <dlxu@fb.com>", "Facebook"]
+authors = ["Meta Platforms, Inc. and affiliates"]
edition = "2021"
description = "A crate for reading cgroupv2 data"
readme = "README"
diff --git a/below/cgroupfs/src/lib.rs b/below/cgroupfs/src/lib.rs
index 2688bdc7..5a2430ed 100644
--- a/below/cgroupfs/src/lib.rs
+++ b/below/cgroupfs/src/lib.rs
@@ -428,6 +428,10 @@ impl CgroupReader {
MemoryEvents::read(self)
}
+ pub fn read_memory_events_local(&self) -> Result<MemoryEventsLocal> {
+ MemoryEventsLocal::read(self)
+ }
+
pub fn read_cgroup_stat(&self) -> Result<CgroupStat> {
CgroupStat::read(self)
}
@@ -713,6 +717,14 @@ key_values_format!(MemoryEvents; memory.events; [
oom_kill
]);
+key_values_format!(MemoryEventsLocal; memory.events.local; [
+ low,
+ high,
+ max,
+ oom,
+ oom_kill
+]);
+
key_values_format!(CgroupStat; cgroup.stat; [nr_descendants, nr_dying_descendants]);
// Trait to add a read() method for `<string> key=value` formatted files
diff --git a/below/cgroupfs/src/types.rs b/below/cgroupfs/src/types.rs
index 764fc49d..d69e11e2 100644
--- a/below/cgroupfs/src/types.rs
+++ b/below/cgroupfs/src/types.rs
@@ -133,6 +133,15 @@ pub struct MemoryEvents {
}
#[derive(Default, Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
+pub struct MemoryEventsLocal {
+ pub low: Option<u64>,
+ pub high: Option<u64>,
+ pub max: Option<u64>,
+ pub oom: Option<u64>,
+ pub oom_kill: Option<u64>,
+}
+
+#[derive(Default, Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
pub struct CgroupStat {
pub nr_descendants: Option<u32>,
pub nr_dying_descendants: Option<u32>,
diff --git a/below/common/Cargo.toml b/below/common/Cargo.toml
index bf4c6c55..96c0f43d 100644
--- a/below/common/Cargo.toml
+++ b/below/common/Cargo.toml
@@ -3,7 +3,7 @@
[package]
name = "below-common"
version = "0.8.1"
-authors = ["Daniel Xu <dlxu@fb.com>", "Facebook"]
+authors = ["Meta Platforms, Inc. and affiliates"]
edition = "2021"
description = "Common below code"
repository = "https://github.com/facebookincubator/below"
diff --git a/below/config/Cargo.toml b/below/config/Cargo.toml
index 386c4a4e..3bc0c9a1 100644
--- a/below/config/Cargo.toml
+++ b/below/config/Cargo.toml
@@ -3,7 +3,7 @@
[package]
name = "below-config"
version = "0.8.1"
-authors = ["Daniel Xu <dlxu@fb.com>", "Facebook"]
+authors = ["Meta Platforms, Inc. and affiliates"]
edition = "2021"
description = "Configurations for below"
repository = "https://github.com/facebookincubator/below"
diff --git a/below/config/src/lib.rs b/below/config/src/lib.rs
index 1f82e9eb..d26d6014 100644
--- a/below/config/src/lib.rs
+++ b/below/config/src/lib.rs
@@ -46,6 +46,7 @@ pub struct BelowConfig {
pub btrfs_samples: u64,
pub btrfs_min_pct: f64,
pub enable_ethtool_stats: bool,
+ pub enable_ksm_stats: bool,
pub enable_resctrl_stats: bool,
pub enable_tc_stats: bool,
}
@@ -63,6 +64,7 @@ impl Default for BelowConfig {
btrfs_samples: btrfs::DEFAULT_SAMPLES,
btrfs_min_pct: btrfs::DEFAULT_MIN_PCT,
enable_ethtool_stats: false,
+ enable_ksm_stats: false,
enable_resctrl_stats: false,
enable_tc_stats: false,
}
diff --git a/below/dump/Cargo.toml b/below/dump/Cargo.toml
index 3ac4c2c0..a729f16e 100644
--- a/below/dump/Cargo.toml
+++ b/below/dump/Cargo.toml
@@ -3,7 +3,7 @@
[package]
name = "below-dump"
version = "0.8.1"
-authors = ["Daniel Xu <dlxu@fb.com>", "Facebook"]
+authors = ["Meta Platforms, Inc. and affiliates"]
edition = "2021"
description = "Dump crate for below"
repository = "https://github.com/facebookincubator/below"
diff --git a/below/dump/src/test.rs b/below/dump/src/test.rs
index 4a0d53f5..765e1f91 100644
--- a/below/dump/src/test.rs
+++ b/below/dump/src/test.rs
@@ -534,6 +534,11 @@ fn test_dump_cgroup_titles() {
"Events Max",
"Events OOM",
"Events Kill",
+ "Events Local Low",
+ "Events Local High",
+ "Events Local Max",
+ "Events Local OOM",
+ "Events Local Kill",
"RBytes",
"WBytes",
"R I/O",
@@ -999,6 +1004,10 @@ fn test_dump_queue_content() {
hostname: "h".to_string(),
};
+ // we are dumping timestamps assuming they are local time
+ // so the timezone needs to be set to the expected TZ
+ std::env::set_var("TZ", "US/Pacific");
+
let result = queue_dumper
.dump_model(&ctx, &model, &mut queue_content, &mut round, false)
.expect("Failed to dump queue model");
diff --git a/below/ethtool/Cargo.toml b/below/ethtool/Cargo.toml
index 3a1053d4..74f6bbe2 100644
--- a/below/ethtool/Cargo.toml
+++ b/below/ethtool/Cargo.toml
@@ -3,9 +3,9 @@
[package]
name = "below-ethtool"
version = "0.8.1"
-authors = ["Daniel Xu <dlxu@fb.com>", "Facebook"]
+authors = ["Meta Platforms, Inc. and affiliates"]
edition = "2021"
-description = "Model crate for below"
+description = "ethtool crate for below"
repository = "https://github.com/facebookincubator/below"
license = "Apache-2.0"
diff --git a/below/gpu_stats/Cargo.toml b/below/gpu_stats/Cargo.toml
index 482f19c1..9ce20b60 100644
--- a/below/gpu_stats/Cargo.toml
+++ b/below/gpu_stats/Cargo.toml
@@ -3,7 +3,7 @@
[package]
name = "below-gpu-stats"
version = "0.8.1"
-authors = ["Daniel Xu <dlxu@fb.com>", "Facebook"]
+authors = ["Meta Platforms, Inc. and affiliates"]
edition = "2021"
description = "GPU stats crate for below"
repository = "https://github.com/facebookincubator/below"
diff --git a/below/model/Cargo.toml b/below/model/Cargo.toml
index d1c1226a..46522df2 100644
--- a/below/model/Cargo.toml
+++ b/below/model/Cargo.toml
@@ -3,7 +3,7 @@
[package]
name = "below-model"
version = "0.8.1"
-authors = ["Daniel Xu <dlxu@fb.com>", "Facebook"]
+authors = ["Meta Platforms, Inc. and affiliates"]
edition = "2021"
description = "Model crate for below"
repository = "https://github.com/facebookincubator/below"
diff --git a/below/model/src/cgroup.rs b/below/model/src/cgroup.rs
index b94f3626..d8d20e8a 100644
--- a/below/model/src/cgroup.rs
+++ b/below/model/src/cgroup.rs
@@ -460,6 +460,11 @@ pub struct CgroupMemoryModel {
pub events_max: Option<u64>,
pub events_oom: Option<u64>,
pub events_oom_kill: Option<u64>,
+ pub events_local_low: Option<u64>,
+ pub events_local_high: Option<u64>,
+ pub events_local_max: Option<u64>,
+ pub events_local_oom: Option<u64>,
+ pub events_local_oom_kill: Option<u64>,
}
impl std::ops::Add for CgroupMemoryModel {
@@ -533,6 +538,11 @@ impl std::ops::Add for CgroupMemoryModel {
events_max: opt_add(self.events_max, other.events_max),
events_oom: opt_add(self.events_oom, other.events_oom),
events_oom_kill: opt_add(self.events_oom_kill, other.events_oom_kill),
+ events_local_low: opt_add(self.events_local_low, other.events_local_low),
+ events_local_high: opt_add(self.events_local_high, other.events_local_high),
+ events_local_max: opt_add(self.events_local_max, other.events_local_max),
+ events_local_oom: opt_add(self.events_local_oom, other.events_local_oom),
+ events_local_oom_kill: opt_add(self.events_local_oom_kill, other.events_local_oom_kill),
}
}
}
@@ -555,6 +565,13 @@ impl CgroupMemoryModel {
model.events_oom = events.oom;
model.events_oom_kill = events.oom_kill;
}
+ if let Some(events_local) = &sample.memory_events_local {
+ model.events_local_low = events_local.low;
+ model.events_local_high = events_local.high;
+ model.events_local_max = events_local.max;
+ model.events_local_oom = events_local.oom;
+ model.events_local_oom_kill = events_local.oom_kill;
+ }
if let Some(stat) = &sample.memory_stat {
model.anon = stat.anon;
model.file = stat.file;
diff --git a/below/model/src/collector.rs b/below/model/src/collector.rs
index bf577f09..8e93310f 100644
--- a/below/model/src/collector.rs
+++ b/below/model/src/collector.rs
@@ -30,6 +30,7 @@ pub struct CollectorOptions {
pub disable_disk_stat: bool,
pub enable_btrfs_stats: bool,
pub enable_ethtool_stats: bool,
+ pub enable_ksm_stats: bool,
pub enable_resctrl_stats: bool,
pub enable_tc_stats: bool,
pub btrfs_samples: u64,
@@ -50,6 +51,7 @@ impl Default for CollectorOptions {
disable_disk_stat: false,
enable_btrfs_stats: false,
enable_ethtool_stats: false,
+ enable_ksm_stats: false,
enable_resctrl_stats: false,
enable_tc_stats: false,
btrfs_samples: btrfs::DEFAULT_SAMPLES,
@@ -186,6 +188,7 @@ fn collect_sample(
let btrfs_reader =
btrfs::BtrfsReader::new(options.btrfs_samples, options.btrfs_min_pct, logger.clone());
let ethtool_reader = ethtool::EthtoolReader::new();
+ let ksm_reader = procfs::KsmReader::new();
// Take mutex, then take all values out of shared map and replace with default map
//
@@ -217,6 +220,11 @@ fn collect_sample(
meminfo: reader.read_meminfo()?,
vmstat: reader.read_vmstat()?,
slabinfo: reader.read_slabinfo().unwrap_or_default(),
+ ksm: if !options.enable_ksm_stats {
+ None
+ } else {
+ Some(ksm_reader.read_ksm())
+ },
hostname: get_hostname()?,
kernel_version: match reader.read_kernel_version() {
Ok(k) => Some(k),
@@ -440,6 +448,7 @@ fn collect_cgroup_sample(
memory_swap_max: wrap(reader.read_memory_swap_max())?,
memory_zswap_max: wrap(reader.read_memory_zswap_max())?,
memory_events: wrap(reader.read_memory_events())?.map(Into::into),
+ memory_events_local: wrap(reader.read_memory_events_local())?.map(Into::into),
inode_number: match reader.read_inode_number() {
Ok(st_ino) => Some(st_ino as i64),
Err(e) => {
diff --git a/below/model/src/common_field_ids.rs b/below/model/src/common_field_ids.rs
index 3b752298..6659fdc5 100644
--- a/below/model/src/common_field_ids.rs
+++ b/below/model/src/common_field_ids.rs
@@ -23,7 +23,7 @@
///
/// This list also servers as documentation for available field ids that could
/// be used in other below crates. A test ensures that this list is up-to-date.
-pub const COMMON_MODEL_FIELD_IDS: [&str; 449] = [
+pub const COMMON_MODEL_FIELD_IDS: [&str; 478] = [
"system.hostname",
"system.kernel_version",
"system.os_release",
@@ -113,6 +113,30 @@ pub const COMMON_MODEL_FIELD_IDS: [&str; 449] = [
"system.slab.<key>.num_caches",
"system.slab.<key>.active_size",
"system.slab.<key>.total_size",
+ "system.ksm.advisor_max_cpu",
+ "system.ksm.advisor_max_pages_to_scan",
+ "system.ksm.advisor_min_pages_to_scan",
+ "system.ksm.advisor_mode",
+ "system.ksm.advisor_target_scan_time",
+ "system.ksm.full_scans",
+ "system.ksm.general_profit",
+ "system.ksm.ksm_zero_pages",
+ "system.ksm.max_page_sharing",
+ "system.ksm.merge_across_nodes",
+ "system.ksm.pages_scanned",
+ "system.ksm.pages_shared",
+ "system.ksm.pages_sharing",
+ "system.ksm.pages_skipped",
+ "system.ksm.pages_to_scan",
+ "system.ksm.pages_unshared",
+ "system.ksm.pages_volatile",
+ "system.ksm.run",
+ "system.ksm.sleep_millisecs",
+ "system.ksm.smart_scan",
+ "system.ksm.stable_node_chains",
+ "system.ksm.stable_node_chains_prune_millisecs",
+ "system.ksm.stable_node_dups",
+ "system.ksm.use_zero_pages",
"system.disks.<key>.name",
"system.disks.<key>.disk_usage",
"system.disks.<key>.partition_size",
@@ -209,6 +233,11 @@ pub const COMMON_MODEL_FIELD_IDS: [&str; 449] = [
"cgroup.[path:/<cgroup_path>/.]mem.events_max",
"cgroup.[path:/<cgroup_path>/.]mem.events_oom",
"cgroup.[path:/<cgroup_path>/.]mem.events_oom_kill",
+ "cgroup.[path:/<cgroup_path>/.]mem.events_local_low",
+ "cgroup.[path:/<cgroup_path>/.]mem.events_local_high",
+ "cgroup.[path:/<cgroup_path>/.]mem.events_local_max",
+ "cgroup.[path:/<cgroup_path>/.]mem.events_local_oom",
+ "cgroup.[path:/<cgroup_path>/.]mem.events_local_oom_kill",
"cgroup.[path:/<cgroup_path>/.]io_details.<key>.rbytes_per_sec",
"cgroup.[path:/<cgroup_path>/.]io_details.<key>.wbytes_per_sec",
"cgroup.[path:/<cgroup_path>/.]io_details.<key>.rios_per_sec",
diff --git a/below/model/src/sample.rs b/below/model/src/sample.rs
index 61c33dde..f768f78b 100644
--- a/below/model/src/sample.rs
+++ b/below/model/src/sample.rs
@@ -45,6 +45,7 @@ pub struct CgroupSample {
pub memory_swap_max: Option<i64>,
pub memory_zswap_max: Option<i64>,
pub memory_events: Option<cgroupfs::MemoryEvents>,
+ pub memory_events_local: Option<cgroupfs::MemoryEventsLocal>,
pub inode_number: Option<i64>,
pub cgroup_stat: Option<cgroupfs::CgroupStat>,
pub memory_numa_stat: Option<BTreeMap<u32, cgroupfs::MemoryNumaStat>>,
@@ -65,6 +66,7 @@ pub struct SystemSample {
pub vmstat: procfs::VmStat,
#[serde(default)]
pub slabinfo: procfs::SlabInfoMap,
+ pub ksm: Option<procfs::Ksm>,
pub hostname: String,
pub disks: procfs::DiskMap,
pub btrfs: Option<btrfs::BtrfsMap>,
diff --git a/below/model/src/sample_model.rs b/below/model/src/sample_model.rs
index 3c026c31..69b9dc7b 100644
--- a/below/model/src/sample_model.rs
+++ b/below/model/src/sample_model.rs
@@ -149,6 +149,32 @@ pub const SAMPLE_MODEL_JSON: &str = r#"
"num_slabs": 100000
}
},
+ "ksm": {
+ "advisor_max_cpu": 70,
+ "advisor_max_pages_to_scan": 30000,
+ "advisor_min_pages_to_scan": 500,
+ "advisor_mode": "scan-time",
+ "advisor_target_scan_time": 200,
+ "full_scans": 25,
+ "general_profit": 0,
+ "ksm_zero_pages": 0,
+ "max_page_sharing": 256,
+ "merge_across_nodes": 1,
+ "pages_scanned": 5149,
+ "pages_shared": 0,
+ "pages_sharing": 0,
+ "pages_skipped": 25,
+ "pages_to_scan": 100,
+ "pages_unshared": 0,
+ "pages_volatile": 0,
+ "run": 1,
+ "sleep_millisecs": 20,
+ "smart_scan": 1,
+ "stable_node_chains": 1,
+ "stable_node_chains_prune_millisecs": 2000,
+ "stable_node_dups": 0,
+ "use_zero_pages": 0
+ },
"disks": {
"vda": {
"name": "vda",
diff --git a/below/model/src/system.rs b/below/model/src/system.rs
index 4d783f9f..635a8e0c 100644
--- a/below/model/src/system.rs
+++ b/below/model/src/system.rs
@@ -33,6 +33,8 @@ pub struct SystemModel {
#[queriable(subquery)]
pub slab: BTreeMap<String, SingleSlabModel>,
#[queriable(subquery)]
+ pub ksm: Option<KsmModel>,
+ #[queriable(subquery)]
pub disks: BTreeMap<String, SingleDiskModel>,
#[queriable(subquery)]
pub btrfs: Option<BTreeMap<String, BtrfsModel>>,
@@ -97,6 +99,8 @@ impl SystemModel {
);
slab.insert(String::from("TOTAL"), slab_total);
+ let ksm = sample.ksm.as_ref().map(KsmModel::new);
+
let mut disks: BTreeMap<String, SingleDiskModel> = BTreeMap::new();
sample.disks.iter().for_each(|(disk_name, end_disk_stat)| {
disks.insert(
@@ -141,6 +145,7 @@ impl SystemModel {
mem,
vm,
slab,
+ ksm,
disks,
btrfs,
}
@@ -409,6 +414,65 @@ impl SingleSlabModel {
}
#[::below_derive::queriable_derives]
+pub struct KsmModel {
+ pub advisor_max_cpu: Option<u64>,
+ pub advisor_max_pages_to_scan: Option<u64>,
+ pub advisor_min_pages_to_scan: Option<u64>,
+ pub advisor_mode: Option<String>,
+ pub advisor_target_scan_time: Option<u64>,
+ pub full_scans: Option<u64>,
+ pub general_profit: Option<i64>,
+ pub ksm_zero_pages: Option<i64>,
+ pub max_page_sharing: Option<u64>,
+ pub merge_across_nodes: Option<u64>,
+ pub pages_scanned: Option<u64>,
+ pub pages_shared: Option<u64>,
+ pub pages_sharing: Option<u64>,
+ pub pages_skipped: Option<u64>,
+ pub pages_to_scan: Option<u64>,
+ pub pages_unshared: Option<u64>,
+ pub pages_volatile: Option<u64>,
+ pub run: Option<u64>,
+ pub sleep_millisecs: Option<u64>,
+ pub smart_scan: Option<u64>,
+ pub stable_node_chains: Option<u64>,
+ pub stable_node_chains_prune_millisecs: Option<u64>,
+ pub stable_node_dups: Option<u64>,
+ pub use_zero_pages: Option<u64>,
+}
+
+impl KsmModel {
+ fn new(ksm: &procfs::Ksm) -> KsmModel {
+ KsmModel {
+ advisor_max_cpu: ksm.advisor_max_cpu,
+ advisor_max_pages_to_scan: ksm.advisor_max_pages_to_scan,
+ advisor_min_pages_to_scan: ksm.advisor_min_pages_to_scan,
+ advisor_mode: ksm.advisor_mode.clone(),
+ advisor_target_scan_time: ksm.advisor_target_scan_time,
+ full_scans: ksm.full_scans,
+ general_profit: ksm.general_profit,
+ ksm_zero_pages: ksm.ksm_zero_pages,
+ max_page_sharing: ksm.max_page_sharing,
+ merge_across_nodes: ksm.merge_across_nodes,
+ pages_scanned: ksm.pages_scanned,
+ pages_shared: ksm.pages_shared,
+ pages_sharing: ksm.pages_sharing,
+ pages_skipped: ksm.pages_skipped,
+ pages_to_scan: ksm.pages_to_scan,
+ pages_unshared: ksm.pages_unshared,
+ pages_volatile: ksm.pages_volatile,
+ run: ksm.run,
+ sleep_millisecs: ksm.sleep_millisecs,
+ smart_scan: ksm.smart_scan,
+ stable_node_chains: ksm.stable_node_chains,
+ stable_node_chains_prune_millisecs: ksm.stable_node_chains_prune_millisecs,
+ stable_node_dups: ksm.stable_node_dups,
+ use_zero_pages: ksm.use_zero_pages,
+ }
+ }
+}
+
+#[::below_derive::queriable_derives]
pub struct SingleDiskModel {
pub name: Option<String>,
pub disk_usage: Option<f32>,
@@ -528,6 +592,7 @@ mod test {
"mem": {},
"vm": {},
"slab": {},
+ "ksm": {},
"disks": {
"sda": {
"name": "sda",
diff --git a/below/procfs/Cargo.toml b/below/procfs/Cargo.toml
index 33a8f3fa..7cfe4605 100644
--- a/below/procfs/Cargo.toml
+++ b/below/procfs/Cargo.toml
@@ -3,7 +3,7 @@
[package]
name = "fb_procfs"
version = "0.8.1"
-authors = ["Daniel Xu <dlxu@fb.com>", "Facebook"]
+authors = ["Meta Platforms, Inc. and affiliates"]
edition = "2021"
description = "A crate for reading procfs"
readme = "README"
diff --git a/below/procfs/src/lib.rs b/below/procfs/src/lib.rs
index bc614ab4..8272125c 100644
--- a/below/procfs/src/lib.rs
+++ b/below/procfs/src/lib.rs
@@ -22,6 +22,7 @@ use std::io::ErrorKind;
use std::io::Read;
use std::path::Path;
use std::path::PathBuf;
+use std::str::FromStr;
use std::sync::mpsc::channel;
use std::sync::mpsc::RecvTimeoutError;
use std::time::Duration;
@@ -39,6 +40,7 @@ pub use types::*;
#[cfg(test)]
mod test;
+pub const KSM_SYSFS: &str = "/sys/kernel/mm/ksm";
pub const NET_SYSFS: &str = "/sys/class/net/";
pub const NET_PROCFS: &str = "/proc/net";
@@ -1363,6 +1365,80 @@ impl NetReader {
}
}
+pub struct KsmReader {
+ path: PathBuf,
+}
+
+impl Default for KsmReader {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl KsmReader {
+ pub fn new() -> KsmReader {
+ KsmReader {
+ path: Path::new(KSM_SYSFS).to_path_buf(),
+ }
+ }
+
+ pub fn new_with_custom_path(path: PathBuf) -> KsmReader {
+ KsmReader { path }
+ }
+
+ pub fn read_ksm(&self) -> Ksm {
+ Ksm {
+ advisor_max_cpu: self.read("advisor_max_cpu"),
+ advisor_max_pages_to_scan: self.read("advisor_max_pages_to_scan"),
+ advisor_min_pages_to_scan: self.read("advisor_min_pages_to_scan"),
+ advisor_mode: self.read_selection("advisor_mode"),
+ advisor_target_scan_time: self.read("advisor_target_scan_time"),
+ full_scans: self.read("full_scans"),
+ general_profit: self.read("general_profit"),
+ ksm_zero_pages: self.read("ksm_zero_pages"),
+ max_page_sharing: self.read("max_page_sharing"),
+ merge_across_nodes: self.read("merge_across_nodes"),
+ pages_scanned: self.read("pages_scanned"),
+ pages_shared: self.read("pages_shared"),
+ pages_sharing: self.read("pages_sharing"),
+ pages_skipped: self.read("pages_skipped"),
+ pages_to_scan: self.read("pages_to_scan"),
+ pages_unshared: self.read("pages_unshared"),
+ pages_volatile: self.read("pages_volatile"),
+ run: self.read("run"),
+ sleep_millisecs: self.read("sleep_millisecs"),
+ smart_scan: self.read("smart_scan"),
+ stable_node_chains: self.read("stable_node_chains"),
+ stable_node_chains_prune_millisecs: self.read("stable_node_chains_prune_millisecs"),
+ stable_node_dups: self.read("stable_node_dups"),
+ use_zero_pages: self.read("use_zero_pages"),
+ }
+ }
+
+ fn read<F>(&self, name: &str) -> Option<F>
+ where
+ F: FromStr,
+ {
+ std::fs::read_to_string(self.path.join(name))
+ .ok()?
+ .trim()
+ .parse()
+ .ok()
+ }
+
+ // parses from string representing one selection out of some choices
+ // i.e. "one [two] three" -> returns "two"
+ fn read_selection(&self, name: &str) -> Option<String> {
+ let val: String = self.read(name)?;
+ let left_bracket_idx = val.find('[')?;
+ let right_bracket_idx = val.rfind(']')?;
+ if left_bracket_idx >= right_bracket_idx {
+ return None;
+ }
+ Some(val[left_bracket_idx + 1..right_bracket_idx].to_string())
+ }
+}
+
/// Wraps the result into an `Option` if the result is not an error.
/// If the error is of type `ENOENT`, it is returned as `Ok(None)`.
/// Else, the error itself is returned.
diff --git a/below/procfs/src/test.rs b/below/procfs/src/test.rs
index 301084db..7fdf1888 100644
--- a/below/procfs/src/test.rs
+++ b/below/procfs/src/test.rs
@@ -21,6 +21,7 @@ use slog::Drain;
use tempfile::TempDir;
use crate::types::*;
+use crate::KsmReader;
use crate::NetReader;
use crate::ProcReader;
use crate::PAGE_SIZE;
@@ -44,6 +45,10 @@ impl TestProcfs {
ProcReader::new_with_custom_procfs(self.path().to_path_buf())
}
+ fn get_ksm_reader(&self) -> KsmReader {
+ KsmReader::new_with_custom_path(self.path().to_path_buf())
+ }
+
fn create_dir<P: AsRef<Path>>(&self, p: P) {
let path = self.path().join(p);
std::fs::create_dir_all(&path)
@@ -579,6 +584,70 @@ fn test_read_slabinfo() {
}
#[test]
+fn test_ksm() {
+ let ksm_inputs = std::collections::BTreeMap::from([
+ ("advisor_max_cpu", "70"),
+ ("advisor_max_pages_to_scan", "30000"),
+ ("advisor_min_pages_to_scan", "500"),
+ ("advisor_mode", "none [scan-time]"),
+ ("advisor_target_scan_time", "200"),
+ ("full_scans", "25"),
+ ("general_profit", "0"),
+ ("ksm_zero_pages", "0"),
+ ("max_page_sharing", "256"),
+ ("merge_across_nodes", "1"),
+ ("pages_scanned", "5149"),
+ ("pages_shared", "0"),
+ ("pages_sharing", "0"),
+ ("pages_skipped", "25"),
+ ("pages_to_scan", "100"),
+ ("pages_unshared", "0"),
+ ("pages_volatile", "0"),
+ ("run", "1"),
+ ("sleep_millisecs", "20"),
+ ("smart_scan", "1"),
+ ("stable_node_chains", "1"),
+ ("stable_node_chains_prune_millisecs", "2000"),
+ ("stable_node_dups", "0"),
+ ("use_zero_pages", "0"),
+ ]);
+
+ let procfs = TestProcfs::new();
+
+ for (key, val) in ksm_inputs.iter() {
+ procfs.create_file_with_content(key, val.as_bytes());
+ }
+
+ let reader = procfs.get_ksm_reader();
+ let ksm = reader.read_ksm();
+
+ assert_eq!(ksm.advisor_max_cpu, Some(70));
+ assert_eq!(ksm.advisor_max_pages_to_scan, Some(30000));
+ assert_eq!(ksm.advisor_min_pages_to_scan, Some(500));
+ assert_eq!(ksm.advisor_mode, Some(String::from("scan-time")));
+ assert_eq!(ksm.advisor_target_scan_time, Some(200));
+ assert_eq!(ksm.full_scans, Some(25));
+ assert_eq!(ksm.general_profit, Some(0));
+ assert_eq!(ksm.ksm_zero_pages, Some(0));
+ assert_eq!(ksm.max_page_sharing, Some(256));
+ assert_eq!(ksm.merge_across_nodes, Some(1));
+ assert_eq!(ksm.pages_scanned, Some(5149));
+ assert_eq!(ksm.pages_shared, Some(0));
+ assert_eq!(ksm.pages_sharing, Some(0));
+ assert_eq!(ksm.pages_skipped, Some(25));
+ assert_eq!(ksm.pages_to_scan, Some(100));
+ assert_eq!(ksm.pages_unshare