diff options
Diffstat (limited to 'below/view/src/system_view.rs')
-rw-r--r-- | below/view/src/system_view.rs | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/below/view/src/system_view.rs b/below/view/src/system_view.rs new file mode 100644 index 00000000..cb05043d --- /dev/null +++ b/below/view/src/system_view.rs @@ -0,0 +1,307 @@ +// Copyright (c) Facebook, Inc. and its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::cell::Ref; +use std::cell::RefCell; +use std::cell::RefMut; +use std::collections::HashMap; +use std::collections::HashSet; +use std::rc::Rc; + +use cursive::utils::markup::StyledString; +use cursive::view::Nameable; +use cursive::views::NamedView; +use cursive::views::SelectView; +use cursive::views::ViewRef; +use cursive::Cursive; +use model::system::SystemModel; +use model::BtrfsModelFieldId; +use model::MemoryModelFieldId; +use model::SingleCpuModelFieldId; +use model::SingleDiskModelFieldId; +use model::SingleSlabModelFieldId; +use model::VmModelFieldId; + +use crate::stats_view::ColumnTitles; +use crate::stats_view::StateCommon; +use crate::stats_view::StatsView; +use crate::stats_view::ViewBridge; +use crate::system_tabs::*; +use crate::ViewState; + +pub type ViewType = StatsView<SystemView>; + +use crate::system_view::default_tabs::SYSTEM_BTRFS_TAB; + +// TODO(T123679020): Ideally we want to decouple states for system view tabs. +// Each system view tab really deserves its own view and state +#[derive(Default)] +pub struct SystemState { + pub filter_info: Option<(SystemStateFieldId, String)>, + pub collapsed_disk: HashSet<String>, + pub model: Rc<RefCell<SystemModel>>, + pub sort_order: Option<SystemStateFieldId>, + pub sort_tags: HashMap<String, default_tabs::SystemTabs>, + pub reverse: bool, +} + +#[derive(PartialEq)] +pub enum SystemStateFieldId { + Disk(SingleDiskModelFieldId), + Btrfs(BtrfsModelFieldId), + Cpu(SingleCpuModelFieldId), + Mem(MemoryModelFieldId), + Vm(VmModelFieldId), + Slab(SingleSlabModelFieldId), +} + +impl std::string::ToString for SystemStateFieldId { + fn to_string(&self) -> String { + match self { + Self::Disk(field) => field.to_string(), + Self::Btrfs(field) => field.to_string(), + Self::Cpu(field) => field.to_string(), + Self::Mem(field) => field.to_string(), + Self::Vm(field) => field.to_string(), + Self::Slab(field) => field.to_string(), + } + } +} + +impl StateCommon for SystemState { + type ModelType = SystemModel; + type TagType = SystemStateFieldId; + type KeyType = String; + + fn get_filter_info(&self) -> &Option<(Self::TagType, String)> { + &self.filter_info + } + + fn is_filter_supported_from_tab_idx(&self, _tab: &str, idx: usize) -> bool { + // we only enable str filtering for first col for System View + if idx == 0 { + return true; + } + false + } + + fn get_tag_from_tab_idx(&self, tab: &str, idx: usize) -> Self::TagType { + match tab { + "Btrfs" => { + let system_tab = self + .sort_tags + .get(tab) + .unwrap_or_else(|| panic!("Fail to find tab: {}", tab)); + let default_tabs::SystemTabs::Btrfs(system_tab) = system_tab; + Self::TagType::Btrfs( + system_tab + .view_items + .get(idx) + .expect("Out of title scope") + .field_id + .to_owned(), + ) + } + "CPU" => SystemStateFieldId::Cpu(SingleCpuModelFieldId::Idx), + "Disk" => SystemStateFieldId::Disk(SingleDiskModelFieldId::Name), + // tabs Mem and Vm have two columns 'Field' and 'Value'. 'Field' contains + // a list of all the FieldIds in MemoryModel and VmModel respectively. + // the field given to filter_info don't matter for these tabs because + // they don't use FieldId as column titles/selected col (it isn't used to filter) + "Mem" => SystemStateFieldId::Mem(MemoryModelFieldId::Total), + "Vm" => SystemStateFieldId::Vm(VmModelFieldId::PgpginPerSec), + "Slab" => SystemStateFieldId::Slab( + enum_iterator::all::<SingleSlabModelFieldId>() + .nth(idx) + .expect("Tag out of range"), + ), + _ => panic!("bug: got unsupported tab {}", tab), + } + } + + fn set_filter_from_tab_idx(&mut self, tab: &str, idx: usize, filter: Option<String>) -> bool { + if !self.is_filter_supported_from_tab_idx(tab, idx) { + return false; + } + + if let Some(filter_text) = filter { + let title = self.get_tag_from_tab_idx(tab, idx); + self.filter_info = Some((title, filter_text)); + } else { + self.filter_info = None; + } + true + } + + fn set_sort_tag(&mut self, sort_order: Self::TagType, reverse: &mut bool) -> bool { + let sort_order = Some(sort_order); + if self.sort_order == sort_order { + *reverse = !*reverse; + } else { + *reverse = true; + self.sort_order = sort_order; + } + self.reverse = *reverse; + true + } + + fn set_sort_tag_from_tab_idx(&mut self, tab: &str, idx: usize, reverse: &mut bool) -> bool { + match tab { + "Btrfs" | "Slab" => { + let sort_order = self.get_tag_from_tab_idx(tab, idx); + self.set_sort_tag(sort_order, reverse) + } + // This is to notify that tab is not currently sortable + _ => false, + } + } + + fn set_sort_string(&mut self, selection: &str, reverse: &mut bool) -> bool { + use std::str::FromStr; + match BtrfsModelFieldId::from_str(selection) { + Ok(field_id) => self.set_sort_tag(SystemStateFieldId::Btrfs(field_id), reverse), + Err(_) => false, + } + } + + fn get_model(&self) -> Ref<Self::ModelType> { + self.model.borrow() + } + + fn get_model_mut(&self) -> RefMut<Self::ModelType> { + self.model.borrow_mut() + } + + fn new(model: Rc<RefCell<Self::ModelType>>) -> Self { + let mut sort_tags = HashMap::new(); + sort_tags.insert( + "Btrfs".into(), + default_tabs::SystemTabs::Btrfs(&*SYSTEM_BTRFS_TAB), + ); + Self { + sort_order: None, + reverse: false, + sort_tags, + model, + ..Default::default() + } + } +} + +pub enum SystemView { + Cpu(SystemCpu), + Mem(SystemMem), + Vm(SystemVm), + Slab(SystemSlab), + Disk(SystemDisk), + Btrfs(SystemBtrfs), +} + +impl SystemView { + pub fn new(c: &mut Cursive) -> NamedView<ViewType> { + let mut list = SelectView::<String>::new(); + list.set_on_submit(|c, idx: &String| { + let mut view = SystemView::get_system_view(c); + // We only care about disk not partition + if view.get_tab_view().get_cur_selected() == "Disk" && idx.ends_with(".0") { + if view.state.borrow_mut().collapsed_disk.contains(idx) { + view.state.borrow_mut().collapsed_disk.remove(idx); + } else { + view.state + .borrow_mut() + .collapsed_disk + .insert(idx.to_string()); + } + + view.refresh(c); + } + }); + + let tabs = vec![ + "CPU".into(), + "Mem".into(), + "Vm".into(), + "Slab".into(), + "Disk".into(), + "Btrfs".into(), + ]; + let mut tabs_map: HashMap<String, SystemView> = HashMap::new(); + tabs_map.insert("CPU".into(), SystemView::Cpu(Default::default())); + tabs_map.insert("Mem".into(), SystemView::Mem(Default::default())); + tabs_map.insert("Vm".into(), SystemView::Vm(Default::default())); + tabs_map.insert("Slab".into(), SystemView::Slab(Default::default())); + tabs_map.insert("Disk".into(), SystemView::Disk(Default::default())); + tabs_map.insert("Btrfs".into(), SystemView::Btrfs(Default::default())); + let user_data = c + .user_data::<ViewState>() + .expect("No data stored in Cursive Object!"); + StatsView::new( + "system", + tabs, + tabs_map, + list, + SystemState::new(user_data.system.clone()), + user_data.event_controllers.clone(), + user_data.cmd_controllers.clone(), + ) + .feed_data(c) + .with_name(Self::get_view_name()) + } + + pub fn get_system_view(c: &mut Cursive) -> ViewRef<ViewType> { + ViewType::get_view(c) + } + + pub fn refresh(c: &mut Cursive) { + Self::get_system_view(c).refresh(c); + } + + fn get_inner(&self) -> Box<dyn SystemTab> { + match self { + 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()), + } + } +} + +impl ViewBridge for SystemView { + type StateType = SystemState; + fn get_view_name() -> &'static str { + "system_view" + } + fn get_titles(&self) -> ColumnTitles { + self.get_inner().get_titles() + } + + fn get_rows( + &mut self, + state: &Self::StateType, + offset: Option<usize>, + ) -> Vec<(StyledString, String)> { + self.get_inner().get_rows(state, offset) + } + + fn on_select_update_cmd_palette( + _view: &Self::StateType, + selected_key: &String, + _current_tab: &str, + _selected_column: usize, + ) -> String { + selected_key.clone() + } +} |