use crate::os::monitor::GpuSnapshot;
use anyhow::{bail, Context, Result};
use std::process::{Command, Stdio};
use std::thread;
use std::time::{Duration, Instant};
pub mod capabilities;
pub mod contract;
pub mod events;
pub mod linux;
pub mod macos;
pub mod process;
pub mod profile;
pub mod windows;
const COMMAND_TIMEOUT: Duration = Duration::from_secs(4);
pub(crate) struct Output {
pub(crate) stdout: String,
pub(crate) success: bool,
}
pub(crate) fn run(program: &str, args: &[&str]) -> Result<Output> {
let mut child = Command::new(program)
.args(args)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.with_context(|| format!("starting {program}"))?;
let deadline = Instant::now() + COMMAND_TIMEOUT;
loop {
if child.try_wait()?.is_some() {
let output = child.wait_with_output()?;
return Ok(Output {
stdout: String::from_utf8_lossy(&output.stdout).into_owned(),
success: output.status.success(),
});
}
if Instant::now() >= deadline {
let _ = child.kill();
let _ = child.wait();
bail!("{program} timed out after {}s", COMMAND_TIMEOUT.as_secs());
}
thread::sleep(Duration::from_millis(20));
}
}
pub(crate) fn nvidia_gpus() -> Option<Vec<GpuSnapshot>> {
let output = run(
"nvidia-smi",
&[
"--query-gpu=name,utilization.gpu,memory.total,memory.used",
"--format=csv,noheader,nounits",
],
)
.ok()?;
if !output.success {
return None;
}
let gpus = output
.stdout
.lines()
.filter_map(|line| {
let fields: Vec<_> = line.split(',').map(str::trim).collect();
if fields.len() != 4 {
return None;
}
Some(GpuSnapshot {
name: fields[0].into(),
vendor: Some("NVIDIA".into()),
utilization_percent: fields[1].parse().ok(),
memory_total_bytes: fields[2].parse::<u64>().ok().map(|mib| mib * 1024 * 1024),
memory_used_bytes: fields[3].parse::<u64>().ok().map(|mib| mib * 1024 * 1024),
source: "nvidia-smi".into(),
status: "ready".into(),
})
})
.collect::<Vec<_>>();
(!gpus.is_empty()).then_some(gpus)
}
#[cfg(target_os = "macos")]
pub fn backend() -> impl contract::TelemetryBackend + contract::AcceleratorBackend {
macos::profile::Backend
}
#[cfg(target_os = "linux")]
pub fn backend() -> impl contract::TelemetryBackend + contract::AcceleratorBackend {
linux::profile::Backend
}
#[cfg(target_os = "windows")]
pub fn backend() -> impl contract::TelemetryBackend + contract::AcceleratorBackend {
windows::profile::Backend
}
#[cfg(any(
test,
not(any(target_os = "linux", target_os = "macos", target_os = "windows"))
))]
pub struct UnsupportedBackend;
#[cfg(any(
test,
not(any(target_os = "linux", target_os = "macos", target_os = "windows"))
))]
impl contract::TelemetryBackend for UnsupportedBackend {
fn host_profile(&self) -> Result<profile::HostProfile> {
Ok(profile::HostProfile {
schema_version: 1,
os: std::env::consts::OS.into(),
arch: std::env::consts::ARCH.into(),
cpu: profile::CpuProfile {
logical_cores: std::thread::available_parallelism().map_or(1, usize::from),
physical_cores: None,
vendor: None,
},
memory: profile::MemoryProfile {
total_bytes: None,
model: profile::MemoryModelKind::Unknown,
},
accelerators: Vec::new(),
capabilities: capabilities::PlatformCapabilities {
native_service_manager: capabilities::Support::Unknown,
filesystem_events: capabilities::Support::Unknown,
secure_secret_storage: capabilities::Support::Unknown,
process_containment: capabilities::Support::Unknown,
native_notifications: capabilities::Support::Unknown,
accelerator_telemetry: capabilities::Support::Unknown,
},
})
}
}
#[cfg(any(
test,
not(any(target_os = "linux", target_os = "macos", target_os = "windows"))
))]
impl contract::AcceleratorBackend for UnsupportedBackend {
fn accelerators(&self) -> Result<Vec<profile::AcceleratorInfo>> {
Ok(Vec::new())
}
}
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
pub fn backend() -> impl contract::TelemetryBackend + contract::AcceleratorBackend {
UnsupportedBackend
}
#[cfg(target_os = "macos")]
pub fn secret_backend() -> impl contract::SecretBackend {
macos::secrets::Backend
}
#[cfg(target_os = "linux")]
pub fn secret_backend() -> impl contract::SecretBackend {
linux::secrets::Backend
}
#[cfg(target_os = "windows")]
pub fn secret_backend() -> impl contract::SecretBackend {
windows::secrets::Backend
}
#[cfg(any(
test,
not(any(target_os = "linux", target_os = "macos", target_os = "windows"))
))]
pub struct UnsupportedSecretBackend;
#[cfg(any(
test,
not(any(target_os = "linux", target_os = "macos", target_os = "windows"))
))]
impl contract::SecretBackend for UnsupportedSecretBackend {
fn has_entry(&self, _key: &str) -> Result<bool> {
Ok(false)
}
}
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
pub fn secret_backend() -> impl contract::SecretBackend {
UnsupportedSecretBackend
}
#[cfg(test)]
mod tests {
use super::*;
use contract::{AcceleratorBackend, SecretBackend, TelemetryBackend};
#[test]
fn unsupported_backend_reports_honest_unknown_not_a_fabricated_verdict() {
let profile = UnsupportedBackend.host_profile().unwrap();
assert_eq!(profile.memory.total_bytes, None);
assert_eq!(
profile.memory.model,
crate::os::platform::profile::MemoryModelKind::Unknown
);
assert_eq!(
profile.capabilities.native_service_manager,
capabilities::Support::Unknown
);
assert_eq!(
profile.capabilities.secure_secret_storage,
capabilities::Support::Unknown
);
assert!(profile.cpu.logical_cores >= 1);
}
#[test]
fn unsupported_backend_reports_no_accelerators_rather_than_guessing() {
assert!(UnsupportedBackend.accelerators().unwrap().is_empty());
}
#[test]
fn unsupported_secret_backend_reports_absent_never_a_fabricated_presence() {
assert!(!UnsupportedSecretBackend.has_entry("ANY_KEY").unwrap());
}
}