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