Skip to content

Commit 1997bce

Browse files
notjedicyqsimon
andauthored
feat: add PID column to Process table (#379)
* feat: add `PID` column to `Process` table * fix(tests): populate fake data with the correct `ProcessInfo` type * test: update snapshots * refactor: use more idiomatic rust * refactor: rename function from `get_proc_name` to `get_proc_info` * refactor: only display PID when width available is highest ref: #165 (comment) * tests: update snapshots * chore: update CHANGELOG * fix: clippy warnings * Revert "fix: clippy warnings" This reverts commit e5f06cb. We will do this separately for the sake of keeping a clean history * refactor: use `u32` for PID * refactor: more idiomatic rust --------- Co-authored-by: cyqsimon <[email protected]>
1 parent f892d2f commit 1997bce

File tree

39 files changed

+219
-790
lines changed

39 files changed

+219
-790
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
1212

1313
## Added
1414
* CI: include generated assets in release archive #359 - @cyqsimon
15+
* Add PID column to the process table #379 - @notjedi
1516

1617
## Changed
1718
* CI: strip release binaries for all targets #358 - @cyqsimon

src/display/components/table.rs

+47-5
Original file line numberDiff line numberDiff line change
@@ -28,36 +28,46 @@ pub enum DisplayLayout {
2828
C2([u16; 2]),
2929
/// Show 3 columns.
3030
C3([u16; 3]),
31+
/// Show 4 columns.
32+
C4([u16; 4]),
3133
}
34+
3235
impl Index<usize> for DisplayLayout {
3336
type Output = u16;
3437

3538
fn index(&self, i: usize) -> &Self::Output {
3639
match self {
3740
Self::C2(arr) => &arr[i],
3841
Self::C3(arr) => &arr[i],
42+
Self::C4(arr) => &arr[i],
3943
}
4044
}
4145
}
46+
4247
impl DisplayLayout {
4348
#[inline]
4449
fn columns_count(&self) -> usize {
4550
match self {
4651
Self::C2(_) => 2,
4752
Self::C3(_) => 3,
53+
Self::C4(_) => 4,
4854
}
4955
}
56+
5057
#[inline]
5158
fn iter(&self) -> impl Iterator<Item = &u16> {
5259
match self {
5360
Self::C2(ws) => ws.iter(),
5461
Self::C3(ws) => ws.iter(),
62+
Self::C4(ws) => ws.iter(),
5563
}
5664
}
65+
5766
#[inline]
5867
fn widths_sum(&self) -> u16 {
5968
self.iter().sum()
6069
}
70+
6171
/// Returns the computed actual width and the spacer width.
6272
///
6373
/// See [`Table`] for layout rules.
@@ -87,6 +97,17 @@ impl DisplayLayout {
8797
let w2_new = (w2 as f64 * m).trunc() as u16;
8898
Self::C3([available_without_spacers - w1_new - w2_new, w1_new, w2_new])
8999
}
100+
Self::C4([_w0, w1, w2, w3]) => {
101+
let w1_new = (w1 as f64 * m).trunc() as u16;
102+
let w2_new = (w2 as f64 * m).trunc() as u16;
103+
let w3_new = (w3 as f64 * m).trunc() as u16;
104+
Self::C4([
105+
available_without_spacers - w1_new - w2_new - w3_new,
106+
w1_new,
107+
w2_new,
108+
w3_new,
109+
])
110+
}
90111
};
91112

92113
(computed, spacer)
@@ -101,26 +122,41 @@ impl DisplayLayout {
101122
enum TableData {
102123
/// A table with 3 columns.
103124
C3(NColsTableData<3>),
125+
/// A table with 4 columns.
126+
C4(NColsTableData<4>),
104127
}
128+
105129
impl From<NColsTableData<3>> for TableData {
106130
fn from(data: NColsTableData<3>) -> Self {
107131
Self::C3(data)
108132
}
109133
}
134+
135+
impl From<NColsTableData<4>> for TableData {
136+
fn from(data: NColsTableData<4>) -> Self {
137+
Self::C4(data)
138+
}
139+
}
140+
110141
impl TableData {
111142
fn column_names(&self) -> &[&str] {
112143
match self {
113144
Self::C3(inner) => &inner.column_names,
145+
Self::C4(inner) => &inner.column_names,
114146
}
115147
}
148+
116149
fn rows(&self) -> Vec<&[String]> {
117150
match self {
118151
Self::C3(inner) => inner.rows.iter().map(|r| r.as_slice()).collect(),
152+
Self::C4(inner) => inner.rows.iter().map(|r| r.as_slice()).collect(),
119153
}
120154
}
155+
121156
fn column_selector(&self) -> &dyn Fn(&DisplayLayout) -> Vec<usize> {
122157
match self {
123158
Self::C3(inner) => inner.column_selector.as_ref(),
159+
Self::C4(inner) => inner.column_selector.as_ref(),
124160
}
125161
}
126162
}
@@ -171,6 +207,7 @@ pub struct Table {
171207
width_cutoffs: Vec<(u16, DisplayLayout)>,
172208
data: TableData,
173209
}
210+
174211
impl Table {
175212
pub fn create_connections_table(state: &UIState, ip_to_host: &HashMap<IpAddr, String>) -> Self {
176213
use DisplayLayout as D;
@@ -214,6 +251,7 @@ impl Table {
214251
let column_selector = Rc::new(|layout: &D| match layout {
215252
D::C2(_) => vec![0, 2],
216253
D::C3(_) => vec![0, 1, 2],
254+
D::C4(_) => unreachable!(),
217255
});
218256

219257
Table {
@@ -236,11 +274,12 @@ impl Table {
236274
(0, D::C2([16, 18])),
237275
(50, D::C3([16, 12, 20])),
238276
(60, D::C3([24, 12, 20])),
239-
(80, D::C3([36, 16, 24])),
277+
(80, D::C4([28, 12, 12, 24])),
240278
];
241279

242280
let column_names = [
243281
"Process",
282+
"PID",
244283
"Connections",
245284
if state.cumulative_mode {
246285
"Data (Up / Down)"
@@ -251,9 +290,10 @@ impl Table {
251290
let rows = state
252291
.processes
253292
.iter()
254-
.map(|(process_name, data_for_process)| {
293+
.map(|(proc_info, data_for_process)| {
255294
[
256-
(*process_name).to_string(),
295+
proc_info.name.to_string(),
296+
proc_info.pid.to_string(),
257297
data_for_process.connection_count.to_string(),
258298
display_upload_and_download(
259299
data_for_process,
@@ -264,8 +304,9 @@ impl Table {
264304
})
265305
.collect();
266306
let column_selector = Rc::new(|layout: &D| match layout {
267-
D::C2(_) => vec![0, 2],
268-
D::C3(_) => vec![0, 1, 2],
307+
D::C2(_) => vec![0, 3],
308+
D::C3(_) => vec![0, 2, 3],
309+
D::C4(_) => vec![0, 1, 2, 3],
269310
});
270311

271312
Table {
@@ -322,6 +363,7 @@ impl Table {
322363
let column_selector = Rc::new(|layout: &D| match layout {
323364
D::C2(_) => vec![0, 2],
324365
D::C3(_) => vec![0, 1, 2],
366+
D::C4(_) => unreachable!(),
325367
});
326368

327369
Table {

src/display/ui.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::{
1010
UIState,
1111
},
1212
network::{display_connection_string, display_ip_or_host, LocalSocket, Utilization},
13+
os::ProcessInfo,
1314
};
1415

1516
pub struct Ui<B>
@@ -53,9 +54,10 @@ where
5354

5455
let output_process_data = |write_to_stdout: &mut (dyn FnMut(String) + Send),
5556
no_traffic: &mut bool| {
56-
for (process, process_network_data) in &state.processes {
57+
for (proc_info, process_network_data) in &state.processes {
5758
write_to_stdout(format!(
58-
"process: <{timestamp}> \"{process}\" up/down Bps: {}/{} connections: {}",
59+
"process: <{timestamp}> \"{}\" up/down Bps: {}/{} connections: {}",
60+
proc_info.name,
5961
process_network_data.total_bytes_uploaded,
6062
process_network_data.total_bytes_downloaded,
6163
process_network_data.connection_count
@@ -173,7 +175,7 @@ where
173175

174176
pub fn update_state(
175177
&mut self,
176-
connections_to_procs: HashMap<LocalSocket, String>,
178+
connections_to_procs: HashMap<LocalSocket, ProcessInfo>,
177179
utilization: Utilization,
178180
ip_to_host: HashMap<IpAddr, String>,
179181
) {

src/display/ui_state.rs

+21-19
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::{
99
display::BandwidthUnitFamily,
1010
mt_log,
1111
network::{Connection, LocalSocket, Utilization},
12+
os::ProcessInfo,
1213
};
1314

1415
static RECALL_LENGTH: usize = 5;
@@ -72,23 +73,23 @@ impl Bandwidth for ConnectionData {
7273
}
7374

7475
pub struct UtilizationData {
75-
connections_to_procs: HashMap<LocalSocket, String>,
76+
connections_to_procs: HashMap<LocalSocket, ProcessInfo>,
7677
network_utilization: Utilization,
7778
}
7879

7980
#[derive(Default)]
8081
pub struct UIState {
8182
/// The interface name in single-interface mode. `None` means all interfaces.
8283
pub interface_name: Option<String>,
83-
pub processes: Vec<(String, NetworkData)>,
84+
pub processes: Vec<(ProcessInfo, NetworkData)>,
8485
pub remote_addresses: Vec<(IpAddr, NetworkData)>,
8586
pub connections: Vec<(Connection, ConnectionData)>,
8687
pub total_bytes_downloaded: u128,
8788
pub total_bytes_uploaded: u128,
8889
pub cumulative_mode: bool,
8990
pub unit_family: BandwidthUnitFamily,
9091
pub utilization_data: VecDeque<UtilizationData>,
91-
pub processes_map: HashMap<String, NetworkData>,
92+
pub processes_map: HashMap<ProcessInfo, NetworkData>,
9293
pub remote_addresses_map: HashMap<IpAddr, NetworkData>,
9394
pub connections_map: HashMap<Connection, ConnectionData>,
9495
/// Used for reducing logging noise.
@@ -98,7 +99,7 @@ pub struct UIState {
9899
impl UIState {
99100
pub fn update(
100101
&mut self,
101-
connections_to_procs: HashMap<LocalSocket, String>,
102+
connections_to_procs: HashMap<LocalSocket, ProcessInfo>,
102103
network_utilization: Utilization,
103104
) {
104105
self.utilization_data.push_back(UtilizationData {
@@ -108,7 +109,7 @@ impl UIState {
108109
if self.utilization_data.len() > RECALL_LENGTH {
109110
self.utilization_data.pop_front();
110111
}
111-
let mut processes: HashMap<String, NetworkData> = HashMap::new();
112+
let mut processes: HashMap<ProcessInfo, NetworkData> = HashMap::new();
112113
let mut remote_addresses: HashMap<IpAddr, NetworkData> = HashMap::new();
113114
let mut connections: HashMap<Connection, ConnectionData> = HashMap::new();
114115
let mut total_bytes_downloaded: u128 = 0;
@@ -140,11 +141,10 @@ impl UIState {
140141

141142
let data_for_process = {
142143
let local_socket = connection.local_socket;
143-
let process_name = get_proc_name(connections_to_procs, &local_socket);
144+
let proc_info = get_proc_info(connections_to_procs, &local_socket);
144145

145146
// only log each orphan connection once
146-
if process_name.is_none() && !self.known_orphan_sockets.contains(&local_socket)
147-
{
147+
if proc_info.is_none() && !self.known_orphan_sockets.contains(&local_socket) {
148148
// newer connections go in the front so that searches are faster
149149
// basically recency bias
150150
self.known_orphan_sockets.push_front(local_socket);
@@ -155,17 +155,18 @@ impl UIState {
155155
.find(|(&LocalSocket { port, protocol, .. }, _)| {
156156
port == local_socket.port && protocol == local_socket.protocol
157157
})
158-
.and_then(|(local_conn_lookalike, name)| {
158+
.and_then(|(local_conn_lookalike, info)| {
159159
network_utilization
160160
.connections
161161
.keys()
162162
.find(|conn| &conn.local_socket == local_conn_lookalike)
163-
.map(|conn| (conn, name))
163+
.map(|conn| (conn, info))
164164
}) {
165-
Some((lookalike, name)) => {
165+
Some((lookalike, proc_info)) => {
166166
mt_log!(
167167
warn,
168-
r#""{name}" owns a similar looking connection, but its local ip doesn't match."#
168+
r#""{0}" owns a similar looking connection, but its local ip doesn't match."#,
169+
proc_info.name
169170
);
170171
mt_log!(warn, "Looking for: {connection:?}; found: {lookalike:?}");
171172
}
@@ -175,9 +176,11 @@ impl UIState {
175176
};
176177
}
177178

178-
let process_display_name = process_name.unwrap_or("<UNKNOWN>").to_owned();
179-
connection_data.process_name = process_display_name.clone();
180-
processes.entry(process_display_name).or_default()
179+
let proc_info = proc_info
180+
.cloned()
181+
.unwrap_or_else(|| ProcessInfo::new("<UNKNOWN>", 0));
182+
connection_data.process_name = proc_info.name.clone();
183+
processes.entry(proc_info).or_default()
181184
};
182185

183186
data_for_process.total_bytes_downloaded += connection_info.total_bytes_downloaded;
@@ -221,10 +224,10 @@ impl UIState {
221224
}
222225
}
223226

224-
fn get_proc_name<'a>(
225-
connections_to_procs: &'a HashMap<LocalSocket, String>,
227+
fn get_proc_info<'a>(
228+
connections_to_procs: &'a HashMap<LocalSocket, ProcessInfo>,
226229
local_socket: &LocalSocket,
227-
) -> Option<&'a str> {
230+
) -> Option<&'a ProcessInfo> {
228231
connections_to_procs
229232
// direct match
230233
.get(local_socket)
@@ -252,7 +255,6 @@ fn get_proc_name<'a>(
252255
..*local_socket
253256
})
254257
})
255-
.map(String::as_str)
256258
}
257259

258260
fn merge_bandwidth<K, V>(self_map: &mut HashMap<K, V>, other_map: HashMap<K, V>)

src/main.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use ratatui::backend::{Backend, CrosstermBackend};
3434
use simplelog::WriteLogger;
3535

3636
use crate::cli::Opt;
37+
use crate::os::ProcessInfo;
3738

3839
const DISPLAY_DELTA: Duration = Duration::from_millis(1000);
3940

@@ -89,7 +90,7 @@ fn main() -> anyhow::Result<()> {
8990
}
9091

9192
pub struct OpenSockets {
92-
sockets_to_procs: HashMap<LocalSocket, String>,
93+
sockets_to_procs: HashMap<LocalSocket, ProcessInfo>,
9394
}
9495

9596
pub struct OsInputOutput {

0 commit comments

Comments
 (0)