diff options
author | Leon Yang (Containers) <lnyng@meta.com> | 2024-01-25 19:36:06 -0800 |
---|---|---|
committer | Facebook GitHub Bot <facebook-github-bot@users.noreply.github.com> | 2024-01-25 19:36:06 -0800 |
commit | 3f5934b0d5428d1482bf7182aef3ecaf3cc22c96 (patch) | |
tree | 6135ac36bbbf94a3cf937c16e1e12f2d877375e3 | |
parent | a1395ebb09e3f647a7c7eedc6f2296b1e22c75a1 (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.rs | 18 | ||||
-rw-r--r-- | below/model/src/common_field_ids.rs | 8 | ||||
-rw-r--r-- | below/model/src/sample.rs | 1 | ||||
-rw-r--r-- | below/model/src/sample_model.rs | 18 | ||||
-rw-r--r-- | below/model/src/system.rs | 42 | ||||
-rw-r--r-- | below/render/src/default_configs.rs | 36 | ||||
-rw-r--r-- | below/view/src/core_tabs.rs | 47 | ||||
-rw-r--r-- | below/view/src/core_view.rs | 14 | ||||
-rw-r--r-- | below/view/src/default_styles.rs | 2 |
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 {} |