radix_engine_profiling_derive/
lib.rs1use proc_macro::TokenStream;
2#[cfg(feature = "resource_tracker")]
3use proc_macro2::Span;
4#[cfg(feature = "resource_tracker")]
5use quote::{quote, ToTokens};
6#[cfg(feature = "resource_tracker")]
7use syn::{
8 parse::{Parse, Parser},
9 parse_quote, FnArg,
10 Pat::Ident,
11 Type::{Path, Reference},
12};
13
14#[cfg(not(feature = "resource_tracker"))]
16#[proc_macro_attribute]
17pub fn trace_resources(_attr: TokenStream, input: TokenStream) -> TokenStream {
18 input
19}
20
21#[cfg(all(target_family = "unix", feature = "resource_tracker"))]
55#[proc_macro_attribute]
56pub fn trace_resources(attr: TokenStream, input: TokenStream) -> TokenStream {
57 let args_parsed =
58 syn::punctuated::Punctuated::<syn::ExprAssign, syn::Token![,]>::parse_terminated
59 .parse(attr.clone())
60 .expect("Wrong arguments passed");
61
62 let mut log_ident: Vec<syn::Ident> = Vec::new();
63 let mut log_expr_quote = Vec::new();
64 let mut log_ident_after: Vec<syn::Ident> = Vec::new();
65 let mut log_expr_after_quote = Vec::new();
66
67 let mut additional_items: Vec<proc_macro2::TokenStream> = Vec::new();
68 let mut additional_items_after: Vec<proc_macro2::TokenStream> = Vec::new();
69
70 for (idx, i) in args_parsed.into_iter().enumerate() {
72 if let Ok(left_ident) = syn::parse::<syn::Ident>(i.left.as_ref().to_token_stream().into()) {
73 let left = left_ident.to_string().clone();
74 let right_arg = i.right.as_ref().to_token_stream();
75 match left.as_str() {
76 "info" => {
77 if let Ok(right) = syn::parse::<syn::LitStr>(right_arg.into()) {
78 let info_value = right.value();
79 additional_items.push( quote!{ OutputParam { name: "info".into(), value: OutputParamValue::Literal(#info_value.into())} } );
80 }
81 }
82 "log" => {
83 if let Ok(right) = syn::parse::<syn::Ident>(right_arg.clone().into()) {
84 log_ident.push(right);
86 } else if let Ok(right) =
87 syn::parse::<syn::ExprMethodCall>(right_arg.clone().into())
88 {
89 let var = syn::Ident::new(&format!("arg{}", idx), Span::call_site());
91 log_expr_quote.push(quote! { let #var = #right; });
92 let var_s = var.to_string();
93 additional_items.push( quote!{ OutputParam { name: #var_s.into(), value: OutputParamValue::Literal(format!("{:?}", #var).into())} } );
94 } else if let Ok(right) = syn::parse::<syn::ExprBlock>(right_arg.clone().into())
95 {
96 let var = syn::Ident::new(&format!("arg{}", idx), Span::call_site());
98 log_expr_quote.push(quote! { let #var = #right; });
99 let var_s = var.to_string();
100 additional_items.push( quote!{ OutputParam { name: #var_s.into(), value: OutputParamValue::Literal(format!("{:?}", #var).into())} } );
101 } else if let Ok(right) = syn::parse::<syn::ExprUnary>(right_arg.into()) {
102 let var_name = match right.expr.as_ref() {
104 syn::Expr::Path(p) => p.path.segments.last().unwrap().ident.clone(),
105 _ => panic!("Not supported path expression: {:?}", right.expr),
106 };
107 let var =
108 syn::Ident::new(&format!("{}_deref", var_name), Span::call_site());
109 log_expr_quote.push(quote! { let #var = #right; });
110 let var_s = var_name.to_string();
111 additional_items.push( quote!{ OutputParam { name: #var_s.into(), value: OutputParamValue::Literal(format!("{:?}", #var).into())} } );
112 } else {
113 panic!("Wrong log value type: {:?}", i.right.as_ref())
114 }
115 }
116 "log_after" => {
117 if let Ok(right) = syn::parse::<syn::Ident>(right_arg.clone().into()) {
118 if right == "ret" {
120 additional_items_after.push( quote!{ OutputParam { name: "ret".into(), value: OutputParamValue::Literal(format!("{:?}", ret).into())} } );
122 } else {
123 log_ident_after.push(right);
124 }
125 } else if let Ok(right) =
126 syn::parse::<syn::ExprMethodCall>(right_arg.clone().into())
127 {
128 let var = syn::Ident::new(&format!("arg{}", idx), Span::call_site());
130 log_expr_after_quote.push(quote! { let #var = #right; });
131 let var_s = var.to_string();
132 additional_items_after.push( quote!{ OutputParam { name: #var_s.into(), value: OutputParamValue::Literal(format!("{:?}", #var).into())} } );
133 } else if let Ok(right) = syn::parse::<syn::ExprBlock>(right_arg.clone().into())
134 {
135 let var = syn::Ident::new(&format!("arg{}", idx), Span::call_site());
137 log_expr_after_quote.push(quote! { let #var = #right; });
138 let var_s = var.to_string();
139 additional_items_after.push( quote!{ OutputParam { name: #var_s.into(), value: OutputParamValue::Literal(format!("{:?}", #var).into())} } );
140 } else if let Ok(right) = syn::parse::<syn::ExprUnary>(right_arg.into()) {
141 let var_name = match right.expr.as_ref() {
143 syn::Expr::Path(p) => p.path.segments.last().unwrap().ident.clone(),
144 _ => panic!("Not supported path expression: {:?}", right.expr),
145 };
146 let var =
147 syn::Ident::new(&format!("{}_deref", var_name), Span::call_site());
148 log_expr_quote.push(quote! { let #var = #right; });
149 let var_s = var_name.to_string();
150 additional_items_after.push( quote!{ OutputParam { name: #var_s.into(), value: OutputParamValue::Literal(format!("{:?}", #var).into())} } );
151 } else {
152 panic!("Wrong log_after value type: {:?}", i.right.as_ref())
153 }
154 }
155 s => panic!("Wrong argument: {}", s),
156 }
157 }
158 }
159
160 let output = if let Ok(mut item) = syn::Item::parse.parse(input.clone()) {
162 match item {
163 syn::Item::Fn(ref mut item_fn) => {
164 let mut arg_evaluate = quote! {};
165 for i in log_expr_quote {
166 arg_evaluate = quote! { #arg_evaluate #i };
167 }
168 let mut arg_evaluate_after = quote! {};
169 for i in log_expr_after_quote {
170 arg_evaluate_after = quote! { #arg_evaluate_after #i };
171 }
172
173 let args_quote_array =
174 create_params(&log_ident, item_fn.clone(), &additional_items);
175 let args_after_quote_array =
176 create_params(&log_ident_after, item_fn.clone(), &additional_items_after);
177 let original_block = &mut item_fn.block;
178 let fn_signature = item_fn.sig.ident.to_string();
179
180 item_fn.block = Box::new(parse_quote! {{
182 extern crate radix_engine_profiling;
183 use radix_engine_profiling::{QEMU_PLUGIN, data_analyzer::{OutputParam, OutputParamValue}};
184 #arg_evaluate;
185 #args_quote_array;
186 QEMU_PLUGIN.with(|v| {
187 v.borrow_mut().start_counting(#fn_signature, qemu_call_args.as_slice());
188 });
189 let ret = #original_block;
190 QEMU_PLUGIN.with(|v| {
191 v.borrow_mut().stop_counting(#fn_signature, qemu_call_args.as_slice());
192 });
193 #arg_evaluate_after;
194 #args_after_quote_array;
195 ret
196 }});
197 item.into_token_stream()
198 }
199 _ => syn::Error::new_spanned(item, "#[trace_resources] is not supported for this item")
200 .to_compile_error(),
201 }
202 } else {
203 let input2 = proc_macro2::TokenStream::from(input);
204 syn::Error::new_spanned(input2, "expected `fn` item").to_compile_error()
205 };
206
207 output.into()
208}
209
210#[cfg(all(target_family = "unix", feature = "resource_tracker"))]
212fn create_params(
213 ident: &Vec<syn::Ident>,
214 fn_sig: syn::ItemFn,
215 additional_items: &Vec<proc_macro2::TokenStream>,
216) -> proc_macro2::TokenStream {
217 let mut args_quote = Vec::new();
218 for arg_ident in ident {
219 let mut arg_found = false;
220 for fn_arg in fn_sig.sig.inputs.iter() {
221 match fn_arg {
222 FnArg::Typed(v) => {
223 match v.pat.as_ref() {
224 Ident(pi) => {
225 if pi.ident == *arg_ident {
226 arg_found = true;
227 let var_name = pi.ident.to_string();
228 match v.ty.as_ref() {
229 Path(tp) => {
230 if let Some(p) = tp.path.segments.last() {
232 if p.ident == "u8"
233 || p.ident == "u16"
234 || p.ident == "u32"
235 || p.ident == "u64"
236 {
237 args_quote.push( quote!{ OutputParam { name: #var_name.into(), value: OutputParamValue::NumberU64( #pi as u64 )} } );
238 break;
239 } else if p.ident == "i8"
240 || p.ident == "i16"
241 || p.ident == "i32"
242 || p.ident == "i64"
243 {
244 args_quote.push( quote!{ OutputParam { name: #var_name.into(), value: OutputParamValue::NumberI64( #pi as i64 )} } );
245 break;
246 } else if p.ident == "bool" {
247 args_quote.push( quote!{ OutputParam { name: #var_name.into(), value: OutputParamValue::NumberU64( #pi as u64 )} } );
248 break;
249 } else {
250 args_quote.push( quote!{ OutputParam { name: #var_name.into(), value: OutputParamValue::Literal(format!("{:?}", #pi).into())} } );
251 break;
252 }
253 }
254 }
255 Reference(tr) => {
256 match tr.elem.as_ref() {
258 Path(tp) => {
259 if let Some(p) = tp.path.segments.last() {
260 if p.ident == "str" {
261 args_quote.push( quote!{ OutputParam { name: #var_name.into(), value: OutputParamValue::Literal(#pi.into())} } );
262 break;
263 } else {
264 panic!(
265 "Not supported arg type: {}",
266 p.ident
267 );
268 }
269 }
270 }
271 _ => (),
272 }
273 }
274 _ => (),
275 }
276 }
277 }
278 _ => (),
279 }
280 }
281 _ => (),
282 }
283 }
284 if !arg_found {
285 panic!("Arg: {} not found", arg_ident.to_string());
286 }
287 }
288
289 let mut args_quote_len = args_quote.len();
290 let mut args_quote_array = quote! {};
291 if args_quote_len > 0 {
292 let q0 = &args_quote[0];
293 args_quote_array = quote! { #q0 };
294 if args_quote_len > 1 {
295 for i in 1..args_quote_len {
296 let q1 = &args_quote[i];
297 args_quote_array = quote! { #args_quote_array, #q1 };
298 }
299 }
300 for i in additional_items {
301 args_quote_len += 1;
302 args_quote_array = quote! { #args_quote_array, #i };
303 }
304 } else if additional_items.len() > 0 {
305 let q0 = &additional_items[0];
306 args_quote_array = quote! { #q0 };
307 if additional_items.len() > 1 {
308 for i in 1..additional_items.len() {
309 let q1 = &additional_items[i];
310 args_quote_array = quote! { #args_quote_array, #q1 };
311 }
312 }
313 args_quote_len = additional_items.len();
314 }
315
316 if args_quote_len > 0 {
317 args_quote_array =
318 quote! { let qemu_call_args: [OutputParam; #args_quote_len] = [#args_quote_array] };
319 } else {
320 args_quote_array = quote! { let qemu_call_args: [OutputParam; 0] = []; };
321 }
322
323 args_quote_array
324}