common_macro/
print_caller.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use proc_macro::TokenStream;
16use quote::{quote, ToTokens};
17use syn::{parse_macro_input, ItemFn, LitInt};
18
19pub(crate) fn process_print_caller(args: TokenStream, input: TokenStream) -> TokenStream {
20    let mut depth = 1;
21    let parser = syn::meta::parser(|meta| {
22        if meta.path.is_ident("depth") {
23            depth = meta
24                .value()?
25                .parse::<LitInt>()
26                .and_then(|v| v.base10_parse::<usize>())
27                .expect("Invalid 'depth' value");
28            Ok(())
29        } else {
30            Err(meta.error("unsupported property"))
31        }
32    });
33
34    parse_macro_input!(args with parser);
35
36    let tokens: TokenStream = quote! {
37        {
38            let curr_file = file!();
39
40            let bt = backtrace::Backtrace::new();
41            let call_stack = bt
42                .frames()
43                .iter()
44                .skip_while(|f| {
45                    !f.symbols().iter().any(|s| {
46                        s.filename()
47                            .map(|p| p.ends_with(curr_file))
48                            .unwrap_or(false)
49                    })
50                })
51                .skip(1)
52                .take(#depth);
53
54            let call_stack = call_stack
55                .map(|f| {
56                    f.symbols()
57                        .iter()
58                        .map(|s| {
59                            let filename = s
60                                .filename()
61                                .map(|p| format!("{:?}", p))
62                                .unwrap_or_else(|| "unknown".to_string());
63
64                            let lineno = s
65                                .lineno()
66                                .map(|l| format!("{}", l))
67                                .unwrap_or_else(|| "unknown".to_string());
68
69                            format!("filename: {}, lineno: {}", filename, lineno)
70                        })
71                        .collect::<Vec<String>>()
72                        .join(", ")
73                })
74                .collect::<Vec<_>>();
75
76            match call_stack.len() {
77                0 => common_telemetry::info!("unable to find call stack"),
78                1 => common_telemetry::info!("caller: {}", call_stack[0]),
79                _ => {
80                    let mut s = String::new();
81                    s.push_str("[\n");
82                    for e in call_stack {
83                        s.push_str("\t");
84                        s.push_str(&e);
85                        s.push_str("\n");
86                    }
87                    s.push_str("]");
88                    common_telemetry::info!("call stack: {}", s)
89                }
90            }
91        }
92    }
93    .into();
94
95    let stmt = match syn::parse(tokens) {
96        Ok(stmt) => stmt,
97        Err(e) => return e.into_compile_error().into(),
98    };
99
100    let mut item = parse_macro_input!(input as ItemFn);
101    item.block.stmts.insert(0, stmt);
102
103    item.into_token_stream().into()
104}