Skip to content
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
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ nom = "7.0"
version = "0.3"
features = ["fileapi", "sysinfoapi", "minwindef", "winbase", "winerror", "ws2def", "ws2ipdef", "pdh"]

[target.'cfg(target_vendor = "apple")'.dependencies]
mach2 = "0.6"

[target.'cfg(any(target_os = "illumos", target_os = "solaris"))'.dependencies]
kstat-rs = "0.2"

[package.metadata.docs.rs]
targets = [
"x86_64-unknown-freebsd",
Expand Down
82 changes: 69 additions & 13 deletions src/platform/illumos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,40 @@ use std::{io, path};

pub struct PlatformImpl;

fn named_u64(named: &[kstat_rs::Named], key: &str) -> usize {
for n in named {
if n.name == key {
if let kstat_rs::NamedData::UInt64(v) = n.value {
return v as usize;
}
}
}
0
}

fn measure_cpu() -> io::Result<Vec<CpuTime>> {
let ctl = kstat_rs::Ctl::new()
.map_err(|e| io::Error::other(e.to_string()))?;
let mut result = Vec::new();
for mut ks in ctl.filter(Some("cpu"), None, Some("sys")) {
let instance = ks.ks_instance;
let data = ctl.read(&mut ks)
.map_err(|e| io::Error::other(e.to_string()))?;
if let kstat_rs::Data::Named(named) = data {
result.push((instance, CpuTime {
user: named_u64(&named, "cpu_ticks_user"),
nice: 0,
system: named_u64(&named, "cpu_ticks_kernel"),
interrupt: 0,
idle: named_u64(&named, "cpu_ticks_idle"),
other: named_u64(&named, "cpu_ticks_wait"),
}));
}
}
result.sort_by_key(|(instance, _)| *instance);
Ok(result.into_iter().map(|(_, cpu)| cpu).collect())
}

/// An implementation of `Platform` for illumos.
/// See `Platform` for documentation.
impl Platform for PlatformImpl {
Expand All @@ -14,58 +48,80 @@ impl Platform for PlatformImpl {
}

fn cpu_load(&self) -> io::Result<DelayedMeasurement<Vec<CPULoad>>> {
Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
let loads = measure_cpu()?;
Ok(DelayedMeasurement::new(
Box::new(move || Ok(loads.iter()
.zip(measure_cpu()?.iter())
.map(|(prev, now)| (*now - prev).to_cpuload())
.collect::<Vec<_>>()))))
}

fn load_average(&self) -> io::Result<LoadAverage> {
unix::load_average()
}

fn memory(&self) -> io::Result<Memory> {
Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
Err(io::Error::other("Not supported"))
}

fn swap(&self) -> io::Result<Swap> {
Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
Err(io::Error::other("Not supported"))
}

fn boot_time(&self) -> io::Result<OffsetDateTime> {
Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
let ctl = kstat_rs::Ctl::new()
.map_err(|e| io::Error::other(e.to_string()))?;
let mut ks = ctl.filter(Some("unix"), Some(0), Some("system_misc"))
.next()
.ok_or_else(|| io::Error::other("kstat unix:0:system_misc not found"))?;
let data = ctl.read(&mut ks)
.map_err(|e| io::Error::other(e.to_string()))?;
if let kstat_rs::Data::Named(named) = data {
for n in &named {
if n.name == "boot_time" {
if let kstat_rs::NamedData::UInt32(v) = n.value {
return OffsetDateTime::from_unix_timestamp(v as i64)
.map_err(|e| io::Error::other(e.to_string()));
}
}
}
}
Err(io::Error::other("boot_time not found in kstat"))
}

fn battery_life(&self) -> io::Result<BatteryLife> {
Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
Err(io::Error::other("Not supported"))
}

fn on_ac_power(&self) -> io::Result<bool> {
Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
Err(io::Error::other("Not supported"))
}

fn mounts(&self) -> io::Result<Vec<Filesystem>> {
Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
Err(io::Error::other("Not supported"))
}

fn mount_at<P: AsRef<path::Path>>(&self, _: P) -> io::Result<Filesystem> {
Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
Err(io::Error::other("Not supported"))
}

fn block_device_statistics(&self) -> io::Result<BTreeMap<String, BlockDeviceStats>> {
Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
Err(io::Error::other("Not supported"))
}

fn networks(&self) -> io::Result<BTreeMap<String, Network>> {
unix::networks()
}

fn network_stats(&self, interface: &str) -> io::Result<NetworkStats> {
Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
fn network_stats(&self, _interface: &str) -> io::Result<NetworkStats> {
Err(io::Error::other("Not supported"))
}

fn cpu_temp(&self) -> io::Result<f32> {
Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
Err(io::Error::other("Not supported"))
}

fn socket_stats(&self) -> io::Result<SocketStats> {
Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
Err(io::Error::other("Not supported"))
}
}
95 changes: 73 additions & 22 deletions src/platform/macos.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
use std::{io, ptr, mem::{self, MaybeUninit}, ffi, slice};
use libc::{
c_int, c_void, host_statistics64, mach_host_self, size_t, statfs, sysconf, sysctl,
sysctlnametomib, timeval, vm_statistics64, xsw_usage, CTL_VM, HOST_VM_INFO64,
HOST_VM_INFO64_COUNT, KERN_SUCCESS, VM_SWAPUSAGE, _SC_PHYS_PAGES,
c_int, c_void, host_processor_info, host_statistics64, mach_msg_type_number_t,
natural_t, processor_cpu_load_info, processor_info_array_t, size_t, statfs,
sysconf, sysctl, sysctlnametomib, timeval, vm_address_t, vm_deallocate, vm_size_t,
vm_statistics64, xsw_usage, CTL_VM, CPU_STATE_IDLE, CPU_STATE_NICE, CPU_STATE_SYSTEM,
CPU_STATE_USER, HOST_VM_INFO64, HOST_VM_INFO64_COUNT, KERN_SUCCESS,
PROCESSOR_CPU_LOAD_INFO, VM_SWAPUSAGE, _SC_PHYS_PAGES,
};
use mach2::mach_init::mach_host_self;
use mach2::traps::mach_task_self;
use crate::data::*;
use super::common::*;
use super::unix;
Expand All @@ -30,7 +35,7 @@ macro_rules! sysctl {
let mut size = $size;
if unsafe { sysctl(&mib[0] as *const _ as *mut _, mib.len() as u32,
$dataptr as *mut _ as *mut c_void, &mut size, ptr::null_mut(), 0) } != 0 && $shouldcheck {
return Err(io::Error::new(io::ErrorKind::Other, "sysctl() failed"))
return Err(io::Error::other("sysctl() failed"))
}
size
}
Expand All @@ -53,7 +58,12 @@ impl Platform for PlatformImpl {
}

fn cpu_load(&self) -> io::Result<DelayedMeasurement<Vec<CPULoad>>> {
Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
let loads = measure_cpu()?;
Ok(DelayedMeasurement::new(
Box::new(move || Ok(loads.iter()
.zip(measure_cpu()?.iter())
.map(|(prev, now)| (*now - prev).to_cpuload())
.collect::<Vec<_>>()))))
}

fn load_average(&self) -> io::Result<LoadAverage> {
Expand All @@ -64,10 +74,7 @@ impl Platform for PlatformImpl {
// Get Total Memory
let total = match unsafe { sysconf(_SC_PHYS_PAGES) } {
-1 => {
return Err(io::Error::new(
io::ErrorKind::Other,
"sysconf(_SC_PHYS_PAGES) failed",
))
return Err(io::Error::other("sysconf(_SC_PHYS_PAGES) failed"))
}
n => n as u64,
};
Expand All @@ -87,10 +94,7 @@ impl Platform for PlatformImpl {
};

if ret != KERN_SUCCESS {
return Err(io::Error::new(
io::ErrorKind::Other,
"host_statistics64() failed",
));
return Err(io::Error::other("host_statistics64() failed"));
}
let stat = unsafe { stat.assume_init() };

Expand All @@ -107,7 +111,7 @@ impl Platform for PlatformImpl {
external: ByteSize::kib((stat.external_page_count as u64) << *bsd::PAGESHIFT),
internal: ByteSize::kib((stat.internal_page_count as u64) << *bsd::PAGESHIFT),
uncompressed_in_compressor: ByteSize::kib(
(stat.total_uncompressed_pages_in_compressor as u64) << *bsd::PAGESHIFT,
stat.total_uncompressed_pages_in_compressor << *bsd::PAGESHIFT,
),
};

Expand Down Expand Up @@ -143,49 +147,96 @@ impl Platform for PlatformImpl {
fn boot_time(&self) -> io::Result<OffsetDateTime> {
let mut data: timeval = unsafe { mem::zeroed() };
sysctl!(KERN_BOOTTIME, &mut data, mem::size_of::<timeval>());
let ts = OffsetDateTime::from_unix_timestamp(data.tv_sec.into()).expect("unix timestamp should be within range") + Duration::from_nanos(data.tv_usec as u64);
let ts = OffsetDateTime::from_unix_timestamp(data.tv_sec).expect("unix timestamp should be within range") + Duration::from_nanos(data.tv_usec as u64);
Ok(ts)
}

fn battery_life(&self) -> io::Result<BatteryLife> {
Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
Err(io::Error::other("Not supported"))
}

fn on_ac_power(&self) -> io::Result<bool> {
Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
Err(io::Error::other("Not supported"))
}

fn mounts(&self) -> io::Result<Vec<Filesystem>> {
let mut mptr: *mut statfs = ptr::null_mut();
let len = unsafe { getmntinfo(&mut mptr, 2_i32) };
if len < 1 {
return Err(io::Error::new(io::ErrorKind::Other, "getmntinfo() failed"))
return Err(io::Error::other("getmntinfo() failed"))
}
let mounts = unsafe { slice::from_raw_parts(mptr, len as usize) };
Ok(mounts.iter().map(statfs_to_fs).collect::<Vec<_>>())
}

fn block_device_statistics(&self) -> io::Result<BTreeMap<String, BlockDeviceStats>> {
Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
Err(io::Error::other("Not supported"))
}

fn networks(&self) -> io::Result<BTreeMap<String, Network>> {
unix::networks()
}

fn network_stats(&self, _interface: &str) -> io::Result<NetworkStats> {
Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
Err(io::Error::other("Not supported"))
}

fn cpu_temp(&self) -> io::Result<f32> {
Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
Err(io::Error::other("Not supported"))
}

fn socket_stats(&self) -> io::Result<SocketStats> {
Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
Err(io::Error::other("Not supported"))
}
}

fn measure_cpu() -> io::Result<Vec<CpuTime>> {
let mut num_cpus: natural_t = 0;
let mut info: processor_info_array_t = ptr::null_mut();
let mut info_count: mach_msg_type_number_t = 0;

let ret = unsafe {
host_processor_info(
mach_host_self(),
PROCESSOR_CPU_LOAD_INFO,
&mut num_cpus,
&mut info,
&mut info_count,
)
};

if ret != KERN_SUCCESS {
return Err(io::Error::other("host_processor_info() failed"));
}

let loads = unsafe {
let cpus = slice::from_raw_parts(
info as *const processor_cpu_load_info,
num_cpus as usize,
);
cpus.iter()
.map(|cpu| CpuTime {
user: cpu.cpu_ticks[CPU_STATE_USER as usize] as usize,
nice: cpu.cpu_ticks[CPU_STATE_NICE as usize] as usize,
system: cpu.cpu_ticks[CPU_STATE_SYSTEM as usize] as usize,
interrupt: 0,
idle: cpu.cpu_ticks[CPU_STATE_IDLE as usize] as usize,
other: 0,
})
.collect::<Vec<_>>()
};

unsafe {
vm_deallocate(
mach_task_self(),
info as vm_address_t,
info_count as vm_size_t * mem::size_of::<natural_t>() as vm_size_t,
);
}

Ok(loads)
}

fn statfs_to_fs(x: &statfs) -> Filesystem {
Filesystem {
files: (x.f_files as usize).saturating_sub(x.f_ffree as usize),
Expand Down
44 changes: 42 additions & 2 deletions src/platform/netbsd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use super::common::*;
use super::unix;
use crate::data::*;
use libc::{c_int, c_void, sysctl, CTL_VM};
use libc::{c_int, c_void, sysctl, CTL_HW, CTL_KERN, CTL_VM, HW_NCPU, KERN_CP_TIME};
use std::{io, mem, path, ptr};

pub struct PlatformImpl;
Expand Down Expand Up @@ -44,7 +44,12 @@ impl Platform for PlatformImpl {
}

fn cpu_load(&self) -> io::Result<DelayedMeasurement<Vec<CPULoad>>> {
Err(io::Error::new(io::ErrorKind::Other, "Not supported"))
let loads = measure_cpu()?;
Ok(DelayedMeasurement::new(
Box::new(move || Ok(loads.iter()
.zip(measure_cpu()?.iter())
.map(|(prev, now)| (*now - prev).to_cpuload())
.collect::<Vec<_>>()))))
}

fn load_average(&self) -> io::Result<LoadAverage> {
Expand Down Expand Up @@ -147,6 +152,41 @@ impl PlatformMemory {
}
}

fn measure_cpu() -> io::Result<Vec<CpuTime>> {
let mut ncpu: usize = 0;
sysctl!([CTL_HW, HW_NCPU], &mut ncpu, mem::size_of::<usize>());
let mut data: Vec<cp_time> = Vec::with_capacity(ncpu);
unsafe { data.set_len(ncpu) };
for i in 0..ncpu {
let mib = [CTL_KERN, KERN_CP_TIME, i as c_int];
sysctl!(mib, &mut data[i], mem::size_of::<cp_time>());
}
Ok(data.into_iter().map(|cpu| cpu.into()).collect())
}

#[repr(C)]
#[derive(Debug, Clone, Copy)]
struct cp_time {
user: u64,
nice: u64,
system: u64,
interrupt: u64,
idle: u64,
}

impl From<cp_time> for CpuTime {
fn from(cpu: cp_time) -> CpuTime {
CpuTime {
user: cpu.user as usize,
nice: cpu.nice as usize,
system: cpu.system as usize,
interrupt: cpu.interrupt as usize,
idle: cpu.idle as usize,
other: 0,
}
}
}

// https://github.com/NetBSD/src/blob/038135cba4b80f5c8d1e32fbc5b73c91c2f276d9/sys/uvm/uvm_extern.h#L420-L515
#[repr(C)]
#[derive(Debug, Default)]
Expand Down