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/// Resources needed by the main panic routines.
153pub struct PanicResources<C: Chip + 'static, PP: ProcessPrinter + 'static> {
154    /// The array of process slots.
155    pub processes: MapCell<&'static [ProcessSlot]>,
156    /// The board-specific chip object.
157    pub chip: MapCell<&'static C>,
158    /// The tool for printing process details.
159    pub printer: MapCell<&'static PP>,
160}
161
162impl<C: Chip, PP: ProcessPrinter> PanicResources<C, PP> {
163    /// Create a new [`BoardPanic`] with nothing stored.
164    pub const fn new() -> Self {
165        Self {
166            processes: MapCell::empty(),
167            chip: MapCell::empty(),
168            printer: MapCell::empty(),
169        }
170    }
171}
172
173/// Tock panic routine, without the infinite LED-blinking loop.
174///
175/// This is useful for boards which do not feature LEDs to blink or want to
176/// implement their own behavior. This method returns after performing the panic
177/// dump.
178///
179/// After this method returns, the system is no longer in a well-defined state.
180/// Care must be taken on how one interacts with the system once this function
181/// returns.
182///
183/// **NOTE:** The supplied `writer` must be synchronous.
184pub unsafe fn panic_print<W: Write + IoWrite, C: Chip, PP: ProcessPrinter>(
185    writer: &mut W,
186    panic_info: &PanicInfo,
187    nop: &dyn Fn(),
188    panic_resources: Option<&PanicResources<C, PP>>,
189) {
190    panic_begin(nop);
191    // Flush debug buffer if needed
192    flush(writer);
193    panic_banner(writer, panic_info);
194
195    panic_resources.map(|pr| {
196        let chip = pr.chip.take();
197        panic_cpu_state(chip, writer);
198
199        chip.map(|c| {
200            // Some systems may enforce memory protection regions for the kernel,
201            // making application memory inaccessible. However, printing process
202            // information will attempt to access memory. If we are provided a chip
203            // reference, attempt to disable userspace memory protection first:
204            use crate::platform::mpu::MPU;
205            c.mpu().disable_app_mpu()
206        });
207        pr.processes.take().map(|p| {
208            panic_process_info(p, pr.printer.take(), writer);
209        });
210    });
211}
212
213/// Tock default panic routine.
214///
215/// **NOTE:** The supplied `writer` must be synchronous.
216///
217/// This will print a detailed debugging message and then loop forever while
218/// blinking an LED in a recognizable pattern.
219pub unsafe fn panic<L: hil::led::Led, W: Write + IoWrite, C: Chip, PP: ProcessPrinter>(
220    leds: &mut [&L],
221    writer: &mut W,
222    panic_info: &PanicInfo,
223    nop: &dyn Fn(),
224    panic_resources: Option<&PanicResources<C, PP>>,
225) -> ! {
226    // Call `panic_print` first which will print out the panic information and
227    // return
228    panic_print(writer, panic_info, nop, panic_resources);
229
230    // The system is no longer in a well-defined state, we cannot
231    // allow this function to return
232    //
233    // Forever blink LEDs in an infinite loop
234    panic_blink_forever(leds)
235}
236
237/// Generic panic entry.
238///
239/// This opaque method should always be called at the beginning of a board's
240/// panic method to allow hooks for any core kernel cleanups that may be
241/// appropriate.
242pub unsafe fn panic_begin(nop: &dyn Fn()) {
243    // Let any outstanding uart DMA's finish
244    for _ in 0..200000 {
245        nop();
246    }
247}
248
249/// Lightweight prints about the current panic and kernel version.
250///
251/// **NOTE:** The supplied `writer` must be synchronous.
252pub unsafe fn panic_banner<W: Write>(writer: &mut W, panic_info: &PanicInfo) {
253    let _ = writer.write_fmt(format_args!("\r\n{}\r\n", panic_info));
254
255    // Print version of the kernel
256    if crate::KERNEL_PRERELEASE_VERSION != 0 {
257        let _ = writer.write_fmt(format_args!(
258            "\tKernel version {}.{}.{}-dev{}\r\n",
259            crate::KERNEL_MAJOR_VERSION,
260            crate::KERNEL_MINOR_VERSION,
261            crate::KERNEL_PATCH_VERSION,
262            crate::KERNEL_PRERELEASE_VERSION,
263        ));
264    } else {
265        let _ = writer.write_fmt(format_args!(
266            "\tKernel version {}.{}.{}\r\n",
267            crate::KERNEL_MAJOR_VERSION,
268            crate::KERNEL_MINOR_VERSION,
269            crate::KERNEL_PATCH_VERSION,
270        ));
271    }
272}
273
274/// Print current machine (CPU) state.
275///
276/// **NOTE:** The supplied `writer` must be synchronous.
277pub unsafe fn panic_cpu_state<W: Write, C: Chip>(chip: Option<&'static C>, writer: &mut W) {
278    C::print_state(chip, writer);
279}
280
281/// More detailed prints about all processes.
282///
283/// **NOTE:** The supplied `writer` must be synchronous.
284pub unsafe fn panic_process_info<PP: ProcessPrinter, W: Write>(
285    processes: &'static [ProcessSlot],
286    process_printer: Option<&'static PP>,
287    writer: &mut W,
288) {
289    process_printer.map(|printer| {
290        // print data about each process
291        let _ = writer.write_fmt(format_args!("\r\n---| App Status |---\r\n"));
292        for slot in processes {
293            slot.proc.get().map(|process| {
294                // Print the memory map and basic process info.
295                //
296                // Because we are using a synchronous printer we do not need to
297                // worry about looping on the print function.
298                printer.print_overview(process, &mut BinaryToWriteWrapper::new(writer), None);
299                // Print all of the process details.
300                process.print_full_process(writer);
301            });
302        }
303    });
304}
305
306/// Blinks a recognizable pattern forever.
307///
308/// The LED will blink "sporadically" in a somewhat irregular pattern. This
309/// should look different from a traditional blinking LED which typically blinks
310/// with a consistent duty cycle. The panic blinking sequence is intentionally
311/// unusual to make it easier to tell when a panic has occurred.
312///
313/// If a multi-color LED is used for the panic pattern, it is advised to turn
314/// off other LEDs before calling this method.
315///
316/// Generally, boards should blink red during panic if possible, otherwise
317/// choose the 'first' or most prominent LED. Some boards may find it
318/// appropriate to blink multiple LEDs (e.g. one on the top and one on the
319/// bottom), thus this method accepts an array, however most will only need one.
320pub fn panic_blink_forever<L: hil::led::Led>(leds: &mut [&L]) -> ! {
321    for led in leds.iter_mut() {
322        led.init();
323    }
324    loop {
325        for _ in 0..1000000 {
326            for led in leds.iter_mut() {
327                led.on();
328            }
329        }
330        for _ in 0..100000 {
331            for led in leds.iter_mut() {
332                led.off();
333            }
334        }
335        for _ in 0..1000000 {
336            for led in leds.iter_mut() {
337                led.on();
338            }
339        }
340        for _ in 0..500000 {
341            for led in leds.iter_mut() {
342                led.off();
343            }
344        }
345    }
346}
347
348// panic! support routines
349///////////////////////////////////////////////////////////////////
350
351///////////////////////////////////////////////////////////////////
352// debug_gpio! support
353
354/// Static variable that holds an array of debug GPIO references.
355pub static DEBUG_GPIOS: SingleThreadValue<MapCell<&'static [&'static dyn hil::gpio::Pin]>> =
356    SingleThreadValue::new(MapCell::empty());
357
358/// Initialize the static debug gpio variable.
359///
360/// This ensures it can safely be used as a global variable.
361#[cfg(target_has_atomic = "ptr")]
362pub fn initialize_debug_gpio<P: ThreadIdProvider>() {
363    DEBUG_GPIOS.bind_to_thread::<P>();
364}
365
366/// Initialize the static debug gpio variable.
367///
368/// This ensures it can safely be used as a global variable.
369///
370/// # Safety
371///
372/// Callers of this function must ensure that this function is never called
373/// concurrently with other calls to [`initialize_debug_gpio_unsafe`].
374pub unsafe fn initialize_debug_gpio_unsafe<P: ThreadIdProvider>() {
375    DEBUG_GPIOS.bind_to_thread_unsafe::<P>();
376}
377
378/// Map an array of GPIO pins to use for debugging.
379pub fn assign_gpios(gpio: &'static [&'static dyn hil::gpio::Pin]) {
380    DEBUG_GPIOS.get().map(|gpio_array_cell| {
381        gpio_array_cell.replace(gpio);
382    });
383}
384
385/// In-kernel gpio debugging that accepts any GPIO HIL method.
386#[macro_export]
387macro_rules! debug_gpio {
388    ($i:tt, $method:ident $(,)?) => {{
389        #[allow(unused_unsafe)]
390        unsafe {
391            $crate::debug::DEBUG_GPIOS.get().map(|debug_gpio_cell| {
392                debug_gpio_cell.map(|debug_gpio_array| {
393                    debug_gpio_array.get($i).map(|g| g.$method());
394                });
395            });
396        }
397    }};
398}
399
400///////////////////////////////////////////////////////////////////
401// debug! and debug_verbose! support
402
403/// A trait for writing debug output.
404///
405/// This can be used for example to implement asynchronous or synchronous
406/// writers, and buffered or unbuffered writers. Various platforms may have
407/// in-memory logs, memory mapped "endless" FIFOs, JTAG support for output,
408/// etc.
409pub trait DebugWriter {
410    /// Write bytes to output with overflow notification.
411    ///
412    /// The `overflow` slice is used as a message to be appended to the end of
413    /// the available buffer if it becomes full.
414    fn write(&self, bytes: &[u8], overflow_message: &[u8]) -> usize;
415
416    /// Available length of the internal buffer if limited.
417    ///
418    /// If the buffer can support a write of any size, it should lie and return
419    /// `usize::MAX`.
420    ///
421    /// Across subsequent calls to this function, without invoking `write()` in
422    /// between, this returned value may only increase, but never decrease.
423    fn available_len(&self) -> usize;
424
425    /// How many bytes are buffered and not yet written.
426    fn to_write_len(&self) -> usize;
427
428    /// Publish bytes from the internal buffer to the output.
429    ///
430    /// Returns how many bytes were written.
431    fn publish(&self) -> usize;
432
433    /// Flush any buffered bytes to the provided output writer.
434    ///
435    /// `flush()` should be used to write an buffered bytes to a new `writer`
436    /// instead of the internal writer that `publish()` would use.
437    fn flush(&self, writer: &mut dyn IoWrite);
438}
439
440/// Static variable that holds the kernel's reference to the debug tool.
441///
442/// This is needed so the `debug!()` macros have a reference to the object to
443/// use.
444static DEBUG_WRITER: SingleThreadValue<MapCell<&'static dyn DebugWriter>> =
445    SingleThreadValue::new(MapCell::empty());
446
447/// Static variable that holds how many times `debug!()` has been called.
448///
449/// This enables printing a verbose header message that enumerates independent
450/// debug messages.
451static DEBUG_WRITER_COUNT: SingleThreadValue<Cell<usize>> = SingleThreadValue::new(Cell::new(0));
452
453/// Initialize the static debug writer.
454///
455/// This ensures it can safely be used as a global variable.
456#[cfg(target_has_atomic = "ptr")]
457pub fn initialize_debug_writer_wrapper<P: ThreadIdProvider>() {
458    DEBUG_WRITER.bind_to_thread::<P>();
459    DEBUG_WRITER_COUNT.bind_to_thread::<P>();
460}
461
462/// Initialize the static debug writer.
463///
464/// This ensures it can safely be used as a global variable.
465///
466/// # Safety
467///
468/// Callers of this function must ensure that this function is never called
469/// concurrently with other calls to [`initialize_debug_writer_wrapper_unsafe`].
470pub unsafe fn initialize_debug_writer_wrapper_unsafe<P: ThreadIdProvider>() {
471    DEBUG_WRITER.bind_to_thread_unsafe::<P>();
472    DEBUG_WRITER_COUNT.bind_to_thread_unsafe::<P>();
473}
474
475fn try_get_debug_writer<F, R>(closure: F) -> Option<R>
476where
477    F: FnOnce(&dyn DebugWriter) -> R,
478{
479    DEBUG_WRITER
480        .get()
481        .and_then(|dw| dw.map_or(None, |writer| Some(closure(*writer))))
482}
483
484/// Function used by board main.rs to set a reference to the writer.
485pub fn set_debug_writer_wrapper<C: SetDebugWriterCapability>(
486    debug_writer: &'static dyn DebugWriter,
487    _cap: C,
488) {
489    DEBUG_WRITER.get().map(|dw| dw.replace(debug_writer));
490}
491
492impl Write for &dyn DebugWriter {
493    fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
494        self.write(s.as_bytes(), b"");
495        Ok(())
496    }
497}
498
499/// Write a debug message without a trailing newline.
500pub fn debug_print(args: Arguments) {
501    try_get_debug_writer(|mut writer| {
502        let _ = write(&mut writer, args);
503        writer.publish();
504    });
505}
506
507/// Write a debug message with a trailing newline.
508pub fn debug_println(args: Arguments) {
509    try_get_debug_writer(|mut writer| {
510        let _ = write(&mut writer, args);
511        let _ = writer.write_str("\r\n");
512        writer.publish();
513    });
514}
515
516/// Write a [`ReadableProcessSlice`] to the debug output.
517///
518/// # Errors
519///
520/// Will return `Err` if it is not possible to write any output.
521pub fn debug_slice(slice: &ReadableProcessSlice) -> Result<usize, ()> {
522    try_get_debug_writer(|writer| {
523        let mut total = 0;
524        for b in slice.iter() {
525            let buf: [u8; 1] = [b.get(); 1];
526            let count = writer.write(&buf, b"");
527            if count > 0 {
528                total += count;
529            } else {
530                break;
531            }
532        }
533        writer.publish();
534        total
535    })
536    .ok_or(())
537}
538
539/// Return how many bytes are remaining in the internal debug buffer.
540pub fn debug_available_len() -> usize {
541    try_get_debug_writer(|writer| writer.available_len()).unwrap_or(0)
542}
543
544fn write_header(
545    writer: &mut &dyn DebugWriter,
546    (file, line): &(&'static str, u32),
547) -> Result<(), core::fmt::Error> {
548    let count = DEBUG_WRITER_COUNT.get().map_or(0, |count| {
549        count.increment();
550        count.get()
551    });
552
553    writer.write_fmt(format_args!("TOCK_DEBUG({}): {}:{}: ", count, file, line))
554}
555
556/// Write a debug message with file and line information without a trailing
557/// newline.
558pub fn debug_verbose_print(args: Arguments, file_line: &(&'static str, u32)) {
559    try_get_debug_writer(|mut writer| {
560        let _ = write_header(&mut writer, file_line);
561        let _ = write(&mut writer, args);
562        writer.publish();
563    });
564}
565
566/// Write a debug message with file and line information with a trailing
567/// newline.
568pub fn debug_verbose_println(args: Arguments, file_line: &(&'static str, u32)) {
569    try_get_debug_writer(|mut writer| {
570        let _ = write_header(&mut writer, file_line);
571        let _ = write(&mut writer, args);
572        let _ = writer.write_str("\r\n");
573        writer.publish();
574    });
575}
576
577/// In-kernel `println()` debugging.
578#[macro_export]
579macro_rules! debug {
580    () => ({
581        // Allow an empty debug!() to print the location when hit
582        debug!("")
583    });
584    ($msg:expr $(,)?) => ({
585        $crate::debug::debug_println(format_args!($msg));
586    });
587    ($fmt:expr, $($arg:tt)+) => ({
588        $crate::debug::debug_println(format_args!($fmt, $($arg)+));
589    });
590}
591
592/// In-kernel `println()` debugging that can take a process slice.
593#[macro_export]
594macro_rules! debug_process_slice {
595    ($msg:expr $(,)?) => {{
596        $crate::debug::debug_slice($msg)
597    }};
598}
599
600/// In-kernel `println()` debugging with filename and line numbers.
601#[macro_export]
602macro_rules! debug_verbose {
603    () => ({
604        // Allow an empty debug_verbose!() to print the location when hit
605        debug_verbose!("")
606    });
607    ($msg:expr $(,)?) => ({
608        $crate::debug::debug_verbose_println(format_args!($msg), {
609            // TODO: Maybe make opposite choice of panic!, no `static`, more
610            // runtime code for less static data
611            static _FILE_LINE: (&'static str, u32) = (file!(), line!());
612            &_FILE_LINE
613        })
614    });
615    ($fmt:expr, $($arg:tt)+) => ({
616        $crate::debug::debug_verbose_println(format_args!($fmt, $($arg)+), {
617            static _FILE_LINE: (&'static str, u32) = (file!(), line!());
618            &_FILE_LINE
619        })
620    });
621}
622
623/// Prints out the expression and its location, then returns it.
624///
625/// ```rust,ignore
626/// let foo: u8 = debug_expr!(0xff);
627/// // Prints [main.rs:2] 0xff = 255
628/// ```
629/// Taken straight from Rust `std::dbg`.
630#[macro_export]
631macro_rules! debug_expr {
632    // NOTE: We cannot use `concat!` to make a static string as a format
633    // argument of `eprintln!` because `file!` could contain a `{` or `$val`
634    // expression could be a block (`{ .. }`), in which case the `eprintln!`
635    // will be malformed.
636    () => {
637        $crate::debug!("[{}:{}]", file!(), line!())
638    };
639    ($val:expr $(,)?) => {
640        // Use of `match` here is intentional because it affects the lifetimes
641        // of temporaries - https://stackoverflow.com/a/48732525/1063961
642        match $val {
643            tmp => {
644                $crate::debug!("[{}:{}] {} = {:#?}",
645                    file!(), line!(), stringify!($val), &tmp);
646                tmp
647            }
648        }
649    };
650    ($($val:expr),+ $(,)?) => {
651        ($($crate::debug_expr!($val)),+,)
652    };
653}
654
655/// Flush any stored messages to the output writer.
656pub unsafe fn flush<W: Write + IoWrite>(writer: &mut W) {
657    try_get_debug_writer(|debug_writer|{
658        if debug_writer.to_write_len() > 0 {
659            let _ = writer.write_str(
660                    "\r\n---| Debug buffer not empty. Flushing. May repeat some of last message(s):\r\n",
661                );
662            debug_writer.flush(writer);
663        }
664    }).or_else(||{
665        let _ = writer.write_str(
666            "\r\n---| Global debug writer not registered.\
667             \r\n     Call `set_debug_writer_wrapper` in board initialization.\r\n",
668        );
669        None
670    });
671}