mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-21 12:45:05 +02:00
Add new option to allow for seeing cpu usage in processes as a percentage of current usage, rather than total
This commit is contained in:
parent
d0a7a0dd72
commit
e5749234a2
@ -46,6 +46,8 @@ Note that all options and keybindings on GitHub may reflect the current developm
|
|||||||
|
|
||||||
- `-l`, `--left_legend` will move external table legends to the left side rather than the right side. Right side is default.
|
- `-l`, `--left_legend` will move external table legends to the left side rather than the right side. Right side is default.
|
||||||
|
|
||||||
|
- `-u`, `--current_usage` will make a process' CPU usage be based on the current total CPU usage, rather than assuming 100% CPU usage. Only affects Linux.
|
||||||
|
|
||||||
### Keybindings
|
### Keybindings
|
||||||
|
|
||||||
#### General
|
#### General
|
||||||
|
@ -47,12 +47,14 @@ pub struct App {
|
|||||||
pub show_help: bool,
|
pub show_help: bool,
|
||||||
pub is_frozen: bool,
|
pub is_frozen: bool,
|
||||||
pub left_legend: bool,
|
pub left_legend: bool,
|
||||||
|
pub use_current_cpu_total: bool,
|
||||||
last_key_press: Instant,
|
last_key_press: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
show_average_cpu: bool, temperature_type: temperature::TemperatureType, update_rate_in_milliseconds: u64, use_dot: bool, left_legend: bool,
|
show_average_cpu: bool, temperature_type: temperature::TemperatureType, update_rate_in_milliseconds: u64, use_dot: bool, left_legend: bool,
|
||||||
|
use_current_cpu_total: bool,
|
||||||
) -> App {
|
) -> App {
|
||||||
App {
|
App {
|
||||||
process_sorting_type: processes::ProcessSorting::CPU,
|
process_sorting_type: processes::ProcessSorting::CPU,
|
||||||
@ -76,6 +78,7 @@ impl App {
|
|||||||
show_help: false,
|
show_help: false,
|
||||||
is_frozen: false,
|
is_frozen: false,
|
||||||
left_legend,
|
left_legend,
|
||||||
|
use_current_cpu_total,
|
||||||
last_key_press: Instant::now(),
|
last_key_press: Instant::now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ pub struct DataState {
|
|||||||
prev_net_access_time: Instant,
|
prev_net_access_time: Instant,
|
||||||
temperature_type: temperature::TemperatureType,
|
temperature_type: temperature::TemperatureType,
|
||||||
last_clean: Instant, // Last time stale data was cleared
|
last_clean: Instant, // Last time stale data was cleared
|
||||||
|
use_current_cpu_total: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for DataState {
|
impl Default for DataState {
|
||||||
@ -66,6 +67,7 @@ impl Default for DataState {
|
|||||||
prev_net_access_time: Instant::now(),
|
prev_net_access_time: Instant::now(),
|
||||||
temperature_type: temperature::TemperatureType::Celsius,
|
temperature_type: temperature::TemperatureType::Celsius,
|
||||||
last_clean: Instant::now(),
|
last_clean: Instant::now(),
|
||||||
|
use_current_cpu_total: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,6 +77,10 @@ impl DataState {
|
|||||||
self.temperature_type = temperature_type;
|
self.temperature_type = temperature_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_use_current_cpu_total(&mut self, use_current_cpu_total: bool) {
|
||||||
|
self.use_current_cpu_total = use_current_cpu_total;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init(&mut self) {
|
pub fn init(&mut self) {
|
||||||
self.sys.refresh_all();
|
self.sys.refresh_all();
|
||||||
}
|
}
|
||||||
@ -104,7 +110,13 @@ impl DataState {
|
|||||||
push_if_valid(&mem::get_mem_data_list().await, &mut self.data.memory);
|
push_if_valid(&mem::get_mem_data_list().await, &mut self.data.memory);
|
||||||
push_if_valid(&mem::get_swap_data_list().await, &mut self.data.swap);
|
push_if_valid(&mem::get_swap_data_list().await, &mut self.data.swap);
|
||||||
set_if_valid(
|
set_if_valid(
|
||||||
&processes::get_sorted_processes_list(&self.sys, &mut self.prev_idle, &mut self.prev_non_idle, &mut self.prev_pid_stats),
|
&processes::get_sorted_processes_list(
|
||||||
|
&self.sys,
|
||||||
|
&mut self.prev_idle,
|
||||||
|
&mut self.prev_non_idle,
|
||||||
|
&mut self.prev_pid_stats,
|
||||||
|
self.use_current_cpu_total,
|
||||||
|
),
|
||||||
&mut self.data.list_of_processes,
|
&mut self.data.list_of_processes,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ fn cpu_usage_calculation(prev_idle: &mut f64, prev_non_idle: &mut f64) -> error:
|
|||||||
|
|
||||||
let cpu_percentage = if total_delta != 0_f64 { result / total_delta } else { 0_f64 };
|
let cpu_percentage = if total_delta != 0_f64 { result / total_delta } else { 0_f64 };
|
||||||
|
|
||||||
Ok((result, cpu_percentage)) // This works, REALLY damn well. The percentage check is within like 2% of the sysinfo one.
|
Ok((result, cpu_percentage))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_ordering<T: std::cmp::PartialOrd>(a_val: T, b_val: T, reverse_order: bool) -> std::cmp::Ordering {
|
fn get_ordering<T: std::cmp::PartialOrd>(a_val: T, b_val: T, reverse_order: bool) -> std::cmp::Ordering {
|
||||||
@ -104,7 +104,7 @@ fn get_ordering<T: std::cmp::PartialOrd>(a_val: T, b_val: T, reverse_order: bool
|
|||||||
}
|
}
|
||||||
Ordering::Equal => Ordering::Equal,
|
Ordering::Equal => Ordering::Equal,
|
||||||
},
|
},
|
||||||
None => Ordering::Equal, // I don't really like this but I think it's fine...
|
None => Ordering::Equal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +125,9 @@ fn get_process_cpu_stats(pid: u32) -> std::io::Result<f64> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Note that cpu_percentage should be represented WITHOUT the \times 100 factor!
|
/// Note that cpu_percentage should be represented WITHOUT the \times 100 factor!
|
||||||
fn linux_cpu_usage(pid: u32, cpu_usage: f64, cpu_percentage: f64, previous_pid_stats: &mut HashMap<String, (f64, Instant)>) -> std::io::Result<f64> {
|
fn linux_cpu_usage(
|
||||||
|
pid: u32, cpu_usage: f64, cpu_percentage: f64, previous_pid_stats: &mut HashMap<String, (f64, Instant)>, use_current_cpu_total: bool,
|
||||||
|
) -> std::io::Result<f64> {
|
||||||
// Based heavily on https://stackoverflow.com/a/23376195 and https://stackoverflow.com/a/1424556
|
// Based heavily on https://stackoverflow.com/a/23376195 and https://stackoverflow.com/a/1424556
|
||||||
let before_proc_val: f64 = if previous_pid_stats.contains_key(&pid.to_string()) {
|
let before_proc_val: f64 = if previous_pid_stats.contains_key(&pid.to_string()) {
|
||||||
previous_pid_stats.get(&pid.to_string()).unwrap_or(&(0_f64, Instant::now())).0
|
previous_pid_stats.get(&pid.to_string()).unwrap_or(&(0_f64, Instant::now())).0
|
||||||
@ -145,11 +147,15 @@ fn linux_cpu_usage(pid: u32, cpu_usage: f64, cpu_percentage: f64, previous_pid_s
|
|||||||
|
|
||||||
let entry = previous_pid_stats.entry(pid.to_string()).or_insert((after_proc_val, Instant::now()));
|
let entry = previous_pid_stats.entry(pid.to_string()).or_insert((after_proc_val, Instant::now()));
|
||||||
*entry = (after_proc_val, Instant::now());
|
*entry = (after_proc_val, Instant::now());
|
||||||
Ok((after_proc_val - before_proc_val) / cpu_usage * 100_f64 * cpu_percentage)
|
if use_current_cpu_total {
|
||||||
|
Ok((after_proc_val - before_proc_val) / cpu_usage * 100_f64)
|
||||||
|
} else {
|
||||||
|
Ok((after_proc_val - before_proc_val) / cpu_usage * 100_f64 * cpu_percentage)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_ps(
|
fn convert_ps(
|
||||||
process: &str, cpu_usage: f64, cpu_percentage: f64, prev_pid_stats: &mut HashMap<String, (f64, Instant)>,
|
process: &str, cpu_usage: f64, cpu_percentage: f64, prev_pid_stats: &mut HashMap<String, (f64, Instant)>, use_current_cpu_total: bool,
|
||||||
) -> std::io::Result<ProcessData> {
|
) -> std::io::Result<ProcessData> {
|
||||||
if process.trim().to_string().is_empty() {
|
if process.trim().to_string().is_empty() {
|
||||||
return Ok(ProcessData {
|
return Ok(ProcessData {
|
||||||
@ -170,12 +176,13 @@ fn convert_ps(
|
|||||||
command,
|
command,
|
||||||
mem_usage_percent,
|
mem_usage_percent,
|
||||||
mem_usage_kb: None,
|
mem_usage_kb: None,
|
||||||
cpu_usage_percent: linux_cpu_usage(pid, cpu_usage, cpu_percentage, prev_pid_stats)?,
|
cpu_usage_percent: linux_cpu_usage(pid, cpu_usage, cpu_percentage, prev_pid_stats, use_current_cpu_total)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_sorted_processes_list(
|
pub fn get_sorted_processes_list(
|
||||||
sys: &System, prev_idle: &mut f64, prev_non_idle: &mut f64, prev_pid_stats: &mut std::collections::HashMap<String, (f64, Instant)>,
|
sys: &System, prev_idle: &mut f64, prev_non_idle: &mut f64, prev_pid_stats: &mut std::collections::HashMap<String, (f64, Instant)>,
|
||||||
|
use_current_cpu_total: bool,
|
||||||
) -> crate::utils::error::Result<Vec<ProcessData>> {
|
) -> crate::utils::error::Result<Vec<ProcessData>> {
|
||||||
let mut process_vector: Vec<ProcessData> = Vec::new();
|
let mut process_vector: Vec<ProcessData> = Vec::new();
|
||||||
|
|
||||||
@ -191,7 +198,7 @@ pub fn get_sorted_processes_list(
|
|||||||
let process_stream = split_string.collect::<Vec<&str>>();
|
let process_stream = split_string.collect::<Vec<&str>>();
|
||||||
|
|
||||||
for process in process_stream {
|
for process in process_stream {
|
||||||
if let Ok(process_object) = convert_ps(process, cpu_usage, cpu_percentage, prev_pid_stats) {
|
if let Ok(process_object) = convert_ps(process, cpu_usage, cpu_percentage, prev_pid_stats, use_current_cpu_total) {
|
||||||
if !process_object.command.is_empty() {
|
if !process_object.command.is_empty() {
|
||||||
process_vector.push(process_object);
|
process_vector.push(process_object);
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,7 @@ fn main() -> error::Result<()> {
|
|||||||
)
|
)
|
||||||
(@arg RATE_MILLIS: -r --rate +takes_value "Sets a refresh rate in milliseconds; the minimum is 250ms, defaults to 1000ms. Smaller values may take more resources.")
|
(@arg RATE_MILLIS: -r --rate +takes_value "Sets a refresh rate in milliseconds; the minimum is 250ms, defaults to 1000ms. Smaller values may take more resources.")
|
||||||
(@arg LEFT_LEGEND: -l --left_legend "Puts external chart legends on the left side rather than the default right side.")
|
(@arg LEFT_LEGEND: -l --left_legend "Puts external chart legends on the left side rather than the default right side.")
|
||||||
|
(@arg USE_CURR_USAGE: -u --current_usage "Within Linux, sets a process' CPU usage to be based on the total current CPU usage, rather than assuming 100% usage.")
|
||||||
//(@arg CONFIG_LOCATION: -co --config +takes_value "Sets the location of the config file. Expects a config file in the JSON format.")
|
//(@arg CONFIG_LOCATION: -co --config +takes_value "Sets the location of the config file. Expects a config file in the JSON format.")
|
||||||
//(@arg BASIC_MODE: -b --basic "Sets bottom to basic mode, not showing graphs and only showing basic tables.")
|
//(@arg BASIC_MODE: -b --basic "Sets bottom to basic mode, not showing graphs and only showing basic tables.")
|
||||||
)
|
)
|
||||||
@ -107,6 +108,7 @@ fn main() -> error::Result<()> {
|
|||||||
let show_average_cpu = matches.is_present("AVG_CPU");
|
let show_average_cpu = matches.is_present("AVG_CPU");
|
||||||
let use_dot = matches.is_present("DOT_MARKER");
|
let use_dot = matches.is_present("DOT_MARKER");
|
||||||
let left_legend = matches.is_present("LEFT_LEGEND");
|
let left_legend = matches.is_present("LEFT_LEGEND");
|
||||||
|
let use_current_cpu_total = matches.is_present("USE_CURR_USAGE");
|
||||||
|
|
||||||
// Create "app" struct, which will control most of the program and store settings/state
|
// Create "app" struct, which will control most of the program and store settings/state
|
||||||
let mut app = app::App::new(
|
let mut app = app::App::new(
|
||||||
@ -115,6 +117,7 @@ fn main() -> error::Result<()> {
|
|||||||
update_rate_in_milliseconds as u64,
|
update_rate_in_milliseconds as u64,
|
||||||
use_dot,
|
use_dot,
|
||||||
left_legend,
|
left_legend,
|
||||||
|
use_current_cpu_total,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Set up up tui and crossterm
|
// Set up up tui and crossterm
|
||||||
@ -169,6 +172,7 @@ fn main() -> error::Result<()> {
|
|||||||
let mut data_state = data_collection::DataState::default();
|
let mut data_state = data_collection::DataState::default();
|
||||||
data_state.init();
|
data_state.init();
|
||||||
data_state.set_temperature_type(temp_type);
|
data_state.set_temperature_type(temp_type);
|
||||||
|
data_state.set_use_current_cpu_total(use_current_cpu_total);
|
||||||
loop {
|
loop {
|
||||||
if let Ok(message) = rrx.try_recv() {
|
if let Ok(message) = rrx.try_recv() {
|
||||||
match message {
|
match message {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user