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}