summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLeon Yang (Containers) <lnyng@meta.com>2024-01-25 19:36:06 -0800
committerFacebook GitHub Bot <facebook-github-bot@users.noreply.github.com>2024-01-25 19:36:06 -0800
commit3f5934b0d5428d1482bf7182aef3ecaf3cc22c96 (patch)
tree6135ac36bbbf94a3cf937c16e1e12f2d877375e3
parenta1395ebb09e3f647a7c7eedc6f2296b1e22c75a1 (diff)
Add /proc/slabinfo support
Summary: Below starts to collect /proc/slabinfo, store it, and display it in UI. Also supported by dump and other commands. Reviewed By: brianc118 Differential Revision: D53074160 fbshipit-source-id: 878b5c59bb957da65d920301bfe33fc08b2678b0
-rw-r--r--below/model/src/collector.rs18
-rw-r--r--below/model/src/common_field_ids.rs8
-rw-r--r--below/model/src/sample.rs1
-rw-r--r--below/model/src/sample_model.rs18
-rw-r--r--below/model/src/system.rs42
-rw-r--r--below/render/src/default_configs.rs36
-rw-r--r--below/view/src/core_tabs.rs47
-rw-r--r--below/view/src/core_view.rs14
-rw-r--r--below/view/src/default_styles.rs2
9 files changed, 171 insertions, 15 deletions
diff --git a/below/model/src/collector.rs b/below/model/src/collector.rs
index 32df150e..57601723 100644
--- a/below/model/src/collector.rs
+++ b/below/model/src/collector.rs
@@ -199,25 +199,19 @@ fn collect_sample(
logger,
&options.cgroup_re,
)?,
- processes: merge_procfs_and_exit_data(
- reader
- .read_all_pids()?
- .into_iter()
- .map(|(k, v)| (k, v.into()))
- .collect(),
- exit_pidmap,
- ),
+ processes: merge_procfs_and_exit_data(reader.read_all_pids()?, exit_pidmap),
netstats: match procfs::NetReader::new(logger.clone()).and_then(|v| v.read_netstat()) {
- Ok(ns) => ns.into(),
+ Ok(ns) => ns,
Err(e) => {
error!(logger, "{:#}", e);
Default::default()
}
},
system: SystemSample {
- stat: reader.read_stat()?.into(),
- meminfo: reader.read_meminfo()?.into(),
- vmstat: reader.read_vmstat()?.into(),
+ stat: reader.read_stat()?,
+ meminfo: reader.read_meminfo()?,
+ vmstat: reader.read_vmstat()?,
+ slabinfo: reader.read_slabinfo().unwrap_or_default(),
hostname: get_hostname()?,
kernel_version: match reader.read_kernel_version() {
Ok(k) => Some(k),
diff --git a/below/model/src/common_field_ids.rs b/below/model/src/common_field_ids.rs
index 59c2b61f..c9a2b14b 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; 410] = [
+pub const COMMON_MODEL_FIELD_IDS: [&str; 416] = [
"system.hostname",
"system.kernel_version",
"system.os_release",
@@ -103,6 +103,12 @@ pub const COMMON_MODEL_FIELD_IDS: [&str; 410] = [
"system.vm.pgscan_kswapd",
"system.vm.pgscan_direct",
"system.vm.oom_kill",
+ "system.slab.<key>.name",
+ "system.slab.<key>.active_objs",
+ "system.slab.<key>.num_objs",
+ "system.slab.<key>.obj_size",
+ "system.slab.<key>.obj_per_slab",
+ "system.slab.<key>.num_slabs",
"system.disks.<key>.name",
"system.disks.<key>.disk_usage",
"system.disks.<key>.partition_size",
diff --git a/below/model/src/sample.rs b/below/model/src/sample.rs
index 603a528c..a5cbfff5 100644
--- a/below/model/src/sample.rs
+++ b/below/model/src/sample.rs
@@ -62,6 +62,7 @@ pub struct SystemSample {
pub stat: procfs::Stat,
pub meminfo: procfs::MemInfo,
pub vmstat: procfs::VmStat,
+ pub slabinfo: procfs::SlabInfoMap,
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 d05ce29c..3e827d7f 100644
--- a/below/model/src/sample_model.rs
+++ b/below/model/src/sample_model.rs
@@ -131,6 +131,24 @@ pub const SAMPLE_MODEL_JSON: &str = r#"
"pgscan_direct": 0,
"oom_kill": 0
},
+ "slab": {
+ "task_struct": {
+ "name": "task_group",
+ "active_objs": 13000,
+ "num_objs": 14000,
+ "obj_size": 6000,
+ "obj_per_slab": 5,
+ "num_slabs": 3000
+ },
+ "vmap_area": {
+ "name": "vmap_area",
+ "active_objs": 4000000,
+ "num_objs": 6000000,
+ "obj_size": 64,
+ "obj_per_slab": 64,
+ "num_slabs": 100000
+ }
+ },
"disks": {
"vda": {
"name": "vda",
diff --git a/below/model/src/system.rs b/below/model/src/system.rs
index 438587cd..04b75e7d 100644
--- a/below/model/src/system.rs
+++ b/below/model/src/system.rs
@@ -39,6 +39,8 @@ pub struct SystemModel {
#[queriable(subquery)]
pub vm: VmModel,
#[queriable(subquery)]
+ pub slab: BTreeMap<String, SingleSlabModel>,
+ #[queriable(subquery)]
pub disks: BTreeMap<String, SingleDiskModel>,
#[queriable(subquery)]
pub btrfs: Option<BTreeMap<String, BtrfsModel>>,
@@ -74,10 +76,15 @@ impl SystemModel {
_ => Default::default(),
};
- let mem = Some(MemoryModel::new(&sample.meminfo)).unwrap_or_default();
+ let mem = MemoryModel::new(&sample.meminfo);
let vm = last
.map(|(last, duration)| VmModel::new(&last.vmstat, &sample.vmstat, duration))
.unwrap_or_default();
+ let slab = sample
+ .slabinfo
+ .iter()
+ .map(|(name, slab_info)| (name.to_owned(), SingleSlabModel::new(slab_info)))
+ .collect::<_>();
let mut disks: BTreeMap<String, SingleDiskModel> = BTreeMap::new();
sample.disks.iter().for_each(|(disk_name, end_disk_stat)| {
disks.insert(
@@ -121,6 +128,7 @@ impl SystemModel {
cpus,
mem,
vm,
+ slab,
disks,
btrfs,
}
@@ -394,6 +402,37 @@ impl VmModel {
Deserialize,
below_derive::Queriable
)]
+pub struct SingleSlabModel {
+ pub name: Option<String>,
+ pub active_objs: Option<u64>,
+ pub num_objs: Option<u64>,
+ pub obj_size: Option<u64>,
+ pub obj_per_slab: Option<u64>,
+ pub num_slabs: Option<u64>,
+}
+
+impl SingleSlabModel {
+ fn new(slabinfo: &procfs::SlabInfo) -> SingleSlabModel {
+ SingleSlabModel {
+ name: slabinfo.name.clone(),
+ active_objs: slabinfo.active_objs,
+ num_objs: slabinfo.num_objs,
+ obj_size: slabinfo.obj_size,
+ obj_per_slab: slabinfo.obj_per_slab,
+ num_slabs: slabinfo.num_slabs,
+ }
+ }
+}
+
+#[derive(
+ Clone,
+ Debug,
+ Default,
+ PartialEq,
+ Serialize,
+ Deserialize,
+ below_derive::Queriable
+)]
pub struct SingleDiskModel {
pub name: Option<String>,
pub disk_usage: Option<f32>,
@@ -520,6 +559,7 @@ mod test {
"cpus": {},
"mem": {},
"vm": {},
+ "slab": {},
"disks": {
"sda": {
"name": "sda",
diff --git a/below/render/src/default_configs.rs b/below/render/src/default_configs.rs
index a88098e9..f825998f 100644
--- a/below/render/src/default_configs.rs
+++ b/below/render/src/default_configs.rs
@@ -1041,6 +1041,9 @@ impl HasRenderConfig for model::SystemModel {
}
Mem(field_id) => model::MemoryModel::get_render_config_builder(field_id),
Vm(field_id) => model::VmModel::get_render_config_builder(field_id),
+ Slab(field_id) => {
+ model::SingleSlabModel::get_render_config_builder(&field_id.subquery_id)
+ }
Disks(field_id) => {
model::SingleDiskModel::get_render_config_builder(&field_id.subquery_id)
}
@@ -1067,6 +1070,7 @@ impl HasRenderConfigForDump for model::SystemModel {
Cpus(field_id) => self.cpus.get_openmetrics_config_for_dump(field_id),
Mem(field_id) => self.mem.get_openmetrics_config_for_dump(field_id),
Vm(field_id) => self.vm.get_openmetrics_config_for_dump(field_id),
+ Slab(_) => None,
// Same as with NetworkModel, we leave disk dumping to `disk` category
Disks(_) => None,
// Same as with above, we leave btrfs dumping to `btrfs` category
@@ -1314,6 +1318,38 @@ impl HasRenderConfigForDump for model::VmModel {
}
}
+impl HasRenderConfig for model::SingleSlabModel {
+ fn get_render_config_builder(field_id: &Self::FieldId) -> RenderConfigBuilder {
+ use model::SingleSlabModelFieldId::*;
+ let rc = RenderConfigBuilder::new();
+ match field_id {
+ Name => rc.title("Name").width(25),
+ ActiveObjs => rc.title("Active"),
+ NumObjs => rc.title("Objs"),
+ ObjSize => rc.title("Size").format(ReadableSize),
+ ObjPerSlab => rc.title("Obj/Slab"),
+ NumSlabs => rc.title("Slabs"),
+ }
+ }
+}
+
+impl HasRenderConfigForDump for model::SingleSlabModel {
+ fn get_openmetrics_config_for_dump(
+ &self,
+ field_id: &Self::FieldId,
+ ) -> Option<RenderOpenMetricsConfigBuilder> {
+ use model::SingleSlabModelFieldId::*;
+ match field_id {
+ Name => None,
+ ActiveObjs => Some(counter()),
+ NumObjs => Some(counter()),
+ ObjSize => Some(counter()),
+ ObjPerSlab => Some(counter()),
+ NumSlabs => Some(counter()),
+ }
+ }
+}
+
impl HasRenderConfig for model::SingleDiskModel {
fn get_render_config_builder(field_id: &Self::FieldId) -> RenderConfigBuilder {
use model::SingleDiskModelFieldId::*;
diff --git a/below/view/src/core_tabs.rs b/below/view/src/core_tabs.rs
index 9e26fc44..e3fb66cb 100644
--- a/below/view/src/core_tabs.rs
+++ b/below/view/src/core_tabs.rs
@@ -20,9 +20,11 @@ use model::system::BtrfsModelFieldId;
use model::system::MemoryModelFieldId;
use model::system::SingleCpuModelFieldId;
use model::system::SingleDiskModelFieldId;
+use model::system::SingleSlabModelFieldId;
use model::system::VmModelFieldId;
use model::BtrfsModel;
use model::Queriable;
+use model::SingleSlabModel;
use crate::core_view::CoreState;
use crate::core_view::CoreStateFieldId;
@@ -161,6 +163,51 @@ impl CoreTab for CoreVm {
}
#[derive(Default, Clone)]
+pub struct CoreSlab;
+
+impl CoreTab for CoreSlab {
+ fn get_titles(&self) -> ColumnTitles {
+ ColumnTitles {
+ titles: enum_iterator::all::<SingleSlabModelFieldId>()
+ .map(|field_id| ViewItem::from_default(field_id).config.render_title())
+ .collect(),
+ pinned_titles: 1,
+ }
+ }
+
+ fn get_rows(&self, state: &CoreState, _offset: Option<usize>) -> Vec<(StyledString, String)> {
+ let model = state.get_model();
+ let mut slab: Vec<&SingleSlabModel> = model.slab.values().collect();
+
+ if let Some(CoreStateFieldId::Slab(sort_order)) = state.sort_order.as_ref() {
+ model::sort_queriables(&mut slab, sort_order, state.reverse);
+ }
+
+ slab.into_iter()
+ .map(|ssm| {
+ enum_iterator::all::<SingleSlabModelFieldId>().fold(
+ StyledString::new(),
+ |mut line, field_id| {
+ let view_item = ViewItem::from_default(field_id.clone());
+ line.append(view_item.render(ssm));
+ line.append_plain(" ");
+ line
+ },
+ )
+ })
+ .filter(|s| {
+ if let Some((_, filter)) = &state.filter_info {
+ s.source().contains(filter)
+ } else {
+ true
+ }
+ })
+ .map(|s| (s.clone(), "".into()))
+ .collect()
+ }
+}
+
+#[derive(Default, Clone)]
pub struct CoreDisk;
impl CoreTab for CoreDisk {
diff --git a/below/view/src/core_view.rs b/below/view/src/core_view.rs
index 4b7aa64c..6780e0b5 100644
--- a/below/view/src/core_view.rs
+++ b/below/view/src/core_view.rs
@@ -30,6 +30,7 @@ use model::BtrfsModelFieldId;
use model::MemoryModelFieldId;
use model::SingleCpuModelFieldId;
use model::SingleDiskModelFieldId;
+use model::SingleSlabModelFieldId;
use model::VmModelFieldId;
use crate::core_tabs::*;
@@ -62,6 +63,7 @@ pub enum CoreStateFieldId {
Cpu(SingleCpuModelFieldId),
Mem(MemoryModelFieldId),
Vm(VmModelFieldId),
+ Slab(SingleSlabModelFieldId),
}
impl std::string::ToString for CoreStateFieldId {
@@ -72,6 +74,7 @@ impl std::string::ToString for CoreStateFieldId {
Self::Cpu(field) => field.to_string(),
Self::Mem(field) => field.to_string(),
Self::Vm(field) => field.to_string(),
+ Self::Slab(field) => field.to_string(),
}
}
}
@@ -118,6 +121,11 @@ impl StateCommon for CoreState {
// they don't use FieldId as column titles/selected col (it isn't used to filter)
"Mem" => CoreStateFieldId::Mem(MemoryModelFieldId::Total),
"Vm" => CoreStateFieldId::Vm(VmModelFieldId::PgpginPerSec),
+ "Slab" => CoreStateFieldId::Slab(
+ enum_iterator::all::<SingleSlabModelFieldId>()
+ .nth(idx)
+ .expect("Tag out of range"),
+ ),
_ => panic!("bug: got unsupported tab {}", tab),
}
}
@@ -150,7 +158,7 @@ impl StateCommon for CoreState {
fn set_sort_tag_from_tab_idx(&mut self, tab: &str, idx: usize, reverse: &mut bool) -> bool {
match tab {
- "Btrfs" => {
+ "Btrfs" | "Slab" => {
let sort_order = self.get_tag_from_tab_idx(tab, idx);
self.set_sort_tag(sort_order, reverse)
}
@@ -195,6 +203,7 @@ pub enum CoreView {
Cpu(CoreCpu),
Mem(CoreMem),
Vm(CoreVm),
+ Slab(CoreSlab),
Disk(CoreDisk),
Btrfs(CoreBtrfs),
}
@@ -223,6 +232,7 @@ impl CoreView {
"CPU".into(),
"Mem".into(),
"Vm".into(),
+ "Slab".into(),
"Disk".into(),
"Btrfs".into(),
];
@@ -230,6 +240,7 @@ impl CoreView {
tabs_map.insert("CPU".into(), CoreView::Cpu(Default::default()));
tabs_map.insert("Mem".into(), CoreView::Mem(Default::default()));
tabs_map.insert("Vm".into(), CoreView::Vm(Default::default()));
+ tabs_map.insert("Slab".into(), CoreView::Slab(Default::default()));
tabs_map.insert("Disk".into(), CoreView::Disk(Default::default()));
tabs_map.insert("Btrfs".into(), CoreView::Btrfs(Default::default()));
let user_data = c
@@ -261,6 +272,7 @@ impl CoreView {
Self::Cpu(inner) => Box::new(inner.clone()),
Self::Mem(inner) => Box::new(inner.clone()),
Self::Vm(inner) => Box::new(inner.clone()),
+ Self::Slab(inner) => Box::new(inner.clone()),
Self::Disk(inner) => Box::new(inner.clone()),
Self::Btrfs(inner) => Box::new(inner.clone()),
}
diff --git a/below/view/src/default_styles.rs b/below/view/src/default_styles.rs
index 2fdffbaa..bdb17e92 100644
--- a/below/view/src/default_styles.rs
+++ b/below/view/src/default_styles.rs
@@ -96,6 +96,8 @@ impl HasViewStyle for model::SingleCpuModel {}
impl HasViewStyle for model::VmModel {}
+impl HasViewStyle for model::SingleSlabModel {}
+
impl HasViewStyle for model::SingleDiskModel {}
impl HasViewStyle for model::BtrfsModel {}