Skip to main content

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
59struct KernelConsoleWriter;
60
61impl core::fmt::Write for KernelConsoleWriter {
62    #[cfg(not(test))]
63    fn write_str(&mut self, s: &str) -> core::fmt::Result {
64        // TODO(https://fxbug.dev/518017761): Update this to use the Rust libc
65        // crate when it is available.
66        unsafe extern "C" {
67            fn printf(format: *const core::ffi::c_char, ...) -> core::ffi::c_int;
68        }
69        // SAFETY: `"%.*s\0"` is a valid C format string requiring an integer length
70        // and a character buffer pointer. `s.len()` and `s.as_ptr()` provide a valid slice.
71        unsafe {
72            const FORMAT: &[u8; 5] = b"%.*s\0";
73            printf(
74                FORMAT.as_ptr() as *const core::ffi::c_char,
75                s.len() as core::ffi::c_int,
76                s.as_ptr() as *const core::ffi::c_char,
77            );
78        }
79        Ok(())
80    }
81
82    #[cfg(test)]
83    fn write_str(&mut self, s: &str) -> core::fmt::Result {
84        std::print!("{}", s);
85        Ok(())
86    }
87}
88
89#[doc(hidden)]
90#[inline(always)]
91pub const fn dprintf_enabled(level: u32) -> bool {
92    let limit = if cfg!(debug_print_level = "0") {
93        CRITICAL
94    } else if cfg!(debug_print_level = "1") {
95        INFO
96    } else if cfg!(debug_print_level = "2") {
97        SPEW
98    } else {
99        SPEW
100    };
101    level <= limit
102}
103
104#[doc(hidden)]
105#[inline(always)]
106pub fn print_dprintf_args(args: core::fmt::Arguments<'_>) {
107    use core::fmt::Write;
108    let mut writer = KernelConsoleWriter;
109    let _ = writer.write_fmt(args);
110}
111
112/// Formats and prints a debug message if the global debug print level is
113/// greater than or equal to the specified level.
114///
115/// Equivalent to C++ `dprintf`.
116///
117/// # Examples
118/// ```rust
119/// dprintf!(INFO, "initialized with value {}\n", val);
120/// ```
121#[macro_export]
122macro_rules! dprintf {
123    (CRITICAL, $($arg:tt)*) => { $crate::dprintf!($crate::dprintf::CRITICAL, $($arg)*) };
124    (ALWAYS, $($arg:tt)*) => { $crate::dprintf!($crate::dprintf::ALWAYS, $($arg)*) };
125    (INFO, $($arg:tt)*) => { $crate::dprintf!($crate::dprintf::INFO, $($arg)*) };
126    (SPEW, $($arg:tt)*) => { $crate::dprintf!($crate::dprintf::SPEW, $($arg)*) };
127    ($level:expr, $($arg:tt)*) => {
128        if $crate::dprintf::dprintf_enabled($level) {
129            $crate::dprintf::print_dprintf_args(core::format_args!($($arg)*));
130        }
131    };
132}
133
134#[cfg(test)]
135mod tests {
136    #[test]
137    fn test_dprintf() {
138        dprintf!(CRITICAL, "critical\n");
139        dprintf!(ALWAYS, "always\n");
140        dprintf!(INFO, "info\n");
141        dprintf!(SPEW, "spew\n");
142        dprintf!(3, "should not print\n");
143        dprintf!(INFO, "info const\n");
144    }
145}