common_macro/
print_caller.rs1use 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}