diff --git a/examples/simple.rs b/examples/simple.rs index 6dd81b2b3..4249e6fe1 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -232,6 +232,9 @@ fn interpret_input(input: &str, sys: &mut System) -> bool { sys.global_cpu_info().frequency() ); } + "governor" => { + writeln!(&mut io::stdout(), "governor: {}", sys.cpus()[0].governor()); + } "vendor_id" => { writeln!( &mut io::stdout(), diff --git a/src/apple/cpu.rs b/src/apple/cpu.rs index 1fe7bc287..638f4fd29 100644 --- a/src/apple/cpu.rs +++ b/src/apple/cpu.rs @@ -1,7 +1,7 @@ // Take a look at the license at the top of the repository in the LICENSE file. use crate::sys::utils::{get_sys_value, get_sys_value_by_name}; -use crate::{CpuExt, CpuRefreshKind}; +use crate::{CpuExt, CpuRefreshKind, GovernorKind}; use libc::{c_char, c_void, host_processor_info, mach_port_t, mach_task_self}; use std::mem; @@ -170,6 +170,10 @@ impl CpuExt for Cpu { fn brand(&self) -> &str { &self.brand } + + fn governor(&self) -> GovernorKind { + GovernorKind::default() + } } pub(crate) unsafe fn get_cpu_frequency() -> u64 { diff --git a/src/apple/system.rs b/src/apple/system.rs index b4eeeda44..6559a04b8 100644 --- a/src/apple/system.rs +++ b/src/apple/system.rs @@ -6,7 +6,8 @@ use crate::sys::process::*; use crate::sys::utils::{get_sys_value, get_sys_value_by_name}; use crate::{ - CpuRefreshKind, Disks, LoadAvg, Networks, Pid, ProcessRefreshKind, RefreshKind, SystemExt, User, + CpuRefreshKind, Disks, GovernorKind, LoadAvg, Networks, Pid, ProcessRefreshKind, RefreshKind, + SystemExt, User, }; #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] @@ -346,6 +347,10 @@ impl SystemExt for System { &self.cpus.global_cpu } + fn governor(&self) -> GovernorKind { + GovernorKind::default() + } + fn cpus(&self) -> &[Cpu] { &self.cpus.cpus } diff --git a/src/common.rs b/src/common.rs index 267ab7e1d..a67928989 100644 --- a/src/common.rs +++ b/src/common.rs @@ -462,6 +462,74 @@ impl RefreshKind { impl_get_set!(RefreshKind, users_list, with_users_list, without_users_list); } +/// Enum containing the different supported kinds CPUs governor. +/// +/// This type is returned by `governor`. +/// +/// ```no_run +/// use sysinfo::{System, SystemExt}; +/// let s = System::new_all(); +/// let governor = s.governor(); +/// ``` +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum GovernorKind { + /// CPU operating in the highest frequency within the borders + /// of scaling_min_freq and scaling_max_freq. + Performance, + /// CPU operating statically to the lowest frequency within + /// the borders of scaling_min_freq and scaling_max_freq. + Powersave, + /// Governor that allows the user, or any userspace program + /// running with UID "root", to set the CPU to a specific frequency + /// by making a sysfs file "scaling_setspeed" available in the CPU-device directory. + Userspace, + /// CPU operating in a frequency depending on the current system load. + Ondemand, + /// Close to Ondemand, differs in behaviour in that it gracefully increases and decreases the + /// CPU speed rather than jumping to max speed the moment there is any load on the CPU. + Conservative, + /// Aims at better integration with the Linux kernel scheduler. + Schedutil, + /// Unknown scheduler type + Unknown(String), +} + +impl FromStr for GovernorKind { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(match s.to_lowercase().as_str() { + "performance" => GovernorKind::Performance, + "powersave" => GovernorKind::Powersave, + "userspace" => GovernorKind::Userspace, + "ondemand" => GovernorKind::Ondemand, + "conservative" => GovernorKind::Conservative, + "schedutil" => GovernorKind::Schedutil, + unknown => GovernorKind::Unknown(unknown.to_string()), + }) + } +} + +impl fmt::Display for GovernorKind { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(match self { + GovernorKind::Performance => "performance", + GovernorKind::Powersave => "powersave", + GovernorKind::Userspace => "userspace", + GovernorKind::Ondemand => "ondemand", + GovernorKind::Conservative => "conservative", + GovernorKind::Schedutil => "schedutil", + GovernorKind::Unknown(other) => other, + }) + } +} + +impl Default for GovernorKind { + fn default() -> Self { + GovernorKind::Unknown(Default::default()) + } +} + /// Networks interfaces. /// /// ```no_run diff --git a/src/debug.rs b/src/debug.rs index 5d4a05a69..5ceb9d5ee 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -13,6 +13,7 @@ impl fmt::Debug for Cpu { .field("name", &self.name()) .field("CPU usage", &self.cpu_usage()) .field("frequency", &self.frequency()) + .field("governor", &self.governor()) .field("vendor ID", &self.vendor_id()) .field("brand", &self.brand()) .finish() diff --git a/src/freebsd/cpu.rs b/src/freebsd/cpu.rs index 6946b6a7e..d6774d1df 100644 --- a/src/freebsd/cpu.rs +++ b/src/freebsd/cpu.rs @@ -3,7 +3,7 @@ use crate::sys::utils::{ get_sys_value_array, get_sys_value_by_name, get_sys_value_str_by_name, init_mib, VecSwitcher, }; -use crate::{CpuExt, CpuRefreshKind}; +use crate::{CpuExt, CpuRefreshKind, GovernorKind}; use libc::{c_int, c_ulong}; @@ -171,6 +171,10 @@ impl CpuExt for Cpu { fn brand(&self) -> &str { "" } + + fn governor(&self) -> GovernorKind { + GovernorKind::default() + } } pub(crate) fn physical_core_count() -> Option { diff --git a/src/freebsd/system.rs b/src/freebsd/system.rs index bccb6ad1b..af6c8677f 100644 --- a/src/freebsd/system.rs +++ b/src/freebsd/system.rs @@ -2,8 +2,8 @@ use crate::{ sys::{component::Component, Cpu, Process}, - CpuRefreshKind, Disks, LoadAvg, Networks, Pid, ProcessRefreshKind, RefreshKind, SystemExt, - User, + CpuRefreshKind, Disks, GovernorKind, LoadAvg, Networks, Pid, ProcessRefreshKind, RefreshKind, + SystemExt, User, }; use std::cell::UnsafeCell; @@ -210,6 +210,10 @@ impl SystemExt for System { &self.cpus.global_cpu } + fn governor(&self) -> GovernorKind { + GovernorKind::default() + } + fn cpus(&self) -> &[Cpu] { &self.cpus.cpus } diff --git a/src/lib.rs b/src/lib.rs index 54b9f0bf9..68b263167 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,9 +80,9 @@ cfg_if::cfg_if! { } pub use common::{ - get_current_pid, CpuRefreshKind, DiskKind, DiskUsage, Disks, Gid, Group, LoadAvg, MacAddr, - Networks, NetworksIter, Pid, PidExt, ProcessRefreshKind, ProcessStatus, RefreshKind, Signal, - Uid, + get_current_pid, CpuRefreshKind, DiskKind, DiskUsage, Disks, Gid, GovernorKind, Group, LoadAvg, + MacAddr, Networks, NetworksIter, Pid, PidExt, ProcessRefreshKind, ProcessStatus, RefreshKind, + Signal, Uid, }; pub use sys::{Component, Cpu, Disk, NetworkData, Process, System, User}; pub use traits::{ diff --git a/src/linux/cpu.rs b/src/linux/cpu.rs index d99b325c1..d7ff299ff 100644 --- a/src/linux/cpu.rs +++ b/src/linux/cpu.rs @@ -5,10 +5,11 @@ use std::collections::HashSet; use std::fs::File; use std::io::{BufRead, BufReader, Read}; +use std::str::FromStr; use std::time::Instant; use crate::sys::utils::to_u64; -use crate::{CpuExt, CpuRefreshKind, SystemExt}; +use crate::{CpuExt, CpuRefreshKind, GovernorKind, SystemExt}; macro_rules! to_str { ($e:expr) => { @@ -47,6 +48,7 @@ impl CpusWrapper { 0, String::new(), String::new(), + GovernorKind::default(), ), cpus: Vec::with_capacity(4), need_cpus_update: true, @@ -77,6 +79,7 @@ impl CpusWrapper { } else { (String::new(), String::new()) }; + let governor = get_governor(); // If the last CPU usage update is too close (less than `MINIMUM_CPU_UPDATE_INTERVAL`), // we don't want to update CPUs times. @@ -142,6 +145,7 @@ impl CpusWrapper { 0, vendor_id.clone(), brand.clone(), + governor.clone().unwrap_or_default(), )); } else { parts.next(); // we don't want the name again @@ -316,6 +320,7 @@ pub struct Cpu { pub(crate) frequency: u64, pub(crate) vendor_id: String, pub(crate) brand: String, + pub(crate) governor: GovernorKind, } impl Cpu { @@ -334,6 +339,7 @@ impl Cpu { frequency: u64, vendor_id: String, brand: String, + governor: GovernorKind, ) -> Cpu { let mut new_values = CpuValues::new(); new_values.set( @@ -349,6 +355,7 @@ impl Cpu { frequency, vendor_id, brand, + governor, } } @@ -403,6 +410,10 @@ impl CpuExt for Cpu { self.frequency } + fn governor(&self) -> GovernorKind { + self.governor.clone() + } + fn vendor_id(&self) -> &str { &self.vendor_id } @@ -447,6 +458,24 @@ pub(crate) fn get_cpu_frequency(cpu_core_index: usize) -> u64 { .unwrap_or_default() } +pub(crate) fn get_governor() -> Option { + let mut s = String::new(); + // String should follow the pattern described in [governors.txt](https://www.kernel.org/doc/Documentation/cpu-freq/governors.txt) + if File::open("/sys/devices/system/cpu/cpufreq/policy0/scaling_governor") + .and_then(|mut f| f.read_to_string(&mut s)) + .is_ok() + { + let governor = s.trim().split('\n').next().unwrap_or_default(); + if governor.is_empty() { + return None; + } + + return Some(GovernorKind::from_str(governor).unwrap()); + } + + None +} + #[allow(unused_assignments)] pub(crate) fn get_physical_core_count() -> Option { let mut s = String::new(); diff --git a/src/linux/system.rs b/src/linux/system.rs index bfd06904d..3aebb26b8 100644 --- a/src/linux/system.rs +++ b/src/linux/system.rs @@ -5,7 +5,8 @@ use crate::sys::cpu::*; use crate::sys::process::*; use crate::sys::utils::{get_all_data, to_u64}; use crate::{ - CpuRefreshKind, Disks, LoadAvg, Networks, Pid, ProcessRefreshKind, RefreshKind, SystemExt, User, + CpuRefreshKind, Disks, GovernorKind, LoadAvg, Networks, Pid, ProcessRefreshKind, RefreshKind, + SystemExt, User, }; use libc::{self, c_char, c_int, sysconf, _SC_CLK_TCK, _SC_HOST_NAME_MAX, _SC_PAGESIZE}; @@ -402,6 +403,10 @@ impl SystemExt for System { &self.cpus.global_cpu } + fn governor(&self) -> GovernorKind { + get_governor().unwrap_or_default() + } + fn cpus(&self) -> &[Cpu] { &self.cpus.cpus } diff --git a/src/traits.rs b/src/traits.rs index c41280df3..4e738dc66 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -5,8 +5,8 @@ use crate::{ sys::{Component, Cpu, Disk, Process}, }; use crate::{ - CpuRefreshKind, DiskKind, DiskUsage, Disks, Group, LoadAvg, Networks, NetworksIter, Pid, - ProcessRefreshKind, ProcessStatus, RefreshKind, Signal, User, + CpuRefreshKind, DiskKind, DiskUsage, Disks, GovernorKind, Group, LoadAvg, Networks, + NetworksIter, Pid, ProcessRefreshKind, ProcessStatus, RefreshKind, Signal, User, }; use std::collections::HashMap; @@ -570,6 +570,20 @@ pub trait CpuExt: Debug { /// } /// ``` fn frequency(&self) -> u64; + + /// Returns the CPU's governor. + /// + /// ```no_run + /// use sysinfo::{CpuExt, GovernorKind, System, SystemExt, RefreshKind, CpuRefreshKind}; + /// + /// let s = System::new_with_specifics( + /// RefreshKind::new().with_cpu(CpuRefreshKind::everything()), + /// ); + /// for cpu in s.cpus() { + /// println!("{}", cpu.governor()); + /// } + /// ``` + fn governor(&self) -> GovernorKind; } /// Contains all the methods of the [`System`][crate::System] type. @@ -1069,6 +1083,18 @@ pub trait SystemExt: Sized + Debug + Default + Send + Sync { /// ``` fn global_cpu_info(&self) -> &Cpu; + /// Returns the CPUs governor being used. + /// + /// ```no_run + /// use sysinfo::{CpuRefreshKind, CpuExt, RefreshKind, System, SystemExt}; + /// + /// let s = System::new_with_specifics( + /// RefreshKind::new().with_cpu(CpuRefreshKind::everything()), + /// ); + /// println!("{}%", s.global_cpu_info().cpu_usage()); + /// ``` + fn governor(&self) -> GovernorKind; + /// Returns the list of the CPUs. /// /// By default, the list of CPUs is empty until you call [`SystemExt::refresh_cpu`] or diff --git a/src/unknown/cpu.rs b/src/unknown/cpu.rs index 72b9be447..caa59a6d7 100644 --- a/src/unknown/cpu.rs +++ b/src/unknown/cpu.rs @@ -1,6 +1,6 @@ // Take a look at the license at the top of the repository in the LICENSE file. -use crate::CpuExt; +use crate::{CpuExt, GovernorKind}; #[doc = include_str!("../../md_doc/cpu.md")] pub struct Cpu {} @@ -31,4 +31,8 @@ impl CpuExt for Cpu { fn brand(&self) -> &str { "" } + + fn governor(&self) -> GovernorKind { + GovernorKind::default() + } } diff --git a/src/unknown/system.rs b/src/unknown/system.rs index a8da84e5a..85d56436f 100644 --- a/src/unknown/system.rs +++ b/src/unknown/system.rs @@ -2,8 +2,8 @@ use crate::{ sys::{component::Component, Cpu, Process}, - CpuRefreshKind, Disks, LoadAvg, Networks, Pid, ProcessRefreshKind, RefreshKind, SystemExt, - User, + CpuRefreshKind, Disks, GovernorKind, LoadAvg, Networks, Pid, ProcessRefreshKind, RefreshKind, + SystemExt, User, }; use std::collections::HashMap; @@ -74,6 +74,10 @@ impl SystemExt for System { &self.global_cpu } + fn governor(&self) -> GovernorKind { + GovernorKind::default() + } + fn cpus(&self) -> &[Cpu] { &[] } diff --git a/src/windows/cpu.rs b/src/windows/cpu.rs index 3a998868e..8b1c1d746 100644 --- a/src/windows/cpu.rs +++ b/src/windows/cpu.rs @@ -1,7 +1,7 @@ // Take a look at the license at the top of the repository in the LICENSE file. use crate::sys::tools::KeyHandler; -use crate::{CpuExt, CpuRefreshKind, LoadAvg}; +use crate::{CpuExt, CpuRefreshKind, GovernorKind, LoadAvg}; use std::collections::HashMap; use std::io::Error; @@ -332,6 +332,10 @@ impl CpuExt for Cpu { fn brand(&self) -> &str { &self.brand } + + fn governor(&self) -> GovernorKind { + GovernorKind::default() + } } impl Cpu { diff --git a/src/windows/system.rs b/src/windows/system.rs index 17466e9d0..29b1240fa 100644 --- a/src/windows/system.rs +++ b/src/windows/system.rs @@ -1,8 +1,8 @@ // Take a look at the license at the top of the repository in the LICENSE file. use crate::{ - CpuRefreshKind, Disks, LoadAvg, Networks, Pid, ProcessExt, ProcessRefreshKind, RefreshKind, - SystemExt, User, + CpuRefreshKind, Disks, GovernorKind, LoadAvg, Networks, Pid, ProcessExt, ProcessRefreshKind, + RefreshKind, SystemExt, User, }; use winapi::um::winreg::HKEY_LOCAL_MACHINE; @@ -369,6 +369,10 @@ impl SystemExt for System { self.cpus.global_cpu() } + fn governor(&self) -> GovernorKind { + GovernorKind::default() + } + fn cpus(&self) -> &[Cpu] { self.cpus.cpus() }