summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authordatabase64128 <free122448@hotmail.com>2022-11-21 16:12:47 +0800
committerGitHub <noreply@github.com>2022-11-21 03:12:47 -0500
commit71bc6c940e880ea8c9801ac606055f1f55d516fe (patch)
tree41ad01d7b210b30b18c58ea529b91a976df1becd /src
parent9e4aed7d566948c7d59bee9af3bd68c6c9960075 (diff)
feature: per-core process cpu usage percentage (#899)
Diffstat (limited to 'src')
-rw-r--r--src/app.rs1
-rw-r--r--src/app/data_harvester.rs40
-rw-r--r--src/app/data_harvester/processes/freebsd.rs4
-rw-r--r--src/app/data_harvester/processes/linux.rs23
-rw-r--r--src/app/data_harvester/processes/macos.rs4
-rw-r--r--src/app/data_harvester/processes/macos_freebsd.rs16
-rw-r--r--src/app/data_harvester/processes/windows.rs10
-rw-r--r--src/clap.rs9
-rw-r--r--src/constants.rs2
-rw-r--r--src/lib.rs3
-rw-r--r--src/options.rs14
11 files changed, 92 insertions, 34 deletions
diff --git a/src/app.rs b/src/app.rs
index 34696d2c..5b75e50f 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -57,6 +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 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 25913077..1d28a020 100644
--- a/src/app/data_harvester.rs
+++ b/src/app/data_harvester.rs
@@ -112,6 +112,7 @@ pub struct DataCollector {
mem_total_kb: u64,
temperature_type: temperature::TemperatureType,
use_current_cpu_total: bool,
+ per_core_percentage: bool,
last_collection_time: Instant,
total_rx: u64,
total_tx: u64,
@@ -144,6 +145,7 @@ impl DataCollector {
mem_total_kb: 0,
temperature_type: temperature::TemperatureType::Celsius,
use_current_cpu_total: false,
+ per_core_percentage: false,
last_collection_time: Instant::now(),
total_rx: 0,
total_tx: 0,
@@ -235,6 +237,10 @@ 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_show_average_cpu(&mut self, show_average_cpu: bool) {
self.show_average_cpu = show_average_cpu;
}
@@ -321,28 +327,39 @@ impl DataCollector {
}
if self.widgets_to_harvest.use_proc {
- if let Ok(mut process_list) = {
- #[cfg(target_os = "linux")]
- {
- processes::get_process_data(
+ #[cfg(target_os = "linux")]
+ {
+ if let Ok(logical_count) = heim::cpu::logical_count().await {
+ if let Ok(mut process_list) = 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,
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"))]
- {
+ }
+
+ #[cfg(not(target_os = "linux"))]
+ {
+ if let Ok(mut process_list) = {
#[cfg(target_family = "unix")]
{
processes::get_process_data(
&self.sys,
self.use_current_cpu_total,
+ self.per_core_percentage,
self.mem_total_kb,
&mut self.user_table,
)
@@ -352,15 +369,14 @@ impl DataCollector {
processes::get_process_data(
&self.sys,
self.use_current_cpu_total,
+ self.per_core_percentage,
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* since this process list is sorted!
- 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 49816bc2..cbcb5eb1 100644
--- a/src/app/data_harvester/processes/freebsd.rs
+++ b/src/app/data_harvester/processes/freebsd.rs
@@ -25,11 +25,13 @@ struct ProcessRow {
}
pub fn get_process_data(
- sys: &System, use_current_cpu_total: bool, mem_total_kb: u64, user_table: &mut UserTable,
+ sys: &System, use_current_cpu_total: bool, per_core_percentage: bool, mem_total_kb: u64,
+ user_table: &mut UserTable,
) -> crate::utils::error::Result<Vec<ProcessHarvest>> {
super::macos_freebsd::get_process_data(
sys,
use_current_cpu_total,
+ per_core_percentage,
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 69a19cc5..d024389d 100644
--- a/src/app/data_harvester/processes/linux.rs
+++ b/src/app/data_harvester/processes/linux.rs
@@ -84,9 +84,10 @@ fn cpu_usage_calculation(prev_idle: &mut f64, prev_non_idle: &mut f64) -> error:
}
/// Returns the usage and a new set of process times. Note: cpu_fraction should be represented WITHOUT the x100 factor!
+#[inline]
fn get_linux_cpu_usage(
- stat: &Stat, cpu_usage: f64, cpu_fraction: f64, prev_proc_times: u64,
- use_current_cpu_total: bool,
+ stat: &Stat, cpu_usage: f64, cpu_fraction: f64, prev_proc_times: u64, logical_count: u64,
+ use_current_cpu_total: bool, per_core_percentage: 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;
@@ -94,6 +95,11 @@ fn get_linux_cpu_usage(
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 {
@@ -101,10 +107,11 @@ 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, time_difference_in_secs: u64, mem_total_kb: u64,
- user_table: &mut UserTable,
+ 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,
) -> error::Result<(ProcessHarvest, u64)> {
let stat = process.stat()?;
let (command, name) = {
@@ -147,7 +154,9 @@ 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()?;
@@ -209,10 +218,12 @@ fn read_proc(
))
}
+#[allow(clippy::too_many_arguments)]
pub fn get_process_data(
prev_idle: &mut f64, prev_non_idle: &mut f64,
pid_mapping: &mut FxHashMap<Pid, PrevProcDetails>, use_current_cpu_total: bool,
- time_difference_in_secs: u64, mem_total_kb: u64, user_table: &mut UserTable,
+ per_core_percentage: bool, time_difference_in_secs: u64, mem_total_kb: u64, logical_count: u64,
+ user_table: &mut UserTable,
) -> crate::utils::error::Result<Vec<ProcessHarvest>> {
// TODO: [PROC THREADS] Add threads
@@ -234,8 +245,10 @@ 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 d0847a1b..c3570290 100644
--- a/src/app/data_harvester/processes/macos.rs
+++ b/src/app/data_harvester/processes/macos.rs
@@ -7,11 +7,13 @@ use crate::{data_harvester::processes::UserTable, Pid};
mod sysctl_bindings;
pub fn get_process_data(
- sys: &System, use_current_cpu_total: bool, mem_total_kb: u64, user_table: &mut UserTable,
+ sys: &System, use_current_cpu_total: bool, per_core_percentage: bool, mem_total_kb: u64,
+ user_table: &mut UserTable,
) -> crate::utils::error::Result<Vec<ProcessHarvest>> {
super::macos_freebsd::get_process_data(
sys,
use_current_cpu_total,
+ per_core_percentage,
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 a4ac1e2d..3f2e340d 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<F>(
- sys: &System, use_current_cpu_total: bool, mem_total_kb: u64, user_table: &mut UserTable,
- get_process_cpu_usage: F,
+ sys: &System, use_current_cpu_total: bool, per_core_percentage: bool, mem_total_kb: u64,
+ user_table: &mut UserTable, get_process_cpu_usage: F,
) -> Result<Vec<ProcessHarvest>>
where
F: Fn(&[Pid]) -> io::Result<HashMap<Pid, f64>>,
@@ -18,7 +18,6 @@ where
let mut process_vector: Vec<ProcessHarvest> = 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();
@@ -51,11 +50,10 @@ where
let pcu = {
let usage = process_val.cpu_usage() as f64;
- let res = usage / num_processors;
- if res.is_finite() {
- res
- } else {
+ if per_core_percentage || sys.cpus().is_empty() {
usage
+ } else {
+ usage / (sys.cpus().len() as f64)
}
};
let process_cpu_usage = if use_current_cpu_total && cpu_usage > 0.0 {
@@ -121,10 +119,10 @@ where
let cpu_usages = get_process_cpu_usage(&cpu_usage_unknown_pids)?;
for process in &mut process_vector {
if cpu_usages.contains_key(&process.pid) {
- process.cpu_usage_percent = if num_processors == 0.0 {
+ process.cpu_usage_percent = if per_core_percentage || sys.cpus().is_empty() {
*cpu_usages.get(&process.pid).unwrap()
} else {
- *cpu_usages.get(&process.pid).unwrap() / num_processors
+ *cpu_usages.get(&process.pid).unwrap() / (sys.cpus().len() as f64)
};
}
}
diff --git a/src/app/data_harvester/processes/windows.rs b/src/app/data_harvester/processes/windows.rs
index 55b59086..d92f2032 100644
--- a/src/app/data_harvester/processes/windows.rs
+++ b/src/app/data_harvester/processes/windows.rs
@@ -5,12 +5,11 @@ use sysinfo::{CpuExt, PidExt, ProcessExt, System, SystemExt};
use super::ProcessHarvest;
pub fn get_process_data(
- sys: &System, use_current_cpu_total: bool, mem_total_kb: u64,
+ sys: &System, use_current_cpu_total: bool, per_core_percentage: bool, mem_total_kb: u64,
) -> crate::utils::error::Result<Vec<ProcessHarvest>> {
let mut process_vector: Vec<ProcessHarvest> = 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();
@@ -43,11 +42,10 @@ pub fn get_process_data(
let pcu = {
let usage = process_val.cpu_usage() as f64;
- let res = usage / num_processors;
- if res.is_finite() {
- res
- } else {
+ if per_core_percentage || sys.cpus().is_empty() {
usage
+ } else {
+ usage / (sys.cpus().len() 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 043b79b3..b7b9f9a6 100644
--- a/src/clap.rs
+++ b/src/clap.rs
@@ -135,6 +135,14 @@ 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")
+ .short('p')
+ .long("per_core_percentage")
+ .help("Sets CPU% to be based on per core CPU%.")
+ .long_help(
+ "Sets CPU% usage to be based on the per core CPU% usage rather than total CPU usage.",
+ );
+
// TODO: [DEBUG] Add a proper debugging solution.
let disable_click = Arg::new("disable_click")
@@ -397,6 +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(use_old_network_legend)
.arg(whole_word)
.arg(retention);
diff --git a/src/constants.rs b/src/constants.rs
index ceee7c4e..ab5a0f0b 100644
--- a/src/constants.rs
+++ b/src/constants.rs
@@ -492,6 +492,8 @@ pub const CONFIG_TEXT: &str = r##"# This is a default config file for bottom. A
#left_legend = false
# 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
# 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 c6b220b9..b23adcdf 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -474,6 +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 show_average_cpu = app_config_fields.show_average_cpu;
let update_rate_in_milliseconds = app_config_fields.update_rate_in_milliseconds;
@@ -483,6 +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_show_average_cpu(show_average_cpu);
data_state.init();
@@ -508,6 +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_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 f2d8c0a8..b0dd974c 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -62,6 +62,7 @@ pub struct ConfigFlags {
pub rate: Option<u64>,
pub left_legend: Option<bool>,
pub current_usage: Option<bool>,
+ pub per_core_percentage: Option<bool>,
pub group_processes: Option<bool>,
pub case_sensitive: Option<bool>,
pub whole_word: Option<bool>,
@@ -229,6 +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),
use_basic_mode,
default_time_value,
time_interval: get_time_interval(matches, config, retention_ms)
@@ -594,6 +596,18 @@ 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") {
+ return true;
+ } else if let Some(flags) = &config.flags {
+ if let Some(per_core_percentage) = flags.per_core_percentage {
+ return per_core_percentage;
+ }
+ }
+
+ false
+}
+
fn get_use_basic_mode(matches: &ArgMatches, config: &Config) -> bool {
if matches.is_present("basic") {
return true;