1use derive_syn_parse::Parse;
21use frame_support_procedural_tools::generate_access_from_frame_or_crate;
22use proc_macro::TokenStream;
23use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
24use quote::{quote, quote_spanned, ToTokens};
25use syn::{
26 parse::{Nothing, ParseStream},
27 parse_quote,
28 punctuated::Punctuated,
29 spanned::Spanned,
30 token::{Comma, Gt, Lt, PathSep},
31 Attribute, Error, Expr, ExprBlock, ExprCall, ExprPath, FnArg, Item, ItemFn, ItemMod, Pat, Path,
32 PathArguments, PathSegment, Result, ReturnType, Signature, Stmt, Token, Type, TypePath,
33 Visibility, WhereClause, WherePredicate,
34};
35
36mod keywords {
37 use syn::custom_keyword;
38
39 custom_keyword!(benchmark);
40 custom_keyword!(benchmarks);
41 custom_keyword!(block);
42 custom_keyword!(extra);
43 custom_keyword!(pov_mode);
44 custom_keyword!(extrinsic_call);
45 custom_keyword!(skip_meta);
46 custom_keyword!(BenchmarkError);
47 custom_keyword!(Result);
48 custom_keyword!(MaxEncodedLen);
49 custom_keyword!(Measured);
50 custom_keyword!(Ignored);
51
52 pub const BENCHMARK_TOKEN: &str = stringify!(benchmark);
53 pub const BENCHMARKS_TOKEN: &str = stringify!(benchmarks);
54}
55
56#[derive(Clone)]
58struct ParamDef {
59 name: String,
60 _typ: Type,
61 start: syn::GenericArgument,
62 end: syn::GenericArgument,
63}
64
65#[derive(Parse)]
67struct RangeArgs {
68 _lt_token: Lt,
69 start: syn::GenericArgument,
70 _comma: Comma,
71 end: syn::GenericArgument,
72 _trailing_comma: Option<Comma>,
73 _gt_token: Gt,
74}
75
76#[derive(Clone, Debug)]
77struct BenchmarkAttrs {
78 skip_meta: bool,
79 extra: bool,
80 pov_mode: Option<PovModeAttr>,
81}
82
83enum BenchmarkAttr {
85 Extra,
86 SkipMeta,
87 PoV(PovModeAttr),
89}
90
91impl syn::parse::Parse for PovModeAttr {
92 fn parse(input: ParseStream) -> Result<Self> {
93 let _pov: keywords::pov_mode = input.parse()?;
94 let _eq: Token![=] = input.parse()?;
95 let root = PovEstimationMode::parse(input)?;
96
97 let mut maybe_content = None;
98 let _ = || -> Result<()> {
99 let content;
100 syn::braced!(content in input);
101 maybe_content = Some(content);
102 Ok(())
103 }();
104
105 let per_key = match maybe_content {
106 Some(content) => {
107 let per_key = Punctuated::<PovModeKeyAttr, Token![,]>::parse_terminated(&content)?;
108 per_key.into_iter().collect()
109 },
110 None => Vec::new(),
111 };
112
113 Ok(Self { root, per_key })
114 }
115}
116
117impl syn::parse::Parse for BenchmarkAttr {
118 fn parse(input: ParseStream) -> Result<Self> {
119 let lookahead = input.lookahead1();
120 if lookahead.peek(keywords::extra) {
121 let _extra: keywords::extra = input.parse()?;
122 Ok(BenchmarkAttr::Extra)
123 } else if lookahead.peek(keywords::skip_meta) {
124 let _skip_meta: keywords::skip_meta = input.parse()?;
125 Ok(BenchmarkAttr::SkipMeta)
126 } else if lookahead.peek(keywords::pov_mode) {
127 PovModeAttr::parse(input).map(BenchmarkAttr::PoV)
128 } else {
129 Err(lookahead.error())
130 }
131 }
132}
133
134#[derive(Debug, Clone)]
136struct PovModeAttr {
137 root: PovEstimationMode,
139 per_key: Vec<PovModeKeyAttr>,
141}
142
143#[derive(Debug, Clone, derive_syn_parse::Parse)]
145struct PovModeKeyAttr {
146 key: Path,
148 _underscore: Token![:],
149 mode: PovEstimationMode,
151}
152
153#[derive(Debug, Eq, PartialEq, Clone, Copy)]
155pub enum PovEstimationMode {
156 MaxEncodedLen,
158 Measured,
160 Ignored,
162}
163
164impl syn::parse::Parse for PovEstimationMode {
165 fn parse(input: ParseStream) -> Result<Self> {
166 let lookahead = input.lookahead1();
167 if lookahead.peek(keywords::MaxEncodedLen) {
168 let _max_encoded_len: keywords::MaxEncodedLen = input.parse()?;
169 return Ok(PovEstimationMode::MaxEncodedLen)
170 } else if lookahead.peek(keywords::Measured) {
171 let _measured: keywords::Measured = input.parse()?;
172 return Ok(PovEstimationMode::Measured)
173 } else if lookahead.peek(keywords::Ignored) {
174 let _ignored: keywords::Ignored = input.parse()?;
175 return Ok(PovEstimationMode::Ignored)
176 } else {
177 return Err(lookahead.error())
178 }
179 }
180}
181
182impl ToString for PovEstimationMode {
183 fn to_string(&self) -> String {
184 match self {
185 PovEstimationMode::MaxEncodedLen => "MaxEncodedLen".into(),
186 PovEstimationMode::Measured => "Measured".into(),
187 PovEstimationMode::Ignored => "Ignored".into(),
188 }
189 }
190}
191
192impl quote::ToTokens for PovEstimationMode {
193 fn to_tokens(&self, tokens: &mut TokenStream2) {
194 match self {
195 PovEstimationMode::MaxEncodedLen => tokens.extend(quote!(MaxEncodedLen)),
196 PovEstimationMode::Measured => tokens.extend(quote!(Measured)),
197 PovEstimationMode::Ignored => tokens.extend(quote!(Ignored)),
198 }
199 }
200}
201
202impl syn::parse::Parse for BenchmarkAttrs {
203 fn parse(input: ParseStream) -> syn::Result<Self> {
204 let mut extra = false;
205 let mut skip_meta = false;
206 let mut pov_mode = None;
207 let args = Punctuated::<BenchmarkAttr, Token![,]>::parse_terminated(&input)?;
208
209 for arg in args.into_iter() {
210 match arg {
211 BenchmarkAttr::Extra => {
212 if extra {
213 return Err(input.error("`extra` can only be specified once"))
214 }
215 extra = true;
216 },
217 BenchmarkAttr::SkipMeta => {
218 if skip_meta {
219 return Err(input.error("`skip_meta` can only be specified once"))
220 }
221 skip_meta = true;
222 },
223 BenchmarkAttr::PoV(mode) => {
224 if pov_mode.is_some() {
225 return Err(input.error("`pov_mode` can only be specified once"))
226 }
227 pov_mode = Some(mode);
228 },
229 }
230 }
231 Ok(BenchmarkAttrs { extra, skip_meta, pov_mode })
232 }
233}
234
235#[derive(Clone)]
237enum BenchmarkCallDef {
238 ExtrinsicCall { origin: Expr, expr_call: ExprCall, attr_span: Span }, Block { block: ExprBlock, attr_span: Span }, }
241
242impl BenchmarkCallDef {
243 fn attr_span(&self) -> Span {
245 match self {
246 BenchmarkCallDef::ExtrinsicCall { origin: _, expr_call: _, attr_span } => *attr_span,
247 BenchmarkCallDef::Block { block: _, attr_span } => *attr_span,
248 }
249 }
250}
251
252#[derive(Clone)]
254struct BenchmarkDef {
255 params: Vec<ParamDef>,
256 setup_stmts: Vec<Stmt>,
257 call_def: BenchmarkCallDef,
258 verify_stmts: Vec<Stmt>,
259 last_stmt: Option<Stmt>,
260 fn_sig: Signature,
261 fn_vis: Visibility,
262 fn_attrs: Vec<Attribute>,
263}
264
265#[derive(Parse)]
267struct ResultDef {
268 _result_kw: keywords::Result,
269 _lt: Token![<],
270 unit: Type,
271 _comma: Comma,
272 e_type: TypePath,
273 _gt: Token![>],
274}
275
276fn ensure_valid_return_type(item_fn: &ItemFn) -> Result<()> {
278 if let ReturnType::Type(_, typ) = &item_fn.sig.output {
279 let non_unit = |span| return Err(Error::new(span, "expected `()`"));
280 let Type::Path(TypePath { path, qself: _ }) = &**typ else {
281 return Err(Error::new(
282 typ.span(),
283 "Only `Result<(), BenchmarkError>` or a blank return type is allowed on benchmark function definitions",
284 ))
285 };
286 let seg = path
287 .segments
288 .last()
289 .expect("to be parsed as a TypePath, it must have at least one segment; qed");
290 let res: ResultDef = syn::parse2(seg.to_token_stream())?;
291 let Type::Tuple(tup) = res.unit else { return non_unit(res.unit.span()) };
293 if !tup.elems.is_empty() {
294 return non_unit(tup.span())
295 }
296 let TypePath { path, qself: _ } = res.e_type;
297 let seg = path
298 .segments
299 .last()
300 .expect("to be parsed as a TypePath, it must have at least one segment; qed");
301 syn::parse2::<keywords::BenchmarkError>(seg.to_token_stream())?;
302 }
303 Ok(())
304}
305
306fn ensure_no_forbidden_variable_names(stmts: &[Stmt]) -> Result<()> {
308 const FORBIDDEN_VAR_NAMES: [&str; 2] = ["recording", "verify"];
309 for stmt in stmts {
310 let Stmt::Local(l) = stmt else { continue };
311 let Pat::Ident(ident) = &l.pat else { continue };
312 if FORBIDDEN_VAR_NAMES.contains(&ident.ident.to_string().as_str()) {
313 return Err(Error::new(
314 ident.span(),
315 format!(
316 "Variables {FORBIDDEN_VAR_NAMES:?} are reserved for benchmarking internals.",
317 ),
318 ));
319 }
320 }
321 Ok(())
322}
323
324fn parse_params(item_fn: &ItemFn) -> Result<Vec<ParamDef>> {
326 let mut params: Vec<ParamDef> = Vec::new();
327 for arg in &item_fn.sig.inputs {
328 let invalid_param = |span| {
329 return Err(Error::new(
330 span,
331 "Invalid benchmark function param. A valid example would be `x: Linear<5, 10>`.",
332 ))
333 };
334
335 let FnArg::Typed(arg) = arg else { return invalid_param(arg.span()) };
336 let Pat::Ident(ident) = &*arg.pat else { return invalid_param(arg.span()) };
337
338 let var_span = ident.span();
340 let invalid_param_name = || {
341 return Err(Error::new(
342 var_span,
343 "Benchmark parameter names must consist of a single lowercase letter (a-z) and no other characters.",
344 ));
345 };
346 let name = ident.ident.to_token_stream().to_string();
347 if name.len() > 1 {
348 return invalid_param_name()
349 };
350 let Some(name_char) = name.chars().next() else { return invalid_param_name() };
351 if !name_char.is_alphabetic() || !name_char.is_lowercase() {
352 return invalid_param_name()
353 }
354
355 let typ = &*arg.ty;
357 let Type::Path(tpath) = typ else { return invalid_param(typ.span()) };
358 let Some(segment) = tpath.path.segments.last() else { return invalid_param(typ.span()) };
359 let args = segment.arguments.to_token_stream().into();
360 let Ok(args) = syn::parse::<RangeArgs>(args) else { return invalid_param(typ.span()) };
361
362 params.push(ParamDef { name, _typ: typ.clone(), start: args.start, end: args.end });
363 }
364 Ok(params)
365}
366
367fn missing_call<T>(item_fn: &ItemFn) -> Result<T> {
369 return Err(Error::new(
370 item_fn.block.brace_token.span.join(),
371 "No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body."
372 ));
373}
374
375fn parse_call_def(item_fn: &ItemFn) -> Result<(usize, BenchmarkCallDef)> {
379 let call_defs = item_fn.block.stmts.iter().enumerate().filter_map(|(i, child)| {
381 if let Stmt::Expr(Expr::Call(expr_call), _semi) = child {
382 expr_call.attrs.iter().enumerate().find_map(|(k, attr)| {
384 let segment = attr.path().segments.last()?;
385 let _: keywords::extrinsic_call = syn::parse(segment.ident.to_token_stream().into()).ok()?;
386 let mut expr_call = expr_call.clone();
387
388 expr_call.attrs.remove(k);
390
391 let Some(origin) = expr_call.args.first().cloned() else {
393 return Some(Err(Error::new(expr_call.span(), "Single-item extrinsic calls must specify their origin as the first argument.")))
394 };
395
396 Some(Ok((i, BenchmarkCallDef::ExtrinsicCall { origin, expr_call, attr_span: attr.span() })))
397 })
398 } else if let Stmt::Expr(Expr::Block(block), _) = child {
399 block.attrs.iter().enumerate().find_map(|(k, attr)| {
401 let segment = attr.path().segments.last()?;
402 let _: keywords::block = syn::parse(segment.ident.to_token_stream().into()).ok()?;
403 let mut block = block.clone();
404
405 block.attrs.remove(k);
407
408 Some(Ok((i, BenchmarkCallDef::Block { block, attr_span: attr.span() })))
409 })
410 } else {
411 None
412 }
413 }).collect::<Result<Vec<_>>>()?;
414 Ok(match &call_defs[..] {
415 [(i, call_def)] => (*i, call_def.clone()), [] => return missing_call(item_fn),
417 _ =>
418 return Err(Error::new(
419 call_defs[1].1.attr_span(),
420 "Only one #[extrinsic_call] or #[block] attribute is allowed per benchmark.",
421 )),
422 })
423}
424
425impl BenchmarkDef {
426 pub fn from(item_fn: &ItemFn) -> Result<BenchmarkDef> {
428 let params = parse_params(item_fn)?;
429 ensure_valid_return_type(item_fn)?;
430 let (i, call_def) = parse_call_def(&item_fn)?;
431
432 let (verify_stmts, last_stmt) = match item_fn.sig.output {
433 ReturnType::Default =>
434 (Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()]), None),
436 ReturnType::Type(_, _) => {
437 if i + 1 >= item_fn.block.stmts.len() {
440 return Err(Error::new(
441 item_fn.block.span(),
442 "Benchmark `#[block]` or `#[extrinsic_call]` item cannot be the \
443 last statement of your benchmark function definition if you have \
444 defined a return type. You should return something compatible \
445 with Result<(), BenchmarkError> (i.e. `Ok(())`) as the last statement \
446 or change your signature to a blank return type.",
447 ))
448 }
449 let Some(stmt) = item_fn.block.stmts.last() else { return missing_call(item_fn) };
450 (
451 Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len() - 1]),
452 Some(stmt.clone()),
453 )
454 },
455 };
456
457 let setup_stmts = Vec::from(&item_fn.block.stmts[0..i]);
458 ensure_no_forbidden_variable_names(&setup_stmts)?;
459
460 Ok(BenchmarkDef {
461 params,
462 setup_stmts,
463 call_def,
464 verify_stmts,
465 last_stmt,
466 fn_sig: item_fn.sig.clone(),
467 fn_vis: item_fn.vis.clone(),
468 fn_attrs: item_fn.attrs.clone(),
469 })
470 }
471}
472
473pub fn benchmarks(
475 attrs: TokenStream,
476 tokens: TokenStream,
477 instance: bool,
478) -> syn::Result<TokenStream> {
479 let krate = generate_access_from_frame_or_crate("frame-benchmarking")?;
480 let module: ItemMod = syn::parse(tokens)?;
482 let mod_span = module.span();
483 let where_clause = match syn::parse::<Nothing>(attrs.clone()) {
484 Ok(_) =>
485 if instance {
486 quote!(T: Config<I>, I: 'static)
487 } else {
488 quote!(T: Config)
489 },
490 Err(_) => {
491 let mut where_clause_predicates = syn::parse::<WhereClause>(attrs)?.predicates;
492
493 if instance {
495 where_clause_predicates.push(syn::parse_str::<WherePredicate>("T: Config<I>")?);
496 where_clause_predicates.push(syn::parse_str::<WherePredicate>("I:'static")?);
497 } else {
498 where_clause_predicates.push(syn::parse_str::<WherePredicate>("T: Config")?);
499 }
500
501 where_clause_predicates.to_token_stream()
502 },
503 };
504 let mod_vis = module.vis;
505 let mod_name = module.ident;
506
507 let mod_attrs: Vec<&Attribute> = module
509 .attrs
510 .iter()
511 .filter(|attr| !attr.path().is_ident(keywords::BENCHMARKS_TOKEN))
512 .collect();
513
514 let mut benchmark_names: Vec<Ident> = Vec::new();
515 let mut extra_benchmark_names: Vec<Ident> = Vec::new();
516 let mut skip_meta_benchmark_names: Vec<Ident> = Vec::new();
517 let mut pov_modes = Vec::new();
519
520 let (_brace, mut content) =
521 module.content.ok_or(syn::Error::new(mod_span, "Module cannot be empty!"))?;
522
523 let benchmark_fn_metas = content.iter_mut().filter_map(|stmt| {
525 let Item::Fn(func) = stmt else { return None };
527
528 let benchmark_attr =
530 func.attrs.iter().find(|attr| attr.path().is_ident(keywords::BENCHMARK_TOKEN))?;
531
532 Some((benchmark_attr.clone(), func.clone(), stmt))
533 });
534
535 for (benchmark_attr, func, stmt) in benchmark_fn_metas {
537 let benchmark_def = BenchmarkDef::from(&func)?;
539
540 let name = &func.sig.ident;
542 benchmark_names.push(name.clone());
543
544 if benchmark_attr.meta.require_path_only().is_err() {
546 let benchmark_attrs: BenchmarkAttrs = benchmark_attr.parse_args()?;
548
549 if benchmark_attrs.extra {
551 extra_benchmark_names.push(name.clone());
552 } else if benchmark_attrs.skip_meta {
553 skip_meta_benchmark_names.push(name.clone());
554 }
555
556 if let Some(mode) = benchmark_attrs.pov_mode {
557 let mut modes = Vec::new();
558 let name = name.to_string();
560 let m = mode.root.to_string();
561 modes.push(quote!(("ALL".as_bytes().to_vec(), #m.as_bytes().to_vec())));
562
563 for attr in mode.per_key.iter() {
564 let key = attr.key.clone().into_token_stream().to_string().replace(" ", "");
566 let mode = attr.mode.to_string();
567 modes.push(quote!((#key.as_bytes().to_vec(), #mode.as_bytes().to_vec())));
568 }
569
570 pov_modes.push(
571 quote!((#name.as_bytes().to_vec(), #krate::__private::vec![#(#modes),*])),
572 );
573 }
574 }
575
576 let expanded = expand_benchmark(benchmark_def, name, instance, where_clause.clone());
578
579 *stmt = Item::Verbatim(expanded);
581 }
582
583 let type_use_generics = match instance {
585 false => quote!(T),
586 true => quote!(T, I),
587 };
588
589 let frame_system = generate_access_from_frame_or_crate("frame-system")?;
590
591 let benchmark_names_str: Vec<String> = benchmark_names.iter().map(|n| n.to_string()).collect();
593 let extra_benchmark_names_str: Vec<String> =
594 extra_benchmark_names.iter().map(|n| n.to_string()).collect();
595 let skip_meta_benchmark_names_str: Vec<String> =
596 skip_meta_benchmark_names.iter().map(|n| n.to_string()).collect();
597 let mut selected_benchmark_mappings: Vec<TokenStream2> = Vec::new();
598 let mut benchmarks_by_name_mappings: Vec<TokenStream2> = Vec::new();
599 let test_idents: Vec<Ident> = benchmark_names_str
600 .iter()
601 .map(|n| Ident::new(format!("test_benchmark_{}", n).as_str(), Span::call_site()))
602 .collect();
603 for i in 0..benchmark_names.len() {
604 let name_ident = &benchmark_names[i];
605 let name_str = &benchmark_names_str[i];
606 let test_ident = &test_idents[i];
607 selected_benchmark_mappings.push(quote!(#name_str => SelectedBenchmark::#name_ident));
608 benchmarks_by_name_mappings.push(quote!(#name_str => Self::#test_ident()))
609 }
610
611 let impl_test_function = content
612 .iter_mut()
613 .find_map(|item| {
614 let Item::Macro(item_macro) = item else {
615 return None;
616 };
617
618 if !item_macro
619 .mac
620 .path
621 .segments
622 .iter()
623 .any(|s| s.ident == "impl_benchmark_test_suite")
624 {
625 return None;
626 }
627
628 let tokens = item_macro.mac.tokens.clone();
629 *item = Item::Verbatim(quote! {});
630
631 Some(quote! {
632 impl_test_function!(
633 (#( {} #benchmark_names )*)
634 (#( #extra_benchmark_names )*)
635 (#( #skip_meta_benchmark_names )*)
636 #tokens
637 );
638 })
639 })
640 .unwrap_or(quote! {});
641
642 let res = quote! {
644 #(#mod_attrs)
645 *
646 #mod_vis mod #mod_name {
647 #(#content)
648 *
649
650 #[allow(non_camel_case_types)]
651 enum SelectedBenchmark {
652 #(#benchmark_names),
653 *
654 }
655
656 impl<#type_use_generics> #krate::BenchmarkingSetup<#type_use_generics> for SelectedBenchmark where #where_clause {
657 fn components(&self) -> #krate::__private::Vec<(#krate::BenchmarkParameter, u32, u32)> {
658 match self {
659 #(
660 Self::#benchmark_names => {
661 <#benchmark_names as #krate::BenchmarkingSetup<#type_use_generics>>::components(&#benchmark_names)
662 }
663 )
664 *
665 }
666 }
667
668 fn instance(
669 &self,
670 recording: &mut impl #krate::Recording,
671 components: &[(#krate::BenchmarkParameter, u32)],
672 verify: bool,
673 ) -> Result<(), #krate::BenchmarkError> {
674 match self {
675 #(
676 Self::#benchmark_names => {
677 <#benchmark_names as #krate::BenchmarkingSetup<
678 #type_use_generics
679 >>::instance(&#benchmark_names, recording, components, verify)
680 }
681 )
682 *
683 }
684 }
685 }
686 #[cfg(any(feature = "runtime-benchmarks", test))]
687 impl<#type_use_generics> #krate::Benchmarking for Pallet<#type_use_generics>
688 where T: #frame_system::Config,#where_clause
689 {
690 fn benchmarks(
691 extra: bool,
692 ) -> #krate::__private::Vec<#krate::BenchmarkMetadata> {
693 let mut all_names = #krate::__private::vec![
694 #(#benchmark_names_str),
695 *
696 ];
697 if !extra {
698 let extra = [
699 #(#extra_benchmark_names_str),
700 *
701 ];
702 all_names.retain(|x| !extra.contains(x));
703 }
704 let pov_modes:
705 #krate::__private::Vec<(
706 #krate::__private::Vec<u8>,
707 #krate::__private::Vec<(
708 #krate::__private::Vec<u8>,
709 #krate::__private::Vec<u8>
710 )>,
711 )> = #krate::__private::vec![
712 #( #pov_modes ),*
713 ];
714 all_names.into_iter().map(|benchmark| {
715 let selected_benchmark = match benchmark {
716 #(#selected_benchmark_mappings),
717 *,
718 _ => panic!("all benchmarks should be selectable")
719 };
720 let components = <SelectedBenchmark as #krate::BenchmarkingSetup<#type_use_generics>>::components(&selected_benchmark);
721 let name = benchmark.as_bytes().to_vec();
722 let modes = pov_modes.iter().find(|p| p.0 == name).map(|p| p.1.clone());
723
724 #krate::BenchmarkMetadata {
725 name: benchmark.as_bytes().to_vec(),
726 components,
727 pov_modes: modes.unwrap_or_default(),
728 }
729 }).collect::<#krate::__private::Vec<_>>()
730 }
731
732 fn run_benchmark(
733 extrinsic: &[u8],
734 c: &[(#krate::BenchmarkParameter, u32)],
735 whitelist: &[#krate::__private::TrackedStorageKey],
736 verify: bool,
737 internal_repeats: u32,
738 ) -> Result<#krate::__private::Vec<#krate::BenchmarkResult>, #krate::BenchmarkError> {
739 #krate::benchmarking::wipe_db();
740 let extrinsic = #krate::__private::str::from_utf8(extrinsic).map_err(|_| "`extrinsic` is not a valid utf-8 string!")?;
741 let selected_benchmark = match extrinsic {
742 #(#selected_benchmark_mappings),
743 *,
744 _ => return Err("Could not find extrinsic.".into()),
745 };
746 let mut whitelist = whitelist.to_vec();
747 let whitelisted_caller_key = <#frame_system::Account<
748 T,
749 > as #krate::__private::storage::StorageMap<_, _,>>::hashed_key_for(
750 #krate::whitelisted_caller::<T::AccountId>()
751 );
752 whitelist.push(whitelisted_caller_key.into());
753 let transactional_layer_key = #krate::__private::TrackedStorageKey::new(
754 #krate::__private::storage::transactional::TRANSACTION_LEVEL_KEY.into(),
755 );
756 whitelist.push(transactional_layer_key);
757 let extrinsic_index = #krate::__private::TrackedStorageKey::new(
759 #krate::__private::well_known_keys::EXTRINSIC_INDEX.into()
760 );
761 whitelist.push(extrinsic_index);
762 let intrablock_entropy = #krate::__private::TrackedStorageKey::new(
764 #krate::__private::well_known_keys::INTRABLOCK_ENTROPY.into()
765 );
766 whitelist.push(intrablock_entropy);
767
768 #krate::benchmarking::set_whitelist(whitelist.clone());
769 let mut results: #krate::__private::Vec<#krate::BenchmarkResult> = #krate::__private::Vec::new();
770
771 let on_before_start = || {
772 if #krate::__private::Zero::is_zero(&#frame_system::Pallet::<T>::block_number()) {
774 #frame_system::Pallet::<T>::set_block_number(1u32.into());
775 }
776
777 #krate::benchmarking::commit_db();
780
781 for key in &whitelist {
784 #krate::__private::storage::unhashed::get_raw(&key.key);
785 }
786
787 #krate::benchmarking::reset_read_write_count();
789 };
790
791 for _ in 0 .. internal_repeats.max(1) {
793 #krate::__private::defer!(#krate::benchmarking::wipe_db());
795
796 #krate::__private::log::trace!(
798 target: "benchmark",
799 "Start Benchmark: {} ({:?})",
800 extrinsic,
801 c
802 );
803
804 let mut recording = #krate::BenchmarkRecording::new(&on_before_start);
805 <SelectedBenchmark as #krate::BenchmarkingSetup<#type_use_generics>>::instance(&selected_benchmark, &mut recording, c, verify)?;
806
807 let elapsed_extrinsic = recording.elapsed_extrinsic().expect("elapsed time should be recorded");
809 let diff_pov = recording.diff_pov().unwrap_or_default();
810
811 #krate::benchmarking::commit_db();
813 #krate::__private::log::trace!(
814 target: "benchmark",
815 "End Benchmark: {} ns", elapsed_extrinsic
816 );
817 let read_write_count = #krate::benchmarking::read_write_count();
818 #krate::__private::log::trace!(
819 target: "benchmark",
820 "Read/Write Count {:?}", read_write_count
821 );
822
823 let start_storage_root = #krate::current_time();
825 #krate::__private::storage_root(#krate::__private::StateVersion::V1);
826 let finish_storage_root = #krate::current_time();
827 let elapsed_storage_root = finish_storage_root - start_storage_root;
828
829 let skip_meta = [ #(#skip_meta_benchmark_names_str),* ];
830 let read_and_written_keys = if skip_meta.contains(&extrinsic) {
831 #krate::__private::vec![(b"Skipped Metadata".to_vec(), 0, 0, false)]
832 } else {
833 #krate::benchmarking::get_read_and_written_keys()
834 };
835
836 results.push(#krate::BenchmarkResult {
837 components: c.to_vec(),
838 extrinsic_time: elapsed_extrinsic,
839 storage_root_time: elapsed_storage_root,
840 reads: read_write_count.0,
841 repeat_reads: read_write_count.1,
842 writes: read_write_count.2,
843 repeat_writes: read_write_count.3,
844 proof_size: diff_pov,
845 keys: read_and_written_keys,
846 });
847 }
848
849 return Ok(results);
850 }
851 }
852
853 #[cfg(test)]
854 impl<#type_use_generics> Pallet<#type_use_generics> where T: #frame_system::Config, #where_clause {
855 #[allow(unused)]
865 fn test_bench_by_name(name: &[u8]) -> Result<(), #krate::BenchmarkError> {
866 let name = #krate::__private::str::from_utf8(name)
867 .map_err(|_| -> #krate::BenchmarkError { "`name` is not a valid utf8 string!".into() })?;
868 match name {
869 #(#benchmarks_by_name_mappings),
870 *,
871 _ => Err("Could not find test for requested benchmark.".into()),
872 }
873 }
874 }
875
876 #impl_test_function
877 }
878 #mod_vis use #mod_name::*;
879 };
880 Ok(res.into())
881}
882
883struct UnrolledParams {
886 param_ranges: Vec<TokenStream2>,
887 param_names: Vec<TokenStream2>,
888}
889
890impl UnrolledParams {
891 fn from(params: &Vec<ParamDef>) -> UnrolledParams {
893 let param_ranges: Vec<TokenStream2> = params
894 .iter()
895 .map(|p| {
896 let name = Ident::new(&p.name, Span::call_site());
897 let start = &p.start;
898 let end = &p.end;
899 quote!(#name, #start, #end)
900 })
901 .collect();
902 let param_names: Vec<TokenStream2> = params
903 .iter()
904 .map(|p| {
905 let name = Ident::new(&p.name, Span::call_site());
906 quote!(#name)
907 })
908 .collect();
909 UnrolledParams { param_ranges, param_names }
910 }
911}
912
913fn expand_benchmark(
915 benchmark_def: BenchmarkDef,
916 name: &Ident,
917 is_instance: bool,
918 where_clause: TokenStream2,
919) -> TokenStream2 {
920 let krate = match generate_access_from_frame_or_crate("frame-benchmarking") {
922 Ok(ident) => ident,
923 Err(err) => return err.to_compile_error().into(),
924 };
925 let frame_system = match generate_access_from_frame_or_crate("frame-system") {
926 Ok(path) => path,
927 Err(err) => return err.to_compile_error().into(),
928 };
929 let codec = quote!(#krate::__private::codec);
930 let traits = quote!(#krate::__private::traits);
931 let setup_stmts = benchmark_def.setup_stmts;
932 let verify_stmts = benchmark_def.verify_stmts;
933 let last_stmt = benchmark_def.last_stmt;
934 let test_ident =
935 Ident::new(format!("test_benchmark_{}", name.to_string()).as_str(), Span::call_site());
936
937 let unrolled = UnrolledParams::from(&benchmark_def.params);
939 let param_names = unrolled.param_names;
940 let param_ranges = unrolled.param_ranges;
941
942 let type_use_generics = match is_instance {
943 false => quote!(T),
944 true => quote!(T, I),
945 };
946
947 let (pre_call, post_call, fn_call_body) = match &benchmark_def.call_def {
949 BenchmarkCallDef::ExtrinsicCall { origin, expr_call, attr_span: _ } => {
950 let mut expr_call = expr_call.clone();
951
952 let mut final_args = Punctuated::<Expr, Comma>::new();
954 let args: Vec<&Expr> = expr_call.args.iter().collect();
955 for arg in &args[1..] {
956 final_args.push((*(*arg)).clone());
957 }
958 expr_call.args = final_args;
959
960 let origin = match origin {
961 Expr::Cast(t) => {
962 let ty = t.ty.clone();
963 quote_spanned! { origin.span() =>
964 <<T as #frame_system::Config>::RuntimeOrigin as From<#ty>>::from(#origin);
965 }
966 },
967 _ => quote_spanned! { origin.span() =>
968 Into::<<T as #frame_system::Config>::RuntimeOrigin>::into(#origin);
969 },
970 };
971
972 let expr_span = expr_call.span();
974 let call_err = || {
975 syn::Error::new(expr_span, "Extrinsic call must be a function call or `_`")
976 .to_compile_error()
977 };
978 let call_name = match *expr_call.func {
979 Expr::Path(expr_path) => {
980 let Some(segment) = expr_path.path.segments.last() else { return call_err() };
982 segment.ident.to_string()
983 },
984 Expr::Infer(_) => {
985 name.to_string()
988 },
989 _ => return call_err(),
990 };
991
992 let call_name = format!("new_call_variant_{}", call_name);
994 let mut punct: Punctuated<PathSegment, PathSep> = Punctuated::new();
995 punct.push(PathSegment {
996 arguments: PathArguments::None,
997 ident: Ident::new(call_name.as_str(), Span::call_site()),
998 });
999 *expr_call.func = Expr::Path(ExprPath {
1000 attrs: vec![],
1001 qself: None,
1002 path: Path { leading_colon: None, segments: punct },
1003 });
1004 let pre_call = quote! {
1005 let __call = Call::<#type_use_generics>::#expr_call;
1006 let __benchmarked_call_encoded = #codec::Encode::encode(&__call);
1007 };
1008 let post_call = quote! {
1009 let __call_decoded = <Call<#type_use_generics> as #codec::Decode>
1010 ::decode(&mut &__benchmarked_call_encoded[..])
1011 .expect("call is encoded above, encoding must be correct");
1012 #[allow(clippy::useless_conversion)]
1013 let __origin = #origin;
1014 <Call<#type_use_generics> as #traits::UnfilteredDispatchable>::dispatch_bypass_filter(
1015 __call_decoded,
1016 __origin,
1017 )
1018 };
1019 (
1020 pre_call.clone(),
1022 quote!(#post_call?;),
1023 quote! {
1024 #pre_call
1025 #post_call.unwrap();
1026 },
1027 )
1028 },
1029 BenchmarkCallDef::Block { block, attr_span: _ } =>
1030 (quote!(), quote!(#block), quote!(#block)),
1031 };
1032
1033 let vis = benchmark_def.fn_vis;
1034
1035 let fn_attrs = benchmark_def
1037 .fn_attrs
1038 .iter()
1039 .filter(|attr| !attr.path().is_ident(keywords::BENCHMARK_TOKEN));
1040
1041 let mut sig = benchmark_def.fn_sig;
1046 sig.generics = parse_quote!(<#type_use_generics>);
1047 sig.generics.where_clause = parse_quote!(where #where_clause);
1048 sig.ident =
1049 Ident::new(format!("_{}", name.to_token_stream().to_string()).as_str(), Span::call_site());
1050 let mut fn_param_inputs: Vec<TokenStream2> =
1051 param_names.iter().map(|name| quote!(#name: u32)).collect();
1052 fn_param_inputs.push(quote!(verify: bool));
1053 sig.inputs = parse_quote!(#(#fn_param_inputs),*);
1054
1055 let impl_last_stmt = match &last_stmt {
1057 Some(stmt) => quote!(#stmt),
1058 None => quote!(Ok(())),
1059 };
1060 let fn_attrs_clone = fn_attrs.clone();
1061
1062 let fn_def = quote! {
1063 #(
1064 #fn_attrs_clone
1065 )*
1066 #vis #sig {
1067 #(
1068 #setup_stmts
1069 )*
1070 #fn_call_body
1071 if verify {
1072 #(
1073 #verify_stmts
1074 )*
1075 }
1076 #last_stmt
1077 }
1078 };
1079
1080 let res = quote! {
1082 #fn_def
1084
1085 #[allow(non_camel_case_types)]
1086 #(
1087 #fn_attrs
1088 )*
1089 struct #name;
1090
1091 #[allow(unused_variables)]
1092 impl<#type_use_generics> #krate::BenchmarkingSetup<#type_use_generics>
1093 for #name where #where_clause {
1094 fn components(&self) -> #krate::__private::Vec<(#krate::BenchmarkParameter, u32, u32)> {
1095 #krate::__private::vec! [
1096 #(
1097 (#krate::BenchmarkParameter::#param_ranges)
1098 ),*
1099 ]
1100 }
1101
1102 fn instance(
1103 &self,
1104 recording: &mut impl #krate::Recording,
1105 components: &[(#krate::BenchmarkParameter, u32)],
1106 verify: bool
1107 ) -> Result<(), #krate::BenchmarkError> {
1108 #(
1109 let #param_names = components.iter()
1111 .find(|&c| c.0 == #krate::BenchmarkParameter::#param_names)
1112 .ok_or("Could not find component during benchmark preparation.")?
1113 .1;
1114 )*
1115
1116 #(
1118 #setup_stmts
1119 )*
1120 #pre_call
1121 recording.start();
1122 #post_call
1123 recording.stop();
1124 if verify {
1125 #(
1126 #verify_stmts
1127 )*
1128 }
1129 #impl_last_stmt
1130 }
1131 }
1132
1133 #[cfg(test)]
1134 impl<#type_use_generics> Pallet<#type_use_generics> where T: #frame_system::Config, #where_clause {
1135 #[allow(unused)]
1136 fn #test_ident() -> Result<(), #krate::BenchmarkError> {
1137 let selected_benchmark = SelectedBenchmark::#name;
1138 let components = <
1139 SelectedBenchmark as #krate::BenchmarkingSetup<T, _>
1140 >::components(&selected_benchmark);
1141 let execute_benchmark = |
1142 c: #krate::__private::Vec<(#krate::BenchmarkParameter, u32)>
1143 | -> Result<(), #krate::BenchmarkError> {
1144 #krate::__private::defer!(#krate::benchmarking::wipe_db());
1146
1147 let on_before_start = || {
1148 if #krate::__private::Zero::is_zero(&#frame_system::Pallet::<T>::block_number()) {
1150 #frame_system::Pallet::<T>::set_block_number(1u32.into());
1151 }
1152 };
1153
1154 <SelectedBenchmark as #krate::BenchmarkingSetup<T, _>>::test_instance(&selected_benchmark, &c, &on_before_start)
1156 };
1157
1158 if components.is_empty() {
1159 execute_benchmark(Default::default())?;
1160 } else {
1161 let num_values: u32 = if let Ok(ev) = std::env::var("VALUES_PER_COMPONENT") {
1162 ev.parse().map_err(|_| {
1163 #krate::BenchmarkError::Stop(
1164 "Could not parse env var `VALUES_PER_COMPONENT` as u32."
1165 )
1166 })?
1167 } else {
1168 6
1169 };
1170
1171 if num_values < 2 {
1172 return Err("`VALUES_PER_COMPONENT` must be at least 2".into());
1173 }
1174
1175 for (name, low, high) in components.clone().into_iter() {
1176 if high < low {
1180 return Err("The start of a `ParamRange` must be less than or equal to the end".into());
1181 }
1182
1183 let mut values = #krate::__private::vec![low];
1184 let diff = (high - low).min(num_values - 1);
1185 let slope = (high - low) as f32 / diff as f32;
1186
1187 for i in 1..=diff {
1188 let value = ((low as f32 + slope * i as f32) as u32)
1189 .clamp(low, high);
1190 values.push(value);
1191 }
1192
1193 for component_value in values {
1194 let c: #krate::__private::Vec<(#krate::BenchmarkParameter, u32)> = components
1196 .iter()
1197 .map(|(n, _, h)|
1198 if *n == name {
1199 (*n, component_value)
1200 } else {
1201 (*n, *h)
1202 }
1203 )
1204 .collect();
1205
1206 execute_benchmark(c)?;
1207 }
1208 }
1209 }
1210 return Ok(());
1211 }
1212 }
1213 };
1214 res
1215}