Instrument async functions to extract their continuation structure
as an inspectable, serializable step graph.
async fn in Rust compiles down to a state machine that the executor
polls. Most of the time you do not need to think about that, but when
something is mysteriously slow, hangs, or stalls, you would really
like to see the shape of what is happening: which await points were
hit, in what order, how often each was polled, and whether each one
returned Ready or Pending.
This crate gives you a low-friction way to capture that. Wrap a future
in [TracedFuture] (or sprinkle [LabeledFuture] across specific
await points), drive it to completion, and you get a [Trace] of
[PollEvent]s. Pass that to [reify_execution] for an
[AsyncStepGraph] grouped by label, and [to_dot] to render it as
Graphviz.
With the serde feature, [PollEvent], [Trace], and
[AsyncStepGraph] all serialize cleanly (timestamps are stored as
Duration since trace start, so traces are
deterministic and portable).
With the macros feature, the [macro@trace_async] attribute proc
macro (re-exported from
async-reify-macros) rewrites
every .await in a function body into a [LabeledFuture] so you do
not have to label each one by hand. Labels look like
"<expr> @ file.rs:42" for easy navigation back to the source.
Workflow
- Wrap futures in [
TracedFuture] (or use [LabeledFuture] /#[trace_async]for finer-grained labels). - Drive them to completion. They behave like ordinary futures; the
only side effect is recording a [
PollEvent] every time they are polled. - Convert the [
Trace] to an [AsyncStepGraph] with [reify_execution]. - Inspect, serialize, or render. [
to_dot] outputs Graphviz DOT.
See the trace_workflow example for an end-to-end run, and
docs/phase4-async-reify.md for the design choices (why
Arc<Mutex<...>> for shared logging, why label-based step grouping,
why DOT rather than full deterministic replay).
Examples
use ;
# block_on;