Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Process::accumulated_cpu_time feature #1458

Merged
merged 2 commits into from
Jan 21, 2025
Merged
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
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
Loading