diff --git a/Cargo.lock b/Cargo.lock index b86f719..5550d44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,6 +106,7 @@ dependencies = [ "sbi-rt", "spin", "static_assertions", + "tracer", "uapi", "uart_16550", "utils", @@ -246,6 +247,14 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tracer" +version = "0.1.0" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "uapi" version = "0.1.0" diff --git a/lib/tracer/Cargo.toml b/lib/tracer/Cargo.toml new file mode 100644 index 0000000..5a70800 --- /dev/null +++ b/lib/tracer/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "tracer" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +syn = { version = "2.0", features = ["full"] } +quote = "1.0" diff --git a/lib/tracer/src/lib.rs b/lib/tracer/src/lib.rs new file mode 100644 index 0000000..1c1029c --- /dev/null +++ b/lib/tracer/src/lib.rs @@ -0,0 +1,60 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; +use quote::quote; +use syn::{ + parse::Parser, parse_macro_input, punctuated::Punctuated, Expr, ExprLit, ExprPath, ItemFn, Lit, MetaNameValue, Token, +}; + +#[proc_macro_attribute] +pub fn call_count(attr: TokenStream, item: TokenStream) -> TokenStream { + let args = Punctuated::::parse_terminated.parse(attr).unwrap(); + + let mut debug_print = false; + let mut callback = vec![]; + + for arg in args { + if !arg.path.is_ident("log") && !arg.path.is_ident("callback") { + panic!("Unknown attribute: {:?}.", arg.path.get_ident()); + } + + if arg.path.is_ident("log") { + if let Expr::Lit(ExprLit { lit: Lit::Bool(b), .. }) = &arg.value { + debug_print = b.value; + } else { + panic!("Unsupported log option provided."); + } + } else if arg.path.is_ident("callback") { + if let Expr::Path(ExprPath { path, .. }) = arg.value { + let ident = path.get_ident().expect("Invalid callback function provided."); + callback.push(ident.clone()); + } else { + panic!("Unsupported callback option provided."); + } + } + } + + let input = parse_macro_input!(item as ItemFn); + let fn_name = &input.sig.ident; + let fn_block = &input.block; + let fn_vis = &input.vis; + let fn_sig = &input.sig; + + let call_callback = callback.iter().map(|ident| { + quote! { + if #debug_print { log::debug!("[tracer][callback] invoking {}", stringify!(#ident)) } + #ident(); + } + }); + + let expanded = quote! { + #fn_vis #fn_sig { + if #debug_print { log::debug!("[tracer] tracing {}", stringify!(#fn_name)) } + { #(#call_callback)* } + if #debug_print { log::debug!("[tracer][function] invoking {}", stringify!(#fn_name)) } + (|| #fn_block)() + } + }; + + TokenStream::from(expanded) +}