use std::io;
use std::mem;
use std::ptr;
use std::slice;
use std::time::Duration;
use mach2::kern_return::{self, kern_return_t};
use mach2::mach_port;
use mach2::mach_types::{host_name_port_t, host_t};
use mach2::message::mach_msg_type_number_t;
use mach2::traps::mach_task_self;
use mach2::vm_types::{integer_t, natural_t, vm_address_t, vm_map_t, vm_size_t};
use nix::libc;
use crate::cpu::CpuTimes;
use crate::{Result, TICKS_PER_SECOND};
const PROCESSOR_CPU_LOAD_INFO: libc::c_int = 2;
const HOST_CPU_LOAD_INFO: libc::c_int = 3;
const CPU_STATE_USER: usize = 0;
const CPU_STATE_SYSTEM: usize = 1;
const CPU_STATE_IDLE: usize = 2;
const CPU_STATE_NICE: usize = 3;
#[allow(non_camel_case_types)]
type processor_flavor_t = libc::c_int;
#[allow(non_camel_case_types)]
type processor_info_array_t = *mut integer_t;
#[allow(non_camel_case_types)]
type host_flavor_t = integer_t;
#[allow(non_camel_case_types)]
type host_info64_t = *mut integer_t;
#[repr(C)]
#[derive(Copy, Clone, Debug, Default, Hash, PartialOrd, PartialEq, Eq, Ord)]
struct host_cpu_load_info {
user: natural_t,
system: natural_t,
idle: natural_t,
nice: natural_t,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Default, Hash, PartialOrd, PartialEq, Eq, Ord)]
struct processor_cpu_load_info {
user: natural_t,
system: natural_t,
idle: natural_t,
nice: natural_t,
}
extern "C" {
fn host_processor_info(
host: host_t,
flavor: processor_flavor_t,
out_processor_count: *mut natural_t,
out_processor_info: *mut processor_info_array_t,
out_processor_infoCnt: *mut mach_msg_type_number_t,
) -> kern_return_t;
fn vm_deallocate(
target_task: vm_map_t,
address: vm_address_t,
size: vm_size_t,
) -> kern_return_t;
fn mach_host_self() -> host_name_port_t;
fn host_statistics64(
host_priv: host_t,
flavor: host_flavor_t,
host_info_out: host_info64_t,
host_info_outCnt: *const mach_msg_type_number_t,
) -> kern_return_t;
}
#[allow(trivial_casts)]
unsafe fn processor_load_info() -> io::Result<Vec<processor_cpu_load_info>> {
let port = mach_host_self();
let mut cpu_count = 0;
let mut processor_info: processor_info_array_t = ptr::null_mut();
let mut cpu_info_count = 0;
let result = host_processor_info(
port,
PROCESSOR_CPU_LOAD_INFO,
&mut cpu_count,
&mut processor_info,
&mut cpu_info_count,
);
let port_result = mach_port::mach_port_deallocate(mach_task_self(), port);
if port_result != kern_return::KERN_SUCCESS {
return Err(io::Error::last_os_error());
}
if result != kern_return::KERN_SUCCESS {
Err(io::Error::last_os_error())
} else {
let cpu_info = slice::from_raw_parts(processor_info, cpu_info_count as usize);
let mut stats = Vec::with_capacity(cpu_count as usize);
for chunk in cpu_info.chunks(4) {
stats.push(processor_cpu_load_info {
user: chunk[CPU_STATE_USER] as natural_t,
system: chunk[CPU_STATE_SYSTEM] as natural_t,
idle: chunk[CPU_STATE_IDLE] as natural_t,
nice: chunk[CPU_STATE_NICE] as natural_t,
})
}
let result = vm_deallocate(
mach_task_self(),
processor_info as vm_address_t,
cpu_info_count as vm_size_t * std::mem::size_of::<natural_t>(),
);
if result != kern_return::KERN_SUCCESS {
return Err(io::Error::last_os_error());
}
Ok(stats)
}
}
#[allow(trivial_casts)]
unsafe fn cpu_load_info() -> io::Result<host_cpu_load_info> {
let port = mach_host_self();
let mut stats = host_cpu_load_info::default();
let count = mem::size_of::<host_cpu_load_info>() / mem::size_of::<integer_t>();
let result = host_statistics64(
port,
HOST_CPU_LOAD_INFO,
&mut stats as *mut _ as host_info64_t,
&count as *const _ as *const mach_msg_type_number_t,
);
let port_result = mach_port::mach_port_deallocate(mach_task_self(), port);
if port_result != kern_return::KERN_SUCCESS {
return Err(io::Error::last_os_error());
}
if result != kern_return::KERN_SUCCESS {
Err(io::Error::last_os_error())
} else {
Ok(stats)
}
}
impl From<host_cpu_load_info> for CpuTimes {
fn from(info: host_cpu_load_info) -> CpuTimes {
let ticks = *TICKS_PER_SECOND;
CpuTimes {
user: Duration::from_secs_f64(f64::from(info.user) / ticks),
system: Duration::from_secs_f64(f64::from(info.system) / ticks),
idle: Duration::from_secs_f64(f64::from(info.idle) / ticks),
nice: Duration::from_secs_f64(f64::from(info.nice) / ticks),
}
}
}
impl From<processor_cpu_load_info> for CpuTimes {
fn from(info: processor_cpu_load_info) -> CpuTimes {
let ticks = *TICKS_PER_SECOND;
CpuTimes {
user: Duration::from_secs_f64(f64::from(info.user) / ticks),
system: Duration::from_secs_f64(f64::from(info.system) / ticks),
idle: Duration::from_secs_f64(f64::from(info.idle) / ticks),
nice: Duration::from_secs_f64(f64::from(info.nice) / ticks),
}
}
}
pub fn cpu_times() -> Result<CpuTimes> {
let info = unsafe { cpu_load_info()? };
Ok(info.into())
}
pub fn cpu_times_percpu() -> Result<Vec<CpuTimes>> {
let processors = unsafe { processor_load_info()? };
Ok(processors
.into_iter()
.map(|processor| processor.into())
.collect())
}