hyperlight_guest_tracing_macro/
lib.rs1use proc_macro::TokenStream;
18use quote::quote;
19use syn::{ItemFn, parse_macro_input};
20
21#[proc_macro_attribute]
27pub fn trace_function(_attr: TokenStream, item: TokenStream) -> TokenStream {
28 let input_fn = parse_macro_input!(item as ItemFn);
29
30 let fn_name = &input_fn.sig.ident;
31 let fn_name_str = fn_name.to_string();
32 let fn_vis = &input_fn.vis;
33 let fn_sig = &input_fn.sig;
34 let fn_block = &input_fn.block;
35 let fn_attrs = &input_fn.attrs;
36 let fn_output = &input_fn.sig.output;
37
38 let entry_msg = format!("> {}", fn_name_str);
40 let _exit_msg = format!("< {}", fn_name_str);
41
42 let expanded = match fn_output {
43 syn::ReturnType::Default => {
44 #[cfg(feature = "trace")]
46 quote! {
47 #(#fn_attrs)*
48 #fn_vis #fn_sig {
49 const _: () = assert!(
50 #entry_msg.len() <= hyperlight_guest_tracing::MAX_TRACE_MSG_LEN,
51 "Trace message exceeds the maximum bytes length",
52 );
53 ::hyperlight_guest_tracing::create_trace_record(#entry_msg);
54 #fn_block
56 ::hyperlight_guest_tracing::create_trace_record(#_exit_msg);
57 }
58 }
59 #[cfg(not(feature = "trace"))]
60 quote! {
61 #(#fn_attrs)*
62 #fn_vis #fn_sig {
63 const _: () = assert!(
64 #entry_msg.len() <= hyperlight_guest_tracing::MAX_TRACE_MSG_LEN,
65 "Trace message exceeds the maximum bytes length",
66 );
67 #fn_block
68 }
69 }
70 }
71 syn::ReturnType::Type(_, _) => {
72 #[cfg(feature = "trace")]
74 quote! {
75 #(#fn_attrs)*
76 #fn_vis #fn_sig {
77 const _: () = assert!(
78 #entry_msg.len() <= hyperlight_guest_tracing::MAX_TRACE_MSG_LEN,
79 "Trace message exceeds the maximum bytes length",
80 );
81 ::hyperlight_guest_tracing::create_trace_record(#entry_msg);
82 let __trace_result = (|| #fn_block )();
83 ::hyperlight_guest_tracing::create_trace_record(#_exit_msg);
84 __trace_result
85 }
86 }
87 #[cfg(not(feature = "trace"))]
88 quote! {
89 #(#fn_attrs)*
90 #fn_vis #fn_sig {
91 const _: () = assert!(
92 #entry_msg.len() <= hyperlight_guest_tracing::MAX_TRACE_MSG_LEN,
93 "Trace message exceeds the maximum bytes length",
94 );
95 #fn_block
96 }
97 }
98 }
99 };
100
101 TokenStream::from(expanded)
102}
103
104struct TraceMacroInput {
106 message: syn::Lit,
107 statement: Option<proc_macro2::TokenStream>,
108}
109
110impl syn::parse::Parse for TraceMacroInput {
111 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
112 let message: syn::Lit = input.parse()?;
113 if !matches!(message, syn::Lit::Str(_)) {
114 return Err(input.error("first argument to trace! must be a string literal"));
115 }
116 if let syn::Lit::Str(ref lit_str) = message
117 && lit_str.value().is_empty()
118 {
119 return Err(input.error("trace message must not be empty"));
120 }
121
122 let statement = if input.peek(syn::Token![,]) {
123 let _: syn::Token![,] = input.parse()?;
124 Some(input.parse()?)
125 } else {
126 None
127 };
128 Ok(TraceMacroInput { message, statement })
129 }
130}
131
132#[proc_macro]
137pub fn trace(input: TokenStream) -> TokenStream {
138 let parsed = syn::parse_macro_input!(input as TraceMacroInput);
139 let trace_message = match parsed.message {
140 syn::Lit::Str(ref lit_str) => lit_str.value(),
141 _ => unreachable!(),
142 };
143 if let Some(statement) = parsed.statement {
144 let entry_msg = format!("+ {}", trace_message);
145 let _exit_msg = format!("- {}", trace_message);
146 #[cfg(feature = "trace")]
147 let expanded = quote! {
148 {
149 const _: () = assert!(
150 #entry_msg.len() <= hyperlight_guest_tracing::MAX_TRACE_MSG_LEN,
151 "Trace message exceeds the maximum bytes length",
152 );
153 ::hyperlight_guest_tracing::create_trace_record(#entry_msg);
154 let __trace_result = #statement;
155 ::hyperlight_guest_tracing::create_trace_record(#_exit_msg);
156 __trace_result
157 }
158 };
159 #[cfg(not(feature = "trace"))]
160 let expanded = quote! {
161 {
162 const _: () = assert!(
163 #entry_msg.len() <= hyperlight_guest_tracing::MAX_TRACE_MSG_LEN,
164 "Trace message exceeds the maximum bytes length",
165 );
166 #statement
167 }
168 };
169
170 TokenStream::from(expanded)
171 } else {
172 #[cfg(feature = "trace")]
173 let expanded = quote! {
174 {
175 const _: () = assert!(
176 #trace_message.len() <= hyperlight_guest_tracing::MAX_TRACE_MSG_LEN,
177 "Trace message exceeds the maximum bytes length",
178 );
179 ::hyperlight_guest_tracing::create_trace_record(#trace_message);
180 }
181 };
182 #[cfg(not(feature = "trace"))]
183 let expanded = quote! {
184 {
185 const _: () = assert!(
186 #trace_message.len() <= hyperlight_guest_tracing::MAX_TRACE_MSG_LEN,
187 "Trace message exceeds the maximum bytes length",
188 );
189 }
190 };
191
192 TokenStream::from(expanded)
193 }
194}
195
196#[proc_macro]
198pub fn flush(_input: TokenStream) -> TokenStream {
199 #[cfg(feature = "trace")]
200 let expanded = quote! {
201 {
202 ::hyperlight_guest_tracing::flush_trace_buffer();
203 }
204 };
205 #[cfg(not(feature = "trace"))]
206 let expanded = quote! {};
207
208 TokenStream::from(expanded)
209}