use core::sync::atomic::Ordering;
use crate::preempt::tcb::{self, TaskState};
fn print_dec(mut n: usize) {
if n == 0 {
crate::console::write_str("0");
return;
}
let mut digits = [0u8; 20];
let mut i = 0;
while n > 0 {
digits[i] = b'0' + (n % 10) as u8;
n /= 10;
i += 1;
}
let mut buf = [0u8; 20];
for j in 0..i {
buf[j] = digits[i - 1 - j];
}
if let Ok(s) = core::str::from_utf8(&buf[..i]) {
crate::console::write_str(s);
}
}
pub fn task_stack_usage(id: usize) -> Option<(usize, usize)> {
let t = tcb::get(id)?;
let base_addr = t.stack_base.load(Ordering::Acquire);
let size = t.stack_size.load(Ordering::Acquire);
if base_addr == 0 || size == 0 {
return None;
}
let used = crate::critical::enter(|| {
crate::port::arch::scratch_open(base_addr, size);
let stack = unsafe { core::slice::from_raw_parts(base_addr as *const u8, size) };
let used = crate::preempt::stack_usage(stack);
crate::port::arch::scratch_close();
used
});
Some((used, size))
}
pub fn report() {
crate::console::write_str("=== rivet::report() ===\n");
let mut used_count = 0usize;
for (id, t) in tcb::TASKS.iter().enumerate() {
if !t.used.load(Ordering::Acquire) {
continue;
}
used_count += 1;
crate::console::write_str("task ");
print_dec(id);
let base = t.base_priority.load(Ordering::Acquire);
let eff = t.effective_priority.load(Ordering::Acquire);
crate::console::write_str(" prio=");
print_dec(base as usize);
if eff != base {
crate::console::write_str("(eff=");
print_dec(eff as usize);
crate::console::write_str(")");
}
crate::console::write_str(" state=");
if t.exited.load(Ordering::Acquire) {
crate::console::write_str("exited");
} else {
crate::console::write_str(match t.state() {
TaskState::Ready => "ready",
TaskState::Running => "running",
TaskState::Blocked => "blocked",
});
}
if let Some((used, size)) = task_stack_usage(id) {
crate::console::write_str(" stack=");
print_dec(used);
crate::console::write_str("/");
print_dec(size);
}
let held = t.held_count.load(Ordering::Acquire);
if held != 0 {
crate::console::write_str(" held_mutexes=");
print_dec(held as usize);
}
crate::console::write_str(" busy=");
print_dec(crate::exec_time::busy_percent(id) as usize);
crate::console::write_str("%");
crate::console::write_str("\n");
}
crate::console::write_str("ptask slots: ");
print_dec(used_count);
crate::console::write_str("/");
print_dec(tcb::MAX_PTASKS);
crate::console::write_str("\ntimer slots: ");
print_dec(crate::timer::slots_in_use());
crate::console::write_str("/");
print_dec(crate::timer::MAX_TIMERS);
crate::console::write_str("\n");
crate::console::write_str("log: ");
print_dec(crate::log::dropped_frames());
crate::console::write_str(" dropped frame(s)\n");
#[cfg(feature = "latency-histograms")]
print_latency_histograms();
crate::console::write_str("=== end report ===\n");
}
#[cfg(feature = "latency-histograms")]
fn print_latency_histograms() {
crate::console::write_str("latency (cycles, log2 buckets):\n");
for kind in crate::latency::ALL_KINDS {
crate::console::write_str(" ");
crate::console::write_str(crate::latency::name(kind));
crate::console::write_str(": ");
let snap = crate::latency::snapshot(kind);
let mut any = false;
for (b, count) in snap.iter().enumerate() {
if *count == 0 {
continue;
}
any = true;
crate::console::write_str("2^");
print_dec(b);
crate::console::write_str(":");
print_dec(*count as usize);
crate::console::write_str(" ");
}
if !any {
crate::console::write_str("(no samples)");
} else {
crate::console::write_str("max=");
print_dec(crate::latency::max_cycles(kind) as usize);
}
crate::console::write_str("\n");
}
}