feature: add buffer and cache memory (#1063)

* First implementation of cache memory data collection, mostly copied from RAM and swap implementations

* First implementation of cache memory display, copied from RAM and swap implementations. placed cache as second in the list as it is more similar to the RAM than any other item in the list

* expanded comment to explain method.

* rustfmt

* all cache-related code excluded on windows, in the process refactored src/data_conversion.rs convert_mem_label() to convert a single label instead of all at once

* better factoring-out of cache memory logic to allow individual disabling

* added --enable_cache_memory flag, disabled cache memory collection by default

* renamed CCH to CHE
not sure how i messed that up

* changelog updated

* Added command line flag documentation

* updated config file documentation

* specified that buffer and cache memory display does not work on windows

* resolved merge conflicts

* added documentation to cache memory data collection

* capitalized Windows

* implemented missing canvas styling logic

* fixed misplaced no-windows flag

* reduced colour collisions, as cache colour was the same as the first GPU colour

* made FIFTH_COLOUR constant windows-only

* Revert "made FIFTH_COLOUR constant windows-only"

This reverts commit 72698f1dd7.

* made FIFTH_COLOUR constant non-windows-only

* minor fix for basic mode row count

* Update src/app/data_harvester/memory/sysinfo.rs

Co-authored-by: Clement Tsang <34804052+ClementTsang@users.noreply.github.com>

* Update src/canvas/widgets/mem_basic.rs

Co-authored-by: Clement Tsang <34804052+ClementTsang@users.noreply.github.com>

* updated default_config.toml

* formatting

---------

Co-authored-by: ClementTsang <34804052+ClementTsang@users.noreply.github.com>
This commit is contained in:
Twan Stok 2023-04-13 04:51:41 +02:00 committed by GitHub
parent e61e5f2af6
commit 1b1e80ec3c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 309 additions and 133 deletions

View file

@ -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. - [#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. - [#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`. - [#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 ## Changes

View file

@ -7,7 +7,7 @@
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): 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 | | Flag | Behaviour |
| -------------------------------------------- | --------------------------------------------------------------- | |----------------------------------------------|--------------------------------------------------------------------------------------|
| `--autohide_time` | Temporarily shows the time scale in graphs. | | `--autohide_time` | Temporarily shows the time scale in graphs. |
| `-b`, `--basic` | Hides graphs and uses a more basic look. | | `-b`, `--basic` | Hides graphs and uses a more basic look. |
| `--battery` | Shows the battery widget. | | `--battery` | Shows the battery widget. |
@ -21,6 +21,7 @@ The following flags can be provided to bottom in the command line to change the
| `--default_widget_type <WIDGET TYPE>` | Sets the default widget type, use --help for more info. | | `--default_widget_type <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_advanced_kill` | Hides advanced options to stop a process on Unix-like systems. |
| `--disable_click` | Disables mouse clicks. | | `--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. | | `-m`, `--dot_marker` | Uses a dot marker for graphs. |
| `-f`, `--fahrenheit` | Sets the temperature type to Fahrenheit. | | `-f`, `--fahrenheit` | Sets the temperature type to Fahrenheit. |
| `-g`, `--group` | Groups processes with the same name by default. | | `-g`, `--group` | Groups processes with the same name by default. |

View file

@ -4,10 +4,11 @@
This section is in progress, and is just copied from the old documentation. 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 | | Field | Type | Functionality |
| ---------------------------- | ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | |------------------------------|------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------|
| `hide_avg_cpu` | Boolean | Hides the average CPU usage. | | `hide_avg_cpu` | Boolean | Hides the average CPU usage. |
| `dot_marker` | Boolean | Uses a dot marker for graphs. | | `dot_marker` | Boolean | Uses a dot marker for graphs. |
| `left_legend` | Boolean | Puts the CPU chart legend to the left side. | | `left_legend` | Boolean | Puts the CPU chart legend to the left side. |
@ -28,6 +29,7 @@ Most of the [command line flags](../../command-line-flags) have config file equi
| `default_widget_count` | Unsigned Int (represents which `default_widget_type`) | Sets the n'th selected widget type as the default. | | `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. | | `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. | | `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. | | `mem_as_value` | Boolean | Defaults to showing process memory usage by value. |
| `tree` | Boolean | Defaults to showing the process widget in tree mode. | | `tree` | Boolean | Defaults to showing the process widget in tree mode. |
| `show_table_scroll_position` | Boolean | Shows the scroll position tracker in table widgets. | | `show_table_scroll_position` | Boolean | Shows the scroll position tracker in table widgets. |

View file

@ -75,6 +75,8 @@
#disable_advanced_kill = false #disable_advanced_kill = false
# Shows GPU(s) memory # Shows GPU(s) memory
#enable_gpu_memory = false #enable_gpu_memory = false
# Shows cache and buffer memory
#enable_cache_memory = false
# How much data is stored at once in terms of time. # How much data is stored at once in terms of time.
#retention = "10m" #retention = "10m"
@ -96,6 +98,8 @@
#swap_color="LightYellow" #swap_color="LightYellow"
# Represents the colour ARC will use in the memory legend and graph. # Represents the colour ARC will use in the memory legend and graph.
#arc_color="LightCyan" #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. # Represents the colour the GPU will use in the memory legend and graph.
#gpu_core_colors=["LightGreen", "LightBlue", "LightRed", "Cyan", "Green", "Blue", "Red"] #gpu_core_colors=["LightGreen", "LightBlue", "LightRed", "Cyan", "Green", "Blue", "Red"]
# Represents the colour rx will use in the network legend and graph. # Represents the colour rx will use in the network legend and graph.

View file

@ -60,6 +60,7 @@ pub struct AppConfigFields {
pub table_gap: u16, pub table_gap: u16,
pub disable_click: bool, pub disable_click: bool,
pub enable_gpu_memory: bool, pub enable_gpu_memory: bool,
pub enable_cache_memory: bool,
pub show_table_scroll_position: bool, pub show_table_scroll_position: bool,
pub is_advanced_kill: bool, pub is_advanced_kill: bool,
// TODO: Remove these, move network details state-side. // TODO: Remove these, move network details state-side.

View file

@ -35,6 +35,8 @@ pub struct TimedData {
pub cpu_data: Vec<Value>, pub cpu_data: Vec<Value>,
pub load_avg_data: [f32; 3], pub load_avg_data: [f32; 3],
pub mem_data: Option<Value>, pub mem_data: Option<Value>,
#[cfg(not(target_os = "windows"))]
pub cache_data: Option<Value>,
pub swap_data: Option<Value>, pub swap_data: Option<Value>,
#[cfg(feature = "zfs")] #[cfg(feature = "zfs")]
pub arc_data: Option<Value>, pub arc_data: Option<Value>,
@ -108,6 +110,8 @@ pub struct DataCollection {
pub timed_data_vec: Vec<(Instant, TimedData)>, pub timed_data_vec: Vec<(Instant, TimedData)>,
pub network_harvest: network::NetworkHarvest, pub network_harvest: network::NetworkHarvest,
pub memory_harvest: memory::MemHarvest, pub memory_harvest: memory::MemHarvest,
#[cfg(not(target_os = "windows"))]
pub cache_harvest: memory::MemHarvest,
pub swap_harvest: memory::MemHarvest, pub swap_harvest: memory::MemHarvest,
pub cpu_harvest: cpu::CpuHarvest, pub cpu_harvest: cpu::CpuHarvest,
pub load_avg_harvest: cpu::LoadAvgHarvest, pub load_avg_harvest: cpu::LoadAvgHarvest,
@ -132,6 +136,8 @@ impl Default for DataCollection {
timed_data_vec: Vec::default(), timed_data_vec: Vec::default(),
network_harvest: network::NetworkHarvest::default(), network_harvest: network::NetworkHarvest::default(),
memory_harvest: memory::MemHarvest::default(), memory_harvest: memory::MemHarvest::default(),
#[cfg(not(target_os = "windows"))]
cache_harvest: memory::MemHarvest::default(),
swap_harvest: memory::MemHarvest::default(), swap_harvest: memory::MemHarvest::default(),
cpu_harvest: cpu::CpuHarvest::default(), cpu_harvest: cpu::CpuHarvest::default(),
load_avg_harvest: cpu::LoadAvgHarvest::default(), load_avg_harvest: cpu::LoadAvgHarvest::default(),
@ -206,11 +212,17 @@ impl DataCollection {
self.eat_network(network, &mut new_entry); self.eat_network(network, &mut new_entry);
} }
// Memory and Swap // Memory, Swap
if let (Some(memory), Some(swap)) = (harvested_data.memory, harvested_data.swap) { if let (Some(memory), Some(swap)) = (harvested_data.memory, harvested_data.swap) {
self.eat_memory_and_swap(memory, swap, &mut new_entry); 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")] #[cfg(feature = "zfs")]
if let Some(arc) = harvested_data.arc { if let Some(arc) = harvested_data.arc {
self.eat_arc(arc, &mut new_entry); self.eat_arc(arc, &mut new_entry);
@ -275,6 +287,15 @@ impl DataCollection {
self.swap_harvest = swap; 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) { fn eat_network(&mut self, network: network::NetworkHarvest, new_entry: &mut TimedData) {
// RX // RX
if network.rx > 0 { if network.rx > 0 {

View file

@ -31,6 +31,8 @@ pub struct Data {
pub cpu: Option<cpu::CpuHarvest>, pub cpu: Option<cpu::CpuHarvest>,
pub load_avg: Option<cpu::LoadAvgHarvest>, pub load_avg: Option<cpu::LoadAvgHarvest>,
pub memory: Option<memory::MemHarvest>, pub memory: Option<memory::MemHarvest>,
#[cfg(not(target_os = "windows"))]
pub cache: Option<memory::MemHarvest>,
pub swap: Option<memory::MemHarvest>, pub swap: Option<memory::MemHarvest>,
pub temperature_sensors: Option<Vec<temperature::TempHarvest>>, pub temperature_sensors: Option<Vec<temperature::TempHarvest>>,
pub network: Option<network::NetworkHarvest>, pub network: Option<network::NetworkHarvest>,
@ -52,6 +54,8 @@ impl Default for Data {
cpu: None, cpu: None,
load_avg: None, load_avg: None,
memory: None, memory: None,
#[cfg(not(target_os = "windows"))]
cache: None,
swap: None, swap: None,
temperature_sensors: None, temperature_sensors: None,
list_of_processes: None, list_of_processes: None,
@ -404,6 +408,12 @@ impl DataCollector {
fn update_memory_usage(&mut self) { fn update_memory_usage(&mut self) {
if self.widgets_to_harvest.use_mem { if self.widgets_to_harvest.use_mem {
self.data.memory = memory::get_ram_usage(&self.sys); 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( self.data.swap = memory::get_swap_usage(
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
&self.sys, &self.sys,

View file

@ -1,8 +1,10 @@
//! Memory data collection. //! 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(crate) use self::sysinfo::get_ram_usage;
pub mod sysinfo;
cfg_if::cfg_if! { cfg_if::cfg_if! {
if #[cfg(target_os = "windows")] { if #[cfg(target_os = "windows")] {
pub mod windows; pub mod windows;

View file

@ -36,3 +36,25 @@ pub(crate) fn get_swap_usage(sys: &System) -> Option<MemHarvest> {
}, },
}) })
} }
/// 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<MemHarvest> {
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)
},
})
}

View file

@ -997,6 +997,7 @@ Supported widget names:
pub struct UsedWidgets { pub struct UsedWidgets {
pub use_cpu: bool, pub use_cpu: bool,
pub use_mem: bool, pub use_mem: bool,
pub use_cache: bool,
pub use_gpu: bool, pub use_gpu: bool,
pub use_net: bool, pub use_net: bool,
pub use_proc: bool, pub use_proc: bool,

View file

@ -20,6 +20,13 @@ use std::{
}; };
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use crossterm::{
event::{EnableBracketedPaste, EnableMouseCapture},
execute,
terminal::{enable_raw_mode, EnterAlternateScreen},
};
use tui::{backend::CrosstermBackend, Terminal};
use bottom::{ use bottom::{
canvas::{self, canvas_styling::CanvasColours}, canvas::{self, canvas_styling::CanvasColours},
constants::*, constants::*,
@ -27,12 +34,6 @@ use bottom::{
options::*, options::*,
*, *,
}; };
use crossterm::{
event::{EnableBracketedPaste, EnableMouseCapture},
execute,
terminal::{enable_raw_mode, EnterAlternateScreen},
};
use tui::{backend::CrosstermBackend, Terminal};
// #[global_allocator] // #[global_allocator]
// static ALLOC: dhat::Alloc = dhat::Alloc; // static ALLOC: dhat::Alloc = dhat::Alloc;
@ -245,6 +246,11 @@ fn main() -> Result<()> {
if app.used_widgets.use_mem { if app.used_widgets.use_mem {
app.converted_data.mem_data = app.converted_data.mem_data =
convert_mem_data_points(&app.data_collection); 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 = app.converted_data.swap_data =
convert_swap_data_points(&app.data_collection); convert_swap_data_points(&app.data_collection);
#[cfg(feature = "zfs")] #[cfg(feature = "zfs")]
@ -257,11 +263,17 @@ fn main() -> Result<()> {
app.converted_data.gpu_data = app.converted_data.gpu_data =
convert_gpu_data(&app.data_collection); 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.mem_labels =
app.converted_data.swap_labels = swap_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")] #[cfg(feature = "zfs")]
{ {
let arc_labels = convert_arc_labels(&app.data_collection); let arc_labels = convert_arc_labels(&app.data_collection);

View file

@ -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")] #[cfg(feature = "gpu")]
{ {
if let Some(gpu_data) = &app_state.converted_data.gpu_data { if let Some(gpu_data) = &app_state.converted_data.gpu_data {

View file

@ -18,6 +18,8 @@ pub struct CanvasColours {
pub currently_selected_text_style: Style, pub currently_selected_text_style: Style,
pub table_header_style: Style, pub table_header_style: Style,
pub ram_style: Style, pub ram_style: Style,
#[cfg(not(target_os = "windows"))]
pub cache_style: Style,
pub swap_style: Style, pub swap_style: Style,
pub arc_style: Style, pub arc_style: Style,
pub gpu_colour_styles: Vec<Style>, pub gpu_colour_styles: Vec<Style>,
@ -54,6 +56,8 @@ impl Default for CanvasColours {
.bg(currently_selected_bg_colour), .bg(currently_selected_bg_colour),
table_header_style: Style::default().fg(HIGHLIGHT_COLOUR), table_header_style: Style::default().fg(HIGHLIGHT_COLOUR),
ram_style: Style::default().fg(FIRST_COLOUR), ram_style: Style::default().fg(FIRST_COLOUR),
#[cfg(not(target_os = "windows"))]
cache_style: Style::default().fg(FIFTH_COLOUR),
swap_style: Style::default().fg(SECOND_COLOUR), swap_style: Style::default().fg(SECOND_COLOUR),
arc_style: Style::default().fg(THIRD_COLOUR), arc_style: Style::default().fg(THIRD_COLOUR),
gpu_colour_styles: vec![ gpu_colour_styles: vec![
@ -162,6 +166,12 @@ impl CanvasColours {
.context("Update 'ram_color' in your config file..")?; .context("Update 'ram_color' in your config file..")?;
} }
#[cfg(not(target_os = "windows"))]
if let Some(cache_color) = &colours.cache_color {
self.set_cache_colour(cache_color)
.context("Update 'cache_color' in your config file..")?;
}
if let Some(swap_color) = &colours.swap_color { if let Some(swap_color) = &colours.swap_color {
self.set_swap_colour(swap_color) self.set_swap_colour(swap_color)
.context("Update 'swap_color' in your config file..")?; .context("Update 'swap_color' in your config file..")?;
@ -275,6 +285,12 @@ impl CanvasColours {
Ok(()) Ok(())
} }
#[cfg(not(target_os = "windows"))]
pub fn set_cache_colour(&mut self, colour: &str) -> error::Result<()> {
self.cache_style = str_to_fg(colour)?;
Ok(())
}
pub fn set_swap_colour(&mut self, colour: &str) -> error::Result<()> { pub fn set_swap_colour(&mut self, colour: &str) -> error::Result<()> {
self.swap_style = str_to_fg(colour)?; self.swap_style = str_to_fg(colour)?;
Ok(()) Ok(())

View file

@ -9,6 +9,8 @@ pub const FIRST_COLOUR: Color = Color::LightMagenta;
pub const SECOND_COLOUR: Color = Color::LightYellow; pub const SECOND_COLOUR: Color = Color::LightYellow;
pub const THIRD_COLOUR: Color = Color::LightCyan; pub const THIRD_COLOUR: Color = Color::LightCyan;
pub const FOURTH_COLOUR: Color = Color::LightGreen; pub const FOURTH_COLOUR: Color = Color::LightGreen;
#[cfg(not(target_os = "windows"))]
pub const FIFTH_COLOUR: Color = Color::LightRed;
pub const HIGHLIGHT_COLOUR: Color = Color::LightBlue; pub const HIGHLIGHT_COLOUR: Color = Color::LightBlue;
pub const AVG_COLOUR: Color = Color::Red; pub const AVG_COLOUR: Color = Color::Red;
pub const ALL_COLOUR: Color = Color::Green; pub const ALL_COLOUR: Color = Color::Green;

View file

@ -53,6 +53,33 @@ impl Painter {
.gauge_style(self.colours.ram_style), .gauge_style(self.colours.ram_style),
); );
#[cfg(not(target_os = "windows"))]
{
if let Some((_, label_frac)) = &app_state.converted_data.cache_labels {
let cache_data = &app_state.converted_data.cache_data;
let cache_percentage = if let Some(cache) = cache_data.last() {
cache.1
} else {
0.0
};
let cache_fraction_label = if app_state.basic_mode_use_percent {
format!("{:3.0}%", cache_percentage.round())
} else {
label_frac.trim().to_string()
};
draw_widgets.push(
PipeGauge::default()
.ratio(cache_percentage / 100.0)
.start_label("CHE")
.inner_label(cache_fraction_label)
.label_style(self.colours.cache_style)
.gauge_style(self.colours.cache_style),
);
}
}
let swap_data = &app_state.converted_data.swap_data; let swap_data = &app_state.converted_data.swap_data;
let swap_percentage = if let Some(swap) = swap_data.last() { let swap_percentage = if let Some(swap) = swap_data.last() {

View file

@ -56,6 +56,15 @@ impl Painter {
name: Some(mem_label.into()), name: Some(mem_label.into()),
}); });
} }
#[cfg(not(target_os = "windows"))]
if let Some((label_percent, label_frac)) = &app_state.converted_data.cache_labels {
let cache_label = format!("CHE:{}{}", label_percent, label_frac);
points.push(GraphData {
points: &app_state.converted_data.cache_data,
style: self.colours.cache_style,
name: Some(cache_label.into()),
});
}
if let Some((label_percent, label_frac)) = &app_state.converted_data.swap_labels { if let Some((label_percent, label_frac)) = &app_state.converted_data.swap_labels {
let swap_label = format!("SWP:{}{}", label_percent, label_frac); let swap_label = format!("SWP:{}{}", label_percent, label_frac);
points.push(GraphData { points.push(GraphData {

View file

@ -441,6 +441,14 @@ use CPU (3) as the default instead.
app = app.arg(enable_gpu_memory); app = app.arg(enable_gpu_memory);
} }
#[cfg(not(target_os = "windows"))]
{
let cache = Arg::new("enable_cache_memory")
.long("enable_cache_memory")
.help("Enable collecting and displaying cache and buffer memory.");
app = app.arg(cache);
}
app app
} }

View file

@ -37,6 +37,8 @@ pub static DEFAULT_LIGHT_MODE_COLOUR_PALETTE: Lazy<ConfigColours> = Lazy::new(||
graph_color: Some("black".into()), graph_color: Some("black".into()),
disabled_text_color: Some("gray".into()), disabled_text_color: Some("gray".into()),
ram_color: Some("blue".into()), ram_color: Some("blue".into()),
#[cfg(not(target_os = "windows"))]
cache_color: Some("LightRed".into()),
swap_color: Some("red".into()), swap_color: Some("red".into()),
arc_color: Some("LightBlue".into()), arc_color: Some("LightBlue".into()),
gpu_core_colors: Some(vec![ gpu_core_colors: Some(vec![
@ -91,6 +93,8 @@ pub static GRUVBOX_COLOUR_PALETTE: Lazy<ConfigColours> = Lazy::new(|| ConfigColo
"#af3a03".into(), "#af3a03".into(),
]), ]),
ram_color: Some("#8ec07c".into()), ram_color: Some("#8ec07c".into()),
#[cfg(not(target_os = "windows"))]
cache_color: Some("#b16286".into()),
swap_color: Some("#fabd2f".into()), swap_color: Some("#fabd2f".into()),
arc_color: Some("#689d6a".into()), arc_color: Some("#689d6a".into()),
gpu_core_colors: Some(vec![ gpu_core_colors: Some(vec![
@ -146,6 +150,8 @@ pub static GRUVBOX_LIGHT_COLOUR_PALETTE: Lazy<ConfigColours> = Lazy::new(|| Conf
"#af3a03".into(), "#af3a03".into(),
]), ]),
ram_color: Some("#427b58".into()), ram_color: Some("#427b58".into()),
#[cfg(not(target_os = "windows"))]
cache_color: Some("d79921".into()),
swap_color: Some("#cc241d".into()), swap_color: Some("#cc241d".into()),
arc_color: Some("#689d6a".into()), arc_color: Some("#689d6a".into()),
gpu_core_colors: Some(vec![ gpu_core_colors: Some(vec![
@ -189,6 +195,8 @@ pub static NORD_COLOUR_PALETTE: Lazy<ConfigColours> = Lazy::new(|| ConfigColours
"#bf616a".into(), "#bf616a".into(),
]), ]),
ram_color: Some("#88c0d0".into()), ram_color: Some("#88c0d0".into()),
#[cfg(not(target_os = "windows"))]
cache_color: Some("#d8dee9".into()),
swap_color: Some("#d08770".into()), swap_color: Some("#d08770".into()),
arc_color: Some("#5e81ac".into()), arc_color: Some("#5e81ac".into()),
gpu_core_colors: Some(vec![ gpu_core_colors: Some(vec![
@ -232,6 +240,8 @@ pub static NORD_LIGHT_COLOUR_PALETTE: Lazy<ConfigColours> = Lazy::new(|| ConfigC
"#bf616a".into(), "#bf616a".into(),
]), ]),
ram_color: Some("#81a1c1".into()), ram_color: Some("#81a1c1".into()),
#[cfg(not(target_os = "windows"))]
cache_color: Some("#4c566a".into()),
swap_color: Some("#d08770".into()), swap_color: Some("#d08770".into()),
arc_color: Some("#5e81ac".into()), arc_color: Some("#5e81ac".into()),
gpu_core_colors: Some(vec![ gpu_core_colors: Some(vec![

View file

@ -3,6 +3,7 @@
use kstring::KString; use kstring::KString;
use crate::app::data_harvester::memory::MemHarvest;
use crate::app::{ use crate::app::{
data_farmer::DataCollection, data_farmer::DataCollection,
data_harvester::{cpu::CpuDataType, temperature::TemperatureType}, data_harvester::{cpu::CpuDataType, temperature::TemperatureType},
@ -68,9 +69,13 @@ pub struct ConvertedData {
pub network_data_tx: Vec<Point>, pub network_data_tx: Vec<Point>,
pub mem_labels: Option<(String, String)>, pub mem_labels: Option<(String, String)>,
#[cfg(not(target_os = "windows"))]
pub cache_labels: Option<(String, String)>,
pub swap_labels: Option<(String, String)>, pub swap_labels: Option<(String, String)>,
pub mem_data: Vec<Point>, /* TODO: Switch this and all data points over to a better data structure... */ pub mem_data: Vec<Point>, /* TODO: Switch this and all data points over to a better data structure... */
#[cfg(not(target_os = "windows"))]
pub cache_data: Vec<Point>,
pub swap_data: Vec<Point>, pub swap_data: Vec<Point>,
#[cfg(feature = "zfs")] #[cfg(feature = "zfs")]
@ -219,6 +224,25 @@ pub fn convert_mem_data_points(current_data: &DataCollection) -> Vec<Point> {
result result
} }
#[cfg(not(target_os = "windows"))]
pub fn convert_cache_data_points(current_data: &DataCollection) -> Vec<Point> {
let mut result: Vec<Point> = Vec::new();
let current_time = current_data.current_instant;
for (time, data) in &current_data.timed_data_vec {
if let Some(cache_data) = data.cache_data {
let time_from_start: f64 =
(current_time.duration_since(*time).as_millis() as f64).floor();
result.push((-time_from_start, cache_data));
if *time == current_time {
break;
}
}
}
result
}
pub fn convert_swap_data_points(current_data: &DataCollection) -> Vec<Point> { pub fn convert_swap_data_points(current_data: &DataCollection) -> Vec<Point> {
let mut result: Vec<Point> = Vec::new(); let mut result: Vec<Point> = Vec::new();
let current_time = current_data.current_instant; let current_time = current_data.current_instant;
@ -257,52 +281,23 @@ fn get_mem_binary_unit_and_denominator(bytes: u64) -> (&'static str, f64) {
} }
} }
pub fn convert_mem_labels( /// Returns the unit type and denominator for given total amount of memory in kibibytes.
current_data: &DataCollection, pub fn convert_mem_label(harvest: &MemHarvest) -> Option<(String, String)> {
) -> (Option<(String, String)>, Option<(String, String)>) { if harvest.total_bytes > 0 {
( Some((format!("{:3.0}%", harvest.use_percent.unwrap_or(0.0)), {
if current_data.memory_harvest.total_bytes > 0 { let (unit, denominator) = get_mem_binary_unit_and_denominator(harvest.total_bytes);
Some((
format!(
"{:3.0}%",
current_data.memory_harvest.use_percent.unwrap_or(0.0)
),
{
let (unit, denominator) = get_mem_binary_unit_and_denominator(
current_data.memory_harvest.total_bytes,
);
format!( format!(
" {:.1}{unit}/{:.1}{unit}", " {:.1}{}/{:.1}{}",
current_data.memory_harvest.used_bytes as f64 / denominator, harvest.used_bytes as f64 / denominator,
(current_data.memory_harvest.total_bytes as f64 / denominator), unit,
(harvest.total_bytes as f64 / denominator),
unit
) )
}, }))
))
} else { } else {
None None
}, }
if current_data.swap_harvest.total_bytes > 0 {
Some((
format!(
"{:3.0}%",
current_data.swap_harvest.use_percent.unwrap_or(0.0)
),
{
let (unit, denominator) =
get_mem_binary_unit_and_denominator(current_data.swap_harvest.total_bytes);
format!(
" {:.1}{unit}/{:.1}{unit}",
current_data.swap_harvest.used_bytes as f64 / denominator,
(current_data.swap_harvest.total_bytes as f64 / denominator),
)
},
))
} else {
None
},
)
} }
pub fn get_rx_tx_data_points( pub fn get_rx_tx_data_points(

View file

@ -386,6 +386,10 @@ pub fn update_data(app: &mut App) {
// TODO: [OPT] Prefer reassignment over new vectors? // TODO: [OPT] Prefer reassignment over new vectors?
if app.mem_state.force_update.is_some() { if app.mem_state.force_update.is_some() {
app.converted_data.mem_data = convert_mem_data_points(data_source); app.converted_data.mem_data = convert_mem_data_points(data_source);
#[cfg(not(target_os = "windows"))]
{
app.converted_data.cache_data = convert_cache_data_points(data_source);
}
app.converted_data.swap_data = convert_swap_data_points(data_source); app.converted_data.swap_data = convert_swap_data_points(data_source);
#[cfg(feature = "zfs")] #[cfg(feature = "zfs")]
{ {

View file

@ -79,6 +79,7 @@ pub struct ConfigFlags {
pub network_use_log: Option<bool>, pub network_use_log: Option<bool>,
pub network_use_binary_prefix: Option<bool>, pub network_use_binary_prefix: Option<bool>,
pub enable_gpu_memory: Option<bool>, pub enable_gpu_memory: Option<bool>,
pub enable_cache_memory: Option<bool>,
#[serde(with = "humantime_serde")] #[serde(with = "humantime_serde")]
#[serde(default)] #[serde(default)]
pub retention: Option<Duration>, pub retention: Option<Duration>,
@ -91,6 +92,8 @@ pub struct ConfigColours {
pub avg_cpu_color: Option<Cow<'static, str>>, pub avg_cpu_color: Option<Cow<'static, str>>,
pub cpu_core_colors: Option<Vec<Cow<'static, str>>>, pub cpu_core_colors: Option<Vec<Cow<'static, str>>>,
pub ram_color: Option<Cow<'static, str>>, pub ram_color: Option<Cow<'static, str>>,
#[cfg(not(target_os = "windows"))]
pub cache_color: Option<Cow<'static, str>>,
pub swap_color: Option<Cow<'static, str>>, pub swap_color: Option<Cow<'static, str>>,
pub arc_color: Option<Cow<'static, str>>, pub arc_color: Option<Cow<'static, str>>,
pub gpu_core_colors: Option<Vec<Cow<'static, str>>>, pub gpu_core_colors: Option<Vec<Cow<'static, str>>>,
@ -235,6 +238,7 @@ pub fn build_app(
table_gap: u16::from(!(is_flag_enabled!(hide_table_gap, matches, config))), table_gap: u16::from(!(is_flag_enabled!(hide_table_gap, matches, config))),
disable_click: is_flag_enabled!(disable_click, matches, config), disable_click: is_flag_enabled!(disable_click, matches, config),
enable_gpu_memory: get_enable_gpu_memory(matches, config), enable_gpu_memory: get_enable_gpu_memory(matches, config),
enable_cache_memory: get_enable_cache_memory(matches, config),
show_table_scroll_position: is_flag_enabled!(show_table_scroll_position, matches, config), show_table_scroll_position: is_flag_enabled!(show_table_scroll_position, matches, config),
is_advanced_kill, is_advanced_kill,
network_scale_type, network_scale_type,
@ -382,6 +386,7 @@ pub fn build_app(
let used_widgets = UsedWidgets { let used_widgets = UsedWidgets {
use_cpu: used_widget_set.get(&Cpu).is_some() || used_widget_set.get(&BasicCpu).is_some(), use_cpu: used_widget_set.get(&Cpu).is_some() || used_widget_set.get(&BasicCpu).is_some(),
use_mem, use_mem,
use_cache: use_mem && get_enable_cache_memory(matches, config),
use_gpu: use_mem && get_enable_gpu_memory(matches, config), use_gpu: use_mem && get_enable_gpu_memory(matches, config),
use_net: used_widget_set.get(&Net).is_some() || used_widget_set.get(&BasicNet).is_some(), use_net: used_widget_set.get(&Net).is_some() || used_widget_set.get(&BasicNet).is_some(),
use_proc: used_widget_set.get(&Proc).is_some(), use_proc: used_widget_set.get(&Proc).is_some(),
@ -712,6 +717,22 @@ fn get_enable_gpu_memory(matches: &ArgMatches, config: &Config) -> bool {
false false
} }
#[allow(unused_variables)]
fn get_enable_cache_memory(matches: &ArgMatches, config: &Config) -> bool {
#[cfg(not(target_os = "windows"))]
{
if matches.contains_id("enable_cache_memory") {
return true;
} else if let Some(flags) = &config.flags {
if let Some(enable_cache_memory) = flags.enable_cache_memory {
return enable_cache_memory;
}
}
}
false
}
fn get_ignore_list(ignore_list: &Option<IgnoreList>) -> error::Result<Option<Filter>> { fn get_ignore_list(ignore_list: &Option<IgnoreList>) -> error::Result<Option<Filter>> {
if let Some(ignore_list) = ignore_list { if let Some(ignore_list) = ignore_list {
let list: Result<Vec<_>, _> = ignore_list let list: Result<Vec<_>, _> = ignore_list