blob: 7c8b957a57b4eab9655884c2c8523c81588ef9ad [file] [log] [blame]
// Take a look at the license at the top of the repository in the LICENSE file.
use crate::SystemExt;
#[allow(deprecated)]
use libc::{mach_timebase_info, mach_timebase_info_data_t};
use libc::{
host_processor_info, mach_port_t, munmap, natural_t, processor_cpu_load_info,
processor_cpu_load_info_t, sysconf, vm_page_size, PROCESSOR_CPU_LOAD_INFO, _SC_CLK_TCK,
};
use std::ptr::null_mut;
struct ProcessorCpuLoadInfo {
cpu_load: processor_cpu_load_info_t,
cpu_count: natural_t,
}
impl ProcessorCpuLoadInfo {
fn new(port: mach_port_t) -> Option<Self> {
let mut info_size = std::mem::size_of::<processor_cpu_load_info_t>() as _;
let mut cpu_count = 0;
let mut cpu_load: processor_cpu_load_info_t = null_mut();
unsafe {
if host_processor_info(
port,
PROCESSOR_CPU_LOAD_INFO,
&mut cpu_count,
&mut cpu_load as *mut _ as *mut _,
&mut info_size,
) != 0
{
sysinfo_debug!("host_processor_info failed, not updating CPU ticks usage...");
None
} else if cpu_count < 1 || cpu_load.is_null() {
None
} else {
Some(Self {
cpu_load,
cpu_count,
})
}
}
}
}
impl Drop for ProcessorCpuLoadInfo {
fn drop(&mut self) {
unsafe {
munmap(self.cpu_load as _, vm_page_size);
}
}
}
pub(crate) struct SystemTimeInfo {
timebase_to_ns: f64,
clock_per_sec: f64,
old_cpu_info: ProcessorCpuLoadInfo,
}
unsafe impl Send for SystemTimeInfo {}
unsafe impl Sync for SystemTimeInfo {}
impl SystemTimeInfo {
#[allow(deprecated)] // Everything related to mach_timebase_info_data_t
pub fn new(port: mach_port_t) -> Option<Self> {
unsafe {
let clock_ticks_per_sec = sysconf(_SC_CLK_TCK);
// FIXME: Maybe check errno here? Problem is that if errno is not 0 before this call,
// we will get an error which isn't related...
// if let Some(er) = std::io::Error::last_os_error().raw_os_error() {
// if err != 0 {
// println!("==> {:?}", er);
// sysinfo_debug!("Failed to get _SC_CLK_TCK value, using old CPU tick measure system");
// return None;
// }
// }
let mut info = mach_timebase_info_data_t { numer: 0, denom: 0 };
if mach_timebase_info(&mut info) != libc::KERN_SUCCESS {
sysinfo_debug!("mach_timebase_info failed, using default value of 1");
info.numer = 1;
info.denom = 1;
}
let old_cpu_info = match ProcessorCpuLoadInfo::new(port) {
Some(cpu_info) => cpu_info,
None => {
sysinfo_debug!("host_processor_info failed, using old CPU tick measure system");
return None;
}
};
let nano_per_seconds = 1_000_000_000.;
sysinfo_debug!("");
Some(Self {
timebase_to_ns: info.numer as f64 / info.denom as f64,
clock_per_sec: nano_per_seconds / clock_ticks_per_sec as f64,
old_cpu_info,
})
}
}
pub fn get_time_interval(&mut self, port: mach_port_t) -> f64 {
let mut total = 0;
let new_cpu_info = match ProcessorCpuLoadInfo::new(port) {
Some(cpu_info) => cpu_info,
None => return 0.,
};
let cpu_count = std::cmp::min(self.old_cpu_info.cpu_count, new_cpu_info.cpu_count);
unsafe {
for i in 0..cpu_count {
let new_load: &processor_cpu_load_info = &*new_cpu_info.cpu_load.offset(i as _);
let old_load: &processor_cpu_load_info =
&*self.old_cpu_info.cpu_load.offset(i as _);
for (new, old) in new_load.cpu_ticks.iter().zip(old_load.cpu_ticks.iter()) {
if new > old {
total += new - old;
}
}
}
self.old_cpu_info = new_cpu_info;
// Now we convert the ticks to nanoseconds (if the interval is less than
// `MINIMUM_CPU_UPDATE_INTERVAL`, we replace it with it instead):
let base_interval = total as f64 / cpu_count as f64 * self.clock_per_sec;
let smallest =
crate::System::MINIMUM_CPU_UPDATE_INTERVAL.as_secs_f64() * 1_000_000_000.0;
if base_interval < smallest {
smallest
} else {
base_interval / self.timebase_to_ns
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
/// Regression test for <https://github.com/GuillaumeGomez/sysinfo/issues/956>.
#[test]
fn test_getting_time_interval() {
if !crate::System::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
return;
}
let port = unsafe { libc::mach_host_self() };
let mut info = SystemTimeInfo::new(port).unwrap();
info.get_time_interval(port);
std::thread::sleep(crate::System::MINIMUM_CPU_UPDATE_INTERVAL.saturating_mul(5));
let val = info.get_time_interval(port);
assert_ne!(
val,
crate::System::MINIMUM_CPU_UPDATE_INTERVAL.as_secs_f64() * 1_000_000_000.0
);
}
}