1use core::sync::atomic::Ordering;
14
15use crate::preempt::tcb::{self, TaskState};
16
17fn print_dec(mut n: usize) {
18 if n == 0 {
19 crate::console::write_str("0");
20 return;
21 }
22 let mut digits = [0u8; 20];
23 let mut i = 0;
24 while n > 0 {
25 digits[i] = b'0' + (n % 10) as u8;
26 n /= 10;
27 i += 1;
28 }
29 let mut buf = [0u8; 20];
30 for j in 0..i {
31 buf[j] = digits[i - 1 - j];
32 }
33 if let Ok(s) = core::str::from_utf8(&buf[..i]) {
34 crate::console::write_str(s);
35 }
36}
37
38pub fn task_stack_usage(id: usize) -> Option<(usize, usize)> {
54 let t = tcb::get(id)?;
55 let base_addr = t.stack_base.load(Ordering::Acquire);
56 let size = t.stack_size.load(Ordering::Acquire);
57 if base_addr == 0 || size == 0 {
58 return None;
59 }
60 let used = crate::critical::enter(|| {
61 crate::port::arch::scratch_open(base_addr, size);
62 let stack = unsafe { core::slice::from_raw_parts(base_addr as *const u8, size) };
71 let used = crate::preempt::stack_usage(stack);
72 crate::port::arch::scratch_close();
73 used
74 });
75 Some((used, size))
76}
77
78pub fn report() {
83 crate::console::write_str("=== rivet::report() ===\n");
84
85 let mut used_count = 0usize;
86 for (id, t) in tcb::TASKS.iter().enumerate() {
87 if !t.used.load(Ordering::Acquire) {
88 continue;
89 }
90 used_count += 1;
91
92 crate::console::write_str("task ");
93 print_dec(id);
94
95 let base = t.base_priority.load(Ordering::Acquire);
96 let eff = t.effective_priority.load(Ordering::Acquire);
97 crate::console::write_str(" prio=");
98 print_dec(base as usize);
99 if eff != base {
100 crate::console::write_str("(eff=");
101 print_dec(eff as usize);
102 crate::console::write_str(")");
103 }
104
105 crate::console::write_str(" state=");
106 if t.exited.load(Ordering::Acquire) {
107 crate::console::write_str("exited");
108 } else {
109 crate::console::write_str(match t.state() {
110 TaskState::Ready => "ready",
111 TaskState::Running => "running",
112 TaskState::Blocked => "blocked",
113 });
114 }
115
116 if let Some((used, size)) = task_stack_usage(id) {
117 crate::console::write_str(" stack=");
118 print_dec(used);
119 crate::console::write_str("/");
120 print_dec(size);
121 }
122
123 let held = t.held_count.load(Ordering::Acquire);
124 if held != 0 {
125 crate::console::write_str(" held_mutexes=");
126 print_dec(held as usize);
127 }
128
129 crate::console::write_str(" busy=");
130 print_dec(crate::exec_time::busy_percent(id) as usize);
131 crate::console::write_str("%");
132
133 crate::console::write_str("\n");
134 }
135
136 crate::console::write_str("ptask slots: ");
137 print_dec(used_count);
138 crate::console::write_str("/");
139 print_dec(tcb::MAX_PTASKS);
140 crate::console::write_str("\ntimer slots: ");
141 print_dec(crate::timer::slots_in_use());
142 crate::console::write_str("/");
143 print_dec(crate::timer::MAX_TIMERS);
144 crate::console::write_str("\n");
145
146 crate::console::write_str("log: ");
147 print_dec(crate::log::dropped_frames());
148 crate::console::write_str(" dropped frame(s)\n");
149
150 #[cfg(feature = "latency-histograms")]
151 print_latency_histograms();
152
153 crate::console::write_str("=== end report ===\n");
154}
155
156#[cfg(feature = "latency-histograms")]
159fn print_latency_histograms() {
160 crate::console::write_str("latency (cycles, log2 buckets):\n");
161 for kind in crate::latency::ALL_KINDS {
162 crate::console::write_str(" ");
163 crate::console::write_str(crate::latency::name(kind));
164 crate::console::write_str(": ");
165 let snap = crate::latency::snapshot(kind);
166 let mut any = false;
167 for (b, count) in snap.iter().enumerate() {
168 if *count == 0 {
169 continue;
170 }
171 any = true;
172 crate::console::write_str("2^");
173 print_dec(b);
174 crate::console::write_str(":");
175 print_dec(*count as usize);
176 crate::console::write_str(" ");
177 }
178 if !any {
179 crate::console::write_str("(no samples)");
180 } else {
181 crate::console::write_str("max=");
182 print_dec(crate::latency::max_cycles(kind) as usize);
183 }
184 crate::console::write_str("\n");
185 }
186}