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