use std::sync::RwLock;
use std::sync::atomic::{AtomicU32, Ordering};
pub static ACTIVE_SAMPLE_RATE: AtomicU32 = AtomicU32::new(0);
pub static ACTIVE_MODEL_NAME: RwLock<String> = RwLock::new(String::new());
pub static ACTIVE_MODEL_INFO: RwLock<Option<ModelInfo>> = RwLock::new(None);
#[derive(Debug, Clone, Default)]
pub struct ModelInfo {
pub arch_label: String,
pub topology: String,
pub channels: usize,
pub receptive_field: usize,
pub model_sample_rate: u32,
pub weights_layout: String,
pub path_basename: String,
}
#[derive(Debug, Clone)]
pub struct AudioInfo {
pub sample_rate: u32,
pub buffer_size: usize,
pub channel_count: usize,
pub host_name: String,
}
impl Default for AudioInfo {
fn default() -> Self {
Self {
sample_rate: 0,
buffer_size: 0,
channel_count: 0,
host_name: "N/A".to_string(),
}
}
}
#[derive(Debug, Clone)]
pub struct RtInfo {
pub thread_priority: i32,
pub scheduler: String,
pub cpu_pinned: Option<usize>,
pub huge_pages_active: bool,
pub huge_page_mode: String,
}
impl Default for RtInfo {
fn default() -> Self {
Self {
thread_priority: -1,
scheduler: "UNKNOWN".to_string(),
cpu_pinned: None,
huge_pages_active: false,
huge_page_mode: "off".to_string(),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct TelemetrySnapshot {
pub p50_us: u64,
pub p99_us: u64,
pub p999_us: u64,
pub max_us: u64,
pub total_blocks: u64,
pub xruns: u32,
pub drains: u32,
pub pw_buffer_miss: u32,
pub playback_miss: u32,
}
#[derive(Debug, Clone, Default)]
pub struct RuntimeSnapshot {
pub model: Option<ModelInfo>,
pub audio: AudioInfo,
pub rt: RtInfo,
pub telemetry: TelemetrySnapshot,
pub flags_seen: u64,
}
impl RuntimeSnapshot {
pub fn capture(provider: &impl HasRuntimeSnapshot) -> Self {
Self {
model: provider.model_info(),
audio: provider.audio_info(),
rt: provider.rt_info(),
telemetry: provider.telemetry_snapshot(),
flags_seen: provider.flags_seen(),
}
}
}
pub trait HasRuntimeSnapshot {
fn model_info(&self) -> Option<ModelInfo>;
fn audio_info(&self) -> AudioInfo;
fn rt_info(&self) -> RtInfo;
fn telemetry_snapshot(&self) -> TelemetrySnapshot;
fn flags_seen(&self) -> u64;
}
impl HasRuntimeSnapshot for crate::common::spsc::RtStatusFlags {
fn model_info(&self) -> Option<ModelInfo> {
if let Ok(info_guard) = ACTIVE_MODEL_INFO.read() {
info_guard.clone()
} else {
None
}
}
fn audio_info(&self) -> AudioInfo {
let sr = self.active_rate.load(Ordering::Relaxed);
let sr = if sr == 0 {
ACTIVE_SAMPLE_RATE.load(Ordering::Relaxed)
} else {
sr
};
let buffer_size = self.last_n_samples.load(Ordering::Relaxed) as usize;
AudioInfo {
sample_rate: sr,
buffer_size,
channel_count: 2,
host_name: "PipeWire".to_string(),
}
}
fn rt_info(&self) -> RtInfo {
let thread_priority = self.confirmed_priority.load(Ordering::Relaxed);
let scheduler_val = self.rt_policy.load(Ordering::Relaxed);
let scheduler = if scheduler_val == -1 {
"UNKNOWN".to_string()
} else if scheduler_val == libc::SCHED_FIFO || scheduler_val == libc::SCHED_RR {
"FIFO".to_string()
} else if scheduler_val == 6 {
"DEADLINE".to_string()
} else {
"OTHER".to_string()
};
let cpu = self.rt_cpu.load(Ordering::Relaxed);
let cpu_pinned = if cpu >= 0 { Some(cpu as usize) } else { None };
let huge_tlb = self.check_flag(crate::common::spsc::RT_STATUS_HUGEPAGE_OK)
|| (self.flags_seen.load(Ordering::Relaxed)
& crate::common::spsc::RT_STATUS_HUGEPAGE_OK)
!= 0;
let thp_active = self.check_flag(crate::common::spsc::RT_STATUS_THP_ACTIVE)
|| (self.flags_seen.load(Ordering::Relaxed)
& crate::common::spsc::RT_STATUS_THP_ACTIVE)
!= 0;
let huge_pages_active = huge_tlb || thp_active;
let huge_page_mode = if huge_tlb {
"hugetlb".to_string()
} else if thp_active {
"thp".to_string()
} else {
"off".to_string()
};
RtInfo {
thread_priority,
scheduler,
cpu_pinned,
huge_pages_active,
huge_page_mode,
}
}
fn telemetry_snapshot(&self) -> TelemetrySnapshot {
let p50_us = self.latency_hist.get_percentile(0.50) / 1000;
let p99_us = self.latency_hist.get_percentile(0.99) / 1000;
let p999_us = self.latency_hist.get_percentile(0.999) / 1000;
let max_us = self.latency_hist.get_max() / 1000;
let total_blocks = self.latency_hist.total_count();
let xruns = self.xruns.load(Ordering::Relaxed);
let drains = self.drains.load(Ordering::Relaxed);
let pw_buffer_miss = self.pw_buffer_miss.load(Ordering::Relaxed);
let playback_miss = self.playback_miss.load(Ordering::Relaxed);
TelemetrySnapshot {
p50_us,
p99_us,
p999_us,
max_us,
total_blocks,
xruns,
drains,
pw_buffer_miss,
playback_miss,
}
}
fn flags_seen(&self) -> u64 {
let current_bits = self.status_bits.load(Ordering::Relaxed);
self.flags_seen.fetch_or(current_bits, Ordering::Relaxed);
self.flags_seen.load(Ordering::Relaxed)
}
}