Skip to content

Commit

Permalink
ksm reading capability
Browse files Browse the repository at this point in the history
Summary: adds reader for parsing the ksm files in `/sys/kernel/mm/ksm`

Reviewed By: lnyng

Differential Revision: D56961774

fbshipit-source-id: efc201d74b872c249d25dbe46bf6fd6762a9e10c
  • Loading branch information
inwardvessel authored and facebook-github-bot committed May 24, 2024
1 parent 8eb5492 commit 5dfb268
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 0 deletions.
2 changes: 2 additions & 0 deletions below/config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand All @@ -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,
}
Expand Down
8 changes: 8 additions & 0 deletions below/model/src/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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
//
Expand Down Expand Up @@ -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),
Expand Down
1 change: 1 addition & 0 deletions below/model/src/sample.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,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>,
Expand Down
76 changes: 76 additions & 0 deletions below/procfs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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";

Expand Down Expand Up @@ -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.
Expand Down
69 changes: 69 additions & 0 deletions below/procfs/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -578,6 +583,70 @@ fn test_read_slabinfo() {
assert_eq!(slabinfo, expected_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_unshared, Some(0));
assert_eq!(ksm.pages_volatile, Some(0));
assert_eq!(ksm.run, Some(1));
assert_eq!(ksm.sleep_millisecs, Some(20));
assert_eq!(ksm.smart_scan, Some(1));
assert_eq!(ksm.stable_node_chains, Some(1));
assert_eq!(ksm.stable_node_chains_prune_millisecs, Some(2000));
assert_eq!(ksm.stable_node_dups, Some(0));
assert_eq!(ksm.use_zero_pages, Some(0));
}

#[test]
fn test_disk_stat() {
let diskstats = b" 1 0 ram0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Expand Down
28 changes: 28 additions & 0 deletions below/procfs/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,34 @@ pub struct SlabInfo {
pub num_slabs: Option<u64>,
}

#[derive(Default, Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct Ksm {
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>,
}

#[derive(Default, Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct MountInfo {
pub mnt_id: Option<i32>,
Expand Down
2 changes: 2 additions & 0 deletions below/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1084,6 +1084,7 @@ fn record(
disable_disk_stat,
enable_btrfs_stats: below_config.enable_btrfs_stats,
enable_ethtool_stats: below_config.enable_ethtool_stats,
enable_ksm_stats: below_config.enable_ksm_stats,
enable_resctrl_stats: below_config.enable_resctrl_stats,
enable_tc_stats: below_config.enable_tc_stats,
btrfs_samples: below_config.btrfs_samples,
Expand Down Expand Up @@ -1215,6 +1216,7 @@ fn live_local(
exit_data: exit_buffer,
enable_btrfs_stats: below_config.enable_btrfs_stats,
enable_ethtool_stats: below_config.enable_ethtool_stats,
enable_ksm_stats: below_config.enable_ksm_stats,
enable_resctrl_stats: below_config.enable_resctrl_stats,
btrfs_samples: below_config.btrfs_samples,
btrfs_min_pct: below_config.btrfs_min_pct,
Expand Down

0 comments on commit 5dfb268

Please sign in to comment.