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