Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ That said, these are more guidelines rather than hard rules, though the project
- [#2066](https://github.com/ClementTsang/bottom/pull/2066): Add search support in the help dialog.
- [#1791](https://github.com/ClementTsang/bottom/pull/1791), [#2072](https://github.com/ClementTsang/bottom/pull/2072): Add support for using a short name for the GPU in memory usage.
- [#2073](https://github.com/ClementTsang/bottom/pull/2073): Add disk I/O time series graph.
- [#2081](https://github.com/ClementTsang/bottom/pull/2081): Add disk usage time series graph.

### Changes

Expand Down
190 changes: 190 additions & 0 deletions schema/nightly/bottom.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,26 @@
}
]
},
"disk_io_graph": {
"anyOf": [
{
"$ref": "#/$defs/DiskIoGraphConfig"
},
{
"type": "null"
}
]
},
"disk_space_graph": {
"anyOf": [
{
"$ref": "#/$defs/DiskSpaceGraphConfig"
},
{
"type": "null"
}
]
},
"flags": {
"anyOf": [
{
Expand Down Expand Up @@ -281,6 +301,154 @@
}
}
},
"DiskGraphLegend": {
"description": "Whether the disk graph legend labels use device names or mount points.",
"oneOf": [
{
"description": "Label entries by kernel device name (e.g. `sda`, `nvme0n1`).",
"type": "string",
"const": "disk"
},
{
"description": "Label entries by mount point (e.g. `/`, `/home`).",
"type": "string",
"const": "mount"
}
]
},
"DiskIoGraphConfig": {
"description": "Disk I/O graph configuration.",
"type": "object",
"properties": {
"legend": {
"description": "Whether to label legend entries by device name or mount point. Defaults to disk name.",
"anyOf": [
{
"$ref": "#/$defs/DiskGraphLegend"
},
{
"type": "null"
}
]
},
"legend_position": {
"description": "Where to position the legend within the widget.",
"type": [
"string",
"null"
]
},
"name_filter": {
"description": "An optional list of device names to include or exclude.",
"anyOf": [
{
"$ref": "#/$defs/IgnoreList"
},
{
"type": "null"
}
]
},
"show_read": {
"description": "Whether to show the read rate line. Defaults to true.",
"type": [
"boolean",
"null"
]
},
"show_write": {
"description": "Whether to show the write rate line. Defaults to true.",
"type": [
"boolean",
"null"
]
},
"use_log": {
"description": "Whether to use a logarithmic scale on the y-axis. Defaults to false.",
"type": [
"boolean",
"null"
]
}
}
},
"DiskIoGraphStyle": {
"description": "Styling specific to the disk I/O graph widget.",
"type": "object",
"properties": {
"read_colours": {
"description": "Colour of each disk's read rate graph line. Read in order.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/ColourStr"
}
},
"write_colours": {
"description": "Colour of each disk's write rate graph line. Read in order.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/ColourStr"
}
}
}
},
"DiskSpaceGraphConfig": {
"description": "Disk space graph configuration.",
"type": "object",
"properties": {
"legend": {
"description": "Whether to label legend entries by device name or mount point. Defaults to disk name.",
"anyOf": [
{
"$ref": "#/$defs/DiskGraphLegend"
},
{
"type": "null"
}
]
},
"legend_position": {
"description": "Where to position the legend within the widget.",
"type": [
"string",
"null"
]
},
"name_filter": {
"description": "An optional list of device names to include or exclude.",
"anyOf": [
{
"$ref": "#/$defs/IgnoreList"
},
{
"type": "null"
}
]
}
}
},
"DiskSpaceGraphStyle": {
"description": "Styling specific to the disk space graph widget.",
"type": "object",
"properties": {
"colours": {
"description": "Colour of each disk's used-space graph line. Read in order.",
"type": [
"array",
"null"
],
"items": {
"$ref": "#/$defs/ColourStr"
}
}
}
},
"DiskWidgetColumn": {
"type": "string",
"enum": [
Expand Down Expand Up @@ -1134,6 +1302,28 @@
}
]
},
"disk_io_graph": {
"description": "Styling for the disk I/O graph widget.",
"anyOf": [
{
"$ref": "#/$defs/DiskIoGraphStyle"
},
{
"type": "null"
}
]
},
"disk_space_graph": {
"description": "Styling for the disk space graph widget.",
"anyOf": [
{
"$ref": "#/$defs/DiskSpaceGraphStyle"
},
{
"type": "null"
}
]
},
"graphs": {
"description": "Styling for graph widgets.",
"anyOf": [
Expand Down
2 changes: 2 additions & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ pub struct AppConfigFields {
pub default_disk_sort_column: Option<DiskWidgetColumn>,
pub temperature_legend_position: Option<LegendPosition>,
pub disk_io_legend_position: Option<LegendPosition>,
pub disk_space_legend_position: Option<LegendPosition>,
}

/// For filtering out information
Expand All @@ -92,6 +93,7 @@ pub struct DataFilters {
pub temp_filter: Option<Filter>,
pub temp_graph_filter: Option<Filter>,
pub disk_io_graph_filter: Option<Filter>,
pub disk_space_graph_filter: Option<Filter>,
pub net_filter: Option<Filter>,
}

Expand Down
26 changes: 17 additions & 9 deletions src/app/data/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,19 @@ impl StoredData {
.unwrap_or_default();

if let Some(disks) = data.disks {
if let Some(io) = data.io {
self.eat_disks(disks, io, harvested_time);
// Disk space only needs the usage data, while I/O rates additionally
// need `data.io`. Keep them separate so a missing I/O harvest doesn't
// also stop the disk space graph from updating.
self.eat_disks(disks, data.io, harvested_time);

if used_widgets.use_disk_io_graph {
self.time_series_data
.update_disk_io(&self.disk_harvest, &filters.disk_io_graph_filter);
}

if used_widgets.use_disk_io_graph {
self.time_series_data
.update_disk_io(&self.disk_harvest, &filters.disk_io_graph_filter);
}
if used_widgets.use_disk_space_graph {
self.time_series_data
.update_disk_space(&self.disk_harvest, &filters.disk_space_graph_filter);
}
}

Expand All @@ -177,7 +183,9 @@ impl StoredData {
}

// TODO: There's a spike on the first hit. We should probably fix this and the index issue.
fn eat_disks(&mut self, disks: Vec<DiskHarvest>, io: IoHarvest, harvested_time: Instant) {
fn eat_disks(
&mut self, disks: Vec<DiskHarvest>, io: Option<IoHarvest>, harvested_time: Instant,
) {
let time_since_last_harvest = harvested_time
.duration_since(self.last_update_time)
.as_secs_f64();
Expand Down Expand Up @@ -219,7 +227,7 @@ impl StoredData {
continue;
};

let io_device = {
let io_device = io.as_ref().and_then(|io| {
#[cfg(target_os = "macos")]
{
use std::sync::OnceLock;
Expand All @@ -246,7 +254,7 @@ impl StoredData {
{
io.get(checked_name)
}
};
});

let (mut io_read_rate_bytes, mut io_write_rate_bytes) = (None, None);
if let Some(Some(io_device)) = io_device {
Expand Down
Loading
Loading