use proc_macro::TokenStream;
use quote::quote;
use syn::{ItemFn, parse_macro_input};
#[proc_macro_attribute]
pub fn instrument(attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as ItemFn);
if !attr.is_empty() {
return syn::Error::new_spanned(
proc_macro2::TokenStream::from(attr),
"#[stack_debug::instrument] does not take any arguments",
)
.to_compile_error()
.into();
}
let block = input.block;
let sig = input.sig;
let vis = input.vis;
let attrs = input.attrs;
#[cfg(feature = "tracing")]
let expanded = quote! {
#(#attrs)*
#[inline(never)]
#[tracing::instrument(skip_all)]
#vis #sig {
let rbp: usize;
let rsp: usize;
unsafe {
#[cfg(target_arch = "x86_64")]
std::arch::asm!(
"mov {}, rbp", "mov {}, rsp", out(reg) rbp,
out(reg) rsp,
);
}
#[cfg(debug_assertions)]
let frame_size = rbp - rsp - 1520; #[cfg(not(debug_assertions))]
let frame_size = rbp - rsp - 224; tracing::info!("stack frame size: {frame_size}");
#block
}
};
#[cfg(not(feature = "tracing"))]
let expanded = {
let function_name = sig.ident.to_string();
quote! {
#(#attrs)*
#[inline(never)]
#vis #sig {
let rbp: usize;
let rsp: usize;
unsafe {
#[cfg(target_arch = "x86_64")]
std::arch::asm!(
"mov {}, rbp", "mov {}, rsp", out(reg) rbp,
out(reg) rsp,
);
}
#[cfg(debug_assertions)]
let frame_size = rbp - rsp - 128; #[cfg(not(debug_assertions))]
let frame_size = rbp - rsp - 80; println!("{}::{}(): stack frame size: {frame_size}", module_path!(), #function_name);
#block
}
}
};
expanded.into()
}