pub struct Trace { /* private fields */ }
rt
only.Expand description
An execution trace of a task’s last poll.
Resolving a backtrace, either via the Display
impl or via
resolve_backtraces
, parses debuginfo, which is
possibly a CPU-expensive operation that can take a platform-specific but
long time to run - often over 100 milliseconds, especially if the current
process’s binary is big. In some cases, the platform might internally cache some of the
debuginfo, so successive calls to resolve_backtraces
might be faster than
the first call, but all guarantees are platform-dependent.
To avoid blocking the runtime, it is recommended
that you resolve backtraces inside of a spawn_blocking()
and to have some concurrency-limiting mechanism to avoid unexpected performance impact.
See Handle::dump
.
Implementations§
Source§impl Trace
impl Trace
Sourcepub fn resolve_backtraces(&self) -> Vec<Backtrace>
pub fn resolve_backtraces(&self) -> Vec<Backtrace>
Resolve and return a list of backtraces that are involved in polls in this trace.
The exact backtraces included here are unstable and might change in the future,
but you can expect one Backtrace
for every call to
poll
to a bottom-level Tokio future - so if something like join!
is
used, there will be a backtrace for each future in the join.
Sourcepub fn capture<F, R>(f: F) -> (R, Trace)where
F: FnOnce() -> R,
pub fn capture<F, R>(f: F) -> (R, Trace)where
F: FnOnce() -> R,
Runs the function f
in tracing mode, and returns its result along with the resulting Trace
.
This is normally called with f
being the poll function of a future, and will give you a backtrace
that tells you what that one future is doing.
Use Handle::dump
instead if you want to know what all the tasks in your program are doing.
Also see Handle::dump
for more documentation about dumps, but unlike Handle::dump
, this function
should not be much slower than calling f
directly.
Due to the way tracing is implemented, Tokio leaf futures will usually, instead of doing their
actual work, do the equivalent of a yield_now
(returning a Poll::Pending
and scheduling the
current context for execution), which means forward progress will probably not happen unless
you eventually call your future outside of capture
.
Example usage:
use std::future::Future;
use std::task::Poll;
use tokio::runtime::dump::Trace;
// some future
let mut test_future = std::pin::pin!(async move { tokio::task::yield_now().await; 0 });
// trace it once, see what it's doing
let (trace, res) = Trace::root(std::future::poll_fn(|cx| {
let (res, trace) = Trace::capture(|| test_future.as_mut().poll(cx));
Poll::Ready((trace, res))
})).await;
// await it to let it finish, outside of a `capture`
let output = match res {
Poll::Ready(output) => output,
Poll::Pending => test_future.await,
};
println!("{trace}");
§Nested calls
Nested calls to capture
might return partial traces, but will not do any other undesirable behavior (for
example, they will not panic).
Sourcepub fn root<F>(f: F) -> Root<F> ⓘwhere
F: Future,
pub fn root<F>(f: F) -> Root<F> ⓘwhere
F: Future,
Create a root for stack traces captured using Trace::capture
. Stack frames above
the root will not be captured.
Nesting multiple Root
futures is fine. Captures will stop at the first root. Not having
a Root
is fine as well, but there is no guarantee on where the capture will stop.