Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion examples/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pid : displays this example's PID
brand : displays CPU brand
cpus : displays CPUs state
frequency : displays CPU frequency
cpu_temperature : displays CPU temperature
vendor_id : displays CPU vendor id

= GPU commands =
Expand Down Expand Up @@ -174,6 +175,10 @@ fn interpret_input(
Some(usage) => println!(" usage: {usage}%"),
None => println!(" usage: N/A"),
}
match gpu.temperature() {
Some(temp) => println!(" temperature: {temp}C"),
None => println!(" temperature: N/A"),
}
if let (Some(used), Some(total)) = (gpu.used_memory(), gpu.total_memory()) {
println!(" memory: {}/{} KB", used / 1_000, total / 1_000);
} else {
Expand Down Expand Up @@ -226,6 +231,23 @@ fn interpret_input(
println!("System information cannot be retrieved: {error}");
}
},
"cpu_temperature" => match sys {
Ok(sys) => {
for cpu in sys.cpus() {
print!("[{}] {}C", cpu.name(), cpu.temperature());
if let Some(max) = cpu.max() {
print!(" (max: {max}C)");
}
if let Some(critical) = cpu.critical() {
print!(" (critical: {critical}C)");
}
println!();
}
}
Err(error) => {
println!("System information cannot be retrieved: {error}");
}
},
"vendor_id" => match sys {
Ok(sys) => {
println!("vendor ID: {}", sys.cpus()[0].vendor_id());
Expand Down Expand Up @@ -375,6 +397,16 @@ fn interpret_input(
Ok(disks) => {
for disk in disks {
println!("{disk:?}");
if let Some(temp) = disk.temperature() {
print!(" temperature: {temp}C");
if let Some(max) = disk.max() {
print!(" (max: {max}C)");
}
if let Some(critical) = disk.critical() {
print!(" (critical: {critical}C)");
}
println!();
}
}
}
Err(error) => {
Expand Down Expand Up @@ -484,7 +516,17 @@ fn interpret_input(
);
}
"motherboard" => match Motherboard::new() {
Ok(m) => println!("{m:#?}"),
Ok(m) => {
println!("{m:#?}");
let temps = m.temperatures();
if !temps.is_empty() {
print!(" temperatures:");
for t in &temps {
print!(" {}C", t);
}
println!();
}
}
Err(error) => println!("Cannot retrieve motherboard information: {error:?}"),
},
"product" => {
Expand Down
35 changes: 35 additions & 0 deletions src/common/disk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,30 @@ impl Disk {
pub fn usage(&self) -> DiskUsage {
self.inner.usage()
}

/// Returns the disk's temperature in degrees Celsius.
///
/// Currently only implemented on Linux (NVMe drives).
/// Returns `None` if the temperature information is not available.
pub fn temperature(&self) -> Option<f32> {
self.inner.temperature()
}

/// Returns the highest temperature (in degrees Celsius) recorded for this disk.
///
/// Currently only implemented on Linux (NVMe drives).
/// Returns `None` if this information isn't available.
pub fn max(&self) -> Option<f32> {
self.inner.max()
}

/// Returns the critical temperature threshold (in degrees Celsius) for this disk.
///
/// Currently only implemented on Linux (NVMe drives).
/// Returns `None` if this information isn't available.
pub fn critical(&self) -> Option<f32> {
self.inner.critical()
}
}

/// Disks interface.
Expand Down Expand Up @@ -411,6 +435,7 @@ impl fmt::Display for DiskKind {
/// * `kind` is about refreshing the [`Disk::kind`] information.
/// * `storage` is about refreshing the [`Disk::available_space`] and [`Disk::total_space`] information.
/// * `io_usage` is about refreshing the [`Disk::usage`] information.
/// * `temperature` is about refreshing the [`Disk::temperature`] information.
///
/// ```no_run
/// use sysinfo::{Disks, DiskRefreshKind};
Expand All @@ -426,6 +451,7 @@ pub struct DiskRefreshKind {
kind: bool,
storage: bool,
io_usage: bool,
temperature: bool,
}

impl DiskRefreshKind {
Expand All @@ -439,6 +465,7 @@ impl DiskRefreshKind {
/// assert_eq!(r.kind(), false);
/// assert_eq!(r.storage(), false);
/// assert_eq!(r.io_usage(), false);
/// assert_eq!(r.temperature(), false);
/// ```
pub fn nothing() -> Self {
Self::default()
Expand All @@ -454,18 +481,26 @@ impl DiskRefreshKind {
/// assert_eq!(r.kind(), true);
/// assert_eq!(r.storage(), true);
/// assert_eq!(r.io_usage(), true);
/// assert_eq!(r.temperature(), true);
/// ```
pub fn everything() -> Self {
Self {
kind: true,
storage: true,
io_usage: true,
temperature: true,
}
}

impl_get_set!(DiskRefreshKind, kind, with_kind, without_kind);
impl_get_set!(DiskRefreshKind, storage, with_storage, without_storage);
impl_get_set!(DiskRefreshKind, io_usage, with_io_usage, without_io_usage);
impl_get_set!(
DiskRefreshKind,
temperature,
with_temperature,
without_temperature
);
}

#[cfg(test)]
Expand Down
48 changes: 47 additions & 1 deletion src/common/gpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ impl core::str::FromStr for PCI {
let Some(value) = iter.next() else {
return Err(missing_msg);
};
value.parse::<u32>().map_err(|_| invalid_msg)
u32::from_str_radix(value, 16).map_err(|_| invalid_msg)
}

let mut iter = s.split(':');
Expand Down Expand Up @@ -282,4 +282,50 @@ impl Gpu {
pub fn used_memory(&self) -> Option<u64> {
self.inner.used_memory()
}

/// Returns the temperature of this GPU in degrees Celsius.
///
/// On Linux, this information is only available for NVIDIA and AMD GPUs.
///
/// On macOS and Windows, availability depends on GPU driver support.
///
/// Returns `None` if the information cannot be retrieved.
pub fn temperature(&self) -> Option<f32> {
self.inner.temperature()
}
}

#[cfg(test)]
mod tests {
use super::PCI;

#[test]
fn test_pci_from_str() {
// sysfs BDF notation (e.g. `/sys/class/drm/*/device`)
let pci: PCI = "0000:c1:00.0".parse().unwrap();
assert_eq!(
pci,
PCI {
domain: 0,
bus: 0xc1,
device: 0,
function: 0,
}
);
assert_eq!(pci.to_string(), "0000:c1:00.0");

// NVML's `busId` format (8-digit hex domain)
let pci: PCI = "00000000:65:00.0".parse().unwrap();
assert_eq!(
pci,
PCI {
domain: 0,
bus: 0x65,
device: 0,
function: 0,
}
);

assert!("0000:01:00".parse::<PCI>().is_err());
}
}
71 changes: 70 additions & 1 deletion src/common/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,22 @@ impl System {
self.refresh_cpu_specifics(CpuRefreshKind::nothing().with_frequency())
}

/// Refreshes CPUs temperature.
///
/// Calling this method is the same as calling
/// `system.refresh_cpu_specifics(CpuRefreshKind::nothing().with_temperature())`.
///
/// ```no_run
/// use sysinfo::System;
///
/// if let Ok(mut s) = System::new_all() {
/// s.refresh_cpu_temperature();
/// }
/// ```
pub fn refresh_cpu_temperature(&mut self) {
self.refresh_cpu_specifics(CpuRefreshKind::nothing().with_temperature())
}

/// Refreshes the list of CPU.
///
/// Normally, this should almost never be needed as it's pretty rare for a computer
Expand Down Expand Up @@ -1070,6 +1086,14 @@ impl Motherboard {
pub fn asset_tag(&self) -> Option<String> {
self.inner.asset_tag()
}

/// Returns the motherboard's temperature sensors in degrees Celsius.
///
/// Currently only implemented on Linux.
/// Returns an empty `Vec` if the information is not available.
pub fn temperatures(&self) -> Vec<f32> {
self.inner.temperatures()
}
}

/// This type allows to retrieve product-related information.
Expand Down Expand Up @@ -2449,7 +2473,7 @@ pub enum ProcessesToUpdate<'a> {
/// information from `/proc/<pid>/` as well as all the information from `/proc/<pid>/task/<tid>/`
/// folders. This makes the refresh mechanism a lot slower depending on the number of tasks
/// each process has.
///
///
/// If you don't care about tasks information, use `ProcessRefreshKind::everything().without_tasks()`
/// as much as possible.
///
Expand Down Expand Up @@ -2623,6 +2647,7 @@ It will retrieve the following information:
pub struct CpuRefreshKind {
usage: bool,
frequency: bool,
temperature: bool,
}

impl CpuRefreshKind {
Expand All @@ -2635,6 +2660,7 @@ impl CpuRefreshKind {
///
/// assert_eq!(r.frequency(), false);
/// assert_eq!(r.usage(), false);
/// assert_eq!(r.temperature(), false);
/// ```
pub fn nothing() -> Self {
Self::default()
Expand All @@ -2649,16 +2675,24 @@ impl CpuRefreshKind {
///
/// assert_eq!(r.frequency(), true);
/// assert_eq!(r.usage(), true);
/// assert_eq!(r.temperature(), true);
/// ```
pub fn everything() -> Self {
Self {
usage: true,
frequency: true,
temperature: true,
}
}

impl_get_set!(CpuRefreshKind, usage, with_usage, without_usage);
impl_get_set!(CpuRefreshKind, frequency, with_frequency, without_frequency);
impl_get_set!(
CpuRefreshKind,
temperature,
with_temperature,
without_temperature
);
}

/// Used to determine which memory you want to refresh specifically.
Expand Down Expand Up @@ -2963,6 +2997,41 @@ impl Cpu {
pub fn frequency(&self) -> u64 {
self.inner.frequency()
}

/// Returns the CPU's temperature in degrees Celsius.
///
/// This is currently only implemented on Linux.
///
/// ```no_run
/// use sysinfo::{System, RefreshKind, CpuRefreshKind};
///
/// if let Ok(s) = System::new_with_specifics(
/// RefreshKind::nothing().with_cpu(CpuRefreshKind::everything()),
/// ) {
/// for cpu in s.cpus() {
/// println!("{}", cpu.temperature());
/// }
/// }
/// ```
pub fn temperature(&self) -> f32 {
self.inner.temperature()
}

/// Returns the highest temperature (in degrees Celsius) recorded for this CPU.
///
/// This is currently only implemented on Linux.
/// Returns `None` if this information isn't available.
pub fn max(&self) -> Option<f32> {
self.inner.max()
}

/// Returns the critical temperature threshold (in degrees Celsius) for this CPU.
///
/// This is currently only implemented on Linux.
/// Returns `None` if this information isn't available.
pub fn critical(&self) -> Option<f32> {
self.inner.critical()
}
}

#[cfg(test)]
Expand Down
12 changes: 12 additions & 0 deletions src/unix/apple/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,18 @@ impl CpuInner {
self.usage.frequency
}

pub(crate) fn temperature(&self) -> f32 {
0.0
}

pub(crate) fn max(&self) -> Option<f32> {
None
}

pub(crate) fn critical(&self) -> Option<f32> {
None
}

pub(crate) fn vendor_id(&self) -> &str {
&self.vendor_id
}
Expand Down
Loading
Loading