blob: 3335a3dc7129fe17ffddfd76107d7fc77b4678b2 [file] [edit]
// Copyright 2026 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
//! Debug logging (`dprintf`) mechanism for the Zircon kernel.
//!
//! # Overview
//!
//! This crate provides a Rust implementation of the global debug printing mechanism
//! (`dprintf!`) used in the Zircon kernel. It is the Rust counterpart to the C++
//! header `zircon/kernel/include/debug.h`.
//!
//! # Verbosity Levels
//!
//! The macro `dprintf!` filters messages at compile time based on the global
//! GN build argument `kernel_debug_print_level`.
//!
//! This argument defaults to `2` (`SPEW`) in the kernel parameters (see
//! `zircon/kernel/params.gni`).
//!
//! To override this level in your local build, add the following to your `args.gn`:
//!
//! ```gn
//! kernel_debug_print_level = 1 # Restrict to INFO and CRITICAL
//! ```
//!
//! The following levels are defined, matching C++:
//! * `CRITICAL` (0) / `ALWAYS` (0): Critical errors or messages that should always be printed.
//! * `INFO` (1): Informational messages.
//! * `SPEW` (2): Verbose debugging messages.
//!
//! If a message's level is greater than the configured `kernel_debug_print_level`,
//! the macro call compiles down to nothing, incurring zero runtime overhead.
//!
//! # Usage
//!
//! To use global debug printing in a Rust file:
//!
//! ```rust
//! use debug::dprintf;
//!
//! fn my_function() {
//! dprintf!(INFO, "This is an info message: {}\n", 42);
//! dprintf!(SPEW, "This is verbose spew\n");
//! }
//! ```
//!
//! The macro supports literal tokens `CRITICAL`, `ALWAYS`, `INFO`, and `SPEW`
//! without needing to import them, but you can also use arbitrary expressions
//! that evaluate to `u32` (e.g. `debug::dprintf::INFO` or a local variable).
pub const CRITICAL: u32 = 0;
pub const ALWAYS: u32 = 0;
pub const INFO: u32 = 1;
pub const SPEW: u32 = 2;
struct KernelConsoleWriter;
impl core::fmt::Write for KernelConsoleWriter {
#[cfg(not(test))]
fn write_str(&mut self, s: &str) -> core::fmt::Result {
// TODO(https://fxbug.dev/518017761): Update this to use the Rust libc
// crate when it is available.
unsafe extern "C" {
fn printf(format: *const core::ffi::c_char, ...) -> core::ffi::c_int;
}
// SAFETY: `"%.*s\0"` is a valid C format string requiring an integer length
// and a character buffer pointer. `s.len()` and `s.as_ptr()` provide a valid slice.
unsafe {
const FORMAT: &[u8; 5] = b"%.*s\0";
printf(
FORMAT.as_ptr() as *const core::ffi::c_char,
s.len() as core::ffi::c_int,
s.as_ptr() as *const core::ffi::c_char,
);
}
Ok(())
}
#[cfg(test)]
fn write_str(&mut self, s: &str) -> core::fmt::Result {
std::print!("{}", s);
Ok(())
}
}
#[doc(hidden)]
#[inline(always)]
pub const fn dprintf_enabled(level: u32) -> bool {
let limit = if cfg!(debug_print_level = "0") {
CRITICAL
} else if cfg!(debug_print_level = "1") {
INFO
} else if cfg!(debug_print_level = "2") {
SPEW
} else {
SPEW
};
level <= limit
}
#[doc(hidden)]
#[inline(always)]
pub fn print_dprintf_args(args: core::fmt::Arguments<'_>) {
use core::fmt::Write;
let mut writer = KernelConsoleWriter;
let _ = writer.write_fmt(args);
}
/// Formats and prints a debug message if the global debug print level is
/// greater than or equal to the specified level.
///
/// Equivalent to C++ `dprintf`.
///
/// # Examples
/// ```rust
/// dprintf!(INFO, "initialized with value {}\n", val);
/// ```
#[macro_export]
macro_rules! dprintf {
(CRITICAL, $($arg:tt)*) => { $crate::dprintf!($crate::dprintf::CRITICAL, $($arg)*) };
(ALWAYS, $($arg:tt)*) => { $crate::dprintf!($crate::dprintf::ALWAYS, $($arg)*) };
(INFO, $($arg:tt)*) => { $crate::dprintf!($crate::dprintf::INFO, $($arg)*) };
(SPEW, $($arg:tt)*) => { $crate::dprintf!($crate::dprintf::SPEW, $($arg)*) };
($level:expr, $($arg:tt)*) => {
if $crate::dprintf::dprintf_enabled($level) {
$crate::dprintf::print_dprintf_args(core::format_args!($($arg)*));
}
};
}
#[cfg(test)]
mod tests {
#[test]
fn test_dprintf() {
dprintf!(CRITICAL, "critical\n");
dprintf!(ALWAYS, "always\n");
dprintf!(INFO, "info\n");
dprintf!(SPEW, "spew\n");
dprintf!(3, "should not print\n");
dprintf!(INFO, "info const\n");
}
}