mirror of
https://codeberg.org/icewind/palantir.git
synced 2026-06-03 18:24:08 +02:00
windows stuff
This commit is contained in:
parent
d12b70d11e
commit
0bb314e5d3
14 changed files with 602 additions and 102 deletions
55
src/data.rs
55
src/data.rs
|
|
@ -1,5 +1,6 @@
|
|||
use crate::SensorData;
|
||||
use std::array::IntoIter;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Write;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
|
|
@ -115,7 +116,7 @@ impl SensorData for NetStats {
|
|||
}
|
||||
|
||||
pub struct GpuUsage {
|
||||
pub system: &'static str,
|
||||
pub system: Cow<'static, str>,
|
||||
pub usage: u32,
|
||||
}
|
||||
|
||||
|
|
@ -129,3 +130,55 @@ impl GpuUsage {
|
|||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct DiskStats {
|
||||
pub interface: String,
|
||||
pub bytes_sent: u64,
|
||||
pub bytes_received: u64,
|
||||
}
|
||||
|
||||
impl SensorData for DiskStats {
|
||||
fn write<W: Write>(&self, mut w: W, hostname: &str) {
|
||||
if self.bytes_received > 0 || self.bytes_sent > 0 {
|
||||
writeln!(
|
||||
&mut w,
|
||||
"disk_sent{{host=\"{}\", disk=\"{}\"}} {}",
|
||||
hostname, self.interface, self.bytes_sent
|
||||
)
|
||||
.ok();
|
||||
writeln!(
|
||||
&mut w,
|
||||
"disk_received{{host=\"{}\", disk=\"{}\"}} {}",
|
||||
hostname, self.interface, self.bytes_received
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DiskUsage {
|
||||
pub name: String,
|
||||
pub size: u64,
|
||||
pub free: u64,
|
||||
}
|
||||
|
||||
impl SensorData for DiskUsage {
|
||||
fn write<W: Write>(&self, mut w: W, hostname: &str) {
|
||||
if self.size > 0 {
|
||||
writeln!(
|
||||
&mut w,
|
||||
"disk_size{{host=\"{}\", disk=\"{}\"}} {}",
|
||||
hostname, self.name, self.size
|
||||
)
|
||||
.ok();
|
||||
writeln!(
|
||||
&mut w,
|
||||
"disk_free{{host=\"{}\", disk=\"{}\"}} {}",
|
||||
hostname, self.name, self.free
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
27
src/lib.rs
27
src/lib.rs
|
|
@ -7,20 +7,22 @@ use std::string::FromUtf8Error;
|
|||
pub mod data;
|
||||
pub mod docker;
|
||||
|
||||
#[cfg(not(feature = "sysinfo"))]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
mod linux;
|
||||
#[cfg(feature = "sysinfo")]
|
||||
mod sys;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub mod win;
|
||||
|
||||
#[cfg(not(feature = "sysinfo"))]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub use linux::{get_metrics, Sensors};
|
||||
#[cfg(feature = "sysinfo")]
|
||||
pub use sys::{get_metrics, Sensors};
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use win::{get_metrics, Sensors};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
#[error("{1}: {0}")]
|
||||
Os(std::io::Error, &'static str),
|
||||
#[error("{0}")]
|
||||
Other(String),
|
||||
#[error("Non UTF8 hostname")]
|
||||
|
|
@ -35,6 +37,19 @@ pub enum Error {
|
|||
InvalidCStringData(#[from] NulError),
|
||||
#[error("Failed to query vfs stats")]
|
||||
StatVfs,
|
||||
#[cfg(target_os = "windows")]
|
||||
#[error(transparent)]
|
||||
Wmi(#[from] wmi::WMIError),
|
||||
#[cfg(target_os = "windows")]
|
||||
#[error("{0}")]
|
||||
Reg(String),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn last_os_error(context: &'static str) -> Error {
|
||||
let err = std::io::Error::last_os_error();
|
||||
Error::Os(err, context)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FromUtf8Error> for Error {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use crate::data::{DiskStats, DiskUsage};
|
||||
use crate::{Error, MultiSensorSource, Result, SensorData};
|
||||
use ahash::{AHashSet, AHasher};
|
||||
use regex::Regex;
|
||||
|
|
@ -10,32 +11,6 @@ use std::mem::MaybeUninit;
|
|||
|
||||
pub mod zfs;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct DiskStats {
|
||||
pub interface: String,
|
||||
pub bytes_sent: u64,
|
||||
pub bytes_received: u64,
|
||||
}
|
||||
|
||||
impl SensorData for DiskStats {
|
||||
fn write<W: Write>(&self, mut w: W, hostname: &str) {
|
||||
if self.bytes_received > 0 || self.bytes_sent > 0 {
|
||||
writeln!(
|
||||
&mut w,
|
||||
"disk_sent{{host=\"{}\", disk=\"{}\"}} {}",
|
||||
hostname, self.interface, self.bytes_sent
|
||||
)
|
||||
.ok();
|
||||
writeln!(
|
||||
&mut w,
|
||||
"disk_received{{host=\"{}\", disk=\"{}\"}} {}",
|
||||
hostname, self.interface, self.bytes_received
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DiskStatSource {
|
||||
source: File,
|
||||
buff: String,
|
||||
|
|
@ -100,32 +75,6 @@ impl Iterator for DiskStatParser<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DiskUsage {
|
||||
pub name: String,
|
||||
pub size: u64,
|
||||
pub free: u64,
|
||||
}
|
||||
|
||||
impl SensorData for DiskUsage {
|
||||
fn write<W: Write>(&self, mut w: W, hostname: &str) {
|
||||
if self.size > 0 {
|
||||
writeln!(
|
||||
&mut w,
|
||||
"disk_size{{host=\"{}\", disk=\"{}\"}} {}",
|
||||
hostname, self.name, self.size
|
||||
)
|
||||
.ok();
|
||||
writeln!(
|
||||
&mut w,
|
||||
"disk_free{{host=\"{}\", disk=\"{}\"}} {}",
|
||||
hostname, self.name, self.free
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DiskUsageSource {
|
||||
source: File,
|
||||
buff: String,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use crate::data::{GpuMemory, GpuUsage};
|
||||
use crate::linux::hwmon::FileSource;
|
||||
use std::borrow::Cow;
|
||||
use std::fs::read_to_string;
|
||||
use std::str::FromStr;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
|
@ -41,7 +42,7 @@ pub fn utilization() -> impl Iterator<Item = GpuUsage> {
|
|||
];
|
||||
let drm = sources.into_iter().flat_map(|(system, usage)| {
|
||||
Some(GpuUsage {
|
||||
system,
|
||||
system: Cow::Borrowed(system),
|
||||
usage: usage?,
|
||||
})
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use crate::data::{GpuMemory, GpuUsage};
|
|||
use nvml_wrapper::enum_wrappers::device::TemperatureSensor;
|
||||
use nvml_wrapper::{Device, Nvml};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::borrow::Cow;
|
||||
|
||||
static NVIDIA: Lazy<Option<Nvml>> = Lazy::new(|| Nvml::init().ok());
|
||||
|
||||
|
|
@ -49,7 +50,7 @@ pub fn utilization() -> impl Iterator<Item = GpuUsage> {
|
|||
};
|
||||
sources.into_iter().flat_map(|(system, usage)| {
|
||||
Some(GpuUsage {
|
||||
system,
|
||||
system: Cow::Borrowed(system),
|
||||
usage: usage?,
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
use crate::data::Temperatures;
|
||||
use crate::hostname;
|
||||
use crate::Result;
|
||||
use std::fmt::Write;
|
||||
use sysinfo::{ComponentExt, System, SystemExt};
|
||||
|
||||
pub struct Sensors {
|
||||
pub hostname: String,
|
||||
}
|
||||
|
||||
impl Sensors {
|
||||
pub fn new() -> Result<Sensors> {
|
||||
let s = System::new_all();
|
||||
for component in s.components() {
|
||||
println!("{} :{}°C", component.label(), component.temperature());
|
||||
}
|
||||
|
||||
Ok(Sensors {
|
||||
hostname: hostname()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn temps() -> Temperatures {
|
||||
Temperatures { cpu: 0.0, gpu: 0.0 }
|
||||
}
|
||||
|
||||
pub fn get_metrics(sensors: &Sensors) -> Result<String> {
|
||||
let hostname = &sensors.hostname;
|
||||
let mut result = String::with_capacity(256);
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
59
src/win/cpu.rs
Normal file
59
src/win/cpu.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
use crate::data::CpuTime;
|
||||
use crate::{Error, Result, SensorSource};
|
||||
use winapi::shared::minwindef;
|
||||
use winapi::um::{processthreadsapi, winbase, winnt};
|
||||
|
||||
pub struct CpuTimeSource {
|
||||
cpu_count: f32,
|
||||
}
|
||||
|
||||
impl CpuTimeSource {
|
||||
pub fn new() -> Result<CpuTimeSource> {
|
||||
Ok(CpuTimeSource {
|
||||
cpu_count: dbg!(cpu_count()?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl SensorSource for CpuTimeSource {
|
||||
type Data = CpuTime;
|
||||
|
||||
fn read(&mut self) -> Result<Self::Data> {
|
||||
let mut user = minwindef::FILETIME::default();
|
||||
let mut kernel = minwindef::FILETIME::default();
|
||||
let mut idle = minwindef::FILETIME::default();
|
||||
|
||||
let result =
|
||||
unsafe { processthreadsapi::GetSystemTimes(&mut idle, &mut kernel, &mut user) };
|
||||
|
||||
if result == 0 {
|
||||
Err(Error::last_os_error("GetSystemTimes"))
|
||||
} else {
|
||||
let user = time_to_float(user);
|
||||
let idle = time_to_float(idle);
|
||||
// Same as `psutil` subtracting idle time
|
||||
// and leaving only busy kernel time
|
||||
let system = time_to_float(kernel) - idle;
|
||||
|
||||
Ok(CpuTime((user + system) / self.cpu_count))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn time_to_float(time: minwindef::FILETIME) -> f32 {
|
||||
const HI_T: f64 = 429.496_729_6;
|
||||
const LO_T: f64 = 1e-7;
|
||||
|
||||
let low = LO_T * f64::from(time.dwLowDateTime);
|
||||
HI_T.mul_add(f64::from(time.dwHighDateTime), low) as f32
|
||||
}
|
||||
|
||||
fn cpu_count() -> Result<f32> {
|
||||
let result = unsafe { winbase::GetActiveProcessorCount(winnt::ALL_PROCESSOR_GROUPS) };
|
||||
|
||||
if result > 0 {
|
||||
Ok(result as f32)
|
||||
} else {
|
||||
Err(Error::last_os_error("GetActiveProcessorCount"))
|
||||
}
|
||||
}
|
||||
0
src/win/disk.rs
Normal file
0
src/win/disk.rs
Normal file
101
src/win/mod.rs
Normal file
101
src/win/mod.rs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
mod cpu;
|
||||
mod disk;
|
||||
mod reg;
|
||||
mod wmi;
|
||||
|
||||
use self::cpu::CpuTimeSource;
|
||||
use crate::data::{DiskUsage, GpuMemory, GpuUsage, Memory, NetStats};
|
||||
use crate::win::wmi::WmiSensor;
|
||||
use crate::Result;
|
||||
use crate::{hostname, SensorData, SensorSource};
|
||||
use once_cell::sync::Lazy;
|
||||
use os_thread_local::ThreadLocal;
|
||||
use std::borrow::Cow;
|
||||
use std::sync::Mutex;
|
||||
use sysinfo::{ComponentExt, DiskExt, NetworkExt, System, SystemExt};
|
||||
|
||||
pub struct Sensors {
|
||||
pub hostname: String,
|
||||
pub system: Mutex<System>,
|
||||
cpu: Mutex<CpuTimeSource>,
|
||||
gpu_mem_total: u64,
|
||||
}
|
||||
|
||||
static WMI: Lazy<ThreadLocal<WmiSensor>> =
|
||||
Lazy::new(|| ThreadLocal::new(|| WmiSensor::new().expect("failed to init wmi")));
|
||||
|
||||
impl Sensors {
|
||||
pub fn new() -> Result<Sensors> {
|
||||
let mut system = System::new_all();
|
||||
system.refresh_all();
|
||||
println!("{:?}", system);
|
||||
for component in system.components() {
|
||||
println!("{} :{}°C", component.label(), component.temperature());
|
||||
}
|
||||
|
||||
let gpu_mem_total = reg::total_gpu_memory()?;
|
||||
|
||||
Ok(Sensors {
|
||||
hostname: hostname()?,
|
||||
system: Mutex::new(system),
|
||||
cpu: Mutex::new(CpuTimeSource::new()?),
|
||||
gpu_mem_total,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_metrics(sensors: &Sensors) -> Result<String> {
|
||||
let mut system = sensors.system.lock().unwrap();
|
||||
system.refresh_disks();
|
||||
system.refresh_networks();
|
||||
system.refresh_memory();
|
||||
|
||||
let hostname = &sensors.hostname;
|
||||
let mut result = String::with_capacity(256);
|
||||
|
||||
let memory = Memory {
|
||||
total: system.total_memory(),
|
||||
available: system.available_memory(),
|
||||
free: system.free_memory(),
|
||||
};
|
||||
memory.write(&mut result, &hostname);
|
||||
for disk in system.disks() {
|
||||
let space = DiskUsage {
|
||||
name: disk.name().to_string_lossy().into(),
|
||||
size: disk.total_space(),
|
||||
free: disk.available_space(),
|
||||
};
|
||||
space.write(&mut result, &hostname);
|
||||
}
|
||||
for (interface, net) in system.networks() {
|
||||
let usage = NetStats {
|
||||
interface: interface.into(),
|
||||
bytes_received: net.total_received(),
|
||||
bytes_sent: net.total_transmitted(),
|
||||
};
|
||||
usage.write(&mut result, &hostname);
|
||||
}
|
||||
let cpu = sensors.cpu.lock().unwrap().read()?;
|
||||
cpu.write(&mut result, &hostname);
|
||||
|
||||
let gpu_mem_used = WMI.with(|wmi| wmi.gpu_mem())?;
|
||||
let gpu_mem = GpuMemory {
|
||||
total: sensors.gpu_mem_total,
|
||||
free: sensors.gpu_mem_total - gpu_mem_used,
|
||||
};
|
||||
gpu_mem.write(&mut result, &hostname);
|
||||
|
||||
let gpu_engines = WMI.with(|wmi| wmi.gpu_usage())?;
|
||||
for (name, usage) in gpu_engines.into_iter() {
|
||||
let gpu_usage = GpuUsage {
|
||||
system: Cow::Owned(name),
|
||||
usage,
|
||||
};
|
||||
gpu_usage.write(&mut result, &hostname);
|
||||
}
|
||||
if let Some(disk_usage) = WMI.with(|wmi| wmi.disk_usage())? {
|
||||
disk_usage.write(&mut result, &hostname);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
24
src/win/reg.rs
Normal file
24
src/win/reg.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
use crate::{Error, Result};
|
||||
use serde::Deserialize;
|
||||
use winreg::enums::*;
|
||||
use winreg::RegKey;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct GpuInfo {
|
||||
#[serde(rename = "HardwareInformation.qwMemorySize")]
|
||||
memory_size: Option<u64>,
|
||||
}
|
||||
|
||||
pub fn total_gpu_memory() -> Result<u64> {
|
||||
let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
|
||||
let mut mem = 0;
|
||||
for i in 0..3 {
|
||||
if let Ok(gpu_key) = hklm.open_subkey(
|
||||
format!("SYSTEM\\ControlSet001\\Control\\Class\\{{4d36e968-e325-11ce-bfc1-08002be10318}}\\{:04}", i),
|
||||
) {
|
||||
let info: GpuInfo = gpu_key.decode().map_err(|e| Error::Reg(e.to_string()))?;
|
||||
mem += info.memory_size.unwrap_or_default();
|
||||
}
|
||||
}
|
||||
Ok(mem)
|
||||
}
|
||||
82
src/win/wmi.rs
Normal file
82
src/win/wmi.rs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
use crate::data::DiskStats;
|
||||
use crate::Result;
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use wmi::{COMLibrary, WMIConnection};
|
||||
|
||||
pub struct WmiSensor {
|
||||
wmi_con: WMIConnection,
|
||||
}
|
||||
|
||||
impl WmiSensor {
|
||||
pub fn new() -> Result<Self> {
|
||||
let com_con = COMLibrary::new()?;
|
||||
let wmi_con = WMIConnection::new(com_con.into())?;
|
||||
|
||||
Ok(WmiSensor { wmi_con })
|
||||
}
|
||||
|
||||
pub fn gpu_mem(&self) -> Result<u64> {
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[allow(non_camel_case_types)]
|
||||
struct Win32_PerfFormattedData_GPUPerformanceCounters_GPUAdapterMemory {
|
||||
#[serde(rename = "DedicatedUsage")]
|
||||
dedicated_usage: u64,
|
||||
}
|
||||
|
||||
let results: Vec<Win32_PerfFormattedData_GPUPerformanceCounters_GPUAdapterMemory> =
|
||||
self.wmi_con.query()?;
|
||||
Ok(results.iter().map(|result| result.dedicated_usage).sum())
|
||||
}
|
||||
|
||||
pub fn gpu_usage(&self) -> Result<HashMap<String, u32>> {
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[allow(non_camel_case_types)]
|
||||
struct Win32_PerfFormattedData_GPUPerformanceCounters_GPUEngine {
|
||||
#[serde(rename = "Name")]
|
||||
name: String,
|
||||
#[serde(rename = "UtilizationPercentage")]
|
||||
usage: u32,
|
||||
}
|
||||
|
||||
let results: Vec<Win32_PerfFormattedData_GPUPerformanceCounters_GPUEngine> =
|
||||
self.wmi_con.query()?;
|
||||
|
||||
let mut data = HashMap::default();
|
||||
|
||||
for result in results {
|
||||
if let Some(eng_type) = result.name.split("_engtype_").skip(1).next() {
|
||||
let entry = data.entry(eng_type.to_string()).or_default();
|
||||
*entry += result.usage;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub fn disk_usage(&self) -> Result<Option<DiskStats>> {
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[allow(non_camel_case_types)]
|
||||
struct Win32_PerfRawData_Counters_FileSystemDiskActivity {
|
||||
#[serde(rename = "Name")]
|
||||
name: String,
|
||||
#[serde(rename = "FileSystemBytesRead")]
|
||||
read: u64,
|
||||
#[serde(rename = "FileSystemBytesWritten")]
|
||||
written: u64,
|
||||
}
|
||||
|
||||
let results: Vec<Win32_PerfRawData_Counters_FileSystemDiskActivity> =
|
||||
self.wmi_con.query()?;
|
||||
for result in results {
|
||||
if result.name == "_Total" {
|
||||
return Ok(Some(DiskStats {
|
||||
interface: "Total".to_string(),
|
||||
bytes_sent: result.written,
|
||||
bytes_received: result.read,
|
||||
}));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue