Fix ps command CPU usage on Apple Silicon M1 macs. #4142 (#6457)

* Fix ps command CPU usage on Apple Silicon M1 macs. #4142

The cpu user and system times returned my libproc are not in
nanoseconds; they are in mach ticks units.  This is not documented very
well.  The convert from mach ticks to ns, the kernel provides a timebase
info function and datatype:

https://developer.apple.com/documentation/driverkit/3433733-mach_timebase_info

The commit makes the PS command work for me.

* Cargo fmt of previous commit.

* Clippy format suggestion

Co-authored-by: Ondrej Baudys <ondrej.baudys@nextgen.net>
This commit is contained in:
Ondrej Baudys 2022-09-01 16:09:52 +10:00 committed by GitHub
parent b27148d14b
commit e266590813
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 31 additions and 3 deletions

10
Cargo.lock generated
View file

@ -2219,6 +2219,15 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
[[package]]
name = "mach2"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8"
dependencies = [
"libc",
]
[[package]]
name = "malloc_buf"
version = "0.0.6"
@ -2863,6 +2872,7 @@ dependencies = [
"errno",
"libc",
"libproc",
"mach2",
"nix",
"ntapi",
"once_cell",

View file

@ -26,6 +26,7 @@ procfs = "0.14.0"
[target.'cfg(target_os = "macos")'.dependencies]
libproc = "0.12.0"
errno = "0.2"
mach2 = "0.4"
[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3.9", features = ["tlhelp32", "fileapi", "handleapi", "ifdef", "ioapiset", "minwindef", "pdh", "psapi", "synchapi", "sysinfoapi", "winbase", "winerror", "winioctl", "winnt", "oleauto", "wbemcli", "rpcdce", "combaseapi", "objidl", "powerbase", "netioapi", "lmcons", "lmaccess", "lmapibuf", "memoryapi", "shellapi", "std", "securitybaseapi"] }

View file

@ -6,6 +6,7 @@ use libproc::libproc::pid_rusage::{pidrusage, RUsageInfoV2};
use libproc::libproc::proc_pid::{listpidinfo, listpids, pidinfo, ListThreads, ProcType};
use libproc::libproc::task_info::{TaskAllInfo, TaskInfo};
use libproc::libproc::thread_info::ThreadInfo;
use mach2::mach_time;
use std::cmp;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
@ -372,9 +373,10 @@ impl ProcessInfo {
self.curr_task.ptinfo.pti_total_user + self.curr_task.ptinfo.pti_total_system;
let prev_time =
self.prev_task.ptinfo.pti_total_user + self.prev_task.ptinfo.pti_total_system;
let usage_ms = (curr_time - prev_time) / 1000000u64;
let interval_ms = self.interval.as_secs() * 1000 + u64::from(self.interval.subsec_millis());
usage_ms as f64 * 100.0 / interval_ms as f64
let usage_ticks = curr_time - prev_time;
let interval_us = self.interval.as_micros();
let ticktime_us = mach_ticktime() / 1000.0;
usage_ticks as f64 * 100.0 * ticktime_us / interval_us as f64
}
/// Memory size in number of bytes
@ -387,3 +389,18 @@ impl ProcessInfo {
self.curr_task.ptinfo.pti_virtual_size
}
}
/// The Macos kernel returns process times in mach ticks rather than nanoseconds. To get times in
/// nanoseconds, we need to multiply by the mach timebase, a fractional value reported by the
/// kernel. It is uncertain if the kernel returns the same value on each call to
/// mach_timebase_info; if it does, it may be worth reimplementing this as a lazy_static value.
fn mach_ticktime() -> f64 {
let mut timebase = mach_time::mach_timebase_info_data_t::default();
let err = unsafe { mach_time::mach_timebase_info(&mut timebase) };
if err == 0 {
timebase.numer as f64 / timebase.denom as f64
} else {
// assume times are in nanoseconds then...
1.0
}
}