embedded_trace/
lib.rs

1//! # embedded-trace
2//!
3//! A [`Future`] tracing utility for embedded systems.
4//!
5//! This crate aims to provide tools to measure the execution time and debug
6//! `async` tasks and [`Future`]s for `#![no_std]` projects.
7//!
8//! # How to use this library
9//!
10//! Two main traits are defined: [`TraceFuture`] and [`Instrument`].
11//!
12//! ## [`TraceFuture`]
13//! [`TraceFuture`] extends the standard library's [`Future`] trait by adding
14//! the [`trace_task`], [`trace_poll`] and [`trace_task_and_poll`] methods.
15//! These methods each take one or more types implementing [`Instrument`]. The
16//! three provided methods call [`on_enter`] and [`on_exit`] when entering the
17//! specified spans, respectively. Consult the [`TraceFuture`] trait
18//! documentation for more information.
19//!
20//! ## [`Instrument`]
21//! [`Instrument`] represents the mechanism by which [`TraceFuture`]'s methods
22//! will signal when a span is entered or exited. You can implement this trait
23//! on your own types. Some implementation for commonly used types are also
24//! provided in the [`instruments`] module.
25//!
26//! For instance, a simple mechanism may be to set a GPIO pin HIGH
27//! when entering the span, and setting it LOW when exiting. This
28//! instrumentation is provided in the [`instruments::gpio`] module.
29//!
30//! ### Supported GPIO implementations:
31//!
32//! * Types implementing `embedded-hal` version 1.0
33//!   [`OutputPin`](embedded_hal_1::digital::OutputPin)
34//! * Types implementing `embedded-hal` version 0.2
35//!   [`OutputPin`](embedded_hal_0_2::digital::v2::OutputPin) (by enabling the
36//!   `embedded-hal_0_2` Cargo feature)
37//!
38//! ## Example use with GPIO instrumentation
39//!
40//! ```
41//! # use embedded_hal_1 as embedded_hal;
42//! use core::future::Future;
43//! // `TraceFuture` must be in scope in order to use its methods.
44//! use embedded_trace::{TraceFuture, Gpio, GpioRef};
45//! use embedded_hal::digital::OutputPin;
46//!
47//! async fn trace_my_future<F, P1, P2>(future: F, task_pin: P1, poll_pin: &mut P2)
48//! where
49//!     F: Future,
50//!     P1: OutputPin,
51//!     P2: OutputPin
52//! {
53//!     // `Gpio` can be used where we can take the pin...
54//!     let mut task_instrument = Gpio::new(task_pin);
55//!     // ...or `GpioRef` when all we have is a mutable reference to a pin.
56//!     let mut poll_instrument = GpioRef::new(poll_pin);
57//!
58//!     // Poll our future while tracing its execution.
59//!     future.trace_task_and_poll(&mut task_instrument, &mut poll_instrument).await;
60//!
61//!     // We can reclaim the pin taken by `Gpio`
62//!     let task_pin = task_instrument.free();
63//! }
64//! ```
65//!
66//! [`trace_task`]: TraceFuture::trace_task
67//! [`trace_poll`]: TraceFuture::trace_poll
68//! [`trace_task_and_poll`]: TraceFuture::trace_task_and_poll
69//! [`on_enter`]: Instrument::on_enter
70//! [`on_exit`]: Instrument::on_exit
71
72#![no_std]
73#![warn(missing_docs)]
74
75use core::{future::Future, pin::Pin, task::Poll};
76
77pub mod instruments;
78pub use instruments::*;
79
80/// Extension to [`Future`] with tracing utilities.
81///
82/// Each method takes one or more [`Instrument`]
83/// parameters which dictate the mechanism used to signal when a span is entered
84/// and exited. Refer to each method's documentation for more information.
85pub trait TraceFuture: Future
86where
87    Self: Sized,
88{
89    /// Trace a [`Future`]'s task execution.
90    ///
91    /// The underlying [`Instrument`]
92    /// calls [`on_enter`](Instrument::on_enter) when the future is first
93    /// polled, and calls [`on_exit`](Instrument::on_exit) when it completes
94    /// (returns [`Poll::Ready`]). This is useful for analyzing the total time
95    /// it takes for your future to complete (note that this is different from
96    /// the real CPU time the task consumes).
97    fn trace_task<I: Instrument>(self, instrument: &mut I) -> TraceTaskFuture<'_, Self, I> {
98        TraceTaskFuture {
99            fut: self,
100            instrument,
101            polled_once: false,
102        }
103    }
104
105    /// Trace a [`Future`] poll execution.
106    ///
107    /// The underlying [`Instrument`]
108    /// calls [`on_enter`](Instrument::on_enter) every time prior to the
109    /// underlying future being polled, and calls and calls
110    /// [`on_exit`](Instrument::on_exit) when it completes (returns
111    /// [`on_exit`](Instrument::on_exit) right after the [`poll`](Future::poll)
112    /// call completes, regardless of whether the underlying future completed or
113    /// not. This is useful for analyzing the time it takes to poll your future
114    /// (ie, actual CPU time used).
115    fn trace_poll<I: Instrument>(self, instrument: &mut I) -> TracePollFuture<'_, Self, I> {
116        TracePollFuture {
117            fut: self,
118            instrument,
119        }
120    }
121
122    /// Trace a [`Future`]'s task and poll execution.
123    ///
124    /// The first underlying [`Instrument`] (`task_instrument`) acts exactly as
125    /// [`trace_task`](TraceFuture::trace_task), and the second underlying
126    /// [`Instrument`] (`poll_instrument`) acts exactly as
127    /// [`trace_poll`](TraceFuture::trace_poll).
128    fn trace_task_and_poll<'a, I1: Instrument, I2: Instrument>(
129        self,
130        task_instrument: &'a mut I1,
131        poll_instrument: &'a mut I2,
132    ) -> TraceTaskAndPollFuture<'a, Self, I1, I2> {
133        TraceTaskAndPollFuture {
134            fut: self,
135            task_instrument,
136            poll_instrument,
137            polled_once: false,
138        }
139    }
140}
141
142impl<F: Future> TraceFuture for F {}
143
144/// Signal when a span is entered or exited.
145///
146/// Some implementations for commonly used types are provided in the
147/// [`instruments`] module. You may also implement this trait yourself.
148pub trait Instrument {
149    /// This method is called when the span is entered.
150    fn on_enter(&mut self);
151
152    /// This method is called when the span is exited.
153    fn on_exit(&mut self);
154}
155
156pin_project_lite::pin_project! {
157    #[must_use = "futures do nothing unless you `.await` or poll them"]
158    #[doc(hidden)]
159    pub struct TraceTaskFuture<'a, F, I>
160    where
161        F: Future,
162        I: Instrument,
163    {
164        #[pin]
165        fut: F,
166        instrument: &'a mut I,
167        polled_once: bool
168    }
169}
170
171impl<'p, F, P> Future for TraceTaskFuture<'p, F, P>
172where
173    F: Future,
174    P: Instrument,
175{
176    type Output = F::Output;
177
178    fn poll(
179        self: Pin<&mut Self>,
180        cx: &mut core::task::Context<'_>,
181    ) -> core::task::Poll<Self::Output> {
182        let this = self.project();
183
184        if !*this.polled_once {
185            this.instrument.on_enter();
186        }
187        *this.polled_once = true;
188
189        let poll_result = this.fut.poll(cx);
190        match poll_result {
191            Poll::Ready(c) => {
192                this.instrument.on_exit();
193                Poll::Ready(c)
194            }
195            Poll::Pending => Poll::Pending,
196        }
197    }
198}
199
200pin_project_lite::pin_project! {
201    #[must_use = "futures do nothing unless you `.await` or poll them"]
202    #[doc(hidden)]
203    pub struct TracePollFuture<'a, F, I>
204    where
205        F: Future,
206        I: Instrument,
207    {
208        #[pin]
209        fut: F,
210        instrument: &'a mut I,
211    }
212}
213
214impl<'p, F, I> Future for TracePollFuture<'p, F, I>
215where
216    F: Future,
217    I: Instrument,
218{
219    type Output = F::Output;
220
221    fn poll(
222        self: Pin<&mut Self>,
223        cx: &mut core::task::Context<'_>,
224    ) -> core::task::Poll<Self::Output> {
225        let this = self.project();
226
227        this.instrument.on_enter();
228        let poll_result = this.fut.poll(cx);
229        this.instrument.on_exit();
230
231        match poll_result {
232            Poll::Ready(c) => Poll::Ready(c),
233            Poll::Pending => Poll::Pending,
234        }
235    }
236}
237
238pin_project_lite::pin_project! {
239    #[must_use = "futures do nothing unless you `.await` or poll them"]
240    #[doc(hidden)]
241    pub struct TraceTaskAndPollFuture<'a, F, T, P>
242    where
243        F: Future,
244        T: Instrument,
245        P: Instrument
246    {
247        #[pin]
248        fut: F,
249        task_instrument: &'a mut T,
250        poll_instrument: &'a mut P,
251        polled_once: bool,
252    }
253}
254
255impl<'p, F, I1, I2> Future for TraceTaskAndPollFuture<'p, F, I1, I2>
256where
257    F: Future,
258    I1: Instrument,
259    I2: Instrument,
260{
261    type Output = F::Output;
262
263    fn poll(
264        self: Pin<&mut Self>,
265        cx: &mut core::task::Context<'_>,
266    ) -> core::task::Poll<Self::Output> {
267        let this = self.project();
268
269        if !*this.polled_once {
270            this.task_instrument.on_enter();
271        }
272        *this.polled_once = true;
273
274        this.poll_instrument.on_enter();
275        let poll_result = this.fut.poll(cx);
276        this.poll_instrument.on_exit();
277
278        match poll_result {
279            Poll::Ready(c) => {
280                this.task_instrument.on_exit();
281                Poll::Ready(c)
282            }
283            Poll::Pending => Poll::Pending,
284        }
285    }
286}