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