debug/dprintf.rs
1// Copyright 2026 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7//! Debug logging (`dprintf`) mechanism for the Zircon kernel.
8//!
9//! # Overview
10//!
11//! This crate provides a Rust implementation of the global debug printing mechanism
12//! (`dprintf!`) used in the Zircon kernel. It is the Rust counterpart to the C++
13//! header `zircon/kernel/include/debug.h`.
14//!
15//! # Verbosity Levels
16//!
17//! The macro `dprintf!` filters messages at compile time based on the global
18//! GN build argument `kernel_debug_print_level`.
19//!
20//! This argument defaults to `2` (`SPEW`) in the kernel parameters (see
21//! `zircon/kernel/params.gni`).
22//!
23//! To override this level in your local build, add the following to your `args.gn`:
24//!
25//! ```gn
26//! kernel_debug_print_level = 1 # Restrict to INFO and CRITICAL
27//! ```
28//!
29//! The following levels are defined, matching C++:
30//! * `CRITICAL` (0) / `ALWAYS` (0): Critical errors or messages that should always be printed.
31//! * `INFO` (1): Informational messages.
32//! * `SPEW` (2): Verbose debugging messages.
33//!
34//! If a message's level is greater than the configured `kernel_debug_print_level`,
35//! the macro call compiles down to nothing, incurring zero runtime overhead.
36//!
37//! # Usage
38//!
39//! To use global debug printing in a Rust file:
40//!
41//! ```rust
42//! use debug::dprintf;
43//!
44//! fn my_function() {
45//! dprintf!(INFO, "This is an info message: {}\n", 42);
46//! dprintf!(SPEW, "This is verbose spew\n");
47//! }
48//! ```
49//!
50//! The macro supports literal tokens `CRITICAL`, `ALWAYS`, `INFO`, and `SPEW`
51//! without needing to import them, but you can also use arbitrary expressions
52//! that evaluate to `u32` (e.g. `debug::dprintf::INFO` or a local variable).
53
54pub const CRITICAL: u32 = 0;
55pub const ALWAYS: u32 = 0;
56pub const INFO: u32 = 1;
57pub const SPEW: u32 = 2;
58
59pub struct KernelConsoleWriter;
60
61impl core::fmt::Write for KernelConsoleWriter {
62 #[cfg(not(test))]
63 fn write_str(&mut self, s: &str) -> core::fmt::Result {
64 kprint::kprint!("{:s}", s);
65 Ok(())
66 }
67
68 #[cfg(test)]
69 fn write_str(&mut self, s: &str) -> core::fmt::Result {
70 std::print!("{}", s);
71 Ok(())
72 }
73}
74
75#[doc(hidden)]
76#[inline(always)]
77pub const fn dprintf_enabled(level: u32) -> bool {
78 let limit = if cfg!(debug_print_level = "0") {
79 CRITICAL
80 } else if cfg!(debug_print_level = "1") {
81 INFO
82 } else {
83 SPEW
84 };
85 level <= limit
86}
87
88#[doc(hidden)]
89#[inline(always)]
90pub fn print_dprintf_args(args: core::fmt::Arguments<'_>) {
91 use core::fmt::Write;
92 let mut writer = KernelConsoleWriter;
93 let _ = writer.write_fmt(args);
94}
95
96/// Formats and prints a debug message if the global debug print level is
97/// greater than or equal to the specified level.
98///
99/// Equivalent to C++ `dprintf`.
100///
101/// # Examples
102/// ```rust
103/// dprintf!(INFO, "initialized with value {}\n", val);
104/// ```
105#[macro_export]
106macro_rules! dprintf {
107 (CRITICAL, $($arg:tt)*) => { $crate::dprintf!($crate::dprintf::CRITICAL, $($arg)*) };
108 (ALWAYS, $($arg:tt)*) => { $crate::dprintf!($crate::dprintf::ALWAYS, $($arg)*) };
109 (INFO, $($arg:tt)*) => { $crate::dprintf!($crate::dprintf::INFO, $($arg)*) };
110 (SPEW, $($arg:tt)*) => { $crate::dprintf!($crate::dprintf::SPEW, $($arg)*) };
111 ($level:expr, $($arg:tt)*) => {
112 if $crate::dprintf::dprintf_enabled($level) {
113 $crate::dprintf::print_dprintf_args(core::format_args!($($arg)*));
114 }
115 };
116}
117
118#[cfg(test)]
119mod tests {
120 #[test]
121 fn test_dprintf() {
122 dprintf!(CRITICAL, "critical\n");
123 dprintf!(ALWAYS, "always\n");
124 dprintf!(INFO, "info\n");
125 dprintf!(SPEW, "spew\n");
126 dprintf!(3, "should not print\n");
127 dprintf!(INFO, "info const\n");
128 }
129}