summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorBrooks Rady <b.j.rady@gmail.com>2020-04-05 17:30:43 +0100
committerGitHub <noreply@github.com>2020-04-05 18:30:43 +0200
commit62a39464d7893cbb747f321c3bb662a0d939de14 (patch)
treeaeae4458ff213d4199805c10b880fe70994d50f4 /src
parent1569f6038df215a85532fe2e77a8ed47ca797c0b (diff)
feat(network): add accumlated usage (#155)
* Add accumlated usage * Fixed a nasty performace leak * Fix a silly reverse-ordering bug * Address some feedback * Finished some reorganizing * Another silly reversal bug * Let the test cases run * Most tests are working * Fix some formatting that got left behind * Fix the rest of the tests * Add some new tests to cover the total mode * Whoops, a double newline
Diffstat (limited to 'src')
-rw-r--r--src/display/components/display_bandwidth.rs20
-rw-r--r--src/display/components/table.rs61
-rw-r--r--src/display/components/total_bandwidth.rs13
-rw-r--r--src/display/ui.rs4
-rw-r--r--src/display/ui_state.rs118
-rw-r--r--src/main.rs3
-rw-r--r--src/tests/cases/raw_mode.rs3
-rw-r--r--src/tests/cases/snapshots/raw_mode__multiple_connections_from_remote_address.snap2
-rw-r--r--src/tests/cases/snapshots/raw_mode__multiple_processes_with_multiple_connections.snap12
-rw-r--r--src/tests/cases/snapshots/raw_mode__one_process_with_multiple_connections.snap2
-rw-r--r--src/tests/cases/snapshots/ui__basic_only_addresses.snap4
-rw-r--r--src/tests/cases/snapshots/ui__basic_only_connections.snap4
-rw-r--r--src/tests/cases/snapshots/ui__basic_only_processes.snap4
-rw-r--r--src/tests/cases/snapshots/ui__basic_startup.snap6
-rw-r--r--src/tests/cases/snapshots/ui__bi_directional_traffic-2.snap2
-rw-r--r--src/tests/cases/snapshots/ui__bi_directional_traffic.snap6
-rw-r--r--src/tests/cases/snapshots/ui__layout_full_width_under_30_height-2.snap4
-rw-r--r--src/tests/cases/snapshots/ui__layout_full_width_under_30_height.snap4
-rw-r--r--src/tests/cases/snapshots/ui__layout_under_120_width_full_height-2.snap6
-rw-r--r--src/tests/cases/snapshots/ui__layout_under_120_width_full_height.snap6
-rw-r--r--src/tests/cases/snapshots/ui__layout_under_120_width_under_30_height-2.snap4
-rw-r--r--src/tests/cases/snapshots/ui__layout_under_120_width_under_30_height.snap4
-rw-r--r--src/tests/cases/snapshots/ui__multiple_connections_from_remote_address-2.snap2
-rw-r--r--src/tests/cases/snapshots/ui__multiple_connections_from_remote_address.snap6
-rw-r--r--src/tests/cases/snapshots/ui__multiple_packets_of_traffic_from_different_connections-2.snap2
-rw-r--r--src/tests/cases/snapshots/ui__multiple_packets_of_traffic_from_different_connections.snap6
-rw-r--r--src/tests/cases/snapshots/ui__multiple_packets_of_traffic_from_single_connection-2.snap2
-rw-r--r--src/tests/cases/snapshots/ui__multiple_packets_of_traffic_from_single_connection.snap6
-rw-r--r--src/tests/cases/snapshots/ui__multiple_processes_with_multiple_connections-2.snap6
-rw-r--r--src/tests/cases/snapshots/ui__multiple_processes_with_multiple_connections.snap6
-rw-r--r--src/tests/cases/snapshots/ui__no_resolve_mode-2.snap2
-rw-r--r--src/tests/cases/snapshots/ui__no_resolve_mode.snap2
-rw-r--r--src/tests/cases/snapshots/ui__one_packet_of_traffic-2.snap2
-rw-r--r--src/tests/cases/snapshots/ui__one_packet_of_traffic.snap6
-rw-r--r--src/tests/cases/snapshots/ui__one_process_with_multiple_connections-2.snap2
-rw-r--r--src/tests/cases/snapshots/ui__one_process_with_multiple_connections.snap6
-rw-r--r--src/tests/cases/snapshots/ui__pause_by_space-2.snap2
-rw-r--r--src/tests/cases/snapshots/ui__pause_by_space.snap6
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_multiple_processes-2.snap2
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_multiple_processes.snap2
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_multiple_processes_bi_directional-2.snap2
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_multiple_processes_bi_directional.snap2
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_multiple_processes_bi_directional_total-2.snap55
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_multiple_processes_bi_directional_total.snap55
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_multiple_processes_total-2.snap55
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_multiple_processes_total.snap55
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_one_process-2.snap2
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_one_process.snap2
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_one_process_total-2.snap55
-rw-r--r--src/tests/cases/snapshots/ui__sustained_traffic_from_one_process_total.snap55
-rw-r--r--src/tests/cases/snapshots/ui__traffic_with_host_names-2.snap2
-rw-r--r--src/tests/cases/snapshots/ui__traffic_with_host_names.snap2
-rw-r--r--src/tests/cases/snapshots/ui__traffic_with_winch_event-3.snap2
-rw-r--r--src/tests/cases/snapshots/ui__traffic_with_winch_event.snap6
-rw-r--r--src/tests/cases/snapshots/ui__truncate_long_hostnames-2.snap2
-rw-r--r--src/tests/cases/snapshots/ui__truncate_long_hostnames.snap2
-rw-r--r--src/tests/cases/snapshots/ui__two_packets_only_addresses-2.snap2
-rw-r--r--src/tests/cases/snapshots/ui__two_packets_only_addresses.snap4
-rw-r--r--src/tests/cases/snapshots/ui__two_packets_only_connections-2.snap2
-rw-r--r--src/tests/cases/snapshots/ui__two_packets_only_connections.snap4
-rw-r--r--src/tests/cases/snapshots/ui__two_packets_only_processes-2.snap2
-rw-r--r--src/tests/cases/snapshots/ui__two_packets_only_processes.snap4
-rw-r--r--src/tests/cases/snapshots/ui__two_windows_split_horizontally.snap6
-rw-r--r--src/tests/cases/snapshots/ui__two_windows_split_vertically.snap4
-rw-r--r--src/tests/cases/test_utils.rs1
-rw-r--r--src/tests/cases/ui.rs195
66 files changed, 757 insertions, 181 deletions
diff --git a/src/display/components/display_bandwidth.rs b/src/display/components/display_bandwidth.rs
index 31653e6..dcc204c 100644
--- a/src/display/components/display_bandwidth.rs
+++ b/src/display/components/display_bandwidth.rs
@@ -1,17 +1,21 @@
use ::std::fmt;
-pub struct DisplayBandwidth(pub f64);
+pub struct DisplayBandwidth {
+ pub bandwidth: f64,
+ pub as_rate: bool,
+}
impl fmt::Display for DisplayBandwidth {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- if self.0 > 999_999_999.0 {
- write!(f, "{:.2}GBps", self.0 / 1_000_000_000.0)
- } else if self.0 > 999_999.0 {
- write!(f, "{:.2}MBps", self.0 / 1_000_000.0)
- } else if self.0 > 999.0 {
- write!(f, "{:.2}KBps", self.0 / 1000.0)
+ let suffix = if self.as_rate { "ps" } else { "" };
+ if self.bandwidth > 999_999_999.0 {
+ write!(f, "{:.2}GB{}", self.bandwidth / 1_000_000_000.0, suffix)
+ } else if self.bandwidth > 999_999.0 {
+ write!(f, "{:.2}MB{}", self.bandwidth / 1_000_000.0, suffix)
+ } else if self.bandwidth > 999.0 {
+ write!(f, "{:.2}KB{}", self.bandwidth / 1000.0, suffix)
} else {
- write!(f, "{}Bps", self.0)
+ write!(f, "{}B{}", self.bandwidth, suffix)
}
}
}
diff --git a/src/display/components/table.rs b/src/display/components/table.rs
index 5786106..059b5ed 100644
--- a/src/display/components/table.rs
+++ b/src/display/components/table.rs
@@ -10,35 +10,21 @@ use crate::display::{Bandwidth, DisplayBandwidth, UIState};
use crate::network::{display_connection_string, display_ip_or_host};
use ::std::net::IpAddr;
-use std::iter::FromIterator;
-fn display_upload_and_download(bandwidth: &impl Bandwidth) -> String {
+fn display_upload_and_download(bandwidth: &impl Bandwidth, total: bool) -> String {
format!(
"{} / {}",
- DisplayBandwidth(bandwidth.get_total_bytes_uploaded() as f64),
- DisplayBandwidth(bandwidth.get_total_bytes_downloaded() as f64)
+ DisplayBandwidth {
+ bandwidth: bandwidth.get_total_bytes_uploaded() as f64,
+ as_rate: !total,
+ },
+ DisplayBandwidth {
+ bandwidth: bandwidth.get_total_bytes_downloaded() as f64,
+ as_rate: !total,
+ },
)
}
-fn sort_by_bandwidth<'a, T>(
- list: &'a mut Vec<(T, &impl Bandwidth)>,
-) -> &'a Vec<(T, &'a impl Bandwidth)> {
- list.sort_by(|(_, a), (_, b)| {
- let a_highest = if a.get_total_bytes_downloaded() > a.get_total_bytes_uploaded() {
- a.get_total_bytes_downloaded()
- } else {
- a.get_total_bytes_uploaded()
- };
- let b_highest = if b.get_total_bytes_downloaded() > b.get_total_bytes_uploaded() {
- b.get_total_bytes_downloaded()
- } else {
- b.get_total_bytes_uploaded()
- };
- b_highest.cmp(&a_highest)
- });
- list
-}
-
pub enum ColumnCount {
Two,
Three,
@@ -77,9 +63,8 @@ fn truncate_middle(row: &str, max_length: u16) -> String {
impl<'a> Table<'a> {
pub fn create_connections_table(state: &UIState, ip_to_host: &HashMap<IpAddr, String>) -> Self {
- let mut connections_list = Vec::from_iter(&state.connections);
- sort_by_bandwidth(&mut connections_list);
- let connections_rows = connections_list
+ let connections_rows = state
+ .connections
.iter()
.map(|(connection, connection_data)| {
vec![
@@ -89,12 +74,12 @@ impl<'a> Table<'a> {
&connection_data.interface_name,
),
connection_data.process_name.to_string(),
- display_upload_and_download(*connection_data),
+ display_upload_and_download(connection_data, state.cumulative_mode),
]
})
.collect();
let connections_title = "Utilization by connection";
- let connections_column_names = &["Connection", "Process", "Rate Up / Down"];
+ let connections_column_names = &["Connection", "Process", "Up / Down"];
let mut breakpoints = BTreeMap::new();
breakpoints.insert(
0,
@@ -132,20 +117,19 @@ impl<'a> Table<'a> {
}
}
pub fn create_processes_table(state: &UIState) -> Self {
- let mut processes_list = Vec::from_iter(&state.processes);
- sort_by_bandwidth(&mut processes_list);
- let processes_rows = processes_list
+ let processes_rows = state
+ .processes
.iter()
.map(|(process_name, data_for_process)| {
vec![
(*process_name).to_string(),
data_for_process.connection_count.to_string(),
- display_upload_and_download(*data_for_process),
+ display_upload_and_download(data_for_process, state.cumulative_mode),
]
})
.collect();
let processes_title = "Utilization by process name";
- let processes_column_names = &["Process", "Connections", "Rate Up / Down"];
+ let processes_column_names = &["Process", "Connections", "Up / Down"];
let mut breakpoints = BTreeMap::new();
breakpoints.insert(
0,
@@ -186,21 +170,20 @@ impl<'a> Table<'a> {
state: &UIState,
ip_to_host: &HashMap<IpAddr, String>,
) -> Self {
- let mut remote_addresses_list = Vec::from_iter(&state.remote_addresses);
- sort_by_bandwidth(&mut remote_addresses_list);
- let remote_addresses_rows = remote_addresses_list
+ let remote_addresses_rows = state
+ .remote_addresses
.iter()
.map(|(remote_address, data_for_remote_address)| {
- let remote_address = display_ip_or_host(**remote_address, &ip_to_host);
+ let remote_address = display_ip_or_host(*remote_address, &ip_to_host);
vec![
remote_address,
data_for_remote_address.connection_count.to_string(),
- display_upload_and_download(*data_for_remote_address),
+ display_upload_and_download(data_for_remote_address, state.cumulative_mode),
]
})
.collect();
let remote_addresses_title = "Utilization by remote address";
- let remote_addresses_column_names = &["Remote Address", "Connections", "Rate Up / Down"];
+ let remote_addresses_column_names = &["Remote Address", "Connections", "Up / Down"];
let mut breakpoints = BTreeMap::new();
breakpoints.insert(
0,
diff --git a/src/display/components/total_bandwidth.rs b/src/display/components/total_bandwidth.rs
index 4a7ae9d..80d892f 100644
--- a/src/display/components/total_bandwidth.rs
+++ b/src/display/components/total_bandwidth.rs
@@ -13,6 +13,7 @@ pub struct TotalBandwidth<'a> {
impl<'a> TotalBandwidth<'a> {
pub fn render(&self, frame: &mut Frame<impl Backend>, rect: Rect) {
+ let c_mode = self.state.cumulative_mode;
let title_text = {
let paused_str = if self.paused { "[PAUSED]" } else { "" };
let color = if self.paused {
@@ -23,9 +24,15 @@ impl<'a> TotalBandwidth<'a> {
[Text::styled(
format!(
- " Total Rate Up / Down: {} / {} {}",
- DisplayBandwidth(self.state.total_bytes_uploaded as f64),
- DisplayBandwidth(self.state.total_bytes_downloaded as f64),
+ " Total Up / Down: {} / {} {}",
+ DisplayBandwidth {
+ bandwidth: self.state.total_bytes_uploaded as f64,
+ as_rate: !c_mode,
+ },
+ DisplayBandwidth {
+ bandwidth: self.state.total_bytes_downloaded as f64,
+ as_rate: !c_mode,
+ },
paused_str
),
Style::default().fg(color).modifier(Modifier::BOLD),
diff --git a/src/display/ui.rs b/src/display/ui.rs
index 4530664..68f133a 100644
--- a/src/display/ui.rs
+++ b/src/display/ui.rs
@@ -30,9 +30,11 @@ where
let mut terminal = Terminal::new(terminal_backend).unwrap();
terminal.clear().unwrap();
terminal.hide_cursor().unwrap();
+ let mut state: UIState = Default::default();
+ state.cumulative_mode = opts.total_utilization;
Ui {
terminal,
- state: Default::default(),
+ state,
ip_to_host: Default::default(),
opts,
}
diff --git a/src/display/ui_state.rs b/src/display/ui_state.rs
index 4edd142..a65e610 100644
--- a/src/display/ui_state.rs
+++ b/src/display/ui_state.rs
@@ -1,23 +1,29 @@
-use ::std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
+use ::std::cmp;
+use ::std::collections::{HashMap, HashSet, VecDeque};
+use ::std::hash::Hash;
+use ::std::iter::FromIterator;
use ::std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use crate::network::{Connection, LocalSocket, Utilization};
static RECALL_LENGTH: usize = 5;
+static MAX_BANDWIDTH_ITEMS: usize = 1000;
pub trait Bandwidth {
fn get_total_bytes_downloaded(&self) -> u128;
fn get_total_bytes_uploaded(&self) -> u128;
+ fn combine_bandwidth(&mut self, other: &Self);
+ fn divide_by(&mut self, amount: u128);
}
-#[derive(Default)]
+#[derive(Clone, Default)]
pub struct NetworkData {
pub total_bytes_downloaded: u128,
pub total_bytes_uploaded: u128,
pub connection_count: u128,
}
-#[derive(Default)]
+#[derive(Clone, Default)]
pub struct ConnectionData {
pub total_bytes_downloaded: u128,
pub total_bytes_uploaded: u128,
@@ -25,35 +31,38 @@ pub struct ConnectionData {
pub interface_name: String,
}
-impl NetworkData {
- pub fn divide_by(&mut self, amount: u128) {
- self.total_bytes_downloaded /= amount;
- self.total_bytes_uploaded /= amount;
+impl Bandwidth for NetworkData {
+ fn get_total_bytes_downloaded(&self) -> u128 {
+ self.total_bytes_downloaded
}
-}
-
-impl ConnectionData {
- pub fn divide_by(&mut self, amount: u128) {
+ fn get_total_bytes_uploaded(&self) -> u128 {
+ self.total_bytes_uploaded
+ }
+ fn combine_bandwidth(&mut self, other: &NetworkData) {
+ self.total_bytes_downloaded += other.get_total_bytes_downloaded();
+ self.total_bytes_uploaded += other.get_total_bytes_uploaded();
+ self.connection_count = other.connection_count;
+ }
+ fn divide_by(&mut self, amount: u128) {
self.total_bytes_downloaded /= amount;
self.total_bytes_uploaded /= amount;
}
}
impl Bandwidth for ConnectionData {
- fn get_total_bytes_uploaded(&self) -> u128 {
- self.total_bytes_uploaded
- }
fn get_total_bytes_downloaded(&self) -> u128 {
self.total_bytes_downloaded
}
-}
-
-impl Bandwidth for NetworkData {
fn get_total_bytes_uploaded(&self) -> u128 {
self.total_bytes_uploaded
}
- fn get_total_bytes_downloaded(&self) -> u128 {
- self.total_bytes_downloaded
+ fn combine_bandwidth(&mut self, other: &ConnectionData) {
+ self.total_bytes_downloaded += other.get_total_bytes_downloaded();
+ self.total_bytes_uploaded += other.get_total_bytes_uploaded();
+ }
+ fn divide_by(&mut self, amount: u128) {
+ self.total_bytes_downloaded /= amount;
+ self.total_bytes_uploaded /= amount;
}
}
@@ -64,12 +73,16 @@ pub struct UtilizationData {
#[derive(Default)]
pub struct UIState {
- pub processes: BTreeMap<String, NetworkData>,
- pub remote_addresses: BTreeMap<IpAddr, NetworkData>,
- pub connections: BTreeMap<Connection, ConnectionData>,
+ pub processes: Vec<(String, NetworkData)>,
+ pub remote_addresses: Vec<(IpAddr, NetworkData)>,
+ pub connections: Vec<(Connection, ConnectionData)>,
pub total_bytes_downloaded: u128,
pub total_bytes_uploaded: u128,
+ pub cumulative_mode: bool,
utilization_data: VecDeque<UtilizationData>,
+ processes_map: HashMap<String, NetworkData>,
+ remote_addresses_map: HashMap<IpAddr, NetworkData>,
+ connections_map: HashMap<Connection, ConnectionData>,
}
impl UIState {
@@ -105,9 +118,9 @@ impl UIState {
if self.utilization_data.len() > RECALL_LENGTH {
self.utilization_data.pop_front();
}
- let mut processes: BTreeMap<String, NetworkData> = BTreeMap::new();
- let mut remote_addresses: BTreeMap<IpAddr, NetworkData> = BTreeMap::new();
- let mut connections: BTreeMap<Connection, ConnectionData> = BTreeMap::new();
+ let mut processes: HashMap<String, NetworkData> = HashMap::new();
+ let mut remote_addresses: HashMap<IpAddr, NetworkData> = HashMap::new();
+ let mut connections: HashMap<Connection, ConnectionData> = HashMap::new();
let mut total_bytes_downloaded: u128 = 0;
let mut total_bytes_uploaded: u128 = 0;
@@ -115,6 +128,7 @@ impl UIState {
for state in self.utilization_data.iter().rev() {
let connections_to_procs = &state.connections_to_procs;
let network_utilization = &state.network_utilization;
+
for (connection, connection_info) in &network_utilization.connections {
let connection_previously_seen = !seen_connections.insert(connection);
let connection_data = connections.entry(connection.clone()).or_default();
@@ -167,10 +181,54 @@ impl UIState {
for (_, connection_data) in connections.iter_mut() {
connection_data.divide_by(divide_by)
}
- self.processes = processes;
- self.remote_addresses = remote_addresses;
- self.connections = connections;
- self.total_bytes_downloaded = total_bytes_downloaded / divide_by;
- self.total_bytes_uploaded = total_bytes_uploaded / divide_by;
+
+ if self.cumulative_mode {
+ merge_bandwidth(&mut self.processes_map, processes);
+ merge_bandwidth(&mut self.remote_addresses_map, remote_addresses);
+ merge_bandwidth(&mut self.connections_map, connections);
+ self.total_bytes_downloaded += total_bytes_downloaded / divide_by;
+ self.total_bytes_uploaded += total_bytes_uploaded / divide_by;
+ } else {
+ self.processes_map = processes;
+ self.remote_addresses_map = remote_addresses;
+ self.connections_map = connections;
+ self.total_bytes_downloaded = total_bytes_downloaded / divide_by;
+ self.total_bytes_uploaded = total_bytes_uploaded / divide_by;
+ }
+ self.processes = sort_and_prune(&mut self.processes_map);
+ self.remote_addresses = sort_and_prune(&mut self.remote_addresses_map);
+ self.connections = sort_and_prune(&mut self.connections_map);
}
}
+
+fn merge_bandwidth<K, V>(self_map: &mut HashMap<K, V>, other_map: HashMap<K, V>)
+where
+ K: Eq + Hash,
+ V: Bandwidth,
+{
+ for (key, b_other) in other_map {
+ self_map
+ .entry(key)
+ .and_modify(|b_self| b_self.combine_bandwidth(&b_other))
+ .or_insert(b_other);
+ }
+}
+
+fn sort_and_prune<K, V>(map: &mut HashMap<K, V>) -> Vec<(K, V)>
+where
+ K: Eq + Hash + Clone,
+ V: Bandwidth + Clone,