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