hotpath_macros/lib.rs
1use proc_macro::TokenStream;
2
3#[cfg(feature = "hotpath")]
4mod lib_on;
5
6#[cfg(not(feature = "hotpath"))]
7mod lib_off;
8
9/// Initializes the hotpath profiling system and generates a performance report on program exit.
10///
11/// This attribute macro should be applied to your program's main (or other entry point) function
12/// to enable profiling. It creates a guard that initializes the background measurement processing
13/// thread and automatically displays a performance summary when the program exits. Additionally
14/// it creates a measurement guard that will be used to measure the wrapper function itself.
15///
16/// For programmatic control over the same options, see
17/// [`HotpathGuardBuilder`](../hotpath/struct.HotpathGuardBuilder.html).
18///
19/// # Parameters
20///
21/// * `percentiles` - Array of percentile values (0.0-100.0) to compute, e.g. `[50, 95, 99.9]`. Default: `[95]`
22/// * `format` - Output format: `"table"` (default), `"json"`, `"json-pretty"`, or `"none"`
23/// * `limit` - Global maximum number of items shown in each report section (functions, channels, streams, futures, threads). `0` = unlimited.
24/// * `functions_limit` - Maximum number of functions shown in the report. Overrides `limit` for functions.
25/// * `channels_limit` - Maximum number of channels shown in the report. Overrides `limit` for channels.
26/// * `streams_limit` - Maximum number of streams shown in the report. Overrides `limit` for streams.
27/// * `futures_limit` - Maximum number of futures shown in the report. Overrides `limit` for futures.
28/// * `threads_limit` - Maximum number of threads shown in the report. Overrides `limit` for threads.
29/// * `output_path` - File path for the report. Defaults to stdout. Overridden by `HOTPATH_OUTPUT_PATH` env var.
30/// * `report` - Report sections spec: `"all"`, `"auto"`, an exact list like `"functions-timing,channels"`, or auto with exclusions like `"auto,-threads"`. Default: auto (function and thread sections plus every instrumented section with data). Overridden by `HOTPATH_REPORT` env var.
31/// * `allocator` - Optional allocator used when `hotpath-alloc` is enabled. The path must
32/// name a unit struct implementing `GlobalAlloc` (e.g. `mimalloc::MiMalloc`); it is used
33/// both as the type and the value of the inner allocator. Defaults to `std::alloc::System`.
34///
35/// Environment variable precedence for report output:
36/// `HOTPATH_LIMIT`, `HOTPATH_FUNCTIONS_LIMIT`, `HOTPATH_CHANNELS_LIMIT`,
37/// `HOTPATH_STREAMS_LIMIT`, `HOTPATH_FUTURES_LIMIT`, and `HOTPATH_THREADS_LIMIT`
38/// override the matching macro arguments. Per-resource env vars override `HOTPATH_LIMIT`.
39///
40/// # Examples
41///
42/// Basic usage with default settings (P95 percentile, table format):
43///
44/// ```rust,no_run
45/// #[hotpath::main]
46/// fn main() {
47/// // Your code here
48/// }
49/// ```
50///
51/// Custom percentiles:
52///
53/// ```rust,no_run
54/// #[tokio::main]
55/// #[hotpath::main(percentiles = [50, 90, 95, 99.9])]
56/// async fn main() {
57/// // Your code here
58/// }
59/// ```
60///
61/// JSON output to file:
62///
63/// ```rust,no_run
64/// #[hotpath::main(format = "json-pretty", output_path = "report.json")]
65/// fn main() {
66/// // Your code here
67/// }
68/// ```
69///
70/// Select exact report sections, or hide a section from the auto report:
71///
72/// ```rust,no_run
73/// #[hotpath::main(report = "functions-timing,channels")]
74/// fn main() {
75/// // Your code here
76/// }
77/// ```
78///
79/// ```rust,no_run
80/// #[hotpath::main(report = "auto,-threads")]
81/// fn main() {
82/// // Your code here
83/// }
84/// ```
85///
86/// Per-resource limits:
87///
88/// ```rust,no_run
89/// #[hotpath::main(limit = 10, functions_limit = 20, channels_limit = 5)]
90/// fn main() {
91/// // Your code here
92/// }
93/// ```
94///
95/// # Usage with Tokio
96///
97/// When using with tokio, place `#[tokio::main]` before `#[hotpath::main]`:
98///
99/// ```rust,no_run
100/// #[tokio::main]
101/// #[hotpath::main]
102/// async fn main() {
103/// // Your code here
104/// }
105/// ```
106///
107/// # Limitations
108///
109/// Only one hotpath guard can be active at a time. Creating a second guard (either via this
110/// macro or via [`HotpathGuardBuilder`](../hotpath/struct.HotpathGuardBuilder.html)) will cause a panic.
111///
112/// # See Also
113///
114/// * [`measure`](macro@measure) - Attribute macro for instrumenting functions
115/// * [`measure_block!`](../hotpath/macro.measure_block.html) - Macro for measuring code blocks
116/// * [`HotpathGuardBuilder`](../hotpath/struct.HotpathGuardBuilder.html) - Programmatic alternative to this macro
117#[proc_macro_attribute]
118pub fn main(attr: TokenStream, item: TokenStream) -> TokenStream {
119 #[cfg(feature = "hotpath")]
120 {
121 lib_on::main_impl(attr, item)
122 }
123 #[cfg(not(feature = "hotpath"))]
124 {
125 lib_off::main_impl(attr, item)
126 }
127}
128
129/// Instruments a function to measure execution time or memory allocations.
130///
131/// Automatically detects sync vs async and inserts the appropriate measurement guard.
132/// Compiles to zero overhead when the `hotpath` feature is disabled.
133///
134/// # Measurements
135///
136/// * **Time profiling** (default) - execution duration via high-precision timers
137/// * **Allocation profiling** (`hotpath-alloc` feature) - bytes allocated and allocation count
138///
139/// # Parameters
140///
141/// * `log` - If `true`, logs the return value on each call (requires `Debug` on return type)
142/// * `future` - If `true`, also tracks the future lifecycle (poll count, state transitions, cancellation). Only valid on async functions.
143///
144/// # Examples
145///
146/// ```rust,no_run
147/// #[hotpath::measure]
148/// fn process(data: &[u8]) -> usize {
149/// data.len()
150/// }
151///
152/// #[hotpath::measure(log = true)]
153/// fn compute() -> i32 {
154/// 42
155/// }
156///
157/// #[hotpath::measure(future = true)]
158/// async fn fetch_data() -> Vec<u8> {
159/// vec![1, 2, 3]
160/// }
161/// ```
162///
163/// # Async Allocation Limitation
164///
165/// Allocation profiling requires `current_thread` tokio runtime because thread-local
166/// tracking cannot follow tasks across threads. Time profiling works with any runtime.
167///
168/// # See Also
169///
170/// * [`main`](macro@main) - Initializes the profiling system
171/// * [`measure_all`](macro@measure_all) - Bulk instrumentation for modules and impl blocks
172/// * [`measure_block!`](../hotpath/macro.measure_block.html) - Instruments code blocks
173#[proc_macro_attribute]
174pub fn measure(attr: TokenStream, item: TokenStream) -> TokenStream {
175 #[cfg(feature = "hotpath")]
176 {
177 lib_on::measure_impl(attr, item)
178 }
179 #[cfg(not(feature = "hotpath"))]
180 {
181 lib_off::measure_impl(attr, item)
182 }
183}
184
185/// Instruments an async function to track its lifecycle as a Future.
186///
187/// Wraps the function body with the `future!` macro to track poll counts,
188/// state transitions (pending/ready/cancelled), and optionally the output value.
189/// Can only be applied to `async fn`.
190///
191/// # Parameters
192///
193/// * `log` - If `true`, logs the output value on completion (requires `Debug` on return type)
194///
195/// # Examples
196///
197/// ```rust,no_run
198/// #[hotpath::future_fn]
199/// async fn fetch_data() -> Vec<u8> {
200/// vec![1, 2, 3]
201/// }
202///
203/// #[hotpath::future_fn(log = true)]
204/// async fn compute() -> i32 {
205/// 42
206/// }
207/// ```
208///
209/// # See Also
210///
211/// * [`measure`](macro@measure) - Instruments execution time / allocations
212/// * [`future!`](../hotpath/macro.future.html) - Declarative macro for wrapping future expressions
213#[proc_macro_attribute]
214pub fn future_fn(attr: TokenStream, item: TokenStream) -> TokenStream {
215 #[cfg(feature = "hotpath")]
216 {
217 lib_on::future_fn_impl(attr, item)
218 }
219 #[cfg(not(feature = "hotpath"))]
220 {
221 lib_off::future_fn_impl(attr, item)
222 }
223}
224
225/// Marks a function to be excluded from profiling when used with [`measure_all`](macro@measure_all).
226///
227/// # Usage
228///
229/// ```rust,no_run
230/// #[hotpath::measure_all]
231/// impl MyStruct {
232/// fn important_method(&self) {
233/// // This will be measured
234/// }
235///
236/// #[hotpath::skip]
237/// fn not_so_important_method(&self) -> usize {
238/// // This will NOT be measured
239/// self.value
240/// }
241/// }
242/// ```
243///
244/// # See Also
245///
246/// * [`measure_all`](macro@measure_all) - Bulk instrumentation macro
247/// * [`measure`](macro@measure) - Individual function instrumentation
248#[proc_macro_attribute]
249pub fn skip(attr: TokenStream, item: TokenStream) -> TokenStream {
250 #[cfg(feature = "hotpath")]
251 {
252 lib_on::skip_impl(attr, item)
253 }
254 #[cfg(not(feature = "hotpath"))]
255 {
256 lib_off::skip_impl(attr, item)
257 }
258}
259
260/// Instruments all functions in a module or impl block with the `measure` profiling macro.
261///
262/// This attribute macro applies the [`measure`](macro@measure) macro to every function
263/// in the annotated module or impl block, providing bulk instrumentation without needing
264/// to annotate each function individually.
265///
266/// # Usage
267///
268/// On modules:
269///
270/// ```rust,no_run
271/// #[hotpath::measure_all]
272/// mod my_module {
273/// fn function_one() {
274/// // This will be automatically measured
275/// }
276///
277/// fn function_two() {
278/// // This will also be automatically measured
279/// }
280/// }
281/// ```
282///
283/// On impl blocks:
284///
285/// ```rust,no_run
286/// struct MyStruct;
287///
288/// #[hotpath::measure_all]
289/// impl MyStruct {
290/// fn method_one(&self) {
291/// // This will be automatically measured
292/// }
293///
294/// fn method_two(&self) {
295/// // This will also be automatically measured
296/// }
297/// }
298/// ```
299///
300/// # See Also
301///
302/// * [`measure`](macro@measure) - Attribute macro for instrumenting individual functions
303/// * [`main`](macro@main) - Attribute macro that initializes profiling
304/// * [`skip`](macro@skip) - Marker to exclude specific functions from measurement
305#[proc_macro_attribute]
306pub fn measure_all(attr: TokenStream, item: TokenStream) -> TokenStream {
307 #[cfg(feature = "hotpath")]
308 {
309 lib_on::measure_all_impl(attr, item)
310 }
311 #[cfg(not(feature = "hotpath"))]
312 {
313 lib_off::measure_all_impl(attr, item)
314 }
315}