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::{trace_with, Root, TraceMeta};
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, return `Poll::Pending` without registering the task's waker with any driver.
231 /// This means forward progress will probably not happen unless you eventually call your future
232 /// outside of `capture`, or explicitly re-schedule the task (e.g. by calling
233 /// [`cx.waker().wake_by_ref()`][std::task::Waker::wake_by_ref]) after `capture` returns.
234 ///
235 /// [`Handle::dump`]: crate::runtime::Handle::dump
236 ///
237 /// Example usage:
238 /// ```
239 /// use std::future::Future;
240 /// use std::task::Poll;
241 /// use tokio::runtime::dump::Trace;
242 ///
243 /// # async fn test_fn() {
244 /// // some future
245 /// let mut test_future = std::pin::pin!(async move { tokio::task::yield_now().await; 0 });
246 ///
247 /// // trace it once, see what it's doing
248 /// let (trace, res) = Trace::root(std::future::poll_fn(|cx| {
249 /// let (res, trace) = Trace::capture(|| test_future.as_mut().poll(cx));
250 /// Poll::Ready((trace, res))
251 /// })).await;
252 ///
253 /// // await it to let it finish, outside of a `capture`
254 /// let output = match res {
255 /// Poll::Ready(output) => output,
256 /// Poll::Pending => test_future.await,
257 /// };
258 ///
259 /// println!("{trace}");
260 /// # }
261 /// ```
262 ///
263 /// ### Nested calls
264 ///
265 /// Nested calls to `capture` might return partial traces, but will not do any other undesirable behavior (for
266 /// example, they will not panic).
267 pub fn capture<F, R>(f: F) -> (R, Trace)
268 where
269 F: FnOnce() -> R,
270 {
271 let (res, trace) = super::task::trace::Trace::capture(f);
272 (res, Trace { inner: trace })
273 }
274
275 /// Create a root for stack traces captured using [`Trace::capture`]. Stack frames above
276 /// the root will not be captured.
277 ///
278 /// Nesting multiple [`Root`] futures is fine. Captures will stop at the first root. Not having
279 /// a [`Root`] is fine as well, but there is no guarantee on where the capture will stop.
280 pub fn root<F>(f: F) -> Root<F>
281 where
282 F: Future,
283 {
284 crate::runtime::task::trace::Trace::root(f)
285 }
286}
287
288impl Dump {
289 pub(crate) fn new(tasks: Vec<Task>) -> Self {
290 Self {
291 tasks: Tasks { tasks },
292 }
293 }
294
295 /// Tasks in this snapshot.
296 pub fn tasks(&self) -> &Tasks {
297 &self.tasks
298 }
299}
300
301impl Tasks {
302 /// Iterate over tasks.
303 pub fn iter(&self) -> impl Iterator<Item = &Task> {
304 self.tasks.iter()
305 }
306}
307
308impl Task {
309 pub(crate) fn new(id: Id, trace: super::task::trace::Trace) -> Self {
310 Self {
311 id,
312 trace: Trace { inner: trace },
313 }
314 }
315
316 /// Returns a [task ID] that uniquely identifies this task relative to other
317 /// tasks spawned at the time of the dump.
318 ///
319 /// **Note**: This is an [unstable API][unstable]. The public API of this type
320 /// may break in 1.x releases. See [the documentation on unstable
321 /// features][unstable] for details.
322 ///
323 /// [task ID]: crate::task::Id
324 /// [unstable]: crate#unstable-features
325 #[cfg(tokio_unstable)]
326 #[cfg_attr(docsrs, doc(cfg(tokio_unstable)))]
327 pub fn id(&self) -> Id {
328 self.id
329 }
330
331 /// A trace of this task's state.
332 pub fn trace(&self) -> &Trace {
333 &self.trace
334 }
335}
336
337impl fmt::Display for Trace {
338 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339 self.inner.fmt(f)
340 }
341}