#![allow(unsafe_code)]
#![allow(clippy::arithmetic_side_effects)]
use crate::control_types::ControlSignal;
use crate::llmosafe_kernel::{KernelError, Synapse};
use std::fs;
use std::io::{BufRead, BufReader};
use std::thread;
use std::time::Duration;
#[derive(Debug, Clone, Default)]
pub struct EnvironmentalVitals {
pub iowait: u64,
pub load_avg: f64,
pub vitals_available: bool,
}
impl EnvironmentalVitals {
pub fn capture() -> Self {
let iowait = Self::read_iowait();
let load_avg = Self::read_loadavg();
let vitals_available = iowait.is_some() && load_avg.is_some();
Self {
iowait: iowait.unwrap_or(0),
load_avg: load_avg.unwrap_or(0.0),
vitals_available,
}
}
#[cfg(target_os = "linux")]
fn read_iowait() -> Option<u64> {
if let Ok(content) = fs::read_to_string("/proc/stat") {
if let Some(line) = content.lines().next() {
if let Some(iowait_str) = line.split_whitespace().nth(5) {
return iowait_str.parse().ok();
}
}
}
None
}
#[cfg(not(target_os = "linux"))]
fn read_iowait() -> Option<u64> {
None
}
#[cfg(target_os = "linux")]
fn read_loadavg() -> Option<f64> {
if let Ok(content) = fs::read_to_string("/proc/loadavg") {
if let Some(line) = content.lines().next() {
if let Some(first_part) = line.split_whitespace().next() {
return first_part.parse().ok();
}
}
}
None
}
#[cfg(not(target_os = "linux"))]
fn read_loadavg() -> Option<f64> {
None
}
}
#[derive(Debug, Clone, Copy)]
pub struct BodyOutput {
pub error_body: f32,
pub pressure: u8,
pub is_exhausted: bool,
}
impl ControlSignal for BodyOutput {
fn error(&self) -> f32 {
self.error_body
}
fn setpoint(&self) -> f32 {
0.0
}
}
#[derive(Debug, Clone)]
pub struct ResourceGuard {
memory_ceiling_bytes: usize,
#[cfg(any(test, feature = "testing"))]
raw_entropy_override: Option<u16>,
#[cfg(any(test, feature = "testing"))]
pressure_override: Option<u8>,
}
impl ResourceGuard {
pub fn new(memory_ceiling_bytes: usize) -> Self {
Self {
memory_ceiling_bytes,
#[cfg(any(test, feature = "testing"))]
raw_entropy_override: None,
#[cfg(any(test, feature = "testing"))]
pressure_override: None,
}
}
#[cfg(any(test, feature = "testing"))]
pub fn for_testing(ceiling_bytes: usize, raw_entropy_val: u16, pressure_val: u8) -> Self {
Self {
memory_ceiling_bytes: ceiling_bytes,
raw_entropy_override: Some(raw_entropy_val),
pressure_override: Some(pressure_val),
}
}
pub fn raw_entropy(&self) -> u16 {
#[cfg(any(test, feature = "testing"))]
if let Some(v) = self.raw_entropy_override {
return v;
}
let current_rss = Self::try_current_rss_bytes().unwrap_or(self.memory_ceiling_bytes);
let rss_ratio = if self.memory_ceiling_bytes > 0 {
(current_rss as f64 / self.memory_ceiling_bytes as f64).min(1.0)
} else {
1.0
};
let vitals = EnvironmentalVitals::capture();
let load_ratio = if vitals.vitals_available {
(vitals.load_avg / 10.0).min(1.0)
} else {
1.0
};
#[cfg(target_os = "linux")]
let iowait_ratio = Self::delta_iowait_ratio();
#[cfg(not(target_os = "linux"))]
let iowait_ratio = 0.0_f64;
let weighted_score = (rss_ratio * 500.0) + (iowait_ratio * 250.0) + (load_ratio * 250.0);
weighted_score.min(1000.0) as u16
}
pub fn pressure(&self) -> u8 {
#[cfg(any(test, feature = "testing"))]
if let Some(v) = self.pressure_override {
return v;
}
if self.memory_ceiling_bytes == 0 {
return 100;
}
let current_rss = Self::try_current_rss_bytes().unwrap_or(self.memory_ceiling_bytes);
let ratio = current_rss as f64 / self.memory_ceiling_bytes as f64;
(ratio * 100.0).min(100.0) as u8
}
#[must_use = "ignoring the safety check defeats the purpose of the guard"]
pub fn check(&self) -> Result<Synapse, KernelError> {
if self.memory_ceiling_bytes == 0 {
return Err(KernelError::ResourceExhaustion);
}
#[cfg(any(test, feature = "testing"))]
let current_rss = if self.pressure_override.is_some() {
self.memory_ceiling_bytes / 2
} else {
match Self::try_current_rss_bytes() {
Some(rss) => rss,
None => return Err(KernelError::ResourceExhaustion),
}
};
#[cfg(not(any(test, feature = "testing")))]
let current_rss = match Self::try_current_rss_bytes() {
Some(rss) => rss,
None => return Err(KernelError::ResourceExhaustion),
};
let ratio = current_rss as f64 / self.memory_ceiling_bytes as f64;
if ratio >= 1.0 {
return Err(KernelError::ResourceExhaustion);
}
let entropy = self.raw_entropy();
let mut synapse = Synapse::new();
synapse.set_raw_entropy(entropy);
synapse.set_raw_surprise(0);
synapse.set_has_bias(false);
synapse.set_anchor_hash(0);
Ok(synapse)
}
#[must_use = "ignoring the safety check defeats the purpose of the guard"]
pub fn check_ctrl(&self) -> Result<BodyOutput, KernelError> {
if self.memory_ceiling_bytes == 0 {
return Err(KernelError::ResourceExhaustion);
}
#[cfg(any(test, feature = "testing"))]
let current_rss = if self.pressure_override.is_some() {
self.memory_ceiling_bytes / 2
} else {
match Self::try_current_rss_bytes() {
Some(rss) => rss,
None => return Err(KernelError::ResourceExhaustion),
}
};
#[cfg(not(any(test, feature = "testing")))]
let current_rss = match Self::try_current_rss_bytes() {
Some(rss) => rss,
None => return Err(KernelError::ResourceExhaustion),
};
let ratio = current_rss as f64 / self.memory_ceiling_bytes as f64;
if ratio >= 1.0 {
return Err(KernelError::ResourceExhaustion);
}
let pressure_pct = (ratio * 100.0).min(100.0) as u8;
Ok(BodyOutput {
error_body: ratio as f32,
pressure: pressure_pct,
is_exhausted: false,
})
}
#[must_use = "ignoring the safety check defeats the purpose of the guard"]
pub fn check_with_entropy(&self, entropy: u16) -> Result<Synapse, KernelError> {
if self.memory_ceiling_bytes == 0 {
return Err(KernelError::ResourceExhaustion);
}
#[cfg(any(test, feature = "testing"))]
let current_rss = if self.pressure_override.is_some() {
self.memory_ceiling_bytes / 2
} else {
match Self::try_current_rss_bytes() {
Some(rss) => rss,
None => return Err(KernelError::ResourceExhaustion),
}
};
#[cfg(not(any(test, feature = "testing")))]
let current_rss = match Self::try_current_rss_bytes() {
Some(rss) => rss,
None => return Err(KernelError::ResourceExhaustion),
};
let ratio = current_rss as f64 / self.memory_ceiling_bytes as f64;
if ratio >= 1.0 {
return Err(KernelError::ResourceExhaustion);
}
let mut synapse = Synapse::new();
synapse.set_raw_entropy(entropy);
synapse.set_raw_surprise(0);
synapse.set_has_bias(false);
synapse.set_anchor_hash(0);
Ok(synapse)
}
#[cfg(feature = "std")]
pub fn check_blocking(&self) -> Result<Synapse, KernelError> {
self.check_blocking_with_max_retries(3)
}
#[cfg(feature = "std")]
pub fn check_blocking_with_max_retries(
&self,
max_retries: u32,
) -> Result<Synapse, KernelError> {
self.check_blocking_with_max_retries_and_policy(
max_retries,
&crate::llmosafe_integration::EscalationPolicy::default(),
)
}
#[cfg(feature = "std")]
pub fn check_blocking_with_max_retries_and_policy(
&self,
max_retries: u32,
policy: &crate::llmosafe_integration::EscalationPolicy,
) -> Result<Synapse, KernelError> {
use crate::llmosafe_integration::{PressureLevel, SafetyDecision};
let mut retries = 0u32;
loop {
if retries >= max_retries {
return Err(KernelError::DeadlineExceeded);
}
let entropy = self.raw_entropy();
let pressure_pct = self.pressure();
let pressure_level = PressureLevel::from_percentage(pressure_pct);
let decision = policy.decide_with_pressure(entropy, 0, false, pressure_level);
match decision {
SafetyDecision::Proceed | SafetyDecision::Warn(_) => {
return self.check_with_entropy(entropy);
}
SafetyDecision::Escalate { cooldown_ms, .. } => {
retries += 1;
thread::sleep(Duration::from_millis((cooldown_ms as u64).max(1)));
}
SafetyDecision::Halt(_, cooldown_ms) => {
retries += 1;
thread::sleep(Duration::from_millis((cooldown_ms as u64).max(1)));
}
SafetyDecision::Exit(err) => {
return Err(err);
}
}
}
}
#[cfg(feature = "std")]
pub fn check_with_deadline(
&self,
deadline: std::time::Instant,
) -> Result<Synapse, KernelError> {
use crate::llmosafe_integration::{EscalationPolicy, PressureLevel, SafetyDecision};
let policy = EscalationPolicy::default();
let mut retries = 0u32;
loop {
if std::time::Instant::now() >= deadline {
return Err(KernelError::DeadlineExceeded);
}
if retries >= 3 {
return Err(KernelError::DeadlineExceeded);
}
let entropy = self.raw_entropy();
let pressure_pct = self.pressure();
let pressure_level = PressureLevel::from_percentage(pressure_pct);
let decision = policy.decide_with_pressure(entropy, 0, false, pressure_level);
match decision {
SafetyDecision::Proceed | SafetyDecision::Warn(_) => {
return self.check_with_entropy(entropy);
}
SafetyDecision::Escalate { cooldown_ms, .. } => {
retries += 1;
thread::sleep(Duration::from_millis((cooldown_ms as u64).max(1)));
}
SafetyDecision::Halt(_, cooldown_ms) => {
retries += 1;
thread::sleep(Duration::from_millis((cooldown_ms as u64).max(1)));
}
SafetyDecision::Exit(err) => {
return Err(err);
}
}
}
}
#[cfg(unix)]
pub fn current_rss_bytes() -> usize {
let mut usage: libc::rusage = unsafe { std::mem::zeroed() };
let ret = unsafe { libc::getrusage(libc::RUSAGE_SELF, &mut usage) };
if ret == 0 {
#[cfg(any(
target_os = "linux",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd",
target_os = "dragonfly",
))]
{
(usage.ru_maxrss as usize).saturating_mul(1024)
}
#[cfg(not(any(
target_os = "linux",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd",
target_os = "dragonfly",
)))]
{
usage.ru_maxrss as usize
}
} else {
Self::read_rss_from_proc().unwrap_or(0)
}
}
#[cfg(windows)]
pub fn current_rss_bytes() -> usize {
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::System::ProcessStatus::{
GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS,
};
let mut counters: PROCESS_MEMORY_COUNTERS = unsafe { std::mem::zeroed() };
let handle: HANDLE = unsafe { windows_sys::Win32::System::Threading::GetCurrentProcess() };
let ret = unsafe {
GetProcessMemoryInfo(
handle,
&mut counters,
std::mem::size_of::<PROCESS_MEMORY_COUNTERS>() as u32,
)
};
if ret != 0 {
counters.WorkingSetSize as usize
} else {
0
}
}
#[cfg(not(any(unix, windows)))]
pub fn current_rss_bytes() -> usize {
0
}
#[cfg(target_os = "linux")]
fn try_current_rss_bytes() -> Option<usize> {
let mut usage: libc::rusage = unsafe { std::mem::zeroed() };
let ret = unsafe { libc::getrusage(libc::RUSAGE_SELF, &mut usage) };
if ret == 0 {
Some((usage.ru_maxrss as usize).saturating_mul(1024))
} else {
Self::read_rss_from_proc()
}
}
#[cfg(unix)]
#[cfg(not(target_os = "linux"))]
fn try_current_rss_bytes() -> Option<usize> {
let mut usage: libc::rusage = unsafe { std::mem::zeroed() };
let ret = unsafe { libc::getrusage(libc::RUSAGE_SELF, &mut usage) };
if ret == 0 {
Some(usage.ru_maxrss as usize)
} else {
None
}
}
#[cfg(windows)]
fn try_current_rss_bytes() -> Option<usize> {
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::System::ProcessStatus::{
GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS,
};
let mut counters: PROCESS_MEMORY_COUNTERS = unsafe { std::mem::zeroed() };
let handle: HANDLE = unsafe { windows_sys::Win32::System::Threading::GetCurrentProcess() };
let ret = unsafe {
GetProcessMemoryInfo(
handle,
&mut counters,
std::mem::size_of::<PROCESS_MEMORY_COUNTERS>() as u32,
)
};
if ret != 0 {
Some(counters.WorkingSetSize as usize)
} else {
None
}
}
#[cfg(not(any(unix, windows)))]
fn try_current_rss_bytes() -> Option<usize> {
None
}
#[cfg(target_os = "linux")]
fn read_rss_from_proc() -> Option<usize> {
if let Ok(file) = fs::File::open("/proc/self/status") {
for line in BufReader::new(file).lines().map_while(Result::ok) {
if line.starts_with("VmRSS:") {
if let Some(size_str) = line.split_whitespace().nth(1) {
if let Ok(kb) = size_str.parse::<usize>() {
return Some(kb.saturating_mul(1024));
}
}
}
}
}
None
}
#[cfg(not(target_os = "linux"))]
fn read_rss_from_proc() -> Option<usize> {
None
}
#[cfg(target_os = "linux")]
pub fn system_memory_bytes() -> usize {
if let Ok(file) = fs::File::open("/proc/meminfo") {
for line in BufReader::new(file).lines().map_while(Result::ok) {
if line.starts_with("MemTotal:") {
if let Some(size_str) = line.split_whitespace().nth(1) {
if let Ok(kb) = size_str.parse::<usize>() {
return kb.saturating_mul(1024);
}
}
}
}
}
0
}
#[cfg(not(target_os = "linux"))]
pub fn system_memory_bytes() -> usize {
0
}
pub fn auto(fraction: f64) -> Self {
let sys_mem = Self::system_memory_bytes();
let ceiling = if sys_mem > 0 {
(sys_mem as f64 * fraction) as usize
} else {
0
};
Self::new(ceiling)
}
#[cfg(target_os = "linux")]
fn parse_proc_stat() -> Option<(u64, u64)> {
let content = fs::read_to_string("/proc/stat").ok()?;
let line = content.lines().next()?;
let mut parts = line.split_whitespace().skip(1);
let user: u64 = parts.next()?.parse().ok()?;
let nice: u64 = parts.next()?.parse().ok()?;
let system: u64 = parts.next()?.parse().ok()?;
let idle: u64 = parts.next()?.parse().ok()?;
let iowait: u64 = parts.next()?.parse().ok()?;
let active = user + nice + system;
let total = active + idle + iowait;
Some((active, total))
}
#[cfg(target_os = "linux")]
fn parse_proc_stat_iowait() -> Option<(u64, u64)> {
let content = fs::read_to_string("/proc/stat").ok()?;
let line = content.lines().next()?;
let mut parts = line.split_whitespace().skip(1);
let user: u64 = parts.next()?.parse().ok()?;
let nice: u64 = parts.next()?.parse().ok()?;
let system: u64 = parts.next()?.parse().ok()?;
let idle: u64 = parts.next()?.parse().ok()?;
let iowait: u64 = parts.next()?.parse().ok()?;
let active = user + nice + system;
let total = active + idle + iowait;
Some((iowait, total))
}
pub fn system_cpu_load() -> u8 {
#[cfg(target_os = "linux")]
{
if let Some((active1, total1)) = Self::parse_proc_stat() {
thread::sleep(Duration::from_millis(100));
if let Some((active2, total2)) = Self::parse_proc_stat() {
let d_active = active2.saturating_sub(active1);
let d_total = total2.saturating_sub(total1);
if d_total == 0 {
return 0;
}
return ((d_active as f64 / d_total as f64) * 100.0) as u8;
}
}
0
}
#[cfg(not(target_os = "linux"))]
{
0
}
}
#[cfg(target_os = "linux")]
fn delta_iowait_ratio() -> f64 {
if let Some((iowait1, total1)) = Self::parse_proc_stat_iowait() {
thread::sleep(Duration::from_millis(100));
if let Some((iowait2, total2)) = Self::parse_proc_stat_iowait() {
let d_iowait = iowait2.saturating_sub(iowait1);
let d_total = total2.saturating_sub(total1);
if d_total == 0 {
return 0.0;
}
return (d_iowait as f64 / d_total as f64).min(1.0);
}
}
0.0
}
}
#[no_mangle]
pub extern "C" fn llmosafe_get_environmental_entropy() -> u16 {
let guard = ResourceGuard::auto(0.5);
guard.raw_entropy()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_system_cpu_load_returns_bounded_value() {
let load = ResourceGuard::system_cpu_load();
assert!(load <= 100, "CPU load {} should be <= 100", load);
}
#[test]
fn test_system_cpu_load_two_calls_consistent() {
let load1 = ResourceGuard::system_cpu_load();
let load2 = ResourceGuard::system_cpu_load();
assert!(load1 <= 100);
assert!(load2 <= 100);
}
#[cfg(target_os = "linux")]
#[test]
fn test_parse_proc_stat_returns_some() {
let result = ResourceGuard::parse_proc_stat();
assert!(result.is_some(), "/proc/stat should be parseable on Linux");
let (active, total) = result.expect("checked above");
assert!(total >= active, "total must be >= active");
assert!(total > 0, "total should be positive on a running system");
}
#[cfg(target_os = "linux")]
#[test]
fn test_delta_iowait_ratio_bounded() {
let ratio = ResourceGuard::delta_iowait_ratio();
assert!(
(0.0..=1.0).contains(&ratio),
"iowait ratio {} should be in [0.0, 1.0]",
ratio
);
}
#[test]
fn test_check_ctrl_zero_ceiling_returns_exhaustion() {
let guard = ResourceGuard::new(0);
let result = guard.check_ctrl();
assert_eq!(result.unwrap_err(), KernelError::ResourceExhaustion);
}
#[test]
fn test_check_ctrl_valid_ceiling_returns_body_output() {
let guard = ResourceGuard::for_testing(100 * 1024 * 1024 * 1024, 100, 20);
let result = guard.check_ctrl();
match result {
Ok(result) => {
assert!((0.0..=1.0).contains(&result.error_body));
assert!(result.pressure <= 100);
assert!(!result.is_exhausted);
}
Err(KernelError::ResourceExhaustion) => {
}
Err(e) => panic!("Unexpected error: {:?}", e),
}
}
#[test]
fn test_check_blocking_returns_deadline_exceeded() {
let guard = ResourceGuard::new(0);
let result = guard.check_blocking();
assert!(result.is_err());
}
#[test]
fn test_capture_vitals_returns_bounded_values() {
let vitals = EnvironmentalVitals::capture();
assert!(vitals.load_avg >= 0.0, "load_avg should be >= 0.0");
}
#[test]
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
fn test_current_rss_bytes_returns_positive() {
let rss = ResourceGuard::current_rss_bytes();
assert!(rss > 0, "current_rss_bytes should be positive");
}
#[test]
#[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
fn test_current_rss_bytes_returns_zero_on_unsupported() {
let rss = ResourceGuard::current_rss_bytes();
assert_eq!(
rss, 0,
"current_rss_bytes should be 0 on unsupported platforms"
);
}
#[test]
fn test_check_blocking_succeeds_under_no_pressure() {
let guard = ResourceGuard::new(1024 * 1024 * 1024);
let result = guard.check_blocking();
match result {
Ok(synapse) => {
assert!(synapse.raw_entropy() <= 1000);
}
Err(KernelError::ResourceExhaustion) => {
}
Err(e) => panic!("Unexpected error: {:?}", e),
}
}
#[test]
fn test_check_with_deadline_succeeds_before_expiration() {
let guard = ResourceGuard::new(1024 * 1024 * 1024); let deadline = std::time::Instant::now() + std::time::Duration::from_secs(1);
let result = guard.check_with_deadline(deadline);
match result {
Ok(synapse) => {
assert!(synapse.raw_entropy() <= 1000);
}
Err(KernelError::ResourceExhaustion) => {
}
Err(e) => panic!("Unexpected error: {:?}", e),
}
}
#[test]
fn test_check_with_deadline_fails_after_expiration() {
let guard = ResourceGuard::new(1024 * 1024 * 1024); let deadline = std::time::Instant::now()
.checked_sub(std::time::Duration::from_secs(1))
.unwrap();
let result = guard.check_with_deadline(deadline);
assert!(
matches!(result.unwrap_err(), KernelError::DeadlineExceeded),
"check_with_deadline should return DeadlineExceeded after deadline"
);
}
}