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//! Example
29//! -------
30//!
31//! ```no_run
32//! # use kernel::{debug, debug_gpio, debug_verbose};
33//! # fn main() {
34//! # let i = 42;
35//! debug!("Yes the code gets here with value {}", i);
36//! debug_verbose!("got here"); // Includes message count, file, and line.
37//!
38//! debug_gpio!(0, toggle); // Toggles the first debug GPIO.
39//!
40//! # }
41//! ```
42//!
43//! ```text
44//! Yes the code gets here with value 42
45//! TOCK_DEBUG(0): /tock/capsules/src/sensys.rs:24: got here
46//! ```
47
48use core::cell::Cell;
49use core::fmt::{write, Arguments, Result, Write};
50use core::panic::PanicInfo;
51use core::ptr::addr_of_mut;
52use core::str;
53
54use crate::capabilities::SetDebugWriterCapability;
55use crate::collections::queue::Queue;
56use crate::collections::ring_buffer::RingBuffer;
57use crate::hil;
58use crate::platform::chip::Chip;
59use crate::process::Process;
60use crate::process::ProcessPrinter;
61use crate::processbuffer::ReadableProcessSlice;
62use crate::utilities::binary_write::BinaryToWriteWrapper;
63use crate::utilities::cells::NumericCellExt;
64use crate::utilities::cells::{MapCell, TakeCell};
65use crate::ErrorCode;
66
67/// Implementation of `std::io::Write` for `no_std`.
68///
69/// This takes bytes instead of a string (contrary to [`core::fmt::Write`]), but
70/// we cannot use `std::io::Write' as it isn't available in `no_std` (due to
71/// `std::io::Error` not being available).
72///
73/// Also, in our use cases, writes are infallible, so the write function cannot
74/// return an `Err`, however it might not be able to write everything, so it
75/// returns the number of bytes written.
76///
77/// See also the tracking issue:
78/// <https://github.com/rust-lang/rfcs/issues/2262>.
79pub trait IoWrite {
80    fn write(&mut self, buf: &[u8]) -> usize;
81
82    fn write_ring_buffer(&mut self, buf: &RingBuffer<'_, u8>) -> usize {
83        let (left, right) = buf.as_slices();
84        let mut total = 0;
85        if let Some(slice) = left {
86            total += self.write(slice);
87        }
88        if let Some(slice) = right {
89            total += self.write(slice);
90        }
91        total
92    }
93}
94
95///////////////////////////////////////////////////////////////////
96// panic! support routines
97
98/// Tock panic routine, without the infinite LED-blinking loop.
99///
100/// This is useful for boards which do not feature LEDs to blink or want to
101/// implement their own behavior. This method returns after performing the panic
102/// dump.
103///
104/// After this method returns, the system is no longer in a well-defined state.
105/// Care must be taken on how one interacts with the system once this function
106/// returns.
107///
108/// **NOTE:** The supplied `writer` must be synchronous.
109pub unsafe fn panic_print<W: Write + IoWrite, C: Chip, PP: ProcessPrinter>(
110    writer: &mut W,
111    panic_info: &PanicInfo,
112    nop: &dyn Fn(),
113    processes: &'static [Option<&'static dyn Process>],
114    chip: &'static Option<&'static C>,
115    process_printer: &'static Option<&'static PP>,
116) {
117    panic_begin(nop);
118    // Flush debug buffer if needed
119    flush(writer);
120    panic_banner(writer, panic_info);
121    panic_cpu_state(chip, writer);
122
123    // Some systems may enforce memory protection regions for the kernel, making
124    // application memory inaccessible. However, printing process information
125    // will attempt to access memory. If we are provided a chip reference,
126    // attempt to disable userspace memory protection first:
127    chip.map(|c| {
128        use crate::platform::mpu::MPU;
129        c.mpu().disable_app_mpu()
130    });
131    panic_process_info(processes, process_printer, writer);
132}
133
134/// Tock default panic routine.
135///
136/// **NOTE:** The supplied `writer` must be synchronous.
137///
138/// This will print a detailed debugging message and then loop forever while
139/// blinking an LED in a recognizable pattern.
140pub unsafe fn panic<L: hil::led::Led, W: Write + IoWrite, C: Chip, PP: ProcessPrinter>(
141    leds: &mut [&L],
142    writer: &mut W,
143    panic_info: &PanicInfo,
144    nop: &dyn Fn(),
145    processes: &'static [Option<&'static dyn Process>],
146    chip: &'static Option<&'static C>,
147    process_printer: &'static Option<&'static PP>,
148) -> ! {
149    // Call `panic_print` first which will print out the panic information and
150    // return
151    panic_print(writer, panic_info, nop, processes, chip, process_printer);
152
153    // The system is no longer in a well-defined state, we cannot
154    // allow this function to return
155    //
156    // Forever blink LEDs in an infinite loop
157    panic_blink_forever(leds)
158}
159
160/// Generic panic entry.
161///
162/// This opaque method should always be called at the beginning of a board's
163/// panic method to allow hooks for any core kernel cleanups that may be
164/// appropriate.
165pub unsafe fn panic_begin(nop: &dyn Fn()) {
166    // Let any outstanding uart DMA's finish
167    for _ in 0..200000 {
168        nop();
169    }
170}
171
172/// Lightweight prints about the current panic and kernel version.
173///
174/// **NOTE:** The supplied `writer` must be synchronous.
175pub unsafe fn panic_banner<W: Write>(writer: &mut W, panic_info: &PanicInfo) {
176    let _ = writer.write_fmt(format_args!("\r\n{}\r\n", panic_info));
177
178    // Print version of the kernel
179    let _ = writer.write_fmt(format_args!(
180        "\tKernel version {}\r\n",
181        option_env!("TOCK_KERNEL_VERSION").unwrap_or("unknown")
182    ));
183}
184
185/// Print current machine (CPU) state.
186///
187/// **NOTE:** The supplied `writer` must be synchronous.
188pub unsafe fn panic_cpu_state<W: Write, C: Chip>(
189    chip: &'static Option<&'static C>,
190    writer: &mut W,
191) {
192    chip.map(|c| {
193        c.print_state(writer);
194    });
195}
196
197/// More detailed prints about all processes.
198///
199/// **NOTE:** The supplied `writer` must be synchronous.
200pub unsafe fn panic_process_info<PP: ProcessPrinter, W: Write>(
201    procs: &'static [Option<&'static dyn Process>],
202    process_printer: &'static Option<&'static PP>,
203    writer: &mut W,
204) {
205    process_printer.map(|printer| {
206        // print data about each process
207        let _ = writer.write_fmt(format_args!("\r\n---| App Status |---\r\n"));
208        for proc in procs {
209            proc.map(|process| {
210                // Print the memory map and basic process info.
211                //
212                // Because we are using a synchronous printer we do not need to
213                // worry about looping on the print function.
214                printer.print_overview(process, &mut BinaryToWriteWrapper::new(writer), None);
215                // Print all of the process details.
216                process.print_full_process(writer);
217            });
218        }
219    });
220}
221
222/// Blinks a recognizable pattern forever.
223///
224/// The LED will blink "sporadically" in a somewhat irregular pattern. This
225/// should look different from a traditional blinking LED which typically blinks
226/// with a consistent duty cycle. The panic blinking sequence is intentionally
227/// unusual to make it easier to tell when a panic has occurred.
228///
229/// If a multi-color LED is used for the panic pattern, it is advised to turn
230/// off other LEDs before calling this method.
231///
232/// Generally, boards should blink red during panic if possible, otherwise
233/// choose the 'first' or most prominent LED. Some boards may find it
234/// appropriate to blink multiple LEDs (e.g. one on the top and one on the
235/// bottom), thus this method accepts an array, however most will only need one.
236pub fn panic_blink_forever<L: hil::led::Led>(leds: &mut [&L]) -> ! {
237    leds.iter_mut().for_each(|led| led.init());
238    loop {
239        for _ in 0..1000000 {
240            leds.iter_mut().for_each(|led| led.on());
241        }
242        for _ in 0..100000 {
243            leds.iter_mut().for_each(|led| led.off());
244        }
245        for _ in 0..1000000 {
246            leds.iter_mut().for_each(|led| led.on());
247        }
248        for _ in 0..500000 {
249            leds.iter_mut().for_each(|led| led.off());
250        }
251    }
252}
253
254// panic! support routines
255///////////////////////////////////////////////////////////////////
256
257///////////////////////////////////////////////////////////////////
258// debug_gpio! support
259
260/// Object to hold the assigned debugging GPIOs.
261pub static mut DEBUG_GPIOS: (
262    Option<&'static dyn hil::gpio::Pin>,
263    Option<&'static dyn hil::gpio::Pin>,
264    Option<&'static dyn hil::gpio::Pin>,
265) = (None, None, None);
266
267/// Map up to three GPIO pins to use for debugging.
268pub unsafe fn assign_gpios(
269    gpio0: Option<&'static dyn hil::gpio::Pin>,
270    gpio1: Option<&'static dyn hil::gpio::Pin>,
271    gpio2: Option<&'static dyn hil::gpio::Pin>,
272) {
273    DEBUG_GPIOS.0 = gpio0;
274    DEBUG_GPIOS.1 = gpio1;
275    DEBUG_GPIOS.2 = gpio2;
276}
277
278/// In-kernel gpio debugging that accepts any GPIO HIL method.
279#[macro_export]
280macro_rules! debug_gpio {
281    ($i:tt, $method:ident $(,)?) => {{
282        #[allow(unused_unsafe)]
283        unsafe {
284            $crate::debug::DEBUG_GPIOS.$i.map(|g| g.$method());
285        }
286    }};
287}
288
289///////////////////////////////////////////////////////////////////
290// debug! and debug_verbose! support
291
292/// Wrapper type that we need a mutable reference to for the
293/// [`core::fmt::Write`] interface.
294pub struct DebugWriterWrapper {
295    dw: MapCell<&'static DebugWriter>,
296}
297
298/// Main type that we share with the UART provider and this debug module.
299pub struct DebugWriter {
300    // What provides the actual writing mechanism.
301    uart: &'static dyn hil::uart::Transmit<'static>,
302    // The buffer that is passed to the writing mechanism.
303    output_buffer: TakeCell<'static, [u8]>,
304    // An internal buffer that is used to hold debug!() calls as they come in.
305    internal_buffer: TakeCell<'static, RingBuffer<'static, u8>>,
306    // Number of debug!() calls.
307    count: Cell<usize>,
308}
309
310/// Static variable that holds the kernel's reference to the debug tool.
311///
312/// This is needed so the `debug!()` macros have a reference to the object to
313/// use.
314static mut DEBUG_WRITER: Option<&'static mut DebugWriterWrapper> = None;
315
316unsafe fn try_get_debug_writer() -> Option<&'static mut DebugWriterWrapper> {
317    (*addr_of_mut!(DEBUG_WRITER)).as_deref_mut()
318}
319
320unsafe fn get_debug_writer() -> &'static mut DebugWriterWrapper {
321    try_get_debug_writer().unwrap() // Unwrap fail = Must call `set_debug_writer_wrapper` in board initialization.
322}
323
324/// Function used by board main.rs to set a reference to the writer.
325pub fn set_debug_writer_wrapper<C: SetDebugWriterCapability>(
326    debug_writer: &'static mut DebugWriterWrapper,
327    _cap: C,
328) {
329    unsafe {
330        DEBUG_WRITER = Some(debug_writer);
331    }
332}
333
334impl DebugWriterWrapper {
335    pub fn new(dw: &'static DebugWriter) -> DebugWriterWrapper {
336        DebugWriterWrapper {
337            dw: MapCell::new(dw),
338        }
339    }
340}
341
342impl DebugWriter {
343    pub fn new(
344        uart: &'static dyn hil::uart::Transmit,
345        out_buffer: &'static mut [u8],
346        internal_buffer: &'static mut RingBuffer<'static, u8>,
347    ) -> DebugWriter {
348        DebugWriter {
349            uart,
350            output_buffer: TakeCell::new(out_buffer),
351            internal_buffer: TakeCell::new(internal_buffer),
352            count: Cell::new(0), // how many debug! calls
353        }
354    }
355
356    fn increment_count(&self) {
357        self.count.increment();
358    }
359
360    fn get_count(&self) -> usize {
361        self.count.get()
362    }
363
364    /// Write as many of the bytes from the internal_buffer to the output
365    /// mechanism as possible, returning the number written.
366    fn publish_bytes(&self) -> usize {
367        // Can only publish if we have the output_buffer. If we don't that is
368        // fine, we will do it when the transmit done callback happens.
369        self.internal_buffer.map_or(0, |ring_buffer| {
370            if let Some(out_buffer) = self.output_buffer.take() {
371                let mut count = 0;
372
373                for dst in out_buffer.iter_mut() {
374                    match ring_buffer.dequeue() {
375                        Some(src) => {
376                            *dst = src;
377                            count += 1;
378                        }
379                        None => {
380                            break;
381                        }
382                    }
383                }
384
385                if count != 0 {
386                    // Transmit the data in the output buffer.
387                    if let Err((_err, buf)) = self.uart.transmit_buffer(out_buffer, count) {
388                        self.output_buffer.put(Some(buf));
389                    } else {
390                        self.output_buffer.put(None);
391                    }
392                }
393                count
394            } else {
395                0
396            }
397        })
398    }
399
400    fn extract(&self) -> Option<&mut RingBuffer<'static, u8>> {
401        self.internal_buffer.take()
402    }
403
404    fn available_len(&self) -> usize {
405        self.internal_buffer.map_or(0, |rb| rb.available_len())
406    }
407}
408
409impl hil::uart::TransmitClient for DebugWriter {
410    fn transmitted_buffer(
411        &self,
412        buffer: &'static mut [u8],
413        _tx_len: usize,
414        _rcode: core::result::Result<(), ErrorCode>,
415    ) {
416        // Replace this buffer since we are done with it.
417        self.output_buffer.replace(buffer);
418
419        if self.internal_buffer.map_or(false, |buf| buf.has_elements()) {
420            // Buffer not empty, go around again
421            self.publish_bytes();
422        }
423    }
424    fn transmitted_word(&self, _rcode: core::result::Result<(), ErrorCode>) {}
425}
426
427/// Pass through functions.
428impl DebugWriterWrapper {
429    fn increment_count(&self) {
430        self.dw.map(|dw| {
431            dw.increment_count();
432        });
433    }
434
435    fn get_count(&self) -> usize {
436        self.dw.map_or(0, |dw| dw.get_count())
437    }
438
439    fn publish_bytes(&self) -> usize {
440        self.dw.map_or(0, |dw| dw.publish_bytes())
441    }
442
443    fn extract(&self) -> Option<&mut RingBuffer<'static, u8>> {
444        self.dw.map_or(None, |dw| dw.extract())
445    }
446
447    fn available_len(&self) -> usize {
448        const FULL_MSG: &[u8] = b"\n*** DEBUG BUFFER FULL ***\n";
449        self.dw
450            .map_or(0, |dw| dw.available_len().saturating_sub(FULL_MSG.len()))
451    }
452}
453
454impl IoWrite for DebugWriterWrapper {
455    fn write(&mut self, bytes: &[u8]) -> usize {
456        const FULL_MSG: &[u8] = b"\n*** DEBUG BUFFER FULL ***\n";
457        self.dw.map_or(0, |dw| {
458            dw.internal_buffer.map_or(0, |ring_buffer| {
459                let available_len_for_msg =
460                    ring_buffer.available_len().saturating_sub(FULL_MSG.len());
461
462                if available_len_for_msg >= bytes.len() {
463                    for &b in bytes {
464                        ring_buffer.enqueue(b);
465                    }
466                    bytes.len()
467                } else {
468                    for &b in &bytes[..available_len_for_msg] {
469                        ring_buffer.enqueue(b);
470                    }
471                    // When the buffer is close to full, print a warning and drop the current
472                    // string.
473                    for &b in FULL_MSG {
474                        ring_buffer.enqueue(b);
475                    }
476                    available_len_for_msg
477                }
478            })
479        })
480    }
481}
482
483impl Write for DebugWriterWrapper {
484    fn write_str(&mut self, s: &str) -> Result {
485        self.write(s.as_bytes());
486        Ok(())
487    }
488}
489
490/// Write a debug message without a trailing newline.
491pub fn debug_print(args: Arguments) {
492    let writer = unsafe { get_debug_writer() };
493
494    let _ = write(writer, args);
495    writer.publish_bytes();
496}
497
498/// Write a debug message with a trailing newline.
499pub fn debug_println(args: Arguments) {
500    let writer = unsafe { get_debug_writer() };
501
502    let _ = write(writer, args);
503    let _ = writer.write_str("\r\n");
504    writer.publish_bytes();
505}
506
507/// Write a [`ReadableProcessSlice`] to the debug output.
508pub fn debug_slice(slice: &ReadableProcessSlice) -> usize {
509    let writer = unsafe { get_debug_writer() };
510    let mut total = 0;
511    for b in slice.iter() {
512        let buf: [u8; 1] = [b.get(); 1];
513        let count = writer.write(&buf);
514        if count > 0 {
515            total += count;
516        } else {
517            break;
518        }
519    }
520    writer.publish_bytes();
521    total
522}
523
524/// Return how many bytes are remaining in the internal debug buffer.
525pub fn debug_available_len() -> usize {
526    let writer = unsafe { get_debug_writer() };
527    writer.available_len()
528}
529
530fn write_header(writer: &mut DebugWriterWrapper, (file, line): &(&'static str, u32)) -> Result {
531    writer.increment_count();
532    let count = writer.get_count();
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    let writer = unsafe { get_debug_writer() };
540
541    let _ = write_header(writer, file_line);
542    let _ = write(writer, args);
543    writer.publish_bytes();
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    let writer = unsafe { get_debug_writer() };
550
551    let _ = write_header(writer, file_line);
552    let _ = write(writer, args);
553    let _ = writer.write_str("\r\n");
554    writer.publish_bytes();
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    if let Some(debug_writer) = try_get_debug_writer() {
638        if let Some(ring_buffer) = debug_writer.extract() {
639            if ring_buffer.has_elements() {
640                let _ = writer.write_str(
641                    "\r\n---| Debug buffer not empty. Flushing. May repeat some of last message(s):\r\n",
642                );
643
644                writer.write_ring_buffer(ring_buffer);
645            }
646        }
647    } else {
648        let _ = writer.write_str(
649            "\r\n---| Global debug writer not registered.\
650             \r\n     Call `set_debug_writer_wrapper` in board initialization.\r\n",
651        );
652    }
653}