tokio/runtime/
dump.rs

1//! Snapshots of runtime state.
2//!
3//! See [`Handle::dump`][crate::runtime::Handle::dump].
4
5use crate::task::Id;
6use std::{fmt, future::Future, path::Path};
7
8pub use crate::runtime::task::trace::Root;
9
10/// A snapshot of a runtime's state.
11///
12/// See [`Handle::dump`][crate::runtime::Handle::dump].
13#[derive(Debug)]
14pub struct Dump {
15    tasks: Tasks,
16}
17
18/// Snapshots of tasks.
19///
20/// See [`Handle::dump`][crate::runtime::Handle::dump].
21#[derive(Debug)]
22pub struct Tasks {
23    tasks: Vec<Task>,
24}
25
26/// A snapshot of a task.
27///
28/// See [`Handle::dump`][crate::runtime::Handle::dump].
29#[derive(Debug)]
30pub struct Task {
31    id: Id,
32    trace: Trace,
33}
34
35/// Represents an address that should not be dereferenced.
36///
37/// This type exists to get the auto traits correct, the public API
38/// uses raw pointers to make life easier for users.
39#[derive(Copy, Clone, Debug)]
40struct Address(*mut std::ffi::c_void);
41
42// Safe since Address should not be dereferenced
43unsafe impl Send for Address {}
44unsafe impl Sync for Address {}
45
46/// A backtrace symbol.
47///
48/// This struct provides accessors for backtrace symbols, similar to [`backtrace::BacktraceSymbol`].
49#[derive(Clone, Debug)]
50pub struct BacktraceSymbol {
51    name: Option<Box<[u8]>>,
52    name_demangled: Option<Box<str>>,
53    addr: Option<Address>,
54    filename: Option<std::path::PathBuf>,
55    lineno: Option<u32>,
56    colno: Option<u32>,
57}
58
59impl BacktraceSymbol {
60    pub(crate) fn from_backtrace_symbol(sym: &backtrace::BacktraceSymbol) -> Self {
61        let name = sym.name();
62        Self {
63            name: name.as_ref().map(|name| name.as_bytes().into()),
64            name_demangled: name.map(|name| format!("{}", name).into()),
65            addr: sym.addr().map(Address),
66            filename: sym.filename().map(From::from),
67            lineno: sym.lineno(),
68            colno: sym.colno(),
69        }
70    }
71
72    /// Return the raw name of the symbol.
73    pub fn name_raw(&self) -> Option<&[u8]> {
74        self.name.as_deref()
75    }
76
77    /// Return the demangled name of the symbol.
78    pub fn name_demangled(&self) -> Option<&str> {
79        self.name_demangled.as_deref()
80    }
81
82    /// Returns the starting address of this symbol.
83    pub fn addr(&self) -> Option<*mut std::ffi::c_void> {
84        self.addr.map(|addr| addr.0)
85    }
86
87    /// Returns the file name where this function was defined. If debuginfo
88    /// is missing, this is likely to return None.
89    pub fn filename(&self) -> Option<&Path> {
90        self.filename.as_deref()
91    }
92
93    /// Returns the line number for where this symbol is currently executing.
94    ///
95    /// If debuginfo is missing, this is likely to return `None`.
96    pub fn lineno(&self) -> Option<u32> {
97        self.lineno
98    }
99
100    /// Returns the column number for where this symbol is currently executing.
101    ///
102    /// If debuginfo is missing, this is likely to return `None`.
103    pub fn colno(&self) -> Option<u32> {
104        self.colno
105    }
106}
107
108/// A backtrace frame.
109///
110/// This struct represents one stack frame in a captured backtrace, similar to [`backtrace::BacktraceFrame`].
111#[derive(Clone, Debug)]
112pub struct BacktraceFrame {
113    ip: Address,
114    symbol_address: Address,
115    symbols: Box<[BacktraceSymbol]>,
116}
117
118impl BacktraceFrame {
119    pub(crate) fn from_resolved_backtrace_frame(frame: &backtrace::BacktraceFrame) -> Self {
120        Self {
121            ip: Address(frame.ip()),
122            symbol_address: Address(frame.symbol_address()),
123            symbols: frame
124                .symbols()
125                .iter()
126                .map(BacktraceSymbol::from_backtrace_symbol)
127                .collect(),
128        }
129    }
130
131    /// Return the instruction pointer of this frame.
132    ///
133    /// See the ABI docs for your platform for the exact meaning.
134    pub fn ip(&self) -> *mut std::ffi::c_void {
135        self.ip.0
136    }
137
138    /// Returns the starting symbol address of the frame of this function.
139    pub fn symbol_address(&self) -> *mut std::ffi::c_void {
140        self.symbol_address.0
141    }
142
143    /// Return an iterator over the symbols of this backtrace frame.
144    ///
145    /// Due to inlining, it is possible for there to be multiple [`BacktraceSymbol`] items relating
146    /// to a single frame. The first symbol listed is the "innermost function",
147    /// whereas the last symbol is the outermost (last caller).
148    pub fn symbols(&self) -> impl Iterator<Item = &BacktraceSymbol> {
149        self.symbols.iter()
150    }
151}
152
153/// A captured backtrace.
154///
155/// This struct provides access to each backtrace frame, similar to [`backtrace::Backtrace`].
156#[derive(Clone, Debug)]
157pub struct Backtrace {
158    frames: Box<[BacktraceFrame]>,
159}
160
161impl Backtrace {
162    /// Return the frames in this backtrace, innermost (in a task dump,
163    /// likely to be a leaf future's poll function) first.
164    pub fn frames(&self) -> impl Iterator<Item = &BacktraceFrame> {
165        self.frames.iter()
166    }
167}
168
169/// An execution trace of a task's last poll.
170///
171/// <div class="warning">
172///
173/// Resolving a backtrace, either via the [`Display`][std::fmt::Display] impl or via
174/// [`resolve_backtraces`][Trace::resolve_backtraces], parses debuginfo, which is
175/// possibly a CPU-expensive operation that can take a platform-specific but
176/// long time to run - often over 100 milliseconds, especially if the current
177/// process's binary is big. In some cases, the platform might internally cache some of the
178/// debuginfo, so successive calls to `resolve_backtraces` might be faster than
179/// the first call, but all guarantees are platform-dependent.
180///
181/// To avoid blocking the runtime, it is recommended
182/// that you resolve backtraces inside of a [`spawn_blocking()`][crate::task::spawn_blocking]
183/// and to have some concurrency-limiting mechanism to avoid unexpected performance impact.
184/// </div>
185///
186/// See [`Handle::dump`][crate::runtime::Handle::dump].
187#[derive(Debug)]
188pub struct Trace {
189    inner: super::task::trace::Trace,
190}
191
192impl Trace {
193    /// Resolve and return a list of backtraces that are involved in polls in this trace.
194    ///
195    /// The exact backtraces included here are unstable and might change in the future,
196    /// but you can expect one [`Backtrace`] for every call to
197    /// [`poll`] to a bottom-level Tokio future - so if something like [`join!`] is
198    /// used, there will be a backtrace for each future in the join.
199    ///
200    /// [`poll`]: std::future::Future::poll
201    /// [`join!`]: macro@join
202    pub fn resolve_backtraces(&self) -> Vec<Backtrace> {
203        self.inner
204            .backtraces()
205            .iter()
206            .map(|backtrace| {
207                let mut backtrace = backtrace::Backtrace::from(backtrace.clone());
208                backtrace.resolve();
209                Backtrace {
210                    frames: backtrace
211                        .frames()
212                        .iter()
213                        .map(BacktraceFrame::from_resolved_backtrace_frame)
214                        .collect(),
215                }
216            })
217            .collect()
218    }
219
220    /// Runs the function `f` in tracing mode, and returns its result along with the resulting [`Trace`].
221    ///
222    /// This is normally called with `f` being the poll function of a future, and will give you a backtrace
223    /// that tells you what that one future is doing.
224    ///
225    /// Use [`Handle::dump`] instead if you want to know what *all the tasks* in your program are doing.
226    /// Also see [`Handle::dump`] for more documentation about dumps, but unlike [`Handle::dump`], this function
227    /// should not be much slower than calling `f` directly.
228    ///
229    /// Due to the way tracing is implemented, Tokio leaf futures will usually, instead of doing their
230    /// actual work, do the equivalent of a `yield_now` (returning a `Poll::Pending` and scheduling the
231    /// current context for execution), which means forward progress will probably not happen unless
232    /// you eventually call your future outside of `capture`.
233    ///
234    /// [`Handle::dump`]: crate::runtime::Handle::dump
235    ///
236    /// Example usage:
237    /// ```
238    /// use std::future::Future;
239    /// use std::task::Poll;
240    /// use tokio::runtime::dump::Trace;
241    ///
242    /// # async fn test_fn() {
243    /// // some future
244    /// let mut test_future = std::pin::pin!(async move { tokio::task::yield_now().await; 0 });
245    ///
246    /// // trace it once, see what it's doing
247    /// let (trace, res) = Trace::root(std::future::poll_fn(|cx| {
248    ///     let (res, trace) = Trace::capture(|| test_future.as_mut().poll(cx));
249    ///     Poll::Ready((trace, res))
250    /// })).await;
251    ///
252    /// // await it to let it finish, outside of a `capture`
253    /// let output = match res {
254    ///    Poll::Ready(output) => output,
255    ///    Poll::Pending => test_future.await,
256    /// };
257    ///
258    /// println!("{trace}");
259    /// # }
260    /// ```
261    ///
262    /// ### Nested calls
263    ///
264    /// Nested calls to `capture` might return partial traces, but will not do any other undesirable behavior (for
265    /// example, they will not panic).
266    pub fn capture<F, R>(f: F) -> (R, Trace)
267    where
268        F: FnOnce() -> R,
269    {
270        let (res, trace) = super::task::trace::Trace::capture(f);
271        (res, Trace { inner: trace })
272    }
273
274    /// Create a root for stack traces captured using [`Trace::capture`]. Stack frames above
275    /// the root will not be captured.
276    ///
277    /// Nesting multiple [`Root`] futures is fine. Captures will stop at the first root. Not having
278    /// a [`Root`] is fine as well, but there is no guarantee on where the capture will stop.
279    pub fn root<F>(f: F) -> Root<F>
280    where
281        F: Future,
282    {
283        crate::runtime::task::trace::Trace::root(f)
284    }
285}
286
287impl Dump {
288    pub(crate) fn new(tasks: Vec<Task>) -> Self {
289        Self {
290            tasks: Tasks { tasks },
291        }
292    }
293
294    /// Tasks in this snapshot.
295    pub fn tasks(&self) -> &Tasks {
296        &self.tasks
297    }
298}
299
300impl Tasks {
301    /// Iterate over tasks.
302    pub fn iter(&self) -> impl Iterator<Item = &Task> {
303        self.tasks.iter()
304    }
305}
306
307impl Task {
308    pub(crate) fn new(id: Id, trace: super::task::trace::Trace) -> Self {
309        Self {
310            id,
311            trace: Trace { inner: trace },
312        }
313    }
314
315    /// Returns a [task ID] that uniquely identifies this task relative to other
316    /// tasks spawned at the time of the dump.
317    ///
318    /// **Note**: This is an [unstable API][unstable]. The public API of this type
319    /// may break in 1.x releases. See [the documentation on unstable
320    /// features][unstable] for details.
321    ///
322    /// [task ID]: crate::task::Id
323    /// [unstable]: crate#unstable-features
324    #[cfg(tokio_unstable)]
325    #[cfg_attr(docsrs, doc(cfg(tokio_unstable)))]
326    pub fn id(&self) -> Id {
327        self.id
328    }
329
330    /// A trace of this task's state.
331    pub fn trace(&self) -> &Trace {
332        &self.trace
333    }
334}
335
336impl fmt::Display for Trace {
337    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
338        self.inner.fmt(f)
339    }
340}