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:
ClementTsang 2019-12-31 22:24:54 -05:00
parent d0a7a0dd72
commit e5749234a2
5 changed files with 36 additions and 8 deletions

View File

@ -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.
- `-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
#### General

View File

@ -47,12 +47,14 @@ pub struct App {
pub show_help: bool,
pub is_frozen: bool,
pub left_legend: bool,
pub use_current_cpu_total: bool,
last_key_press: Instant,
}
impl App {
pub fn new(
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 {
process_sorting_type: processes::ProcessSorting::CPU,
@ -76,6 +78,7 @@ impl App {
show_help: false,
is_frozen: false,
left_legend,
use_current_cpu_total,
last_key_press: Instant::now(),
}
}

View File

@ -49,6 +49,7 @@ pub struct DataState {
prev_net_access_time: Instant,
temperature_type: temperature::TemperatureType,
last_clean: Instant, // Last time stale data was cleared
use_current_cpu_total: bool,
}
impl Default for DataState {
@ -66,6 +67,7 @@ impl Default for DataState {
prev_net_access_time: Instant::now(),
temperature_type: temperature::TemperatureType::Celsius,
last_clean: Instant::now(),
use_current_cpu_total: false,
}
}
}
@ -75,6 +77,10 @@ impl DataState {
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) {
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_swap_data_list().await, &mut self.data.swap);
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,
);

View File

@ -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 };
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 {
@ -104,7 +104,7 @@ fn get_ordering<T: std::cmp::PartialOrd>(a_val: T, b_val: T, reverse_order: bool
}
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!
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
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
@ -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()));
*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(
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> {
if process.trim().to_string().is_empty() {
return Ok(ProcessData {
@ -170,12 +176,13 @@ fn convert_ps(
command,
mem_usage_percent,
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(
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>> {
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>>();
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() {
process_vector.push(process_object);
}

View File

@ -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 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 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 use_dot = matches.is_present("DOT_MARKER");
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
let mut app = app::App::new(
@ -115,6 +117,7 @@ fn main() -> error::Result<()> {
update_rate_in_milliseconds as u64,
use_dot,
left_legend,
use_current_cpu_total,
);
// Set up up tui and crossterm
@ -169,6 +172,7 @@ fn main() -> error::Result<()> {
let mut data_state = data_collection::DataState::default();
data_state.init();
data_state.set_temperature_type(temp_type);
data_state.set_use_current_cpu_total(use_current_cpu_total);
loop {
if let Ok(message) = rrx.try_recv() {
match message {