diff --git a/CHANGELOG.md b/CHANGELOG.md index cead9d4c..f8a7dc92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#1016](https://github.com/ClementTsang/bottom/pull/1016): Add support for displaying process usernames on Windows. - [#1022](https://github.com/ClementTsang/bottom/pull/1022): Support three-character hex colour strings for styling. - [#1024](https://github.com/ClementTsang/bottom/pull/1024): Support FreeBSD temperature sensors based on `hw.temperature`. +- [#1063](https://github.com/ClementTsang/bottom/pull/1063): Add buffer and cache memory tracking ## Changes diff --git a/docs/content/configuration/command-line-flags.md b/docs/content/configuration/command-line-flags.md index 86fad044..6bf0abea 100644 --- a/docs/content/configuration/command-line-flags.md +++ b/docs/content/configuration/command-line-flags.md @@ -6,44 +6,45 @@ The following flags can be provided to bottom in the command line to change the behaviour of the program (run `btm --help` for more information on each flag): -| Flag | Behaviour | -| -------------------------------------------- | --------------------------------------------------------------- | -| `--autohide_time` | Temporarily shows the time scale in graphs. | -| `-b`, `--basic` | Hides graphs and uses a more basic look. | -| `--battery` | Shows the battery widget. | -| `-S`, `--case_sensitive` | Enables case sensitivity by default. | -| `-c`, `--celsius` | Sets the temperature type to Celsius. | -| `--color ` | Use a color scheme, use --help for supported values. | -| `-C `, `--config ` | Sets the location of the config file. | -| `-u`, `--current_usage` | Sets process CPU% to be based on current CPU%. | -| `-t `, `--default_time_value ` | Default time value for graphs in ms. | -| `--default_widget_count ` | Sets the n'th selected widget type as the default. | -| `--default_widget_type ` | Sets the default widget type, use --help for more info. | -| `--disable_advanced_kill` | Hides advanced options to stop a process on Unix-like systems. | -| `--disable_click` | Disables mouse clicks. | -| `-m`, `--dot_marker` | Uses a dot marker for graphs. | -| `-f`, `--fahrenheit` | Sets the temperature type to Fahrenheit. | -| `-g`, `--group` | Groups processes with the same name by default. | -| `-h`, `--help` | Prints help information. Use --help for more info. | -| `-a`, `--hide_avg_cpu` | Hides the average CPU usage. | -| `--hide_table_gap` | Hides the spacing between table headers and entries. | -| `--hide_time` | Hides the time scale. | -| `-k`, `--kelvin` | Sets the temperature type to Kelvin. | -| `-l`, `--left_legend` | Puts the CPU chart legend to the left side. | -| `--mem_as_value` | Defaults to showing process memory usage by value. | -| `--network_use_binary_prefix` | Displays the network widget with binary prefixes. | -| `--network_use_bytes` | Displays the network widget using bytes. | -| `--network_use_log` | Displays the network widget with a log scale. | -| `--process_command` | Show processes as their commands by default. | -| `-r`, `--rate ` | Sets a refresh rate in ms. | -| `-R`, `--regex` | Enables regex by default. | -| `--show_table_scroll_position` | Shows the scroll position tracker in table widgets. | -| `-d `, `--time_delta ` | The amount in ms changed upon zooming. | -| `-T`, `--tree` | Defaults to showing the process widget in tree mode. | -| `--use_old_network_legend` | DEPRECATED - uses the older network legend. | -| `-V`, `--version` | Prints version information. | -| `-W`, `--whole_word` | Enables whole-word matching by default. | -| `--enable_gpu_memory` | Enable collecting and displaying GPU memory usage. | -| `--retention` | How much data is stored at once in terms of time. | -| `-n`, `--unnormalized_cpu` | Show process CPU% without normalizing over the number of cores. | -| `-e`, `--expanded` | Expand the default widget upon starting the app. | +| Flag | Behaviour | +|----------------------------------------------|--------------------------------------------------------------------------------------| +| `--autohide_time` | Temporarily shows the time scale in graphs. | +| `-b`, `--basic` | Hides graphs and uses a more basic look. | +| `--battery` | Shows the battery widget. | +| `-S`, `--case_sensitive` | Enables case sensitivity by default. | +| `-c`, `--celsius` | Sets the temperature type to Celsius. | +| `--color ` | Use a color scheme, use --help for supported values. | +| `-C `, `--config ` | Sets the location of the config file. | +| `-u`, `--current_usage` | Sets process CPU% to be based on current CPU%. | +| `-t `, `--default_time_value ` | Default time value for graphs in ms. | +| `--default_widget_count ` | Sets the n'th selected widget type as the default. | +| `--default_widget_type ` | Sets the default widget type, use --help for more info. | +| `--disable_advanced_kill` | Hides advanced options to stop a process on Unix-like systems. | +| `--disable_click` | Disables mouse clicks. | +| `--enable_cache_memory` | Enable collecting and displaying cache and buffer memory (not available on Windows). | +| `-m`, `--dot_marker` | Uses a dot marker for graphs. | +| `-f`, `--fahrenheit` | Sets the temperature type to Fahrenheit. | +| `-g`, `--group` | Groups processes with the same name by default. | +| `-h`, `--help` | Prints help information. Use --help for more info. | +| `-a`, `--hide_avg_cpu` | Hides the average CPU usage. | +| `--hide_table_gap` | Hides the spacing between table headers and entries. | +| `--hide_time` | Hides the time scale. | +| `-k`, `--kelvin` | Sets the temperature type to Kelvin. | +| `-l`, `--left_legend` | Puts the CPU chart legend to the left side. | +| `--mem_as_value` | Defaults to showing process memory usage by value. | +| `--network_use_binary_prefix` | Displays the network widget with binary prefixes. | +| `--network_use_bytes` | Displays the network widget using bytes. | +| `--network_use_log` | Displays the network widget with a log scale. | +| `--process_command` | Show processes as their commands by default. | +| `-r`, `--rate ` | Sets a refresh rate in ms. | +| `-R`, `--regex` | Enables regex by default. | +| `--show_table_scroll_position` | Shows the scroll position tracker in table widgets. | +| `-d `, `--time_delta ` | The amount in ms changed upon zooming. | +| `-T`, `--tree` | Defaults to showing the process widget in tree mode. | +| `--use_old_network_legend` | DEPRECATED - uses the older network legend. | +| `-V`, `--version` | Prints version information. | +| `-W`, `--whole_word` | Enables whole-word matching by default. | +| `--enable_gpu_memory` | Enable collecting and displaying GPU memory usage. | +| `--retention` | How much data is stored at once in terms of time. | +| `-n`, `--unnormalized_cpu` | Show process CPU% without normalizing over the number of cores. | +| `-e`, `--expanded` | Expand the default widget upon starting the app. | diff --git a/docs/content/configuration/config-file/flags.md b/docs/content/configuration/config-file/flags.md index af84fcbd..b3a6362c 100644 --- a/docs/content/configuration/config-file/flags.md +++ b/docs/content/configuration/config-file/flags.md @@ -4,39 +4,41 @@ This section is in progress, and is just copied from the old documentation. -Most of the [command line flags](../../command-line-flags) have config file equivalents to avoid having to type them out each time: +Most of the [command line flags](../../command-line-flags) have config file equivalents to avoid having to type them out +each time: -| Field | Type | Functionality | -| ---------------------------- | ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | -| `hide_avg_cpu` | Boolean | Hides the average CPU usage. | -| `dot_marker` | Boolean | Uses a dot marker for graphs. | -| `left_legend` | Boolean | Puts the CPU chart legend to the left side. | -| `current_usage` | Boolean | Sets process CPU% to be based on current CPU%. | -| `group_processes` | Boolean | Groups processes with the same name by default. | -| `case_sensitive` | Boolean | Enables case sensitivity by default. | -| `whole_word` | Boolean | Enables whole-word matching by default. | -| `regex` | Boolean | Enables regex by default. | -| `basic` | Boolean | Hides graphs and uses a more basic look. | -| `use_old_network_legend` | Boolean | DEPRECATED - uses the older network legend. | -| `battery` | Boolean | Shows the battery widget. | -| `rate` | Unsigned Int (represents milliseconds) | Sets a refresh rate in ms. | -| `default_time_value` | Unsigned Int (represents milliseconds) | Default time value for graphs in ms. | -| `time_delta` | Unsigned Int (represents milliseconds) | The amount in ms changed upon zooming. | -| `hide_time` | Boolean | Hides the time scale. | -| `temperature_type` | String (one of ["k", "f", "c", "kelvin", "fahrenheit", "celsius"]) | Sets the temperature unit type. | -| `default_widget_type` | String (one of ["cpu", "proc", "net", "temp", "mem", "disk"], same as layout options) | Sets the default widget type, use --help for more info. | -| `default_widget_count` | Unsigned Int (represents which `default_widget_type`) | Sets the n'th selected widget type as the default. | -| `disable_click` | Boolean | Disables mouse clicks. | -| `color` | String (one of ["default", "default-light", "gruvbox", "gruvbox-light", "nord", "nord-light"]) | Use a color scheme, use --help for supported values. | -| `mem_as_value` | Boolean | Defaults to showing process memory usage by value. | -| `tree` | Boolean | Defaults to showing the process widget in tree mode. | -| `show_table_scroll_position` | Boolean | Shows the scroll position tracker in table widgets. | -| `process_command` | Boolean | Show processes as their commands by default. | -| `disable_advanced_kill` | Boolean | Hides advanced options to stop a process on Unix-like systems. | -| `network_use_binary_prefix` | Boolean | Displays the network widget with binary prefixes. | -| `network_use_bytes` | Boolean | Displays the network widget using bytes. | -| `network_use_log` | Boolean | Displays the network widget with a log scale. | -| `enable_gpu_memory` | Boolean | Shows the GPU memory widget. | -| `retention` | String (human readable time, such as "10m", "1h", etc.) | How much data is stored at once in terms of time. | -| `unnormalized_cpu` | Boolean | Show process CPU% without normalizing over the number of cores. | -| `expanded_on_startup` | Boolean | Expand the default widget upon starting the app. | +| Field | Type | Functionality | +|------------------------------|------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------| +| `hide_avg_cpu` | Boolean | Hides the average CPU usage. | +| `dot_marker` | Boolean | Uses a dot marker for graphs. | +| `left_legend` | Boolean | Puts the CPU chart legend to the left side. | +| `current_usage` | Boolean | Sets process CPU% to be based on current CPU%. | +| `group_processes` | Boolean | Groups processes with the same name by default. | +| `case_sensitive` | Boolean | Enables case sensitivity by default. | +| `whole_word` | Boolean | Enables whole-word matching by default. | +| `regex` | Boolean | Enables regex by default. | +| `basic` | Boolean | Hides graphs and uses a more basic look. | +| `use_old_network_legend` | Boolean | DEPRECATED - uses the older network legend. | +| `battery` | Boolean | Shows the battery widget. | +| `rate` | Unsigned Int (represents milliseconds) | Sets a refresh rate in ms. | +| `default_time_value` | Unsigned Int (represents milliseconds) | Default time value for graphs in ms. | +| `time_delta` | Unsigned Int (represents milliseconds) | The amount in ms changed upon zooming. | +| `hide_time` | Boolean | Hides the time scale. | +| `temperature_type` | String (one of ["k", "f", "c", "kelvin", "fahrenheit", "celsius"]) | Sets the temperature unit type. | +| `default_widget_type` | String (one of ["cpu", "proc", "net", "temp", "mem", "disk"], same as layout options) | Sets the default widget type, use --help for more info. | +| `default_widget_count` | Unsigned Int (represents which `default_widget_type`) | Sets the n'th selected widget type as the default. | +| `disable_click` | Boolean | Disables mouse clicks. | +| `color` | String (one of ["default", "default-light", "gruvbox", "gruvbox-light", "nord", "nord-light"]) | Use a color scheme, use --help for supported values. | +| `enable_cache_memory` | Boolean | Enable collecting and displaying cache and buffer memory (not available on Windows). | +| `mem_as_value` | Boolean | Defaults to showing process memory usage by value. | +| `tree` | Boolean | Defaults to showing the process widget in tree mode. | +| `show_table_scroll_position` | Boolean | Shows the scroll position tracker in table widgets. | +| `process_command` | Boolean | Show processes as their commands by default. | +| `disable_advanced_kill` | Boolean | Hides advanced options to stop a process on Unix-like systems. | +| `network_use_binary_prefix` | Boolean | Displays the network widget with binary prefixes. | +| `network_use_bytes` | Boolean | Displays the network widget using bytes. | +| `network_use_log` | Boolean | Displays the network widget with a log scale. | +| `enable_gpu_memory` | Boolean | Shows the GPU memory widget. | +| `retention` | String (human readable time, such as "10m", "1h", etc.) | How much data is stored at once in terms of time. | +| `unnormalized_cpu` | Boolean | Show process CPU% without normalizing over the number of cores. | +| `expanded_on_startup` | Boolean | Expand the default widget upon starting the app. | diff --git a/sample_configs/default_config.toml b/sample_configs/default_config.toml index 947c8710..538d6347 100644 --- a/sample_configs/default_config.toml +++ b/sample_configs/default_config.toml @@ -75,6 +75,8 @@ #disable_advanced_kill = false # Shows GPU(s) memory #enable_gpu_memory = false +# Shows cache and buffer memory +#enable_cache_memory = false # How much data is stored at once in terms of time. #retention = "10m" @@ -96,6 +98,8 @@ #swap_color="LightYellow" # Represents the colour ARC will use in the memory legend and graph. #arc_color="LightCyan" +# Represents the colour cache and buffer memory will use in the memory legend and graph. +#cache_color="LightRed" # Represents the colour the GPU will use in the memory legend and graph. #gpu_core_colors=["LightGreen", "LightBlue", "LightRed", "Cyan", "Green", "Blue", "Red"] # Represents the colour rx will use in the network legend and graph. diff --git a/src/app.rs b/src/app.rs index 7136deb8..750fa240 100644 --- a/src/app.rs +++ b/src/app.rs @@ -60,6 +60,7 @@ pub struct AppConfigFields { pub table_gap: u16, pub disable_click: bool, pub enable_gpu_memory: bool, + pub enable_cache_memory: bool, pub show_table_scroll_position: bool, pub is_advanced_kill: bool, // TODO: Remove these, move network details state-side. diff --git a/src/app/data_farmer.rs b/src/app/data_farmer.rs index af130c3c..c11595bc 100644 --- a/src/app/data_farmer.rs +++ b/src/app/data_farmer.rs @@ -35,6 +35,8 @@ pub struct TimedData { pub cpu_data: Vec, pub load_avg_data: [f32; 3], pub mem_data: Option, + #[cfg(not(target_os = "windows"))] + pub cache_data: Option, pub swap_data: Option, #[cfg(feature = "zfs")] pub arc_data: Option, @@ -108,6 +110,8 @@ pub struct DataCollection { pub timed_data_vec: Vec<(Instant, TimedData)>, pub network_harvest: network::NetworkHarvest, pub memory_harvest: memory::MemHarvest, + #[cfg(not(target_os = "windows"))] + pub cache_harvest: memory::MemHarvest, pub swap_harvest: memory::MemHarvest, pub cpu_harvest: cpu::CpuHarvest, pub load_avg_harvest: cpu::LoadAvgHarvest, @@ -132,6 +136,8 @@ impl Default for DataCollection { timed_data_vec: Vec::default(), network_harvest: network::NetworkHarvest::default(), memory_harvest: memory::MemHarvest::default(), + #[cfg(not(target_os = "windows"))] + cache_harvest: memory::MemHarvest::default(), swap_harvest: memory::MemHarvest::default(), cpu_harvest: cpu::CpuHarvest::default(), load_avg_harvest: cpu::LoadAvgHarvest::default(), @@ -206,11 +212,17 @@ impl DataCollection { self.eat_network(network, &mut new_entry); } - // Memory and Swap + // Memory, Swap if let (Some(memory), Some(swap)) = (harvested_data.memory, harvested_data.swap) { self.eat_memory_and_swap(memory, swap, &mut new_entry); } + // Cache memory + #[cfg(not(target_os = "windows"))] + if let Some(cache) = harvested_data.cache { + self.eat_cache(cache, &mut new_entry); + } + #[cfg(feature = "zfs")] if let Some(arc) = harvested_data.arc { self.eat_arc(arc, &mut new_entry); @@ -275,6 +287,15 @@ impl DataCollection { self.swap_harvest = swap; } + #[cfg(not(target_os = "windows"))] + fn eat_cache(&mut self, cache: memory::MemHarvest, new_entry: &mut TimedData) { + // Cache and buffer memory + new_entry.cache_data = cache.use_percent; + + // In addition copy over latest data for easy reference + self.cache_harvest = cache; + } + fn eat_network(&mut self, network: network::NetworkHarvest, new_entry: &mut TimedData) { // RX if network.rx > 0 { diff --git a/src/app/data_harvester.rs b/src/app/data_harvester.rs index 7904f495..67ff1e5b 100644 --- a/src/app/data_harvester.rs +++ b/src/app/data_harvester.rs @@ -31,6 +31,8 @@ pub struct Data { pub cpu: Option, pub load_avg: Option, pub memory: Option, + #[cfg(not(target_os = "windows"))] + pub cache: Option, pub swap: Option, pub temperature_sensors: Option>, pub network: Option, @@ -52,6 +54,8 @@ impl Default for Data { cpu: None, load_avg: None, memory: None, + #[cfg(not(target_os = "windows"))] + cache: None, swap: None, temperature_sensors: None, list_of_processes: None, @@ -404,6 +408,12 @@ impl DataCollector { fn update_memory_usage(&mut self) { if self.widgets_to_harvest.use_mem { self.data.memory = memory::get_ram_usage(&self.sys); + + #[cfg(not(target_os = "windows"))] + if self.widgets_to_harvest.use_cache { + self.data.cache = memory::get_cache_usage(&self.sys); + } + self.data.swap = memory::get_swap_usage( #[cfg(not(target_os = "windows"))] &self.sys, diff --git a/src/app/data_harvester/memory.rs b/src/app/data_harvester/memory.rs index e8906284..2154b00c 100644 --- a/src/app/data_harvester/memory.rs +++ b/src/app/data_harvester/memory.rs @@ -1,8 +1,10 @@ //! Memory data collection. -pub mod sysinfo; +#[cfg(not(target_os = "windows"))] +pub(crate) use self::sysinfo::get_cache_usage; pub(crate) use self::sysinfo::get_ram_usage; +pub mod sysinfo; cfg_if::cfg_if! { if #[cfg(target_os = "windows")] { pub mod windows; diff --git a/src/app/data_harvester/memory/sysinfo.rs b/src/app/data_harvester/memory/sysinfo.rs index 56844728..119d44c5 100644 --- a/src/app/data_harvester/memory/sysinfo.rs +++ b/src/app/data_harvester/memory/sysinfo.rs @@ -36,3 +36,25 @@ pub(crate) fn get_swap_usage(sys: &System) -> Option { }, }) } + +/// Returns cache usage. sysinfo has no way to do this directly but it should equal the difference +/// between the available and free memory. Free memory is defined as memory not containing any data, +/// which means cache and buffer memory are not "free". Available memory is defined as memory able +/// to be allocated by processes, which includes cache and buffer memory. On Windows, this will +/// always be 0. For more information, see [docs](https://docs.rs/sysinfo/0.28.4/sysinfo/trait.SystemExt.html#tymethod.available_memory) +/// and [memory explanation](https://askubuntu.com/questions/867068/what-is-available-memory-while-using-free-command) +#[cfg(not(target_os = "windows"))] +pub(crate) fn get_cache_usage(sys: &System) -> Option { + let mem_used = sys.available_memory().saturating_sub(sys.free_memory()); + let mem_total = sys.total_memory(); + + Some(MemHarvest { + total_bytes: mem_total, + used_bytes: mem_used, + use_percent: if mem_total == 0 { + None + } else { + Some(mem_used as f64 / mem_total as f64 * 100.0) + }, + }) +} diff --git a/src/app/layout_manager.rs b/src/app/layout_manager.rs index abc60899..032ccf98 100644 --- a/src/app/layout_manager.rs +++ b/src/app/layout_manager.rs @@ -997,6 +997,7 @@ Supported widget names: pub struct UsedWidgets { pub use_cpu: bool, pub use_mem: bool, + pub use_cache: bool, pub use_gpu: bool, pub use_net: bool, pub use_proc: bool, diff --git a/src/bin/main.rs b/src/bin/main.rs index 3b0c41df..b0596532 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -20,6 +20,13 @@ use std::{ }; use anyhow::{Context, Result}; +use crossterm::{ + event::{EnableBracketedPaste, EnableMouseCapture}, + execute, + terminal::{enable_raw_mode, EnterAlternateScreen}, +}; +use tui::{backend::CrosstermBackend, Terminal}; + use bottom::{ canvas::{self, canvas_styling::CanvasColours}, constants::*, @@ -27,12 +34,6 @@ use bottom::{ options::*, *, }; -use crossterm::{ - event::{EnableBracketedPaste, EnableMouseCapture}, - execute, - terminal::{enable_raw_mode, EnterAlternateScreen}, -}; -use tui::{backend::CrosstermBackend, Terminal}; // #[global_allocator] // static ALLOC: dhat::Alloc = dhat::Alloc; @@ -245,6 +246,11 @@ fn main() -> Result<()> { if app.used_widgets.use_mem { app.converted_data.mem_data = convert_mem_data_points(&app.data_collection); + #[cfg(not(target_os = "windows"))] + { + app.converted_data.cache_data = + convert_cache_data_points(&app.data_collection); + } app.converted_data.swap_data = convert_swap_data_points(&app.data_collection); #[cfg(feature = "zfs")] @@ -257,11 +263,17 @@ fn main() -> Result<()> { app.converted_data.gpu_data = convert_gpu_data(&app.data_collection); } - let (memory_labels, swap_labels) = - convert_mem_labels(&app.data_collection); - app.converted_data.mem_labels = memory_labels; - app.converted_data.swap_labels = swap_labels; + app.converted_data.mem_labels = + convert_mem_label(&app.data_collection.memory_harvest); + app.converted_data.swap_labels = + convert_mem_label(&app.data_collection.swap_harvest); + #[cfg(not(target_os = "windows"))] + { + app.converted_data.cache_labels = + convert_mem_label(&app.data_collection.cache_harvest); + } + #[cfg(feature = "zfs")] { let arc_labels = convert_arc_labels(&app.data_collection); diff --git a/src/canvas.rs b/src/canvas.rs index 9ac75585..1b2c66d2 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -435,6 +435,13 @@ impl Painter { } } + #[cfg(not(target_os = "windows"))] + { + if app_state.converted_data.cache_labels.is_some() { + mem_rows += 1; + } + } + #[cfg(feature = "gpu")] { if let Some(gpu_data) = &app_state.converted_data.gpu_data { diff --git a/src/canvas/canvas_styling.rs b/src/canvas/canvas_styling.rs index e0de7285..ffb60cb2 100644 --- a/src/canvas/canvas_styling.rs +++ b/src/canvas/canvas_styling.rs @@ -18,6 +18,8 @@ pub struct CanvasColours { pub currently_selected_text_style: Style, pub table_header_style: Style, pub ram_style: Style, + #[cfg(not(target_os = "windows"))] + pub cache_style: Style, pub swap_style: Style, pub arc_style: Style, pub gpu_colour_styles: Vec