bquarkz db338d0c4e dev/asset-management (#6)
Co-authored-by: Nilton Constantino <nilton.constantino@visma.com>
Reviewed-on: #6
2026-01-22 15:22:13 +00:00

123 lines
3.5 KiB
Rust

use crate::log::{LogLevel, LogService, LogSource};
#[derive(Debug, Clone, Copy, Default)]
pub struct TelemetryFrame {
pub frame_index: u64,
pub vm_steps: u32,
pub cycles_used: u64,
pub syscalls: u32,
pub host_cpu_time_us: u64,
pub violations: u32,
// GFX Banks
pub gfx_used_bytes: usize,
pub gfx_inflight_bytes: usize,
pub gfx_slots_occupied: u32,
// Audio Banks
pub audio_used_bytes: usize,
pub audio_inflight_bytes: usize,
pub audio_slots_occupied: u32,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct CertificationConfig {
pub enabled: bool,
pub cycles_budget_per_frame: Option<u64>,
pub max_syscalls_per_frame: Option<u32>,
pub max_host_cpu_us_per_frame: Option<u64>,
}
pub struct Certifier {
pub config: CertificationConfig,
}
impl Certifier {
pub fn new(config: CertificationConfig) -> Self {
Self { config }
}
pub fn evaluate(&self, telemetry: &TelemetryFrame, log_service: &mut LogService, ts_ms: u64) -> usize {
if !self.config.enabled {
return 0;
}
let mut violations = 0;
if let Some(budget) = self.config.cycles_budget_per_frame {
if telemetry.cycles_used > budget {
log_service.log(
ts_ms,
telemetry.frame_index,
LogLevel::Warn,
LogSource::Pos,
0xCA01,
format!("Cert: cycles_used exceeded budget ({} > {})", telemetry.cycles_used, budget),
);
violations += 1;
}
}
if let Some(limit) = self.config.max_syscalls_per_frame {
if telemetry.syscalls > limit {
log_service.log(
ts_ms,
telemetry.frame_index,
LogLevel::Warn,
LogSource::Pos,
0xCA02,
format!("Cert: syscalls per frame exceeded limit ({} > {})", telemetry.syscalls, limit),
);
violations += 1;
}
}
if let Some(limit) = self.config.max_host_cpu_us_per_frame {
if telemetry.host_cpu_time_us > limit {
log_service.log(
ts_ms,
telemetry.frame_index,
LogLevel::Warn,
LogSource::Pos,
0xCA03,
format!("Cert: host_cpu_time_us exceeded limit ({} > {})", telemetry.host_cpu_time_us, limit),
);
violations += 1;
}
}
violations
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::log::LogService;
#[test]
fn test_certifier_violations() {
let config = CertificationConfig {
enabled: true,
cycles_budget_per_frame: Some(100),
max_syscalls_per_frame: Some(5),
max_host_cpu_us_per_frame: Some(1000),
};
let cert = Certifier::new(config);
let mut ls = LogService::new(10);
let mut tel = TelemetryFrame::default();
tel.cycles_used = 150;
tel.syscalls = 10;
tel.host_cpu_time_us = 500;
let violations = cert.evaluate(&tel, &mut ls, 1000);
assert_eq!(violations, 2);
let logs = ls.get_recent(10);
assert_eq!(logs.len(), 2);
assert!(logs[0].msg.contains("cycles_used"));
assert!(logs[1].msg.contains("syscalls"));
}
}