near_performance_metrics_macros/
lib.rs

1extern crate proc_macro;
2extern crate syn;
3
4use proc_macro::TokenStream;
5use quote::quote;
6
7/// Wrap the method call with near_performance_metrics::stats::measure_performance function.
8///
9/// This derive can be used to provide performance metrics to method calls with Actors. Currently
10/// we print performance stats per thread every minute, and we print a warning whenever a function
11/// call exceeds took more than given time limit. It should have no performance impact unless
12/// `performance_stats` feature is enabled.
13///
14/// This function assumes it wraps around a method with `&mut self, msg: NetworkClientMessages,
15/// ctx: &mut Self::Context<Self>` as arguments. There is currently a requirement that the second
16/// argument is called msg.
17///
18/// # Examples
19/// ```ignore
20///
21/// pub enum MyMessage {
22///      ExampleMessage()
23/// }
24///
25/// pub struct ExampleResponse {}
26/// use actix::Context;
27/// impl Handler<NetworkClientMessages> for ClientActor {
28///    type Result = ExampleResponse;
29///
30///    #[perf]
31///    fn handle(&mut self, msg: NetworkClientMessages, ctx: &mut Self::Context<Self>) -> Self::Result {
32///        ExampleResponse{}
33///    }
34/// }
35/// ```
36#[proc_macro_attribute]
37pub fn perf(_attr: TokenStream, item: TokenStream) -> TokenStream {
38    perf_internal(_attr, item, false)
39}
40
41/// Wrap the method call with near_performance_metrics::stats::measure_performance_with_debug function.
42///
43/// This derive can be used to provide performance metrics to method calls with Actors. Currently
44/// we print performance stats per thread every minute, and we print a warning whenever a function
45/// call exceeds took more than given time limit. It should have no performance impact unless
46/// `performance_stats` feature is enabled. In addition to prints provided by `perf`,
47/// `perf_with_debug` prints enum variant type of the message.
48///
49/// This function assumes it wraps around a method with `&mut self, msg: NetworkClientMessages,
50/// ctx: &mut Self::Context<Self>` as arguments. There is currently a requirement that the second
51/// argument is called msg. There is an assumption that the argument called `msg` is an enum, which
52/// has `#[derive(AsStaticStr)]`.
53///
54/// # Examples
55/// ```ignore
56/// use strum::AsStaticStr;
57///
58/// #[derive(AsStaticStr)]
59/// pub enum MyMessage {
60///      ExampleMessage()
61/// }
62///
63/// pub struct ExampleResponse {}
64/// impl Handler<NetworkClientMessages> for ClientActor {
65///    type Result = ExampleResponse;
66///
67///    #[perf_with_debug]
68///    fn handle(&mut self, msg: NetworkClientMessages, ctx: &mut Self::Context<Self>) -> Self::Result {
69///        ExampleResponse{}
70///    }
71/// }
72/// ```
73#[proc_macro_attribute]
74pub fn perf_with_debug(_attr: TokenStream, item: TokenStream) -> TokenStream {
75    perf_internal(_attr, item, true)
76}
77
78fn perf_internal(_attr: TokenStream, item: TokenStream, debug: bool) -> TokenStream {
79    let item: syn::Item = syn::parse(item).expect("failed to parse input");
80
81    if let syn::Item::Fn(mut func) = item.clone() {
82        let block = func.clone().block;
83
84        let function_body = quote! { #block };
85
86        let new_body: TokenStream = if debug {
87            let b: TokenStream = quote! {
88                fn xxx() {
89                    use near_performance_metrics::stats::measure_performance_with_debug;
90                    near_performance_metrics::stats::measure_performance_with_debug(std::any::type_name::<Self>(), msg, move |msg| {
91                        #function_body
92                    })
93                }
94            }.into();
95            b
96        } else {
97            let b: TokenStream = quote! {
98                fn xxx() {
99                    use near_performance_metrics::stats::measure_performance;
100                    near_performance_metrics::stats::measure_performance(std::any::type_name::<Self>(), msg, move |msg| {
101                        #function_body
102                    })
103                }
104             }.into();
105            b
106        };
107
108        if let syn::Item::Fn(func2) = syn::parse(new_body).expect("failed to parse input") {
109            func.block = func2.block;
110        } else {
111            panic!("failed to parse example function");
112        }
113
114        let result_item = syn::Item::Fn(func);
115        let output = quote! { #result_item };
116        output.into()
117    } else {
118        panic!("not a function");
119    }
120}