use crate::mock::metrics::GpuMetrics;
pub const PROCESS_ENV_VAR: &str = "ALL_SMI_MOCK_PROCESSES";
const USER_POOL: &[&str] = &["inureyes", "yeonji", "mira", "root"];
const COMMAND_POOL: &[&str] = &[
"python train.py --bs=128 --epochs=100 --model=llama-70b",
"python eval.py --split=val --checkpoint=epoch_32.pt",
"/opt/llm/infer -m /models/phi-4-14b.safetensors --batch 4",
"containerd-shim-runc-v2 -namespace k8s.io -id abc123",
"node /workspace/run-agent.js --env prod",
];
pub fn is_process_mock_enabled() -> bool {
std::env::var(PROCESS_ENV_VAR)
.map(|v| !v.is_empty())
.unwrap_or(false)
}
pub fn maybe_add_process_template(template: &mut String, instance_name: &str, gpus: &[GpuMetrics]) {
if !is_process_mock_enabled() {
return;
}
add_process_template(template, instance_name, gpus);
}
pub fn add_process_template(template: &mut String, instance_name: &str, gpus: &[GpuMetrics]) {
template
.push_str("# HELP all_smi_process_memory_used_bytes Process GPU memory used in bytes\n");
template.push_str("# TYPE all_smi_process_memory_used_bytes gauge\n");
let mut memory_lines = String::new();
let mut start_lines = String::new();
let mut cpu_lines = String::new();
let mut pid_counter: u32 = 10_000;
for (i, gpu) in gpus.iter().enumerate() {
let process_count = 1 + (i % 4);
for j in 0..process_count {
let user = USER_POOL[(i + j) % USER_POOL.len()];
let command = COMMAND_POOL[(i * 2 + j) % COMMAND_POOL.len()];
let name = command.split_whitespace().next().unwrap_or("process");
let memory =
((gpu.memory_used_bytes as usize) / process_count.max(1)).max(1024 * 1024 * 64); let start_seconds = 60 * (1 + i as u64 * 7 + j as u64);
let cpu_pct = 5.0 + (j as f64) * 3.5;
let labels = format!(
"pid=\"{pid_counter}\", name=\"{name}\", user=\"{user}\", device_id=\"{i}\", \
gpu_index=\"{i}\", device_uuid=\"{uuid}\", command=\"{command}\", \
instance=\"{instance_name}\", host=\"{instance_name}\"",
uuid = gpu.uuid,
);
memory_lines.push_str(&format!(
"all_smi_process_memory_used_bytes{{{labels}}} {memory}\n"
));
start_lines.push_str(&format!(
"all_smi_process_start_time_seconds{{{labels}}} {start_seconds}\n"
));
cpu_lines.push_str(&format!(
"all_smi_process_cpu_percent{{{labels}}} {cpu_pct:.2}\n"
));
pid_counter += 1;
}
}
template.push_str(&memory_lines);
template.push_str(
"# HELP all_smi_process_start_time_seconds Wall-clock seconds since the \
process started (TIME+ equivalent)\n",
);
template.push_str("# TYPE all_smi_process_start_time_seconds gauge\n");
template.push_str(&start_lines);
template.push_str("# HELP all_smi_process_cpu_percent Process CPU utilization percentage\n");
template.push_str("# TYPE all_smi_process_cpu_percent gauge\n");
template.push_str(&cpu_lines);
}
#[cfg(test)]
mod tests {
use super::*;
fn make_gpu(idx: usize) -> GpuMetrics {
GpuMetrics {
uuid: format!("GPU-MOCK-{idx}"),
utilization: 50.0,
memory_used_bytes: 10 * 1024 * 1024 * 1024,
memory_total_bytes: 40 * 1024 * 1024 * 1024,
temperature_celsius: 60,
power_consumption_watts: 200.0,
frequency_mhz: 1500,
ane_utilization_watts: 0.0,
thermal_pressure_level: None,
}
}
#[test]
fn template_is_empty_when_env_var_unset() {
unsafe { std::env::remove_var(PROCESS_ENV_VAR) };
let mut out = String::new();
maybe_add_process_template(&mut out, "node1", &[make_gpu(0)]);
assert!(out.is_empty(), "got: {out}");
}
#[test]
fn add_process_template_emits_three_families() {
let mut out = String::new();
add_process_template(&mut out, "node1", &[make_gpu(0), make_gpu(1)]);
assert!(out.contains("all_smi_process_memory_used_bytes"));
assert!(out.contains("all_smi_process_start_time_seconds"));
assert!(out.contains("all_smi_process_cpu_percent"));
assert!(out.contains("user=\"inureyes\""));
assert!(out.contains("gpu_index=\"0\""));
assert!(out.contains("gpu_index=\"1\""));
}
#[test]
fn every_row_carries_instance_and_host_labels() {
let mut out = String::new();
add_process_template(&mut out, "dgx-7", &[make_gpu(0)]);
assert!(out.contains("instance=\"dgx-7\""));
assert!(out.contains("host=\"dgx-7\""));
}
}