WIP
Add attribute macro that converts a module definition into trace events.
Currently it allocates a string to pass to
::qemu_api::log::LogGuard::log_fmt. If you pass format_args! directly
the arguments are not properly written, might be a bug in the
qemu_api::log implementation.
Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
---
rust/qemu-api-macros/src/lib.rs | 140 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 137 insertions(+), 3 deletions(-)
diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs
index b525d89c09e496a1f7f5582dc6d994e318f62bca..7d13b1c195085f1d153514cc01ec5c389160916a 100644
--- a/rust/qemu-api-macros/src/lib.rs
+++ b/rust/qemu-api-macros/src/lib.rs
@@ -3,10 +3,15 @@
// SPDX-License-Identifier: GPL-2.0-or-later
use proc_macro::TokenStream;
-use quote::quote;
+use quote::{format_ident, quote};
use syn::{
- parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, Data,
- DeriveInput, Error, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token, Variant,
+ parse::{Parse, ParseStream},
+ parse_macro_input, parse_quote,
+ punctuated::Punctuated,
+ spanned::Spanned,
+ token::Comma,
+ Data, DeriveInput, Error, Field, Fields, FieldsUnnamed, FnArg, Ident, LitCStr, LitStr, Meta,
+ Path, Token, Variant,
};
mod bits;
use bits::BitsConstInternal;
@@ -263,3 +268,132 @@ pub fn bits_const_internal(ts: TokenStream) -> TokenStream {
.unwrap_or_else(syn::Error::into_compile_error)
.into()
}
+
+#[derive(Debug)]
+struct TraceModule {
+ module_name: syn::Ident,
+ trace_events: Vec<(proc_macro2::TokenStream, syn::Ident)>,
+}
+
+impl Parse for TraceModule {
+ fn parse(input: ParseStream) -> syn::Result<Self> {
+ if input.peek(Token![pub]) {
+ input.parse::<Token![pub]>()?;
+ }
+ input.parse::<Token![mod]>()?;
+ let module_name: Ident = input.parse()?;
+ let braced;
+ _ = syn::braced!(braced in input);
+ let mut trace_events = vec![];
+ while !braced.is_empty() {
+ braced.parse::<Token![fn]>()?;
+ let name = braced.parse::<Ident>()?;
+ let name_cstr = LitCStr::new(
+ &std::ffi::CString::new(name.to_string()).unwrap(),
+ name.span(),
+ );
+ let name_cstr_ident = format_ident!("trace_{name}_name");
+ let arguments_inner;
+ _ = syn::parenthesized!(arguments_inner in braced);
+ let fn_arguments: Punctuated<FnArg, Token![,]> =
+ Punctuated::parse_terminated(&arguments_inner)?;
+ let body;
+ _ = syn::braced!(body in braced);
+ let trace_event_format_str: LitStr = body.parse()?;
+ assert!(body.is_empty(), "{body:?}");
+
+ let trace_macro_ident = format_ident!("trace_{name}");
+ let name_ident = format_ident!("trace_{name}_EVENT");
+ let dstate = format_ident!("TRACE_{name}_DSTATE");
+ let enabled = format_ident!("TRACE_{name}_ENABLED");
+ trace_events.push(
+ (
+ quote! {
+ static mut #dstate: u16 = 0;
+ const #enabled: bool = true;
+ const #name_cstr_ident: &::std::ffi::CStr = #name_cstr;
+
+ static mut #name_ident: ::qemu_api::bindings::TraceEvent = ::qemu_api::bindings::TraceEvent {
+ id: 0,
+ name: #name_cstr_ident.as_ptr(),
+ sstate: #enabled,
+ dstate: &raw mut #dstate,
+ };
+
+ macro_rules! #trace_macro_ident {
+ ($($args:tt)*) => {{
+ crate::#module_name::#name($($args)*);
+ }};
+ }
+ pub(crate) use #trace_macro_ident;
+
+ pub fn #name(#fn_arguments) {
+ if #enabled
+ && unsafe { ::qemu_api::bindings::trace_events_enabled_count > 0 }
+ && unsafe { #dstate > 0 }
+ && unsafe {
+ (::qemu_api::bindings::qemu_loglevel & (::qemu_api::log::Log::Trace as std::os::raw::c_int)) != 0
+ } {
+ _ = ::qemu_api::log::LogGuard::log_fmt(
+ format_args!("{}", format!("{} {}\n", stringify!(#name), format_args!(#trace_event_format_str)))
+ );
+ }
+ }
+ },
+ name_ident,
+ )
+ );
+ }
+
+ Ok(Self {
+ module_name,
+ trace_events,
+ })
+ }
+}
+
+#[proc_macro_attribute]
+pub fn trace_events(_attr: TokenStream, item: TokenStream) -> TokenStream {
+ let input = parse_macro_input!(item as TraceModule);
+ let TraceModule {
+ module_name,
+ trace_events,
+ } = input;
+ let mut event_names = quote! {};
+ let mut trace_event_impl = quote! {};
+ let tracevents_len = trace_events.len() + 1;
+ for (body, event_name) in trace_events {
+ event_names = quote! {
+ #event_names
+ &raw mut #event_name,
+ };
+ trace_event_impl = quote! {
+ #trace_event_impl
+ #body
+ };
+ }
+
+ quote! {
+ #[macro_use]
+ mod #module_name {
+ #![allow(static_mut_refs)]
+ #![allow(non_upper_case_globals)]
+
+ #trace_event_impl
+
+ #[used]
+ static mut TRACE_EVENTS: [*mut ::qemu_api::bindings::TraceEvent; #tracevents_len] = unsafe {
+ [
+ #event_names
+ ::core::ptr::null_mut(),
+ ]
+ };
+
+ ::qemu_api::module_init!(
+ MODULE_INIT_TRACE => unsafe {
+ ::qemu_api::bindings::trace_event_register_group(TRACE_EVENTS.as_mut_ptr())
+ }
+ );
+ }
+ }.into()
+}
--
2.47.2
© 2016 - 2025 Red Hat, Inc.