kernel/debug.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Support for in-kernel debugging.
6//!
7//! For printing, this module uses an internal buffer to write the strings into.
8//! If you are writing and the buffer fills up, you can make the size of
9//! `output_buffer` larger.
10//!
11//! Before debug interfaces can be used, the board file must assign them
12//! hardware:
13//!
14//! ```ignore
15//! let debug_gpios = static_init!(
16//! [&'static dyn kernel::hil::gpio::Pin; 2],
17//! [
18//! &sam4l::gpio::PA[13],
19//! &sam4l::gpio::PA[15],
20//! ]
21//! );
22//! kernel::debug::initialize_debug_gpio::<
23//! <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
24//! >();
25//! kernel::debug::assign_gpios(debug_gpios);
26//!
27//! components::debug_writer::DebugWriterComponent::new(
28//! uart_mux,
29//! create_capability!(kernel::capabilities::SetDebugWriterCapability)
30//! )
31//! .finalize(components::debug_writer_component_static!());
32//! ```
33//!
34//! An alternative to using the default `DebugWriterComponent`, which defaults
35//! to sending over UART, is to implement a custom [`DebugWriter`] that can be
36//! used for other types of output. For example a simple "endless" FIFO with
37//! fixed "push" address:
38//!
39//! ```ignore
40//! use kernel::debug::DebugWriter;
41//!
42//! pub struct SyncDebugWriter;
43//!
44//! impl DebugWriter for SyncDebugWriter {
45//! fn write(&self, buf: &[u8], _overflow: &[u8]) -> usize {
46//! let out_reg = 0x4000 as *mut u8; // Replace with the actual address of the FIFO
47//! for c in buf.iter() {
48//! unsafe { out_reg.write_volatile(*c) };
49//! }
50//! buf.len()
51//! }
52//!
53//! fn available_len(&self) -> usize {
54//! usize::MAX
55//! }
56//!
57//! fn to_write_len(&self) -> usize {
58//! 0
59//! }
60//!
61//! fn publish(&self) -> usize {
62//! 0
63//! }
64//!
65//! fn flush(&self, _writer: &mut dyn IoWrite) { }
66//! ```
67//! And instantiate it in the main board file:
68//!
69//! ```ignore
70//! let debug_writer = static_init!(
71//! utils::SyncDebugWriter,
72//! utils::SyncDebugWriter
73//! );
74//!
75//! kernel::debug::set_debug_writer_wrapper(
76//! static_init!(
77//! kernel::debug::DebugWriterWrapper,
78//! kernel::debug::DebugWriterWrapper::new(debug_writer)
79//! ),
80//! );
81//! ```
82//!
83//! Example
84//! -------
85//!
86//! ```no_run
87//! # use kernel::{debug, debug_gpio, debug_verbose};
88//! # fn main() {
89//! # let i = 42;
90//! debug!("Yes the code gets here with value {}", i);
91//! debug_verbose!("got here"); // Includes message count, file, and line.
92//!
93//! debug_gpio!(0, toggle); // Toggles the first debug GPIO.
94//!
95//! # }
96//! ```
97//!
98//! ```text
99//! Yes the code gets here with value 42
100//! TOCK_DEBUG(0): /tock/capsules/src/sensys.rs:24: got here
101//! ```
102
103use core::cell::Cell;
104use core::fmt::{write, Arguments, Write};
105use core::panic::PanicInfo;
106use core::str;
107
108use crate::capabilities::SetDebugWriterCapability;
109use crate::collections::ring_buffer::RingBuffer;
110use crate::hil;
111use crate::platform::chip::Chip;
112use crate::platform::chip::ThreadIdProvider;
113use crate::process::ProcessPrinter;
114use crate::process::ProcessSlot;
115use crate::processbuffer::ReadableProcessSlice;
116use crate::utilities::binary_write::BinaryToWriteWrapper;
117use crate::utilities::cells::MapCell;
118use crate::utilities::cells::NumericCellExt;
119use crate::utilities::single_thread_value::SingleThreadValue;
120
121/// Implementation of `std::io::Write` for `no_std`.
122///
123/// This takes bytes instead of a string (contrary to [`core::fmt::Write`]), but
124/// we cannot use `std::io::Write' as it isn't available in `no_std` (due to
125/// `std::io::Error` not being available).
126///
127/// Also, in our use cases, writes are infallible, so the write function cannot
128/// return an `Err`, however it might not be able to write everything, so it
129/// returns the number of bytes written.
130///
131/// See also the tracking issue:
132/// <https://github.com/rust-lang/rfcs/issues/2262>.
133pub trait IoWrite {
134 fn write(&mut self, buf: &[u8]) -> usize;
135
136 fn write_ring_buffer(&mut self, buf: &RingBuffer<'_, u8>) -> usize {
137 let (left, right) = buf.as_slices();
138 let mut total = 0;
139 if let Some(slice) = left {
140 total += self.write(slice);
141 }
142 if let Some(slice) = right {
143 total += self.write(slice);
144 }
145 total
146 }
147}
148
149///////////////////////////////////////////////////////////////////
150// panic! support routines
151
152/// Tock panic routine, without the infinite LED-blinking loop.
153///
154/// This is useful for boards which do not feature LEDs to blink or want to
155/// implement their own behavior. This method returns after performing the panic
156/// dump.
157///
158/// After this method returns, the system is no longer in a well-defined state.
159/// Care must be taken on how one interacts with the system once this function
160/// returns.
161///
162/// **NOTE:** The supplied `writer` must be synchronous.
163pub unsafe fn panic_print<W: Write + IoWrite, C: Chip, PP: ProcessPrinter>(
164 writer: &mut W,
165 panic_info: &PanicInfo,
166 nop: &dyn Fn(),
167 processes: &'static [ProcessSlot],
168 chip: &'static Option<&'static C>,
169 process_printer: &'static Option<&'static PP>,
170) {
171 panic_begin(nop);
172 // Flush debug buffer if needed
173 flush(writer);
174 panic_banner(writer, panic_info);
175 panic_cpu_state(chip, writer);
176
177 // Some systems may enforce memory protection regions for the kernel, making
178 // application memory inaccessible. However, printing process information
179 // will attempt to access memory. If we are provided a chip reference,
180 // attempt to disable userspace memory protection first:
181 chip.map(|c| {
182 use crate::platform::mpu::MPU;
183 c.mpu().disable_app_mpu()
184 });
185 panic_process_info(processes, process_printer, writer);
186}
187
188/// Tock default panic routine.
189///
190/// **NOTE:** The supplied `writer` must be synchronous.
191///
192/// This will print a detailed debugging message and then loop forever while
193/// blinking an LED in a recognizable pattern.
194pub unsafe fn panic<L: hil::led::Led, W: Write + IoWrite, C: Chip, PP: ProcessPrinter>(
195 leds: &mut [&L],
196 writer: &mut W,
197 panic_info: &PanicInfo,
198 nop: &dyn Fn(),
199 processes: &'static [ProcessSlot],
200 chip: &'static Option<&'static C>,
201 process_printer: &'static Option<&'static PP>,
202) -> ! {
203 // Call `panic_print` first which will print out the panic information and
204 // return
205 panic_print(writer, panic_info, nop, processes, chip, process_printer);
206
207 // The system is no longer in a well-defined state, we cannot
208 // allow this function to return
209 //
210 // Forever blink LEDs in an infinite loop
211 panic_blink_forever(leds)
212}
213
214/// Generic panic entry.
215///
216/// This opaque method should always be called at the beginning of a board's
217/// panic method to allow hooks for any core kernel cleanups that may be
218/// appropriate.
219pub unsafe fn panic_begin(nop: &dyn Fn()) {
220 // Let any outstanding uart DMA's finish
221 for _ in 0..200000 {
222 nop();
223 }
224}
225
226/// Lightweight prints about the current panic and kernel version.
227///
228/// **NOTE:** The supplied `writer` must be synchronous.
229pub unsafe fn panic_banner<W: Write>(writer: &mut W, panic_info: &PanicInfo) {
230 let _ = writer.write_fmt(format_args!("\r\n{}\r\n", panic_info));
231
232 // Print version of the kernel
233 if crate::KERNEL_PRERELEASE_VERSION != 0 {
234 let _ = writer.write_fmt(format_args!(
235 "\tKernel version {}.{}.{}-dev{}\r\n",
236 crate::KERNEL_MAJOR_VERSION,
237 crate::KERNEL_MINOR_VERSION,
238 crate::KERNEL_PATCH_VERSION,
239 crate::KERNEL_PRERELEASE_VERSION,
240 ));
241 } else {
242 let _ = writer.write_fmt(format_args!(
243 "\tKernel version {}.{}.{}\r\n",
244 crate::KERNEL_MAJOR_VERSION,
245 crate::KERNEL_MINOR_VERSION,
246 crate::KERNEL_PATCH_VERSION,
247 ));
248 }
249}
250
251/// Print current machine (CPU) state.
252///
253/// **NOTE:** The supplied `writer` must be synchronous.
254pub unsafe fn panic_cpu_state<W: Write, C: Chip>(
255 chip: &'static Option<&'static C>,
256 writer: &mut W,
257) {
258 C::print_state(*chip, writer);
259}
260
261/// More detailed prints about all processes.
262///
263/// **NOTE:** The supplied `writer` must be synchronous.
264pub unsafe fn panic_process_info<PP: ProcessPrinter, W: Write>(
265 processes: &'static [ProcessSlot],
266 process_printer: &'static Option<&'static PP>,
267 writer: &mut W,
268) {
269 process_printer.map(|printer| {
270 // print data about each process
271 let _ = writer.write_fmt(format_args!("\r\n---| App Status |---\r\n"));
272 for slot in processes {
273 slot.proc.get().map(|process| {
274 // Print the memory map and basic process info.
275 //
276 // Because we are using a synchronous printer we do not need to
277 // worry about looping on the print function.
278 printer.print_overview(process, &mut BinaryToWriteWrapper::new(writer), None);
279 // Print all of the process details.
280 process.print_full_process(writer);
281 });
282 }
283 });
284}
285
286/// Blinks a recognizable pattern forever.
287///
288/// The LED will blink "sporadically" in a somewhat irregular pattern. This
289/// should look different from a traditional blinking LED which typically blinks
290/// with a consistent duty cycle. The panic blinking sequence is intentionally
291/// unusual to make it easier to tell when a panic has occurred.
292///
293/// If a multi-color LED is used for the panic pattern, it is advised to turn
294/// off other LEDs before calling this method.
295///
296/// Generally, boards should blink red during panic if possible, otherwise
297/// choose the 'first' or most prominent LED. Some boards may find it
298/// appropriate to blink multiple LEDs (e.g. one on the top and one on the
299/// bottom), thus this method accepts an array, however most will only need one.
300pub fn panic_blink_forever<L: hil::led::Led>(leds: &mut [&L]) -> ! {
301 for led in leds.iter_mut() {
302 led.init();
303 }
304 loop {
305 for _ in 0..1000000 {
306 for led in leds.iter_mut() {
307 led.on();
308 }
309 }
310 for _ in 0..100000 {
311 for led in leds.iter_mut() {
312 led.off();
313 }
314 }
315 for _ in 0..1000000 {
316 for led in leds.iter_mut() {
317 led.on();
318 }
319 }
320 for _ in 0..500000 {
321 for led in leds.iter_mut() {
322 led.off();
323 }
324 }
325 }
326}
327
328// panic! support routines
329///////////////////////////////////////////////////////////////////
330
331///////////////////////////////////////////////////////////////////
332// debug_gpio! support
333
334/// Static variable that holds an array of debug GPIO references.
335pub static DEBUG_GPIOS: SingleThreadValue<MapCell<&'static [&'static dyn hil::gpio::Pin]>> =
336 SingleThreadValue::new(MapCell::empty());
337
338/// Initialize the static debug gpio variable.
339///
340/// This ensures it can safely be used as a global variable.
341#[cfg(target_has_atomic = "ptr")]
342pub fn initialize_debug_gpio<P: ThreadIdProvider>() {
343 DEBUG_GPIOS.bind_to_thread::<P>();
344}
345
346/// Initialize the static debug gpio variable.
347///
348/// This ensures it can safely be used as a global variable.
349///
350/// # Safety
351///
352/// Callers of this function must ensure that this function is never called
353/// concurrently with other calls to [`initialize_debug_gpio_unsafe`].
354pub unsafe fn initialize_debug_gpio_unsafe<P: ThreadIdProvider>() {
355 DEBUG_GPIOS.bind_to_thread_unsafe::<P>();
356}
357
358/// Map an array of GPIO pins to use for debugging.
359pub fn assign_gpios(gpio: &'static [&'static dyn hil::gpio::Pin]) {
360 DEBUG_GPIOS.get().map(|gpio_array_cell| {
361 gpio_array_cell.replace(gpio);
362 });
363}
364
365/// In-kernel gpio debugging that accepts any GPIO HIL method.
366#[macro_export]
367macro_rules! debug_gpio {
368 ($i:tt, $method:ident $(,)?) => {{
369 #[allow(unused_unsafe)]
370 unsafe {
371 $crate::debug::DEBUG_GPIOS.get().map(|debug_gpio_cell| {
372 debug_gpio_cell.map(|debug_gpio_array| {
373 debug_gpio_array.get($i).map(|g| g.$method());
374 });
375 });
376 }
377 }};
378}
379
380///////////////////////////////////////////////////////////////////
381// debug! and debug_verbose! support
382
383/// A trait for writing debug output.
384///
385/// This can be used for example to implement asynchronous or synchronous
386/// writers, and buffered or unbuffered writers. Various platforms may have
387/// in-memory logs, memory mapped "endless" FIFOs, JTAG support for output,
388/// etc.
389pub trait DebugWriter {
390 /// Write bytes to output with overflow notification.
391 ///
392 /// The `overflow` slice is used as a message to be appended to the end of
393 /// the available buffer if it becomes full.
394 fn write(&self, bytes: &[u8], overflow_message: &[u8]) -> usize;
395
396 /// Available length of the internal buffer if limited.
397 ///
398 /// If the buffer can support a write of any size, it should lie and return
399 /// `usize::MAX`.
400 ///
401 /// Across subsequent calls to this function, without invoking `write()` in
402 /// between, this returned value may only increase, but never decrease.
403 fn available_len(&self) -> usize;
404
405 /// How many bytes are buffered and not yet written.
406 fn to_write_len(&self) -> usize;
407
408 /// Publish bytes from the internal buffer to the output.
409 ///
410 /// Returns how many bytes were written.
411 fn publish(&self) -> usize;
412
413 /// Flush any buffered bytes to the provided output writer.
414 ///
415 /// `flush()` should be used to write an buffered bytes to a new `writer`
416 /// instead of the internal writer that `publish()` would use.
417 fn flush(&self, writer: &mut dyn IoWrite);
418}
419
420/// Static variable that holds the kernel's reference to the debug tool.
421///
422/// This is needed so the `debug!()` macros have a reference to the object to
423/// use.
424static DEBUG_WRITER: SingleThreadValue<MapCell<&'static dyn DebugWriter>> =
425 SingleThreadValue::new(MapCell::empty());
426
427/// Static variable that holds how many times `debug!()` has been called.
428///
429/// This enables printing a verbose header message that enumerates independent
430/// debug messages.
431static DEBUG_WRITER_COUNT: SingleThreadValue<Cell<usize>> = SingleThreadValue::new(Cell::new(0));
432
433/// Initialize the static debug writer.
434///
435/// This ensures it can safely be used as a global variable.
436#[cfg(target_has_atomic = "ptr")]
437pub fn initialize_debug_writer_wrapper<P: ThreadIdProvider>() {
438 DEBUG_WRITER.bind_to_thread::<P>();
439 DEBUG_WRITER_COUNT.bind_to_thread::<P>();
440}
441
442/// Initialize the static debug writer.
443///
444/// This ensures it can safely be used as a global variable.
445///
446/// # Safety
447///
448/// Callers of this function must ensure that this function is never called
449/// concurrently with other calls to [`initialize_debug_writer_wrapper_unsafe`].
450pub unsafe fn initialize_debug_writer_wrapper_unsafe<P: ThreadIdProvider>() {
451 DEBUG_WRITER.bind_to_thread_unsafe::<P>();
452 DEBUG_WRITER_COUNT.bind_to_thread_unsafe::<P>();
453}
454
455fn try_get_debug_writer<F, R>(closure: F) -> Option<R>
456where
457 F: FnOnce(&dyn DebugWriter) -> R,
458{
459 DEBUG_WRITER
460 .get()
461 .and_then(|dw| dw.map_or(None, |writer| Some(closure(*writer))))
462}
463
464/// Function used by board main.rs to set a reference to the writer.
465pub fn set_debug_writer_wrapper<C: SetDebugWriterCapability>(
466 debug_writer: &'static dyn DebugWriter,
467 _cap: C,
468) {
469 DEBUG_WRITER.get().map(|dw| dw.replace(debug_writer));
470}
471
472impl Write for &dyn DebugWriter {
473 fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
474 self.write(s.as_bytes(), b"");
475 Ok(())
476 }
477}
478
479/// Write a debug message without a trailing newline.
480pub fn debug_print(args: Arguments) {
481 try_get_debug_writer(|mut writer| {
482 let _ = write(&mut writer, args);
483 writer.publish();
484 });
485}
486
487/// Write a debug message with a trailing newline.
488pub fn debug_println(args: Arguments) {
489 try_get_debug_writer(|mut writer| {
490 let _ = write(&mut writer, args);
491 let _ = writer.write_str("\r\n");
492 writer.publish();
493 });
494}
495
496/// Write a [`ReadableProcessSlice`] to the debug output.
497///
498/// # Errors
499///
500/// Will return `Err` if it is not possible to write any output.
501pub fn debug_slice(slice: &ReadableProcessSlice) -> Result<usize, ()> {
502 try_get_debug_writer(|writer| {
503 let mut total = 0;
504 for b in slice.iter() {
505 let buf: [u8; 1] = [b.get(); 1];
506 let count = writer.write(&buf, b"");
507 if count > 0 {
508 total += count;
509 } else {
510 break;
511 }
512 }
513 writer.publish();
514 total
515 })
516 .ok_or(())
517}
518
519/// Return how many bytes are remaining in the internal debug buffer.
520pub fn debug_available_len() -> usize {
521 try_get_debug_writer(|writer| writer.available_len()).unwrap_or(0)
522}
523
524fn write_header(
525 writer: &mut &dyn DebugWriter,
526 (file, line): &(&'static str, u32),
527) -> Result<(), core::fmt::Error> {
528 let count = DEBUG_WRITER_COUNT.get().map_or(0, |count| {
529 count.increment();
530 count.get()
531 });
532
533 writer.write_fmt(format_args!("TOCK_DEBUG({}): {}:{}: ", count, file, line))
534}
535
536/// Write a debug message with file and line information without a trailing
537/// newline.
538pub fn debug_verbose_print(args: Arguments, file_line: &(&'static str, u32)) {
539 try_get_debug_writer(|mut writer| {
540 let _ = write_header(&mut writer, file_line);
541 let _ = write(&mut writer, args);
542 writer.publish();
543 });
544}
545
546/// Write a debug message with file and line information with a trailing
547/// newline.
548pub fn debug_verbose_println(args: Arguments, file_line: &(&'static str, u32)) {
549 try_get_debug_writer(|mut writer| {
550 let _ = write_header(&mut writer, file_line);
551 let _ = write(&mut writer, args);
552 let _ = writer.write_str("\r\n");
553 writer.publish();
554 });
555}
556
557/// In-kernel `println()` debugging.
558#[macro_export]
559macro_rules! debug {
560 () => ({
561 // Allow an empty debug!() to print the location when hit
562 debug!("")
563 });
564 ($msg:expr $(,)?) => ({
565 $crate::debug::debug_println(format_args!($msg));
566 });
567 ($fmt:expr, $($arg:tt)+) => ({
568 $crate::debug::debug_println(format_args!($fmt, $($arg)+));
569 });
570}
571
572/// In-kernel `println()` debugging that can take a process slice.
573#[macro_export]
574macro_rules! debug_process_slice {
575 ($msg:expr $(,)?) => {{
576 $crate::debug::debug_slice($msg)
577 }};
578}
579
580/// In-kernel `println()` debugging with filename and line numbers.
581#[macro_export]
582macro_rules! debug_verbose {
583 () => ({
584 // Allow an empty debug_verbose!() to print the location when hit
585 debug_verbose!("")
586 });
587 ($msg:expr $(,)?) => ({
588 $crate::debug::debug_verbose_println(format_args!($msg), {
589 // TODO: Maybe make opposite choice of panic!, no `static`, more
590 // runtime code for less static data
591 static _FILE_LINE: (&'static str, u32) = (file!(), line!());
592 &_FILE_LINE
593 })
594 });
595 ($fmt:expr, $($arg:tt)+) => ({
596 $crate::debug::debug_verbose_println(format_args!($fmt, $($arg)+), {
597 static _FILE_LINE: (&'static str, u32) = (file!(), line!());
598 &_FILE_LINE
599 })
600 });
601}
602
603/// Prints out the expression and its location, then returns it.
604///
605/// ```rust,ignore
606/// let foo: u8 = debug_expr!(0xff);
607/// // Prints [main.rs:2] 0xff = 255
608/// ```
609/// Taken straight from Rust `std::dbg`.
610#[macro_export]
611macro_rules! debug_expr {
612 // NOTE: We cannot use `concat!` to make a static string as a format
613 // argument of `eprintln!` because `file!` could contain a `{` or `$val`
614 // expression could be a block (`{ .. }`), in which case the `eprintln!`
615 // will be malformed.
616 () => {
617 $crate::debug!("[{}:{}]", file!(), line!())
618 };
619 ($val:expr $(,)?) => {
620 // Use of `match` here is intentional because it affects the lifetimes
621 // of temporaries - https://stackoverflow.com/a/48732525/1063961
622 match $val {
623 tmp => {
624 $crate::debug!("[{}:{}] {} = {:#?}",
625 file!(), line!(), stringify!($val), &tmp);
626 tmp
627 }
628 }
629 };
630 ($($val:expr),+ $(,)?) => {
631 ($($crate::debug_expr!($val)),+,)
632 };
633}
634
635/// Flush any stored messages to the output writer.
636pub unsafe fn flush<W: Write + IoWrite>(writer: &mut W) {
637 try_get_debug_writer(|debug_writer|{
638 if debug_writer.to_write_len() > 0 {
639 let _ = writer.write_str(
640 "\r\n---| Debug buffer not empty. Flushing. May repeat some of last message(s):\r\n",
641 );
642 debug_writer.flush(writer);
643 }
644 }).or_else(||{
645 let _ = writer.write_str(
646 "\r\n---| Global debug writer not registered.\
647 \r\n Call `set_debug_writer_wrapper` in board initialization.\r\n",
648 );
649 None
650 });
651}