| 1 | // Copyright 2025 Bernhard Beschow <shentey@gmail.com> |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | |
| 4 | //! Bindings for QEMU's logging infrastructure |
| 5 | |
| 6 | use std::{ |
| 7 | io::{self, Write}, |
| 8 | ptr::NonNull, |
| 9 | }; |
| 10 | |
| 11 | use common::errno; |
| 12 | |
| 13 | use crate::bindings; |
| 14 | |
| 15 | #[repr(u32)] |
| 16 | /// Represents specific error categories within QEMU's logging system. |
| 17 | /// |
| 18 | /// The `Log` enum provides a Rust abstraction for logging errors, corresponding |
| 19 | /// to a subset of the error categories defined in the C implementation. |
| 20 | pub enum Log { |
| 21 | /// Log invalid access caused by the guest. |
| 22 | /// Corresponds to `LOG_GUEST_ERROR` in the C implementation. |
| 23 | GuestError = bindings::LOG_GUEST_ERROR, |
| 24 | |
| 25 | /// Log guest access of unimplemented functionality. |
| 26 | /// Corresponds to `LOG_UNIMP` in the C implementation. |
| 27 | Unimp = bindings::LOG_UNIMP, |
| 28 | } |
| 29 | |
| 30 | /// A RAII guard for QEMU's logging infrastructure. Creating the guard |
| 31 | /// locks the log file, and dropping it (letting it go out of scope) unlocks |
| 32 | /// the file. |
| 33 | /// |
| 34 | /// As long as the guard lives, it can be written to using [`std::io::Write`]. |
| 35 | /// |
| 36 | /// The locking is recursive, therefore owning a guard does not prevent |
| 37 | /// using [`log_mask_ln!()`](crate::log_mask_ln). |
| 38 | pub struct LogGuard(NonNull<bindings::FILE>); |
| 39 | |
| 40 | impl LogGuard { |
| 41 | /// Return a RAII guard that writes to QEMU's logging infrastructure. |
| 42 | /// The log file is locked while the guard exists, ensuring that there |
| 43 | /// is no tearing of the messages. |
| 44 | /// |
| 45 | /// Return `None` if the log file is closed and could not be opened. |
| 46 | /// Do *not* use `unwrap()` on the result; failure can be handled simply |
| 47 | /// by not logging anything. |
| 48 | /// |
| 49 | /// # Examples |
| 50 | /// |
| 51 | /// ``` |
| 52 | /// # use util::log::LogGuard; |
| 53 | /// # use std::io::Write; |
| 54 | /// if let Some(mut log) = LogGuard::new() { |
| 55 | /// writeln!(log, "test"); |
| 56 | /// } |
| 57 | /// ``` |
| 58 | pub fn new() -> Option<Self> { |
| 59 | let f = unsafe { bindings::qemu_log_trylock() }.cast(); |
| 60 | NonNull::new(f).map(Self) |
| 61 | } |
| 62 | |
| 63 | /// Writes a formatted string into the log, returning any error encountered. |
| 64 | /// |
| 65 | /// This method is primarily used by the |
| 66 | /// [`log_mask_ln!()`](crate::log_mask_ln) macro, and it is rare for it |
| 67 | /// to be called explicitly. It is public because it is the only way to |
| 68 | /// examine the error, which `log_mask_ln!()` ignores |
| 69 | /// |
| 70 | /// Unlike `log_mask_ln!()`, it does *not* append a newline at the end. |
| 71 | pub fn log_fmt(args: std::fmt::Arguments) -> io::Result<()> { |
| 72 | if let Some(mut log) = Self::new() { |
| 73 | log.write_fmt(args)?; |
| 74 | } |
| 75 | Ok(()) |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | impl Write for LogGuard { |
| 80 | fn write(&mut self, bytes: &[u8]) -> io::Result<usize> { |
| 81 | let ret = unsafe { |
| 82 | bindings::rust_fwrite(bytes.as_ptr().cast(), 1, bytes.len(), self.0.as_ptr()) |
| 83 | }; |
| 84 | errno::into_io_result(ret) |
| 85 | } |
| 86 | |
| 87 | fn flush(&mut self) -> io::Result<()> { |
| 88 | // Do nothing, dropping the guard takes care of flushing |
| 89 | Ok(()) |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | impl Drop for LogGuard { |
| 94 | fn drop(&mut self) { |
| 95 | unsafe { |
| 96 | bindings::qemu_log_unlock(self.0.as_ptr()); |
| 97 | } |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | /// A macro to log messages conditionally based on a provided mask. |
| 102 | /// |
| 103 | /// The `log_mask_ln` macro checks whether the given mask matches the current |
| 104 | /// log level and, if so, formats and logs the message. It is the Rust |
| 105 | /// counterpart of the `qemu_log_mask()` macro in the C implementation. |
| 106 | /// |
| 107 | /// Errors from writing to the log are ignored. |
| 108 | /// |
| 109 | /// # Parameters |
| 110 | /// |
| 111 | /// - `$mask`: A log level mask. This should be a variant of the `Log` enum. |
| 112 | /// - `$fmt`: A format string following the syntax and rules of the `format!` |
| 113 | /// macro. It specifies the structure of the log message. |
| 114 | /// - `$args`: Optional arguments to be interpolated into the format string. |
| 115 | /// |
| 116 | /// # Example |
| 117 | /// |
| 118 | /// ``` |
| 119 | /// use util::{log::Log, log_mask_ln}; |
| 120 | /// |
| 121 | /// let error_address = 0xbad; |
| 122 | /// log_mask_ln!(Log::GuestError, "Address 0x{error_address:x} out of range"); |
| 123 | /// ``` |
| 124 | /// |
| 125 | /// It is also possible to use printf-style formatting, as well as having a |
| 126 | /// trailing `,`: |
| 127 | /// |
| 128 | /// ``` |
| 129 | /// use util::{log::Log, log_mask_ln}; |
| 130 | /// |
| 131 | /// let error_address = 0xbad; |
| 132 | /// log_mask_ln!( |
| 133 | /// Log::GuestError, |
| 134 | /// "Address 0x{:x} out of range", |
| 135 | /// error_address, |
| 136 | /// ); |
| 137 | /// ``` |
| 138 | /// |
| 139 | /// The `log_mask_ln` macro must only be used for emitting complete |
| 140 | /// log messages. Where it is required to incrementally output string |
| 141 | /// fragments to construct a complete message, `LogGuard::new()` must |
| 142 | /// be directly used in combination with `writeln()` to avoid output |
| 143 | /// races with other QEMU threads. |
| 144 | #[macro_export] |
| 145 | macro_rules! log_mask_ln { |
| 146 | ($mask:expr, $fmt:tt $($args:tt)*) => {{ |
| 147 | // Type assertion to enforce type `Log` for $mask |
| 148 | let _: $crate::log::Log = $mask; |
| 149 | |
| 150 | if unsafe { |
| 151 | ($crate::bindings::qemu_loglevel & ($mask as std::os::raw::c_uint)) != 0 |
| 152 | } { |
| 153 | _ = $crate::log::LogGuard::log_fmt( |
| 154 | format_args!("{}\n", format_args!($fmt $($args)*))); |
| 155 | } |
| 156 | }}; |
| 157 | } |