diff --git a/CHANGELOG.md b/CHANGELOG.md index b5b11afe..d134d3f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#870](https://github.com/ClementTsang/bottom/pull/870): Make disk widget sortable. - [#881](https://github.com/ClementTsang/bottom/pull/881): Add pasting to the search bar. - [#892](https://github.com/ClementTsang/bottom/pull/892): Add custom retention periods for data. +- [#899](https://github.com/ClementTsang/bottom/pull/899): Add non-normalized CPU usage to processes. ## [0.6.8] - 2022-02-01 diff --git a/docs/content/configuration/command-line-flags.md b/docs/content/configuration/command-line-flags.md index febf2068..6780e392 100644 --- a/docs/content/configuration/command-line-flags.md +++ b/docs/content/configuration/command-line-flags.md @@ -6,42 +6,43 @@ The following flags can be provided to bottom in the command line to change the behaviour of the program (run `btm --help` for more information on each flag): -| Flag | Behaviour | -| ------------------------------------- | -------------------------------------------------------------- | -| `--autohide_time` | Temporarily shows the time scale in graphs. | -| `-b, --basic` | Hides graphs and uses a more basic look. | -| `--battery` | Shows the battery widget. | -| `-S, --case_sensitive` | Enables case sensitivity by default. | -| `-c, --celsius` | Sets the temperature type to Celsius. | -| `--color ` | Use a color scheme, use --help for supported values. | -| `-C, --config ` | Sets the location of the config file. | -| `-u, --current_usage` | Sets process CPU% to be based on current CPU%. | -| `-t, --default_time_value ` | Default time value for graphs in ms. | -| `--default_widget_count ` | Sets the n'th selected widget type as the default. | -| `--default_widget_type ` | Sets the default widget type, use --help for more info. | -| `--disable_advanced_kill` | Hides advanced options to stop a process on Unix-like systems. | -| `--disable_click` | Disables mouse clicks. | -| `-m, --dot_marker` | Uses a dot marker for graphs. | -| `-f, --fahrenheit` | Sets the temperature type to Fahrenheit. | -| `-g, --group` | Groups processes with the same name by default. | -| `-h, --help` | Prints help information. Use --help for more info. | -| `-a, --hide_avg_cpu` | Hides the average CPU usage. | -| `--hide_table_gap` | Hides the spacing between table headers and entries. | -| `--hide_time` | Hides the time scale. | -| `-k, --kelvin` | Sets the temperature type to Kelvin. | -| `-l, --left_legend` | Puts the CPU chart legend to the left side. | -| `--mem_as_value` | Defaults to showing process memory usage by value. | -| `--network_use_binary_prefix` | Displays the network widget with binary prefixes. | -| `--network_use_bytes` | Displays the network widget using bytes. | -| `--network_use_log` | Displays the network widget with a log scale. | -| `--process_command` | Show processes as their commands by default. | -| `-r, --rate ` | Sets a refresh rate in ms. | -| `-R, --regex` | Enables regex by default. | -| `--show_table_scroll_position` | Shows the scroll position tracker in table widgets. | -| `-d, --time_delta ` | The amount in ms changed upon zooming. | -| `-T, --tree` | Defaults to showing the process widget in tree mode. | -| `--use_old_network_legend` | DEPRECATED - uses the older network legend. | -| `-V, --version` | Prints version information. | -| `-W, --whole_word` | Enables whole-word matching by default. | -| `--enable_gpu_memory` | Enable collecting and displaying GPU memory usage. | -| `--retention` | How much data is stored at once in terms of time. | +| Flag | Behaviour | +| ------------------------------------- | --------------------------------------------------------------- | +| `--autohide_time` | Temporarily shows the time scale in graphs. | +| `-b, --basic` | Hides graphs and uses a more basic look. | +| `--battery` | Shows the battery widget. | +| `-S, --case_sensitive` | Enables case sensitivity by default. | +| `-c, --celsius` | Sets the temperature type to Celsius. | +| `--color ` | Use a color scheme, use --help for supported values. | +| `-C, --config ` | Sets the location of the config file. | +| `-u, --current_usage` | Sets process CPU% to be based on current CPU%. | +| `-t, --default_time_value ` | Default time value for graphs in ms. | +| `--default_widget_count ` | Sets the n'th selected widget type as the default. | +| `--default_widget_type ` | Sets the default widget type, use --help for more info. | +| `--disable_advanced_kill` | Hides advanced options to stop a process on Unix-like systems. | +| `--disable_click` | Disables mouse clicks. | +| `-m, --dot_marker` | Uses a dot marker for graphs. | +| `-f, --fahrenheit` | Sets the temperature type to Fahrenheit. | +| `-g, --group` | Groups processes with the same name by default. | +| `-h, --help` | Prints help information. Use --help for more info. | +| `-a, --hide_avg_cpu` | Hides the average CPU usage. | +| `--hide_table_gap` | Hides the spacing between table headers and entries. | +| `--hide_time` | Hides the time scale. | +| `-k, --kelvin` | Sets the temperature type to Kelvin. | +| `-l, --left_legend` | Puts the CPU chart legend to the left side. | +| `--mem_as_value` | Defaults to showing process memory usage by value. | +| `--network_use_binary_prefix` | Displays the network widget with binary prefixes. | +| `--network_use_bytes` | Displays the network widget using bytes. | +| `--network_use_log` | Displays the network widget with a log scale. | +| `--process_command` | Show processes as their commands by default. | +| `-r, --rate ` | Sets a refresh rate in ms. | +| `-R, --regex` | Enables regex by default. | +| `--show_table_scroll_position` | Shows the scroll position tracker in table widgets. | +| `-d, --time_delta ` | The amount in ms changed upon zooming. | +| `-T, --tree` | Defaults to showing the process widget in tree mode. | +| `--use_old_network_legend` | DEPRECATED - uses the older network legend. | +| `-V, --version` | Prints version information. | +| `-W, --whole_word` | Enables whole-word matching by default. | +| `--enable_gpu_memory` | Enable collecting and displaying GPU memory usage. | +| `--retention` | How much data is stored at once in terms of time. | +| `--unnormalized_cpu` | Show process CPU% without normalizing over the number of cores. | diff --git a/docs/content/configuration/config-file/flags.md b/docs/content/configuration/config-file/flags.md index 5aa218ca..606eef63 100644 --- a/docs/content/configuration/config-file/flags.md +++ b/docs/content/configuration/config-file/flags.md @@ -6,35 +6,36 @@ Most of the [command line flags](../../command-line-flags) have config file equivalents to avoid having to type them out each time: -| Field | Type | Functionality | -| ---------------------------- | ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | -| `hide_avg_cpu` | Boolean | Hides the average CPU usage. | -| `dot_marker` | Boolean | Uses a dot marker for graphs. | -| `left_legend` | Boolean | Puts the CPU chart legend to the left side. | -| `current_usage` | Boolean | Sets process CPU% to be based on current CPU%. | -| `group_processes` | Boolean | Groups processes with the same name by default. | -| `case_sensitive` | Boolean | Enables case sensitivity by default. | -| `whole_word` | Boolean | Enables whole-word matching by default. | -| `regex` | Boolean | Enables regex by default. | -| `basic` | Boolean | Hides graphs and uses a more basic look. | -| `use_old_network_legend` | Boolean | DEPRECATED - uses the older network legend. | -| `battery` | Boolean | Shows the battery widget. | -| `rate` | Unsigned Int (represents milliseconds) | Sets a refresh rate in ms. | -| `default_time_value` | Unsigned Int (represents milliseconds) | Default time value for graphs in ms. | -| `time_delta` | Unsigned Int (represents milliseconds) | The amount in ms changed upon zooming. | -| `hide_time` | Boolean | Hides the time scale. | -| `temperature_type` | String (one of ["k", "f", "c", "kelvin", "fahrenheit", "celsius"]) | Sets the temperature unit type. | -| `default_widget_type` | String (one of ["cpu", "proc", "net", "temp", "mem", "disk"], same as layout options) | Sets the default widget type, use --help for more info. | -| `default_widget_count` | Unsigned Int (represents which `default_widget_type`) | Sets the n'th selected widget type as the default. | -| `disable_click` | Boolean | Disables mouse clicks. | -| `color` | String (one of ["default", "default-light", "gruvbox", "gruvbox-light", "nord", "nord-light"]) | Use a color scheme, use --help for supported values. | -| `mem_as_value` | Boolean | Defaults to showing process memory usage by value. | -| `tree` | Boolean | Defaults to showing the process widget in tree mode. | -| `show_table_scroll_position` | Boolean | Shows the scroll position tracker in table widgets. | -| `process_command` | Boolean | Show processes as their commands by default. | -| `disable_advanced_kill` | Boolean | Hides advanced options to stop a process on Unix-like systems. | -| `network_use_binary_prefix` | Boolean | Displays the network widget with binary prefixes. | -| `network_use_bytes` | Boolean | Displays the network widget using bytes. | -| `network_use_log` | Boolean | Displays the network widget with a log scale. | -| `enable_gpu_memory` | Boolean | Shows the GPU memory widget. | -| `retention` | String (human readable time, such as "10m", "1h", etc.) | How much data is stored at once in terms of time. | +| Field | Type | Functionality | +| ---------------------------- | ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | +| `hide_avg_cpu` | Boolean | Hides the average CPU usage. | +| `dot_marker` | Boolean | Uses a dot marker for graphs. | +| `left_legend` | Boolean | Puts the CPU chart legend to the left side. | +| `current_usage` | Boolean | Sets process CPU% to be based on current CPU%. | +| `group_processes` | Boolean | Groups processes with the same name by default. | +| `case_sensitive` | Boolean | Enables case sensitivity by default. | +| `whole_word` | Boolean | Enables whole-word matching by default. | +| `regex` | Boolean | Enables regex by default. | +| `basic` | Boolean | Hides graphs and uses a more basic look. | +| `use_old_network_legend` | Boolean | DEPRECATED - uses the older network legend. | +| `battery` | Boolean | Shows the battery widget. | +| `rate` | Unsigned Int (represents milliseconds) | Sets a refresh rate in ms. | +| `default_time_value` | Unsigned Int (represents milliseconds) | Default time value for graphs in ms. | +| `time_delta` | Unsigned Int (represents milliseconds) | The amount in ms changed upon zooming. | +| `hide_time` | Boolean | Hides the time scale. | +| `temperature_type` | String (one of ["k", "f", "c", "kelvin", "fahrenheit", "celsius"]) | Sets the temperature unit type. | +| `default_widget_type` | String (one of ["cpu", "proc", "net", "temp", "mem", "disk"], same as layout options) | Sets the default widget type, use --help for more info. | +| `default_widget_count` | Unsigned Int (represents which `default_widget_type`) | Sets the n'th selected widget type as the default. | +| `disable_click` | Boolean | Disables mouse clicks. | +| `color` | String (one of ["default", "default-light", "gruvbox", "gruvbox-light", "nord", "nord-light"]) | Use a color scheme, use --help for supported values. | +| `mem_as_value` | Boolean | Defaults to showing process memory usage by value. | +| `tree` | Boolean | Defaults to showing the process widget in tree mode. | +| `show_table_scroll_position` | Boolean | Shows the scroll position tracker in table widgets. | +| `process_command` | Boolean | Show processes as their commands by default. | +| `disable_advanced_kill` | Boolean | Hides advanced options to stop a process on Unix-like systems. | +| `network_use_binary_prefix` | Boolean | Displays the network widget with binary prefixes. | +| `network_use_bytes` | Boolean | Displays the network widget using bytes. | +| `network_use_log` | Boolean | Displays the network widget with a log scale. | +| `enable_gpu_memory` | Boolean | Shows the GPU memory widget. | +| `retention` | String (human readable time, such as "10m", "1h", etc.) | How much data is stored at once in terms of time. | +| `unnormalized_cpu` | Boolean | Show process CPU% without normalizing over the number of cores. | diff --git a/sample_configs/default_config.toml b/sample_configs/default_config.toml index a2eabb38..bab1833d 100644 --- a/sample_configs/default_config.toml +++ b/sample_configs/default_config.toml @@ -18,7 +18,7 @@ # Whether to set CPU% on a process to be based on the total CPU or just current usage. #current_usage = false # Whether to set CPU% on a process to be based on the total CPU or per-core CPU% (not divided by the number of cpus). -#per_core_percentage = false +#unnormalized_cpu = false # Whether to group processes with the same name together by default. #group_processes = false # Whether to make process searching case sensitive by default. @@ -148,7 +148,6 @@ # type="proc" # default=true - # Filters - you can hide specific temperature sensors, network interfaces, and disks using filters. This is admittedly # a bit hard to use as of now, and there is a planned in-app interface for managing this in the future: #[disk_filter] diff --git a/src/app.rs b/src/app.rs index 5b75e50f..4e77998a 100644 --- a/src/app.rs +++ b/src/app.rs @@ -57,7 +57,7 @@ pub struct AppConfigFields { pub left_legend: bool, pub show_average_cpu: bool, pub use_current_cpu_total: bool, - pub per_core_percentage: bool, + pub unnormalized_cpu: bool, pub use_basic_mode: bool, pub default_time_value: u64, pub time_interval: u64, diff --git a/src/app/data_harvester.rs b/src/app/data_harvester.rs index 1d28a020..b4d437a5 100644 --- a/src/app/data_harvester.rs +++ b/src/app/data_harvester.rs @@ -112,7 +112,7 @@ pub struct DataCollector { mem_total_kb: u64, temperature_type: temperature::TemperatureType, use_current_cpu_total: bool, - per_core_percentage: bool, + unnormalized_cpu: bool, last_collection_time: Instant, total_rx: u64, total_tx: u64, @@ -145,7 +145,7 @@ impl DataCollector { mem_total_kb: 0, temperature_type: temperature::TemperatureType::Celsius, use_current_cpu_total: false, - per_core_percentage: false, + unnormalized_cpu: false, last_collection_time: Instant::now(), total_rx: 0, total_tx: 0, @@ -237,8 +237,8 @@ impl DataCollector { self.use_current_cpu_total = use_current_cpu_total; } - pub fn set_per_core_percentage(&mut self, per_core_percentage: bool) { - self.per_core_percentage = per_core_percentage; + pub fn set_unnormalized_cpu(&mut self, unnormalized_cpu: bool) { + self.unnormalized_cpu = unnormalized_cpu; } pub fn set_show_average_cpu(&mut self, show_average_cpu: bool) { @@ -327,39 +327,42 @@ impl DataCollector { } if self.widgets_to_harvest.use_proc { - #[cfg(target_os = "linux")] - { - if let Ok(logical_count) = heim::cpu::logical_count().await { - if let Ok(mut process_list) = processes::get_process_data( + if let Ok(mut process_list) = { + #[cfg(target_os = "linux")] + { + // Must do this here since we otherwise have to make `get_process_data` async. + use self::processes::CpuUsageStrategy; + + let normalize_cpu = if self.unnormalized_cpu { + heim::cpu::logical_count() + .await + .map(|v| CpuUsageStrategy::NonNormalized(v as f64)) + .unwrap_or(CpuUsageStrategy::Normalized) + } else { + CpuUsageStrategy::Normalized + }; + + processes::get_process_data( &mut self.prev_idle, &mut self.prev_non_idle, &mut self.pid_mapping, self.use_current_cpu_total, - self.per_core_percentage, + normalize_cpu, current_instant .duration_since(self.last_collection_time) .as_secs(), self.mem_total_kb, - logical_count, &mut self.user_table, - ) { - // NB: To avoid duplicate sorts on rerenders/events, we sort the processes by PID here. - // We also want to avoid re-sorting *again* since this process list is sorted! - process_list.sort_unstable_by_key(|p| p.pid); - self.data.list_of_processes = Some(process_list); - } + ) } - } - - #[cfg(not(target_os = "linux"))] - { - if let Ok(mut process_list) = { + #[cfg(not(target_os = "linux"))] + { #[cfg(target_family = "unix")] { processes::get_process_data( &self.sys, self.use_current_cpu_total, - self.per_core_percentage, + self.unnormalized_cpu, self.mem_total_kb, &mut self.user_table, ) @@ -369,14 +372,17 @@ impl DataCollector { processes::get_process_data( &self.sys, self.use_current_cpu_total, - self.per_core_percentage, + self.unnormalized_cpu, self.mem_total_kb, ) } - } { - process_list.sort_unstable_by_key(|p| p.pid); - self.data.list_of_processes = Some(process_list); } + } { + // NB: To avoid duplicate sorts on rerenders/events, we sort the processes by PID here. + // We also want to avoid re-sorting *again* later on if we're sorting by PID, since we already + // did it here! + process_list.sort_unstable_by_key(|p| p.pid); + self.data.list_of_processes = Some(process_list); } } diff --git a/src/app/data_harvester/processes/freebsd.rs b/src/app/data_harvester/processes/freebsd.rs index cbcb5eb1..4c5a9e45 100644 --- a/src/app/data_harvester/processes/freebsd.rs +++ b/src/app/data_harvester/processes/freebsd.rs @@ -25,13 +25,13 @@ struct ProcessRow { } pub fn get_process_data( - sys: &System, use_current_cpu_total: bool, per_core_percentage: bool, mem_total_kb: u64, + sys: &System, use_current_cpu_total: bool, unnormalized_cpu: bool, mem_total_kb: u64, user_table: &mut UserTable, ) -> crate::utils::error::Result> { super::macos_freebsd::get_process_data( sys, use_current_cpu_total, - per_core_percentage, + unnormalized_cpu, mem_total_kb, user_table, get_freebsd_process_cpu_usage, diff --git a/src/app/data_harvester/processes/linux.rs b/src/app/data_harvester/processes/linux.rs index d024389d..282e56a3 100644 --- a/src/app/data_harvester/processes/linux.rs +++ b/src/app/data_harvester/processes/linux.rs @@ -48,7 +48,15 @@ fn calculate_idle_values(line: &str) -> Point { (idle, non_idle) } -fn cpu_usage_calculation(prev_idle: &mut f64, prev_non_idle: &mut f64) -> error::Result { +struct CpuUsage { + /// Difference between the total delta and the idle delta. + cpu_usage: f64, + + /// Overall CPU usage as a fraction. + cpu_fraction: f64, +} + +fn cpu_usage_calculation(prev_idle: &mut f64, prev_non_idle: &mut f64) -> error::Result { let (idle, non_idle) = { // From SO answer: https://stackoverflow.com/a/23376195 let mut reader = BufReader::new(File::open("/proc/stat")?); @@ -68,38 +76,37 @@ fn cpu_usage_calculation(prev_idle: &mut f64, prev_non_idle: &mut f64) -> error: *prev_non_idle = non_idle; // TODO: Should these return errors instead? - let result = if total_delta - idle_delta != 0.0 { + let cpu_usage = if total_delta - idle_delta != 0.0 { total_delta - idle_delta } else { 1.0 }; - let cpu_percentage = if total_delta != 0.0 { - result / total_delta + let cpu_fraction = if total_delta != 0.0 { + cpu_usage / total_delta } else { 0.0 }; - Ok((result, cpu_percentage)) + Ok(CpuUsage { + cpu_usage, + cpu_fraction, + }) } -/// Returns the usage and a new set of process times. Note: cpu_fraction should be represented WITHOUT the x100 factor! -#[inline] +/// Returns the usage and a new set of process times. +/// +/// NB: cpu_fraction should be represented WITHOUT the x100 factor! fn get_linux_cpu_usage( - stat: &Stat, cpu_usage: f64, cpu_fraction: f64, prev_proc_times: u64, logical_count: u64, - use_current_cpu_total: bool, per_core_percentage: bool, + stat: &Stat, cpu_usage: f64, cpu_fraction: f64, prev_proc_times: u64, + use_current_cpu_total: bool, ) -> (f64, u64) { // Based heavily on https://stackoverflow.com/a/23376195 and https://stackoverflow.com/a/1424556 let new_proc_times = stat.utime + stat.stime; - let diff = (new_proc_times - prev_proc_times) as f64; // I HATE that it's done like this but there isn't a try_from for u64 -> f64... we can accept a bit of loss in the worst case though + let diff = (new_proc_times - prev_proc_times) as f64; // No try_from for u64 -> f64... oh well. if cpu_usage == 0.0 { (0.0, new_proc_times) - } else if per_core_percentage { - ( - diff / cpu_usage * 100_f64 * cpu_fraction * (logical_count as f64), - new_proc_times, - ) } else if use_current_cpu_total { ((diff / cpu_usage) * 100.0, new_proc_times) } else { @@ -107,11 +114,10 @@ fn get_linux_cpu_usage( } } -#[allow(clippy::too_many_arguments)] fn read_proc( prev_proc: &PrevProcDetails, process: &Process, cpu_usage: f64, cpu_fraction: f64, - use_current_cpu_total: bool, per_core_percentage: bool, time_difference_in_secs: u64, - mem_total_kb: u64, logical_count: u64, user_table: &mut UserTable, + use_current_cpu_total: bool, time_difference_in_secs: u64, mem_total_kb: u64, + user_table: &mut UserTable, ) -> error::Result<(ProcessHarvest, u64)> { let stat = process.stat()?; let (command, name) = { @@ -154,9 +160,7 @@ fn read_proc( cpu_usage, cpu_fraction, prev_proc.cpu_time, - logical_count, use_current_cpu_total, - per_core_percentage, ); let parent_pid = Some(stat.ppid); let mem_usage_bytes = stat.rss_bytes()?; @@ -164,7 +168,6 @@ fn read_proc( let mem_usage_percent = mem_usage_kb as f64 / mem_total_kb as f64 * 100.0; // This can fail if permission is denied! - let (total_read_bytes, total_write_bytes, read_bytes_per_sec, write_bytes_per_sec) = if let Ok(io) = process.io() { let total_read_bytes = io.read_bytes; @@ -218,16 +221,38 @@ fn read_proc( )) } -#[allow(clippy::too_many_arguments)] +/// How to calculate CPU usage. +pub enum CpuUsageStrategy { + /// Normalized means the displayed usage percentage is divided over the number of CPU cores. + /// + /// For example, if the "overall" usage over the entire system is 105%, and there are 5 cores, then + /// the displayed percentage is 21%. + Normalized, + + /// Non-normalized means that the overall usage over the entire system is shown, without dividing + /// over the number of cores. + NonNormalized(f64), +} + pub fn get_process_data( prev_idle: &mut f64, prev_non_idle: &mut f64, pid_mapping: &mut FxHashMap, use_current_cpu_total: bool, - per_core_percentage: bool, time_difference_in_secs: u64, mem_total_kb: u64, logical_count: u64, + normalization: CpuUsageStrategy, time_difference_in_secs: u64, mem_total_kb: u64, user_table: &mut UserTable, ) -> crate::utils::error::Result> { // TODO: [PROC THREADS] Add threads - if let Ok((cpu_usage, cpu_fraction)) = cpu_usage_calculation(prev_idle, prev_non_idle) { + if let Ok(CpuUsage { + mut cpu_usage, + cpu_fraction, + }) = cpu_usage_calculation(prev_idle, prev_non_idle) + { + if let CpuUsageStrategy::NonNormalized(num_cores) = normalization { + // Note we *divide* here because the later calculation divides `cpu_usage` - in effect, + // multiplying over the number of cores. + cpu_usage /= num_cores; + } + let mut pids_to_clear: FxHashSet = pid_mapping.keys().cloned().collect(); let process_vector: Vec = std::fs::read_dir("/proc")? @@ -245,10 +270,8 @@ pub fn get_process_data( cpu_usage, cpu_fraction, use_current_cpu_total, - per_core_percentage, time_difference_in_secs, mem_total_kb, - logical_count, user_table, ) { prev_proc_details.cpu_time = new_process_times; diff --git a/src/app/data_harvester/processes/macos.rs b/src/app/data_harvester/processes/macos.rs index c3570290..a2b61b69 100644 --- a/src/app/data_harvester/processes/macos.rs +++ b/src/app/data_harvester/processes/macos.rs @@ -7,13 +7,13 @@ use crate::{data_harvester::processes::UserTable, Pid}; mod sysctl_bindings; pub fn get_process_data( - sys: &System, use_current_cpu_total: bool, per_core_percentage: bool, mem_total_kb: u64, + sys: &System, use_current_cpu_total: bool, unnormalized_cpu: bool, mem_total_kb: u64, user_table: &mut UserTable, ) -> crate::utils::error::Result> { super::macos_freebsd::get_process_data( sys, use_current_cpu_total, - per_core_percentage, + unnormalized_cpu, mem_total_kb, user_table, get_macos_process_cpu_usage, diff --git a/src/app/data_harvester/processes/macos_freebsd.rs b/src/app/data_harvester/processes/macos_freebsd.rs index 3f2e340d..251bc847 100644 --- a/src/app/data_harvester/processes/macos_freebsd.rs +++ b/src/app/data_harvester/processes/macos_freebsd.rs @@ -9,8 +9,8 @@ use super::ProcessHarvest; use crate::{data_harvester::processes::UserTable, utils::error::Result, Pid}; pub fn get_process_data( - sys: &System, use_current_cpu_total: bool, per_core_percentage: bool, mem_total_kb: u64, - user_table: &mut UserTable, get_process_cpu_usage: F, + sys: &System, use_current_cpu_total: bool, unnormalized_cpu: bool, mem_total_kb: u64, + user_table: &mut UserTable, backup_cpu_proc_usage: F, ) -> Result> where F: Fn(&[Pid]) -> io::Result>, @@ -18,6 +18,8 @@ where let mut process_vector: Vec = Vec::new(); let process_hashmap = sys.processes(); let cpu_usage = sys.global_cpu_info().cpu_usage() as f64 / 100.0; + let num_processors = sys.cpus().len() as f64; + for process_val in process_hashmap.values() { let name = if process_val.name().is_empty() { let process_cmd = process_val.cmd(); @@ -50,10 +52,10 @@ where let pcu = { let usage = process_val.cpu_usage() as f64; - if per_core_percentage || sys.cpus().is_empty() { + if unnormalized_cpu || num_processors == 0.0 { usage } else { - usage / (sys.cpus().len() as f64) + usage / num_processors } }; let process_cpu_usage = if use_current_cpu_total && cpu_usage > 0.0 { @@ -116,13 +118,13 @@ where .filter(|process| process.process_state.0 == unknown_state) .map(|process| process.pid) .collect(); - let cpu_usages = get_process_cpu_usage(&cpu_usage_unknown_pids)?; + let cpu_usages = backup_cpu_proc_usage(&cpu_usage_unknown_pids)?; for process in &mut process_vector { if cpu_usages.contains_key(&process.pid) { - process.cpu_usage_percent = if per_core_percentage || sys.cpus().is_empty() { + process.cpu_usage_percent = if unnormalized_cpu || num_processors == 0.0 { *cpu_usages.get(&process.pid).unwrap() } else { - *cpu_usages.get(&process.pid).unwrap() / (sys.cpus().len() as f64) + *cpu_usages.get(&process.pid).unwrap() / num_processors }; } } diff --git a/src/app/data_harvester/processes/windows.rs b/src/app/data_harvester/processes/windows.rs index d92f2032..62749052 100644 --- a/src/app/data_harvester/processes/windows.rs +++ b/src/app/data_harvester/processes/windows.rs @@ -5,11 +5,13 @@ use sysinfo::{CpuExt, PidExt, ProcessExt, System, SystemExt}; use super::ProcessHarvest; pub fn get_process_data( - sys: &System, use_current_cpu_total: bool, per_core_percentage: bool, mem_total_kb: u64, + sys: &System, use_current_cpu_total: bool, unnormalized_cpu: bool, mem_total_kb: u64, ) -> crate::utils::error::Result> { let mut process_vector: Vec = Vec::new(); let process_hashmap = sys.processes(); let cpu_usage = sys.global_cpu_info().cpu_usage() as f64 / 100.0; + let num_processors = sys.cpus().len(); + for process_val in process_hashmap.values() { let name = if process_val.name().is_empty() { let process_cmd = process_val.cmd(); @@ -42,10 +44,10 @@ pub fn get_process_data( let pcu = { let usage = process_val.cpu_usage() as f64; - if per_core_percentage || sys.cpus().is_empty() { + if unnormalized_cpu || num_processors == 0 { usage } else { - usage / (sys.cpus().len() as f64) + usage / (num_processors as f64) } }; let process_cpu_usage = if use_current_cpu_total && cpu_usage > 0.0 { diff --git a/src/clap.rs b/src/clap.rs index b7b9f9a6..9163cf7e 100644 --- a/src/clap.rs +++ b/src/clap.rs @@ -135,12 +135,12 @@ pub fn build_app() -> Command<'static> { .help("Sets process CPU% to be based on current CPU%.") .long_help("Sets process CPU% usage to be based on the current system CPU% usage rather than total CPU usage."); - let per_core_percentage = Arg::new("per_core_percentage") + let unnormalized_cpu = Arg::new("unnormalized_cpu") .short('p') - .long("per_core_percentage") - .help("Sets CPU% to be based on per core CPU%.") + .long("unnormalized_cpu") + .help("Show process CPU% without normalizing over the number of cores.") .long_help( - "Sets CPU% usage to be based on the per core CPU% usage rather than total CPU usage.", + "Shows process CPU usage without averaging over the number of CPU cores in the system.", ); // TODO: [DEBUG] Add a proper debugging solution. @@ -405,7 +405,7 @@ use CPU (3) as the default instead. .arg(network_use_log) .arg(network_use_binary_prefix) .arg(current_usage) - .arg(per_core_percentage) + .arg(unnormalized_cpu) .arg(use_old_network_legend) .arg(whole_word) .arg(retention); diff --git a/src/constants.rs b/src/constants.rs index ab5a0f0b..b42bbfab 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -493,7 +493,7 @@ pub const CONFIG_TEXT: &str = r##"# This is a default config file for bottom. A # Whether to set CPU% on a process to be based on the total CPU or just current usage. #current_usage = false # Whether to set CPU% on a process to be based on the total CPU or per-core CPU% (not divided by the number of cpus). -#per_core_percentage = false +#unnormalized_cpu = false # Whether to group processes with the same name together by default. #group_processes = false # Whether to make process searching case sensitive by default. diff --git a/src/lib.rs b/src/lib.rs index b23adcdf..b17a3443 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -474,7 +474,7 @@ pub fn create_collection_thread( ) -> JoinHandle<()> { let temp_type = app_config_fields.temperature_type; let use_current_cpu_total = app_config_fields.use_current_cpu_total; - let per_core_percentage = app_config_fields.per_core_percentage; + let unnormalized_cpu = app_config_fields.unnormalized_cpu; let show_average_cpu = app_config_fields.show_average_cpu; let update_rate_in_milliseconds = app_config_fields.update_rate_in_milliseconds; @@ -484,7 +484,7 @@ pub fn create_collection_thread( data_state.set_data_collection(used_widget_set); data_state.set_temperature_type(temp_type); data_state.set_use_current_cpu_total(use_current_cpu_total); - data_state.set_per_core_percentage(per_core_percentage); + data_state.set_unnormalized_cpu(unnormalized_cpu); data_state.set_show_average_cpu(show_average_cpu); data_state.init(); @@ -510,7 +510,7 @@ pub fn create_collection_thread( data_state.set_temperature_type(app_config_fields.temperature_type); data_state .set_use_current_cpu_total(app_config_fields.use_current_cpu_total); - data_state.set_per_core_percentage(per_core_percentage); + data_state.set_unnormalized_cpu(unnormalized_cpu); data_state.set_show_average_cpu(app_config_fields.show_average_cpu); } ThreadControlEvent::UpdateUsedWidgets(used_widget_set) => { diff --git a/src/options.rs b/src/options.rs index b0dd974c..b7fa777f 100644 --- a/src/options.rs +++ b/src/options.rs @@ -62,7 +62,7 @@ pub struct ConfigFlags { pub rate: Option, pub left_legend: Option, pub current_usage: Option, - pub per_core_percentage: Option, + pub unnormalized_cpu: Option, pub group_processes: Option, pub case_sensitive: Option, pub whole_word: Option, @@ -230,7 +230,7 @@ pub fn build_app( use_dot: get_use_dot(matches, config), left_legend: get_use_left_legend(matches, config), use_current_cpu_total: get_use_current_cpu_total(matches, config), - per_core_percentage: get_per_core_percentage(matches, config), + unnormalized_cpu: get_unnormalized_cpu(matches, config), use_basic_mode, default_time_value, time_interval: get_time_interval(matches, config, retention_ms) @@ -596,12 +596,12 @@ fn get_use_current_cpu_total(matches: &ArgMatches, config: &Config) -> bool { false } -fn get_per_core_percentage(matches: &ArgMatches, config: &Config) -> bool { - if matches.is_present("per_core_percentage") { +fn get_unnormalized_cpu(matches: &ArgMatches, config: &Config) -> bool { + if matches.is_present("unnormalized_cpu") { return true; } else if let Some(flags) = &config.flags { - if let Some(per_core_percentage) = flags.per_core_percentage { - return per_core_percentage; + if let Some(unnormalized_cpu) = flags.unnormalized_cpu { + return unnormalized_cpu; } }