Skip to content

Commit

Permalink
Merge pull request #1458 from GuillaumeGomez/accumulated-cpu
Browse files Browse the repository at this point in the history
Add `Process::accumulated_cpu_time` feature
  • Loading branch information
GuillaumeGomez authored Jan 21, 2025
2 parents 6749d8a + c25010a commit 35a52d1
Show file tree
Hide file tree
Showing 12 changed files with 176 additions and 13 deletions.
25 changes: 24 additions & 1 deletion src/common/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1480,6 +1480,22 @@ impl Process {
self.inner.cpu_usage()
}

/// Returns the total accumulated CPU usage (in CPU-milliseconds). Note
/// that it might be bigger than the total clock run time of a process if
/// run on a multi-core machine.
///
/// ```no_run
/// use sysinfo::{Pid, System};
///
/// let s = System::new_all();
/// if let Some(process) = s.process(Pid::from(1337)) {
/// println!("{}", process.accumulated_cpu_time());
/// }
/// ```
pub fn accumulated_cpu_time(&self) -> u64 {
self.inner.accumulated_cpu_time()
}

/// Returns number of bytes read and written to disk.
///
/// ⚠️ On Windows, this method actually returns **ALL** I/O read and
Expand Down Expand Up @@ -1897,7 +1913,14 @@ impl ProcessRefreshKind {
}
}

impl_get_set!(ProcessRefreshKind, cpu, with_cpu, without_cpu);
impl_get_set!(
ProcessRefreshKind,
cpu,
with_cpu,
without_cpu,
"\
It will retrieve both CPU usage and CPU accumulated time,"
);
impl_get_set!(
ProcessRefreshKind,
disk_usage,
Expand Down
1 change: 1 addition & 0 deletions src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ impl std::fmt::Debug for crate::Process {
.field("memory usage", &self.memory())
.field("virtual memory usage", &self.virtual_memory())
.field("CPU usage", &self.cpu_usage())
.field("accumulated CPU time", &self.accumulated_cpu_time())
.field("status", &self.status())
.field("root", &self.root())
.field("disk_usage", &self.disk_usage())
Expand Down
1 change: 1 addition & 0 deletions src/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ impl Serialize for crate::Process {
state.serialize_field("start_time", &self.start_time())?;
state.serialize_field("run_time", &self.run_time())?;
state.serialize_field("cpu_usage", &self.cpu_usage())?;
state.serialize_field("accumulated_cpu_time", &self.accumulated_cpu_time())?;
state.serialize_field("disk_usage", &self.disk_usage())?;
state.serialize_field("user_id", &self.user_id())?;
state.serialize_field("group_id", &self.group_id())?;
Expand Down
4 changes: 4 additions & 0 deletions src/unix/apple/app_store/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ impl ProcessInner {
0.0
}

pub(crate) fn accumulated_cpu_time(&self) -> u64 {
0
}

pub(crate) fn disk_usage(&self) -> DiskUsage {
DiskUsage::default()
}
Expand Down
34 changes: 29 additions & 5 deletions src/unix/apple/macos/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pub(crate) struct ProcessInner {
pub(crate) old_written_bytes: u64,
pub(crate) read_bytes: u64,
pub(crate) written_bytes: u64,
accumulated_cpu_time: u64,
}

impl ProcessInner {
Expand Down Expand Up @@ -76,6 +77,7 @@ impl ProcessInner {
old_written_bytes: 0,
read_bytes: 0,
written_bytes: 0,
accumulated_cpu_time: 0,
}
}

Expand Down Expand Up @@ -107,6 +109,7 @@ impl ProcessInner {
old_written_bytes: 0,
read_bytes: 0,
written_bytes: 0,
accumulated_cpu_time: 0,
}
}

Expand Down Expand Up @@ -178,6 +181,10 @@ impl ProcessInner {
self.cpu_usage
}

pub(crate) fn accumulated_cpu_time(&self) -> u64 {
self.accumulated_cpu_time
}

pub(crate) fn disk_usage(&self) -> DiskUsage {
DiskUsage {
read_bytes: self.read_bytes.saturating_sub(self.old_read_bytes),
Expand Down Expand Up @@ -342,6 +349,7 @@ unsafe fn create_new_process(
now: u64,
refresh_kind: ProcessRefreshKind,
info: Option<libc::proc_bsdinfo>,
timebase_to_ms: f64,
) -> Result<Option<Process>, ()> {
let info = match info {
Some(info) => info,
Expand All @@ -368,10 +376,20 @@ unsafe fn create_new_process(
}
get_cwd_root(&mut p, refresh_kind);

if refresh_kind.memory() {
if refresh_kind.cpu() || refresh_kind.memory() {
let task_info = get_task_info(pid);
p.memory = task_info.pti_resident_size;
p.virtual_memory = task_info.pti_virtual_size;

if refresh_kind.cpu() {
p.accumulated_cpu_time = (task_info
.pti_total_user
.saturating_add(task_info.pti_total_system)
as f64
* timebase_to_ms) as u64;
}
if refresh_kind.memory() {
p.memory = task_info.pti_resident_size;
p.virtual_memory = task_info.pti_virtual_size;
}
}

p.user_id = Some(Uid(info.pbi_ruid));
Expand Down Expand Up @@ -630,6 +648,7 @@ pub(crate) fn update_process(
now: u64,
refresh_kind: ProcessRefreshKind,
check_if_alive: bool,
timebase_to_ms: f64,
) -> Result<Option<Process>, ()> {
unsafe {
if let Some(ref mut p) = (*wrap.0.get()).get_mut(&pid) {
Expand All @@ -640,7 +659,7 @@ pub(crate) fn update_process(
// We don't it to be removed, just replaced.
p.updated = true;
// The owner of this PID changed.
return create_new_process(pid, now, refresh_kind, Some(info));
return create_new_process(pid, now, refresh_kind, Some(info), timebase_to_ms);
}
let parent = get_parent(&info);
// Update the parent if it changed.
Expand Down Expand Up @@ -688,6 +707,11 @@ pub(crate) fn update_process(

if refresh_kind.cpu() {
compute_cpu_usage(p, task_info, system_time, user_time, time_interval);
p.accumulated_cpu_time = (task_info
.pti_total_user
.saturating_add(task_info.pti_total_system)
as f64
* timebase_to_ms) as u64;
}
if refresh_kind.memory() {
p.memory = task_info.pti_resident_size;
Expand All @@ -697,7 +721,7 @@ pub(crate) fn update_process(
p.updated = true;
Ok(None)
} else {
create_new_process(pid, now, refresh_kind, get_bsd_info(pid))
create_new_process(pid, now, refresh_kind, get_bsd_info(pid), timebase_to_ms)
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/unix/apple/macos/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ impl Drop for ProcessorCpuLoadInfo {

pub(crate) struct SystemTimeInfo {
timebase_to_ns: f64,
pub(crate) timebase_to_ms: f64,
clock_per_sec: f64,
old_cpu_info: ProcessorCpuLoadInfo,
last_update: Option<Instant>,
Expand Down Expand Up @@ -95,9 +96,12 @@ impl SystemTimeInfo {
};

let nano_per_seconds = 1_000_000_000.;
let timebase_to_ns = info.numer as f64 / info.denom as f64;
sysinfo_debug!("");
Some(Self {
timebase_to_ns: info.numer as f64 / info.denom as f64,
timebase_to_ns,
// We convert from nano (10^-9) to ms (10^3).
timebase_to_ms: timebase_to_ns / 1_000_000.,
clock_per_sec: nano_per_seconds / clock_ticks_per_sec as f64,
old_cpu_info,
last_update: None,
Expand Down
17 changes: 15 additions & 2 deletions src/unix/apple/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,11 @@ impl SystemInner {
let now = get_now();
let port = self.port;
let time_interval = self.clock_info.as_mut().map(|c| c.get_time_interval(port));
let timebase_to_ms = self
.clock_info
.as_ref()
.map(|c| c.timebase_to_ms)
.unwrap_or_default();
let entries: Vec<Process> = {
let wrap = &Wrap(UnsafeCell::new(&mut self.process_list));

Expand All @@ -293,8 +298,16 @@ impl SystemInner {
return None;
}
nb_updated.fetch_add(1, Ordering::Relaxed);
update_process(wrap, pid, time_interval, now, refresh_kind, false)
.unwrap_or_default()
update_process(
wrap,
pid,
time_interval,
now,
refresh_kind,
false,
timebase_to_ms,
)
.unwrap_or_default()
})
.collect()
};
Expand Down
19 changes: 19 additions & 0 deletions src/unix/freebsd/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ pub(crate) struct ProcessInner {
old_read_bytes: u64,
written_bytes: u64,
old_written_bytes: u64,
accumulated_cpu_time: u64,
}

impl ProcessInner {
Expand Down Expand Up @@ -128,6 +129,10 @@ impl ProcessInner {
self.cpu_usage
}

pub(crate) fn accumulated_cpu_time(&self) -> u64 {
self.accumulated_cpu_time
}

pub(crate) fn disk_usage(&self) -> DiskUsage {
DiskUsage {
written_bytes: self.written_bytes.saturating_sub(self.old_written_bytes),
Expand Down Expand Up @@ -173,6 +178,12 @@ impl ProcessInner {
}
}

#[inline]
fn get_accumulated_cpu_time(kproc: &libc::kinfo_proc) -> u64 {
// from FreeBSD source /bin/ps/print.c
kproc.ki_runtime / 1_000
}

pub(crate) unsafe fn get_process_data(
kproc: &libc::kinfo_proc,
wrap: &WrapMap,
Expand Down Expand Up @@ -236,6 +247,9 @@ pub(crate) unsafe fn get_process_data(
proc_.old_written_bytes = proc_.written_bytes;
proc_.written_bytes = kproc.ki_rusage.ru_oublock as _;
}
if refresh_kind.cpu() {
proc_.accumulated_cpu_time = get_accumulated_cpu_time(kproc);
}

return Ok(None);
}
Expand Down Expand Up @@ -286,6 +300,11 @@ pub(crate) unsafe fn get_process_data(
old_read_bytes: 0,
written_bytes: kproc.ki_rusage.ru_oublock as _,
old_written_bytes: 0,
accumulated_cpu_time: if refresh_kind.cpu() {
get_accumulated_cpu_time(kproc)
} else {
0
},
updated: true,
},
}))
Expand Down
13 changes: 13 additions & 0 deletions src/unix/linux/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ pub(crate) struct ProcessInner {
written_bytes: u64,
thread_kind: Option<ThreadKind>,
proc_path: PathBuf,
accumulated_cpu_time: u64,
}

impl ProcessInner {
Expand Down Expand Up @@ -163,6 +164,7 @@ impl ProcessInner {
written_bytes: 0,
thread_kind: None,
proc_path,
accumulated_cpu_time: 0,
}
}

Expand Down Expand Up @@ -227,6 +229,10 @@ impl ProcessInner {
self.cpu_usage
}

pub(crate) fn accumulated_cpu_time(&self) -> u64 {
self.accumulated_cpu_time
}

pub(crate) fn disk_usage(&self) -> DiskUsage {
DiskUsage {
written_bytes: self.written_bytes.saturating_sub(self.old_written_bytes),
Expand Down Expand Up @@ -426,6 +432,13 @@ fn update_proc_info(
if refresh_kind.disk_usage() {
update_process_disk_activity(p, proc_path);
}
// Needs to be after `update_time_and_memory`.
if refresh_kind.cpu() {
// The external values for CPU times are in "ticks", which are
// scaled by "HZ", which is pegged externally at 100 ticks/second.
p.accumulated_cpu_time =
p.utime.saturating_add(p.stime).saturating_mul(1_000) / info.clock_cycle;
}
}

fn update_parent_pid(p: &mut ProcessInner, parent_pid: Option<Pid>, str_parts: &[&str]) {
Expand Down
4 changes: 4 additions & 0 deletions src/unknown/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ impl ProcessInner {
0.0
}

pub(crate) fn accumulated_cpu_time(&self) -> u64 {
0
}

pub(crate) fn disk_usage(&self) -> DiskUsage {
DiskUsage::default()
}
Expand Down
17 changes: 13 additions & 4 deletions src/windows/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ use windows::Win32::UI::Shell::CommandLineToArgvW;

use super::MINIMUM_CPU_UPDATE_INTERVAL;

const FILETIMES_PER_MILLISECONDS: u64 = 10_000; // 100 nanosecond units

impl fmt::Display for ProcessStatus {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match *self {
Expand Down Expand Up @@ -194,6 +196,7 @@ pub(crate) struct ProcessInner {
old_written_bytes: u64,
read_bytes: u64,
written_bytes: u64,
accumulated_cpu_time: u64,
}

struct CPUsageCalculationValues {
Expand Down Expand Up @@ -274,6 +277,7 @@ impl ProcessInner {
old_written_bytes: 0,
read_bytes: 0,
written_bytes: 0,
accumulated_cpu_time: 0,
}
}

Expand Down Expand Up @@ -414,6 +418,10 @@ impl ProcessInner {
self.cpu_usage
}

pub(crate) fn accumulated_cpu_time(&self) -> u64 {
self.accumulated_cpu_time
}

pub(crate) fn disk_usage(&self) -> DiskUsage {
DiskUsage {
written_bytes: self.written_bytes.saturating_sub(self.old_written_bytes),
Expand Down Expand Up @@ -986,10 +994,7 @@ fn check_sub(a: u64, b: u64) -> u64 {
/// Before changing this function, you must consider the following:
/// <https://github.com/GuillaumeGomez/sysinfo/issues/459>
pub(crate) fn compute_cpu_usage(p: &mut ProcessInner, nb_cpus: u64) {
if p.cpu_calc_values.last_update.elapsed() <= MINIMUM_CPU_UPDATE_INTERVAL {
// cpu usage hasn't updated. p.cpu_usage remains the same
return;
}
let need_update = p.cpu_calc_values.last_update.elapsed() > MINIMUM_CPU_UPDATE_INTERVAL;

unsafe {
let mut ftime: FILETIME = zeroed();
Expand Down Expand Up @@ -1018,6 +1023,10 @@ pub(crate) fn compute_cpu_usage(p: &mut ProcessInner, nb_cpus: u64) {
let global_kernel_time = filetime_to_u64(fglobal_kernel_time);
let global_user_time = filetime_to_u64(fglobal_user_time);

p.accumulated_cpu_time = user.saturating_add(sys) / FILETIMES_PER_MILLISECONDS;
if !need_update {
return;
}
let delta_global_kernel_time =
check_sub(global_kernel_time, p.cpu_calc_values.old_system_sys_cpu);
let delta_global_user_time =
Expand Down
Loading

0 comments on commit 35a52d1

Please sign in to comment.