Skip to content

Commit f9386ae

Browse files
authored
Merge pull request #518 from Bluemangoo/feature/top-tui-23
top: implement tui `2` `3` `c` `z`
2 parents 3b01201 + 842fbb5 commit f9386ae

File tree

10 files changed

+493
-145
lines changed

10 files changed

+493
-145
lines changed

src/uu/top/src/header.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::picker::sysinfo;
77
use crate::platform::*;
88
use crate::tui::stat::{CpuValueMode, TuiStat};
99
use bytesize::ByteSize;
10-
use uu_vmstat::CpuLoad;
10+
use uu_vmstat::{CpuLoad, CpuLoadRaw};
1111
use uu_w::get_formatted_uptime_procps;
1212
use uucore::uptime::{get_formatted_loadavg, get_formatted_nusers, get_formatted_time};
1313

@@ -119,8 +119,8 @@ fn user() -> String {
119119
get_formatted_nusers()
120120
}
121121

122-
fn sum_cpu_loads(cpu_loads: &[uu_vmstat::CpuLoadRaw]) -> uu_vmstat::CpuLoadRaw {
123-
let mut total = uu_vmstat::CpuLoadRaw {
122+
fn sum_cpu_loads(cpu_loads: Vec<&CpuLoadRaw>) -> CpuLoadRaw {
123+
let mut total = CpuLoadRaw {
124124
user: 0,
125125
nice: 0,
126126
system: 0,
@@ -162,9 +162,38 @@ fn cpu(stat: &TuiStat) -> Vec<(String, CpuLoad)> {
162162
})
163163
.collect::<Vec<(String, CpuLoad)>>(),
164164
CpuValueMode::Sum => {
165-
let total = sum_cpu_loads(&cpu_loads);
165+
let total = sum_cpu_loads(cpu_loads.iter().collect());
166166
let cpu_load = CpuLoad::from_raw(&total);
167167
vec![(String::from("Cpu(s)"), cpu_load)]
168168
}
169+
CpuValueMode::Numa => {
170+
let numa_nodes = get_numa_nodes();
171+
let total = sum_cpu_loads(cpu_loads.iter().collect());
172+
let cpu_load = CpuLoad::from_raw(&total);
173+
let mut data = vec![(String::from("Cpu(s)"), cpu_load)];
174+
for (id, cores) in numa_nodes {
175+
let loads = cores.iter().map(|id| &cpu_loads[*id]).collect();
176+
let total = sum_cpu_loads(loads);
177+
let cpu_load = CpuLoad::from_raw(&total);
178+
data.push((format!("Node{id}"), cpu_load));
179+
}
180+
data
181+
}
182+
CpuValueMode::NumaNode(id) => {
183+
let numa_nodes = get_numa_nodes();
184+
if let Some(cores) = numa_nodes.get(&id) {
185+
let loads = cores.iter().map(|id| &cpu_loads[*id]).collect();
186+
let total = sum_cpu_loads(loads);
187+
let cpu_load = CpuLoad::from_raw(&total);
188+
let mut data = vec![(format!("Node{id}"), cpu_load)];
189+
data.extend(cores.iter().map(|core_id| {
190+
let cpu_load = CpuLoad::from_raw(&cpu_loads[*core_id]);
191+
(format!("Cpu{core_id}"), cpu_load)
192+
}));
193+
data
194+
} else {
195+
vec![]
196+
}
197+
}
169198
}
170199
}

src/uu/top/src/picker.rs

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// For the full copyright and license information, please view the LICENSE
44
// file that was distributed with this source code.
55

6+
use crate::tui::stat::TuiStat;
7+
use crate::Settings;
68
use std::{
79
ffi::OsString,
810
fs::File,
@@ -19,7 +21,10 @@ pub fn sysinfo() -> &'static RwLock<System> {
1921
SYSINFO.get_or_init(|| RwLock::new(System::new_all()))
2022
}
2123

22-
pub(crate) fn pickers(fields: &[String]) -> Vec<Box<dyn Fn(u32) -> String>> {
24+
type Stat<'a> = (&'a Settings, &'a TuiStat);
25+
type Picker = Box<dyn Fn(u32, Stat) -> String>;
26+
27+
pub(crate) fn pickers(fields: &[String]) -> Vec<Picker> {
2328
fields
2429
.iter()
2530
.map(|field| match field.as_str() {
@@ -41,7 +46,7 @@ pub(crate) fn pickers(fields: &[String]) -> Vec<Box<dyn Fn(u32) -> String>> {
4146
}
4247

4348
#[inline]
44-
fn helper(f: impl Fn(u32) -> String + 'static) -> Box<dyn Fn(u32) -> String> {
49+
fn helper(f: impl Fn(u32, Stat) -> String + 'static) -> Picker {
4550
Box::new(f)
4651
}
4752

@@ -55,11 +60,11 @@ fn format_memory(memory_b: u64) -> String {
5560
}
5661
}
5762

58-
fn todo(_pid: u32) -> String {
63+
fn todo(_pid: u32, _stat: Stat) -> String {
5964
"TODO".into()
6065
}
6166

62-
fn cpu(pid: u32) -> String {
67+
fn cpu(pid: u32, _stat: Stat) -> String {
6368
let binding = sysinfo().read().unwrap();
6469
let Some(proc) = binding.process(Pid::from_u32(pid)) else {
6570
return "0.0".into();
@@ -70,11 +75,11 @@ fn cpu(pid: u32) -> String {
7075
format!("{usage:.2}")
7176
}
7277

73-
fn pid(pid: u32) -> String {
78+
fn pid(pid: u32, _stat: Stat) -> String {
7479
pid.to_string()
7580
}
7681

77-
fn user(pid: u32) -> String {
82+
fn user(pid: u32, _stat: Stat) -> String {
7883
let binding = sysinfo().read().unwrap();
7984
let Some(proc) = binding.process(Pid::from_u32(pid)) else {
8085
return "0.0".into();
@@ -89,7 +94,7 @@ fn user(pid: u32) -> String {
8994
}
9095

9196
#[cfg(target_os = "linux")]
92-
fn pr(pid: u32) -> String {
97+
fn pr(pid: u32, _stat: Stat) -> String {
9398
use uucore::libc::*;
9499
let policy = unsafe { sched_getscheduler(pid as i32) };
95100
if policy == -1 {
@@ -111,8 +116,8 @@ fn pr(pid: u32) -> String {
111116
}
112117

113118
#[cfg(not(target_os = "linux"))]
114-
fn pr(pid: u32) -> String {
115-
todo(pid)
119+
fn pr(pid: u32, stat: Stat) -> String {
120+
todo(pid, stat)
116121
}
117122

118123
#[cfg(not(target_os = "windows"))]
@@ -134,18 +139,18 @@ fn get_nice(pid: u32) -> i32 {
134139
}
135140

136141
#[cfg(not(target_os = "windows"))]
137-
fn ni(pid: u32) -> String {
142+
fn ni(pid: u32, _stat: Stat) -> String {
138143
format!("{}", get_nice(pid))
139144
}
140145

141146
// TODO: Implement this function for Windows
142147
#[cfg(target_os = "windows")]
143-
fn ni(_pid: u32) -> String {
148+
fn ni(_pid: u32, _stat: Stat) -> String {
144149
"0".into()
145150
}
146151

147152
#[cfg(target_os = "linux")]
148-
fn virt(pid: u32) -> String {
153+
fn virt(pid: u32, _stat: Stat) -> String {
149154
let binding = sysinfo().read().unwrap();
150155
let Some(proc) = binding.process(Pid::from_u32(pid)) else {
151156
return "0.0".into();
@@ -154,12 +159,12 @@ fn virt(pid: u32) -> String {
154159
}
155160

156161
#[cfg(not(target_os = "linux"))]
157-
fn virt(_pid: u32) -> String {
158-
"TODO".into()
162+
fn virt(pid: u32, stat: Stat) -> String {
163+
todo(pid, stat)
159164
}
160165

161166
#[cfg(target_os = "linux")]
162-
fn res(pid: u32) -> String {
167+
fn res(pid: u32, _stat: Stat) -> String {
163168
let binding = sysinfo().read().unwrap();
164169
let Some(proc) = binding.process(Pid::from_u32(pid)) else {
165170
return "0.0".into();
@@ -168,12 +173,12 @@ fn res(pid: u32) -> String {
168173
}
169174

170175
#[cfg(not(target_os = "linux"))]
171-
fn res(_pid: u32) -> String {
172-
"TODO".into()
176+
fn res(pid: u32, stat: Stat) -> String {
177+
todo(pid, stat)
173178
}
174179

175180
#[cfg(target_os = "linux")]
176-
fn shr(pid: u32) -> String {
181+
fn shr(pid: u32, _stat: Stat) -> String {
177182
let file_path = format!("/proc/{pid}/statm");
178183
let Ok(file) = File::open(file_path) else {
179184
return "0.0".into();
@@ -189,11 +194,11 @@ fn shr(pid: u32) -> String {
189194
}
190195

191196
#[cfg(not(target_os = "linux"))]
192-
fn shr(_pid: u32) -> String {
193-
"TODO".into()
197+
fn shr(pid: u32, stat: Stat) -> String {
198+
todo(pid, stat)
194199
}
195200

196-
fn s(pid: u32) -> String {
201+
fn s(pid: u32, _stat: Stat) -> String {
197202
let binding = sysinfo().read().unwrap();
198203
let Some(proc) = binding.process(Pid::from_u32(pid)) else {
199204
return "?".into();
@@ -208,7 +213,7 @@ fn s(pid: u32) -> String {
208213
.to_string()
209214
}
210215

211-
fn time_plus(pid: u32) -> String {
216+
fn time_plus(pid: u32, _stat: Stat) -> String {
212217
let binding = sysinfo().read().unwrap();
213218
let Some(proc) = binding.process(Pid::from_u32(pid)) else {
214219
return "0:00.00".into();
@@ -226,7 +231,7 @@ fn time_plus(pid: u32) -> String {
226231
format!("{hour}:{min:0>2}.{sec:0>2}")
227232
}
228233

229-
fn mem(pid: u32) -> String {
234+
fn mem(pid: u32, _stat: Stat) -> String {
230235
let binding = sysinfo().read().unwrap();
231236
let Some(proc) = binding.process(Pid::from_u32(pid)) else {
232237
return "0.0".into();
@@ -238,7 +243,8 @@ fn mem(pid: u32) -> String {
238243
)
239244
}
240245

241-
fn command(pid: u32) -> String {
246+
fn command(pid: u32, stat: Stat) -> String {
247+
let full_command_line = stat.1.full_command_line;
242248
let f = |cmd: &[OsString]| -> String {
243249
let binding = cmd
244250
.iter()
@@ -250,6 +256,7 @@ fn command(pid: u32) -> String {
250256
let result: String = trimmed.into();
251257

252258
if cfg!(target_os = "linux") && result.is_empty() {
259+
// actually executable name
253260
let path = PathBuf::from_str(&format!("/proc/{pid}/status")).unwrap();
254261
if let Ok(file) = File::open(path) {
255262
let content = read_to_string(file).unwrap();
@@ -276,8 +283,17 @@ fn command(pid: u32) -> String {
276283
};
277284

278285
proc.exe()
279-
.and_then(|it| it.iter().next_back())
280-
.map(|it| it.to_str().unwrap())
281-
.unwrap_or(&f(proc.cmd()))
282-
.into()
286+
.and_then(|it| {
287+
if full_command_line {
288+
it.iter().next_back()
289+
} else {
290+
it.file_name()
291+
}
292+
})
293+
.map(|it| it.to_str().unwrap().to_string())
294+
.unwrap_or(if full_command_line {
295+
f(proc.cmd())
296+
} else {
297+
proc.name().to_str().unwrap().to_string()
298+
})
283299
}

src/uu/top/src/platform/fallback.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
use crate::header::Memory;
99
use crate::picker::sysinfo;
10+
use std::collections::HashMap;
1011

1112
pub fn get_cpu_loads() -> Vec<uu_vmstat::CpuLoadRaw> {
1213
vec![]
@@ -26,3 +27,6 @@ pub fn get_memory() -> Memory {
2627
used_swap: binding.used_swap(),
2728
}
2829
}
30+
pub fn get_numa_nodes() -> HashMap<usize, Vec<usize>> {
31+
HashMap::new()
32+
}

src/uu/top/src/platform/linux.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// file that was distributed with this source code.
55

66
use crate::header::Memory;
7+
use std::collections::HashMap;
78
use std::str::FromStr;
89

910
extern "C" {
@@ -92,3 +93,38 @@ pub fn get_memory() -> Memory {
9293
used_swap: mem_info.swap_total.0 - mem_info.swap_free.0,
9394
}
9495
}
96+
97+
pub fn get_numa_nodes() -> HashMap<usize, Vec<usize>> {
98+
let mut map = HashMap::new();
99+
if let Ok(entries) = std::fs::read_dir("/sys/devices/system/node/") {
100+
for entry in entries.flatten() {
101+
let file_name = entry.file_name();
102+
let file_name = file_name.to_string_lossy();
103+
if file_name.starts_with("node") {
104+
let node_id = file_name.trim_start_matches("node").parse::<usize>();
105+
let node_id = match node_id {
106+
Ok(id) => id,
107+
Err(_) => continue,
108+
};
109+
110+
let mut nodes = Vec::new();
111+
112+
if let Ok(node_entries) = std::fs::read_dir(entry.path()) {
113+
for node_entry in node_entries.flatten() {
114+
let cpu_file_name = node_entry.file_name();
115+
let cpu_file_name = cpu_file_name.to_string_lossy();
116+
if cpu_file_name.starts_with("cpu") {
117+
let cpu_id = cpu_file_name.trim_start_matches("cpu").parse::<usize>();
118+
if let Ok(cpu_id) = cpu_id {
119+
nodes.push(cpu_id);
120+
}
121+
}
122+
}
123+
nodes.sort();
124+
map.insert(node_id, nodes);
125+
}
126+
}
127+
}
128+
}
129+
map
130+
}

src/uu/top/src/platform/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ pub mod windows;
1111
pub mod fallback;
1212

1313
#[cfg(target_os = "linux")]
14-
pub use linux::{get_cpu_loads, get_memory, get_nusers_systemd};
14+
pub use linux::{get_cpu_loads, get_memory, get_numa_nodes, get_nusers_systemd};
1515
#[cfg(target_os = "windows")]
1616
pub use windows::get_cpu_loads;
1717

0 commit comments

Comments
 (0)