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