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}