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::ProcessPrinter;
60use crate::process::ProcessSlot;
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 [ProcessSlot],
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 [ProcessSlot],
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    if crate::KERNEL_PRERELEASE_VERSION != 0 {
180        let _ = writer.write_fmt(format_args!(
181            "\tKernel version {}.{}.{}-dev{}\r\n",
182            crate::KERNEL_MAJOR_VERSION,
183            crate::KERNEL_MINOR_VERSION,
184            crate::KERNEL_PATCH_VERSION,
185            crate::KERNEL_PRERELEASE_VERSION,
186        ));
187    } else {
188        let _ = writer.write_fmt(format_args!(
189            "\tKernel version {}.{}.{}\r\n",
190            crate::KERNEL_MAJOR_VERSION,
191            crate::KERNEL_MINOR_VERSION,
192            crate::KERNEL_PATCH_VERSION,
193        ));
194    }
195}
196
197/// Print current machine (CPU) state.
198///
199/// **NOTE:** The supplied `writer` must be synchronous.
200pub unsafe fn panic_cpu_state<W: Write, C: Chip>(
201    chip: &'static Option<&'static C>,
202    writer: &mut W,
203) {
204    chip.map(|c| {
205        c.print_state(writer);
206    });
207}
208
209/// More detailed prints about all processes.
210///
211/// **NOTE:** The supplied `writer` must be synchronous.
212pub unsafe fn panic_process_info<PP: ProcessPrinter, W: Write>(
213    processes: &'static [ProcessSlot],
214    process_printer: &'static Option<&'static PP>,
215    writer: &mut W,
216) {
217    process_printer.map(|printer| {
218        // print data about each process
219        let _ = writer.write_fmt(format_args!("\r\n---| App Status |---\r\n"));
220        for slot in processes {
221            slot.proc.get().map(|process| {
222                // Print the memory map and basic process info.
223                //
224                // Because we are using a synchronous printer we do not need to
225                // worry about looping on the print function.
226                printer.print_overview(process, &mut BinaryToWriteWrapper::new(writer), None);
227                // Print all of the process details.
228                process.print_full_process(writer);
229            });
230        }
231    });
232}
233
234/// Blinks a recognizable pattern forever.
235///
236/// The LED will blink "sporadically" in a somewhat irregular pattern. This
237/// should look different from a traditional blinking LED which typically blinks
238/// with a consistent duty cycle. The panic blinking sequence is intentionally
239/// unusual to make it easier to tell when a panic has occurred.
240///
241/// If a multi-color LED is used for the panic pattern, it is advised to turn
242/// off other LEDs before calling this method.
243///
244/// Generally, boards should blink red during panic if possible, otherwise
245/// choose the 'first' or most prominent LED. Some boards may find it
246/// appropriate to blink multiple LEDs (e.g. one on the top and one on the
247/// bottom), thus this method accepts an array, however most will only need one.
248pub fn panic_blink_forever<L: hil::led::Led>(leds: &mut [&L]) -> ! {
249    leds.iter_mut().for_each(|led| led.init());
250    loop {
251        for _ in 0..1000000 {
252            leds.iter_mut().for_each(|led| led.on());
253        }
254        for _ in 0..100000 {
255            leds.iter_mut().for_each(|led| led.off());
256        }
257        for _ in 0..1000000 {
258            leds.iter_mut().for_each(|led| led.on());
259        }
260        for _ in 0..500000 {
261            leds.iter_mut().for_each(|led| led.off());
262        }
263    }
264}
265
266// panic! support routines
267///////////////////////////////////////////////////////////////////
268
269///////////////////////////////////////////////////////////////////
270// debug_gpio! support
271
272/// Object to hold the assigned debugging GPIOs.
273pub static mut DEBUG_GPIOS: (
274    Option<&'static dyn hil::gpio::Pin>,
275    Option<&'static dyn hil::gpio::Pin>,
276    Option<&'static dyn hil::gpio::Pin>,
277) = (None, None, None);
278
279/// Map up to three GPIO pins to use for debugging.
280pub unsafe fn assign_gpios(
281    gpio0: Option<&'static dyn hil::gpio::Pin>,
282    gpio1: Option<&'static dyn hil::gpio::Pin>,
283    gpio2: Option<&'static dyn hil::gpio::Pin>,
284) {
285    DEBUG_GPIOS.0 = gpio0;
286    DEBUG_GPIOS.1 = gpio1;
287    DEBUG_GPIOS.2 = gpio2;
288}
289
290/// In-kernel gpio debugging that accepts any GPIO HIL method.
291#[macro_export]
292macro_rules! debug_gpio {
293    ($i:tt, $method:ident $(,)?) => {{
294        #[allow(unused_unsafe)]
295        unsafe {
296            $crate::debug::DEBUG_GPIOS.$i.map(|g| g.$method());
297        }
298    }};
299}
300
301///////////////////////////////////////////////////////////////////
302// debug! and debug_verbose! support
303
304/// Wrapper type that we need a mutable reference to for the
305/// [`core::fmt::Write`] interface.
306pub struct DebugWriterWrapper {
307    dw: MapCell<&'static DebugWriter>,
308}
309
310/// Main type that we share with the UART provider and this debug module.
311pub struct DebugWriter {
312    // What provides the actual writing mechanism.
313    uart: &'static dyn hil::uart::Transmit<'static>,
314    // The buffer that is passed to the writing mechanism.
315    output_buffer: TakeCell<'static, [u8]>,
316    // An internal buffer that is used to hold debug!() calls as they come in.
317    internal_buffer: TakeCell<'static, RingBuffer<'static, u8>>,
318    // Number of debug!() calls.
319    count: Cell<usize>,
320}
321
322/// Static variable that holds the kernel's reference to the debug tool.
323///
324/// This is needed so the `debug!()` macros have a reference to the object to
325/// use.
326static mut DEBUG_WRITER: Option<&'static mut DebugWriterWrapper> = None;
327
328unsafe fn try_get_debug_writer() -> Option<&'static mut DebugWriterWrapper> {
329    (*addr_of_mut!(DEBUG_WRITER)).as_deref_mut()
330}
331
332unsafe fn get_debug_writer() -> &'static mut DebugWriterWrapper {
333    try_get_debug_writer().unwrap() // Unwrap fail = Must call `set_debug_writer_wrapper` in board initialization.
334}
335
336/// Function used by board main.rs to set a reference to the writer.
337pub fn set_debug_writer_wrapper<C: SetDebugWriterCapability>(
338    debug_writer: &'static mut DebugWriterWrapper,
339    _cap: C,
340) {
341    unsafe {
342        DEBUG_WRITER = Some(debug_writer);
343    }
344}
345
346impl DebugWriterWrapper {
347    pub fn new(dw: &'static DebugWriter) -> DebugWriterWrapper {
348        DebugWriterWrapper {
349            dw: MapCell::new(dw),
350        }
351    }
352}
353
354impl DebugWriter {
355    pub fn new(
356        uart: &'static dyn hil::uart::Transmit,
357        out_buffer: &'static mut [u8],
358        internal_buffer: &'static mut RingBuffer<'static, u8>,
359    ) -> DebugWriter {
360        DebugWriter {
361            uart,
362            output_buffer: TakeCell::new(out_buffer),
363            internal_buffer: TakeCell::new(internal_buffer),
364            count: Cell::new(0), // how many debug! calls
365        }
366    }
367
368    fn increment_count(&self) {
369        self.count.increment();
370    }
371
372    fn get_count(&self) -> usize {
373        self.count.get()
374    }
375
376    /// Write as many of the bytes from the internal_buffer to the output
377    /// mechanism as possible, returning the number written.
378    fn publish_bytes(&self) -> usize {
379        // Can only publish if we have the output_buffer. If we don't that is
380        // fine, we will do it when the transmit done callback happens.
381        self.internal_buffer.map_or(0, |ring_buffer| {
382            if let Some(out_buffer) = self.output_buffer.take() {
383                let mut count = 0;
384
385                for dst in out_buffer.iter_mut() {
386                    match ring_buffer.dequeue() {
387                        Some(src) => {
388                            *dst = src;
389                            count += 1;
390                        }
391                        None => {
392                            break;
393                        }
394                    }
395                }
396
397                if count != 0 {
398                    // Transmit the data in the output buffer.
399                    if let Err((_err, buf)) = self.uart.transmit_buffer(out_buffer, count) {
400                        self.output_buffer.put(Some(buf));
401                    } else {
402                        self.output_buffer.put(None);
403                    }
404                }
405                count
406            } else {
407                0
408            }
409        })
410    }
411
412    fn extract(&self) -> Option<&mut RingBuffer<'static, u8>> {
413        self.internal_buffer.take()
414    }
415
416    fn available_len(&self) -> usize {
417        self.internal_buffer.map_or(0, |rb| rb.available_len())
418    }
419}
420
421impl hil::uart::TransmitClient for DebugWriter {
422    fn transmitted_buffer(
423        &self,
424        buffer: &'static mut [u8],
425        _tx_len: usize,
426        _rcode: core::result::Result<(), ErrorCode>,
427    ) {
428        // Replace this buffer since we are done with it.
429        self.output_buffer.replace(buffer);
430
431        if self.internal_buffer.map_or(false, |buf| buf.has_elements()) {
432            // Buffer not empty, go around again
433            self.publish_bytes();
434        }
435    }
436    fn transmitted_word(&self, _rcode: core::result::Result<(), ErrorCode>) {}
437}
438
439/// Pass through functions.
440impl DebugWriterWrapper {
441    fn increment_count(&self) {
442        self.dw.map(|dw| {
443            dw.increment_count();
444        });
445    }
446
447    fn get_count(&self) -> usize {
448        self.dw.map_or(0, |dw| dw.get_count())
449    }
450
451    fn publish_bytes(&self) -> usize {
452        self.dw.map_or(0, |dw| dw.publish_bytes())
453    }
454
455    fn extract(&self) -> Option<&mut RingBuffer<'static, u8>> {
456        self.dw.map_or(None, |dw| dw.extract())
457    }
458
459    fn available_len(&self) -> usize {
460        const FULL_MSG: &[u8] = b"\n*** DEBUG BUFFER FULL ***\n";
461        self.dw
462            .map_or(0, |dw| dw.available_len().saturating_sub(FULL_MSG.len()))
463    }
464}
465
466impl IoWrite for DebugWriterWrapper {
467    fn write(&mut self, bytes: &[u8]) -> usize {
468        const FULL_MSG: &[u8] = b"\n*** DEBUG BUFFER FULL ***\n";
469        self.dw.map_or(0, |dw| {
470            dw.internal_buffer.map_or(0, |ring_buffer| {
471                let available_len_for_msg =
472                    ring_buffer.available_len().saturating_sub(FULL_MSG.len());
473
474                if available_len_for_msg >= bytes.len() {
475                    for &b in bytes {
476                        ring_buffer.enqueue(b);
477                    }
478                    bytes.len()
479                } else {
480                    for &b in &bytes[..available_len_for_msg] {
481                        ring_buffer.enqueue(b);
482                    }
483                    // When the buffer is close to full, print a warning and drop the current
484                    // string.
485                    for &b in FULL_MSG {
486                        ring_buffer.enqueue(b);
487                    }
488                    available_len_for_msg
489                }
490            })
491        })
492    }
493}
494
495impl Write for DebugWriterWrapper {
496    fn write_str(&mut self, s: &str) -> Result {
497        self.write(s.as_bytes());
498        Ok(())
499    }
500}
501
502/// Write a debug message without a trailing newline.
503pub fn debug_print(args: Arguments) {
504    let writer = unsafe { get_debug_writer() };
505
506    let _ = write(writer, args);
507    writer.publish_bytes();
508}
509
510/// Write a debug message with a trailing newline.
511pub fn debug_println(args: Arguments) {
512    let writer = unsafe { get_debug_writer() };
513
514    let _ = write(writer, args);
515    let _ = writer.write_str("\r\n");
516    writer.publish_bytes();
517}
518
519/// Write a [`ReadableProcessSlice`] to the debug output.
520pub fn debug_slice(slice: &ReadableProcessSlice) -> usize {
521    let writer = unsafe { get_debug_writer() };
522    let mut total = 0;
523    for b in slice.iter() {
524        let buf: [u8; 1] = [b.get(); 1];
525        let count = writer.write(&buf);
526        if count > 0 {
527            total += count;
528        } else {
529            break;
530        }
531    }
532    writer.publish_bytes();
533    total
534}
535
536/// Return how many bytes are remaining in the internal debug buffer.
537pub fn debug_available_len() -> usize {
538    let writer = unsafe { get_debug_writer() };
539    writer.available_len()
540}
541
542fn write_header(writer: &mut DebugWriterWrapper, (file, line): &(&'static str, u32)) -> Result {
543    writer.increment_count();
544    let count = writer.get_count();
545    writer.write_fmt(format_args!("TOCK_DEBUG({}): {}:{}: ", count, file, line))
546}
547
548/// Write a debug message with file and line information without a trailing
549/// newline.
550pub fn debug_verbose_print(args: Arguments, file_line: &(&'static str, u32)) {
551    let writer = unsafe { get_debug_writer() };
552
553    let _ = write_header(writer, file_line);
554    let _ = write(writer, args);
555    writer.publish_bytes();
556}
557
558/// Write a debug message with file and line information with a trailing
559/// newline.
560pub fn debug_verbose_println(args: Arguments, file_line: &(&'static str, u32)) {
561    let writer = unsafe { get_debug_writer() };
562
563    let _ = write_header(writer, file_line);
564    let _ = write(writer, args);
565    let _ = writer.write_str("\r\n");
566    writer.publish_bytes();
567}
568
569/// In-kernel `println()` debugging.
570#[macro_export]
571macro_rules! debug {
572    () => ({
573        // Allow an empty debug!() to print the location when hit
574        debug!("")
575    });
576    ($msg:expr $(,)?) => ({
577        $crate::debug::debug_println(format_args!($msg));
578    });
579    ($fmt:expr, $($arg:tt)+) => ({
580        $crate::debug::debug_println(format_args!($fmt, $($arg)+));
581    });
582}
583
584/// In-kernel `println()` debugging that can take a process slice.
585#[macro_export]
586macro_rules! debug_process_slice {
587    ($msg:expr $(,)?) => {{
588        $crate::debug::debug_slice($msg)
589    }};
590}
591
592/// In-kernel `println()` debugging with filename and line numbers.
593#[macro_export]
594macro_rules! debug_verbose {
595    () => ({
596        // Allow an empty debug_verbose!() to print the location when hit
597        debug_verbose!("")
598    });
599    ($msg:expr $(,)?) => ({
600        $crate::debug::debug_verbose_println(format_args!($msg), {
601            // TODO: Maybe make opposite choice of panic!, no `static`, more
602            // runtime code for less static data
603            static _FILE_LINE: (&'static str, u32) = (file!(), line!());
604            &_FILE_LINE
605        })
606    });
607    ($fmt:expr, $($arg:tt)+) => ({
608        $crate::debug::debug_verbose_println(format_args!($fmt, $($arg)+), {
609            static _FILE_LINE: (&'static str, u32) = (file!(), line!());
610            &_FILE_LINE
611        })
612    });
613}
614
615/// Prints out the expression and its location, then returns it.
616///
617/// ```rust,ignore
618/// let foo: u8 = debug_expr!(0xff);
619/// // Prints [main.rs:2] 0xff = 255
620/// ```
621/// Taken straight from Rust `std::dbg`.
622#[macro_export]
623macro_rules! debug_expr {
624    // NOTE: We cannot use `concat!` to make a static string as a format
625    // argument of `eprintln!` because `file!` could contain a `{` or `$val`
626    // expression could be a block (`{ .. }`), in which case the `eprintln!`
627    // will be malformed.
628    () => {
629        $crate::debug!("[{}:{}]", file!(), line!())
630    };
631    ($val:expr $(,)?) => {
632        // Use of `match` here is intentional because it affects the lifetimes
633        // of temporaries - https://stackoverflow.com/a/48732525/1063961
634        match $val {
635            tmp => {
636                $crate::debug!("[{}:{}] {} = {:#?}",
637                    file!(), line!(), stringify!($val), &tmp);
638                tmp
639            }
640        }
641    };
642    ($($val:expr),+ $(,)?) => {
643        ($($crate::debug_expr!($val)),+,)
644    };
645}
646
647/// Flush any stored messages to the output writer.
648pub unsafe fn flush<W: Write + IoWrite>(writer: &mut W) {
649    if let Some(debug_writer) = try_get_debug_writer() {
650        if let Some(ring_buffer) = debug_writer.extract() {
651            if ring_buffer.has_elements() {
652                let _ = writer.write_str(
653                    "\r\n---| Debug buffer not empty. Flushing. May repeat some of last message(s):\r\n",
654                );
655
656                writer.write_ring_buffer(ring_buffer);
657            }
658        }
659    } else {
660        let _ = writer.write_str(
661            "\r\n---| Global debug writer not registered.\
662             \r\n     Call `set_debug_writer_wrapper` in board initialization.\r\n",
663        );
664    }
665}