Co-authored-by: Nilton Constantino <nilton.constantino@visma.com> Reviewed-on: #6
123 lines
3.5 KiB
Rust
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"));
|
|
}
|
|
}
|