From 47ee059c9753dc6f92e8b508579244b32f02ac6b Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Thu, 14 Nov 2024 16:47:07 +0000 Subject: [PATCH 01/10] allow getting task dump backtraces in a programmatic format Fixes #6309. --- spellcheck.dic | 6 ++ tokio/src/runtime/dump.rs | 146 +++++++++++++++++++++++++++- tokio/src/runtime/task/trace/mod.rs | 4 + 3 files changed, 155 insertions(+), 1 deletion(-) diff --git a/spellcheck.dic b/spellcheck.dic index f368d2d7214..80b645ea150 100644 --- a/spellcheck.dic +++ b/spellcheck.dic @@ -30,6 +30,7 @@ 450ms 50ms 8MB +ABI adaptor adaptors Adaptors @@ -44,7 +45,9 @@ awaitable backend backpressure backtrace +BacktraceFrame backtraces +BacktraceSymbol backtracing binded bitfield @@ -75,7 +78,9 @@ datagrams deallocate deallocated Deallocates +debuginfo decrementing +demangled dequeued deregister deregistered @@ -133,6 +138,7 @@ implementers implementor implementors incrementing +inlining interoperate invariants Invariants diff --git a/tokio/src/runtime/dump.rs b/tokio/src/runtime/dump.rs index aea2381127b..7bcec99d765 100644 --- a/tokio/src/runtime/dump.rs +++ b/tokio/src/runtime/dump.rs @@ -3,7 +3,7 @@ //! See [Handle::dump][crate::runtime::Handle::dump]. use crate::task::Id; -use std::fmt; +use std::{fmt, path::Path}; /// A snapshot of a runtime's state. /// @@ -30,14 +30,158 @@ pub struct Task { trace: Trace, } +/// A backtrace symbol. This is similar to [backtrace::BacktraceSymbol], +/// but is a separate struct to avoid public dependency issues. +/// +/// This struct is guaranteed to be pure data and operations involving +/// it will not call platform functions that take an unpredictable amount +/// of time to finish. +#[derive(Clone, Debug)] +pub struct BacktraceSymbol { + name: Option>, + name_demangled: Option, + addr: Option<*mut std::ffi::c_void>, + filename: Option, + lineno: Option, + colno: Option, +} + +impl BacktraceSymbol { + pub(crate) fn from_backtrace_symbol(sym: &backtrace::BacktraceSymbol) -> Self { + let name = sym.name(); + Self { + name: name.as_ref().map(|name| name.as_bytes().into()), + name_demangled: name.map(|name| format!("{}", name)), + addr: sym.addr(), + filename: sym.filename().map(From::from), + lineno: sym.lineno(), + colno: sym.colno(), + } + } + + /// Return the raw name of the symbol. + pub fn name_raw(&self) -> Option<&[u8]> { + self.name.as_deref() + } + + /// Return the demangled name of the symbol. + pub fn name_demangled(&self) -> Option<&str> { + self.name_demangled.as_deref() + } + + /// Returns the starting address of this symbol. + pub fn addr(&self) -> Option<*mut std::ffi::c_void> { + self.addr + } + + /// Returns the file name where this function was defined. If debuginfo + /// is missing, this is likely to return None. + pub fn filename(&self) -> Option<&Path> { + self.filename.as_deref() + } + + /// Returns the line number for where this symbol is currently executing If debuginfo + /// is missing, this is likely to return None. + pub fn lineno(&self) -> Option { + self.lineno + } + + /// Returns the column number for where this symbol is currently executing If debuginfo + /// is missing, this is likely to return None. + pub fn colno(&self) -> Option { + self.colno + } +} + +/// A backtrace frame. This is similar to [backtrace::BacktraceFrame], +/// but is a separate struct to avoid public dependency issues. +/// +/// This struct is guaranteed to be pure data and operations involving +/// it will not call platform functions that take an unpredictable amount +/// of time to finish. +#[derive(Clone, Debug)] +pub struct BacktraceFrame { + ip: *mut std::ffi::c_void, + symbol_address: *mut std::ffi::c_void, + symbols: Vec, +} + +impl BacktraceFrame { + pub(crate) fn from_resolved_backtrace_frame(frame: &backtrace::BacktraceFrame) -> Self { + Self { + ip: frame.ip(), + symbol_address: frame.symbol_address(), + symbols: frame + .symbols() + .iter() + .map(BacktraceSymbol::from_backtrace_symbol) + .collect(), + } + } + + /// Return the instruction pointer of this frame. + /// + /// See the ABI docs for your platform for the exact meaning. + pub fn ip(&self) -> *mut std::ffi::c_void { + self.ip + } + + /// Returns the starting symbol address of the frame of this function. + pub fn symbol_address(&self) -> *mut std::ffi::c_void { + self.symbol_address + } + + /// Return an iterator over the symbols of this backtrace frame. + /// + /// Due to inlining, it is possible for there to be multiple [BacktraceSymbol] items relating + /// to a single frame. The first symbol listed is the "innermost function", + /// whereas the last symbol is the outermost (last caller). + pub fn symbols(&self) -> impl Iterator { + self.symbols.iter() + } +} + /// An execution trace of a task's last poll. /// +///
+/// Resolving a backtrace, either via the [`Display`][std::fmt::Display] impl or via +/// [`resolve_backtraces`][Trace::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()][crate::task::spawn_blocking] +/// and to have some concurrency-limiting mechanism to avoid unexpected performance impact. +///
+/// /// See [Handle::dump][crate::runtime::Handle::dump]. #[derive(Debug)] pub struct Trace { inner: super::task::trace::Trace, } +impl Trace { + /// Resolve and return a list of backtraces that are involved in polls in this task. + pub fn resolve_backtraces(&self) -> Vec> { + self.inner + .backtraces() + .iter() + .map(|backtrace| { + let mut backtrace = backtrace::Backtrace::from(backtrace.clone()); + backtrace.resolve(); + backtrace + .frames() + .iter() + .map(BacktraceFrame::from_resolved_backtrace_frame) + .collect() + }) + .collect() + } +} + impl Dump { pub(crate) fn new(tasks: Vec) -> Self { Self { diff --git a/tokio/src/runtime/task/trace/mod.rs b/tokio/src/runtime/task/trace/mod.rs index bb411f42d72..851e96a73a1 100644 --- a/tokio/src/runtime/task/trace/mod.rs +++ b/tokio/src/runtime/task/trace/mod.rs @@ -138,6 +138,10 @@ impl Trace { pub(crate) fn root(future: F) -> Root { Root { future } } + + pub(crate) fn backtraces(&self) -> &[Backtrace] { + &self.backtraces + } } /// If this is a sub-invocation of [`Trace::capture`], capture a backtrace. From dc8c526ee2a445a8465ddce494aab08cf422b3bb Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Fri, 22 Nov 2024 14:35:46 +0000 Subject: [PATCH 02/10] better docs --- tokio/src/runtime/dump.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tokio/src/runtime/dump.rs b/tokio/src/runtime/dump.rs index 7bcec99d765..c1311829847 100644 --- a/tokio/src/runtime/dump.rs +++ b/tokio/src/runtime/dump.rs @@ -164,7 +164,16 @@ pub struct Trace { } impl Trace { - /// Resolve and return a list of backtraces that are involved in polls in this task. + /// 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 (one [`Vec`]) 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. + /// + + /// [`poll`]: std::future::Future::poll + /// [`join!`]: macro@join pub fn resolve_backtraces(&self) -> Vec> { self.inner .backtraces() From 7aaf2e7f1f89ac6fbbf899c909bccdbbb39679a7 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sat, 30 Nov 2024 16:54:23 +0000 Subject: [PATCH 03/10] address more review comments --- tokio/src/runtime/dump.rs | 42 +++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/tokio/src/runtime/dump.rs b/tokio/src/runtime/dump.rs index c1311829847..707e0cc5bda 100644 --- a/tokio/src/runtime/dump.rs +++ b/tokio/src/runtime/dump.rs @@ -38,8 +38,8 @@ pub struct Task { /// of time to finish. #[derive(Clone, Debug)] pub struct BacktraceSymbol { - name: Option>, - name_demangled: Option, + name: Option>, + name_demangled: Option>, addr: Option<*mut std::ffi::c_void>, filename: Option, lineno: Option, @@ -51,7 +51,7 @@ impl BacktraceSymbol { let name = sym.name(); Self { name: name.as_ref().map(|name| name.as_bytes().into()), - name_demangled: name.map(|name| format!("{}", name)), + name_demangled: name.map(|name| format!("{}", name).into()), addr: sym.addr(), filename: sym.filename().map(From::from), lineno: sym.lineno(), @@ -141,6 +141,25 @@ impl BacktraceFrame { } } +/// A backtrace. This is similar to [backtrace::Backtrace], +/// but is a separate struct to avoid public dependency issues. +/// +/// This struct is guaranteed to be pure data and operations involving +/// it will not call platform functions that take an unpredictable amount +/// of time to finish. +#[derive(Clone, Debug)] +pub struct Backtrace { + frames: Box<[BacktraceFrame]>, +} + +impl Backtrace { + /// Return the frames in this backtrace, innermost (in a task dump, + /// likely to be a leaf future's poll function) first. + pub fn frames(&self) -> impl Iterator { + self.frames.iter() + } +} + /// An execution trace of a task's last poll. /// ///
@@ -167,25 +186,26 @@ impl Trace { /// 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 (one [`Vec`]) for every call to + /// 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. /// - /// [`poll`]: std::future::Future::poll /// [`join!`]: macro@join - pub fn resolve_backtraces(&self) -> Vec> { + pub fn resolve_backtraces(&self) -> Vec { self.inner .backtraces() .iter() .map(|backtrace| { let mut backtrace = backtrace::Backtrace::from(backtrace.clone()); backtrace.resolve(); - backtrace - .frames() - .iter() - .map(BacktraceFrame::from_resolved_backtrace_frame) - .collect() + Backtrace { + frames: backtrace + .frames() + .iter() + .map(BacktraceFrame::from_resolved_backtrace_frame) + .collect(), + } }) .collect() } From b8133cdabd15125089e4763fbad92dc70debdeaf Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Wed, 4 Dec 2024 14:46:12 +0000 Subject: [PATCH 04/10] address more comments --- spellcheck.dic | 2 -- tokio/src/runtime/dump.rs | 10 +++++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/spellcheck.dic b/spellcheck.dic index 80b645ea150..0ac43414b07 100644 --- a/spellcheck.dic +++ b/spellcheck.dic @@ -45,9 +45,7 @@ awaitable backend backpressure backtrace -BacktraceFrame backtraces -BacktraceSymbol backtracing binded bitfield diff --git a/tokio/src/runtime/dump.rs b/tokio/src/runtime/dump.rs index 707e0cc5bda..deb89d334d1 100644 --- a/tokio/src/runtime/dump.rs +++ b/tokio/src/runtime/dump.rs @@ -30,7 +30,7 @@ pub struct Task { trace: Trace, } -/// A backtrace symbol. This is similar to [backtrace::BacktraceSymbol], +/// A backtrace symbol. This is similar to [`backtrace::BacktraceSymbol`], /// but is a separate struct to avoid public dependency issues. /// /// This struct is guaranteed to be pure data and operations involving @@ -93,7 +93,7 @@ impl BacktraceSymbol { } } -/// A backtrace frame. This is similar to [backtrace::BacktraceFrame], +/// A backtrace frame. This is similar to [`backtrace::BacktraceFrame`], /// but is a separate struct to avoid public dependency issues. /// /// This struct is guaranteed to be pure data and operations involving @@ -106,6 +106,10 @@ pub struct BacktraceFrame { symbols: Vec, } +// Raw pointers are not actually used as pointers. +unsafe impl Send for BacktraceFrame {} +unsafe impl Sync for BacktraceFrame {} + impl BacktraceFrame { pub(crate) fn from_resolved_backtrace_frame(frame: &backtrace::BacktraceFrame) -> Self { Self { @@ -133,7 +137,7 @@ impl BacktraceFrame { /// Return an iterator over the symbols of this backtrace frame. /// - /// Due to inlining, it is possible for there to be multiple [BacktraceSymbol] items relating + /// Due to inlining, it is possible for there to be multiple [`BacktraceSymbol`] items relating /// to a single frame. The first symbol listed is the "innermost function", /// whereas the last symbol is the outermost (last caller). pub fn symbols(&self) -> impl Iterator { From 6dc30e3550c812692417321702aa00e41710b572 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Wed, 4 Dec 2024 15:41:28 +0000 Subject: [PATCH 05/10] more address review comments --- tokio/src/runtime/dump.rs | 43 +++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/tokio/src/runtime/dump.rs b/tokio/src/runtime/dump.rs index deb89d334d1..988ce278aa2 100644 --- a/tokio/src/runtime/dump.rs +++ b/tokio/src/runtime/dump.rs @@ -30,6 +30,17 @@ pub struct Task { trace: Trace, } +/// Represents an address that should not be dereferenced. +/// +/// This type exists to get the auto traits correct, the public API +/// uses raw pointers to make life easier for users. +#[derive(Copy, Clone, Debug)] +struct Address(*mut std::ffi::c_void); + +// Safe since Address should not be dereferenced +unsafe impl Send for Address {} +unsafe impl Sync for Address {} + /// A backtrace symbol. This is similar to [`backtrace::BacktraceSymbol`], /// but is a separate struct to avoid public dependency issues. /// @@ -40,7 +51,7 @@ pub struct Task { pub struct BacktraceSymbol { name: Option>, name_demangled: Option>, - addr: Option<*mut std::ffi::c_void>, + addr: Option
, filename: Option, lineno: Option, colno: Option, @@ -52,7 +63,7 @@ impl BacktraceSymbol { Self { name: name.as_ref().map(|name| name.as_bytes().into()), name_demangled: name.map(|name| format!("{}", name).into()), - addr: sym.addr(), + addr: sym.addr().map(Address), filename: sym.filename().map(From::from), lineno: sym.lineno(), colno: sym.colno(), @@ -71,7 +82,7 @@ impl BacktraceSymbol { /// Returns the starting address of this symbol. pub fn addr(&self) -> Option<*mut std::ffi::c_void> { - self.addr + self.addr.map(|addr| addr.0) } /// Returns the file name where this function was defined. If debuginfo @@ -80,14 +91,16 @@ impl BacktraceSymbol { self.filename.as_deref() } - /// Returns the line number for where this symbol is currently executing If debuginfo - /// is missing, this is likely to return None. + /// Returns the line number for where this symbol is currently executing. + /// + /// If debuginfo is missing, this is likely to return `None`. pub fn lineno(&self) -> Option { self.lineno } - /// Returns the column number for where this symbol is currently executing If debuginfo - /// is missing, this is likely to return None. + /// Returns the column number for where this symbol is currently executing. + /// + /// If debuginfo is missing, this is likely to return `None`. pub fn colno(&self) -> Option { self.colno } @@ -101,20 +114,16 @@ impl BacktraceSymbol { /// of time to finish. #[derive(Clone, Debug)] pub struct BacktraceFrame { - ip: *mut std::ffi::c_void, - symbol_address: *mut std::ffi::c_void, + ip: Address, + symbol_address: Address, symbols: Vec, } -// Raw pointers are not actually used as pointers. -unsafe impl Send for BacktraceFrame {} -unsafe impl Sync for BacktraceFrame {} - impl BacktraceFrame { pub(crate) fn from_resolved_backtrace_frame(frame: &backtrace::BacktraceFrame) -> Self { Self { - ip: frame.ip(), - symbol_address: frame.symbol_address(), + ip: Address(frame.ip()), + symbol_address: Address(frame.symbol_address()), symbols: frame .symbols() .iter() @@ -127,12 +136,12 @@ impl BacktraceFrame { /// /// See the ABI docs for your platform for the exact meaning. pub fn ip(&self) -> *mut std::ffi::c_void { - self.ip + self.ip.0 } /// Returns the starting symbol address of the frame of this function. pub fn symbol_address(&self) -> *mut std::ffi::c_void { - self.symbol_address + self.symbol_address.0 } /// Return an iterator over the symbols of this backtrace frame. From dd6a58330f31c24eeb6a702856364975bae56e82 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Wed, 4 Dec 2024 15:51:14 +0000 Subject: [PATCH 06/10] add dereferenced to spelling dict --- spellcheck.dic | 1 + 1 file changed, 1 insertion(+) diff --git a/spellcheck.dic b/spellcheck.dic index 0ac43414b07..91cef681eef 100644 --- a/spellcheck.dic +++ b/spellcheck.dic @@ -80,6 +80,7 @@ debuginfo decrementing demangled dequeued +dereferenced deregister deregistered deregistering From 832dd2e828d89a732989ed812739f77b51789522 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Wed, 4 Dec 2024 16:00:12 +0000 Subject: [PATCH 07/10] add backticks --- tokio/src/runtime/dump.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tokio/src/runtime/dump.rs b/tokio/src/runtime/dump.rs index 988ce278aa2..9159b4118d5 100644 --- a/tokio/src/runtime/dump.rs +++ b/tokio/src/runtime/dump.rs @@ -1,13 +1,13 @@ //! Snapshots of runtime state. //! -//! See [Handle::dump][crate::runtime::Handle::dump]. +//! See [`Handle::dump`][crate::runtime::Handle::dump]. use crate::task::Id; use std::{fmt, path::Path}; /// A snapshot of a runtime's state. /// -/// See [Handle::dump][crate::runtime::Handle::dump]. +/// See [`Handle::dump`][crate::runtime::Handle::dump]. #[derive(Debug)] pub struct Dump { tasks: Tasks, @@ -15,7 +15,7 @@ pub struct Dump { /// Snapshots of tasks. /// -/// See [Handle::dump][crate::runtime::Handle::dump]. +/// See [`Handle::dump`][crate::runtime::Handle::dump]. #[derive(Debug)] pub struct Tasks { tasks: Vec, @@ -23,7 +23,7 @@ pub struct Tasks { /// A snapshot of a task. /// -/// See [Handle::dump][crate::runtime::Handle::dump]. +/// See [`Handle::dump`][crate::runtime::Handle::dump]. #[derive(Debug)] pub struct Task { id: Id, @@ -154,7 +154,7 @@ impl BacktraceFrame { } } -/// A backtrace. This is similar to [backtrace::Backtrace], +/// A backtrace. This is similar to [`backtrace::Backtrace`], /// but is a separate struct to avoid public dependency issues. /// /// This struct is guaranteed to be pure data and operations involving @@ -185,11 +185,11 @@ impl Backtrace { /// 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()][crate::task::spawn_blocking] +/// that you resolve backtraces inside of a [`spawn_blocking()`][crate::task::spawn_blocking] /// and to have some concurrency-limiting mechanism to avoid unexpected performance impact. ///
/// -/// See [Handle::dump][crate::runtime::Handle::dump]. +/// See [`Handle::dump`][crate::runtime::Handle::dump]. #[derive(Debug)] pub struct Trace { inner: super::task::trace::Trace, From b0283878d07e4eb3d9541b22ec20a85b33e05d06 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Wed, 4 Dec 2024 16:03:01 +0000 Subject: [PATCH 08/10] more review comments --- tokio/src/runtime/dump.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokio/src/runtime/dump.rs b/tokio/src/runtime/dump.rs index 9159b4118d5..30e7b408bac 100644 --- a/tokio/src/runtime/dump.rs +++ b/tokio/src/runtime/dump.rs @@ -116,7 +116,7 @@ impl BacktraceSymbol { pub struct BacktraceFrame { ip: Address, symbol_address: Address, - symbols: Vec, + symbols: Box<[BacktraceSymbol]>, } impl BacktraceFrame { From 8bc4216211720d7c626a63ce5f8e2bedc23d8f51 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Wed, 4 Dec 2024 16:12:49 +0000 Subject: [PATCH 09/10] increase wordcount in spellcheck.dic --- spellcheck.dic | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spellcheck.dic b/spellcheck.dic index 91cef681eef..aa309f5e557 100644 --- a/spellcheck.dic +++ b/spellcheck.dic @@ -1,4 +1,4 @@ -292 +297 & + < @@ -296,4 +296,3 @@ Wakers wakeup wakeups workstealing - From f579f9812b3b071a6e7df03f04ea9640876f7d63 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Thu, 5 Dec 2024 12:45:34 +0200 Subject: [PATCH 10/10] Update docs Co-authored-by: Motoyuki Kimura --- spellcheck.dic | 3 ++- tokio/src/runtime/dump.rs | 22 +++++++--------------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/spellcheck.dic b/spellcheck.dic index aa309f5e557..ad73ea17bdc 100644 --- a/spellcheck.dic +++ b/spellcheck.dic @@ -1,4 +1,4 @@ -297 +298 & + < @@ -31,6 +31,7 @@ 50ms 8MB ABI +accessors adaptor adaptors Adaptors diff --git a/tokio/src/runtime/dump.rs b/tokio/src/runtime/dump.rs index 30e7b408bac..d262f3987cc 100644 --- a/tokio/src/runtime/dump.rs +++ b/tokio/src/runtime/dump.rs @@ -41,12 +41,9 @@ struct Address(*mut std::ffi::c_void); unsafe impl Send for Address {} unsafe impl Sync for Address {} -/// A backtrace symbol. This is similar to [`backtrace::BacktraceSymbol`], -/// but is a separate struct to avoid public dependency issues. +/// A backtrace symbol. /// -/// This struct is guaranteed to be pure data and operations involving -/// it will not call platform functions that take an unpredictable amount -/// of time to finish. +/// This struct provides accessors for backtrace symbols, similar to [`backtrace::BacktraceSymbol`]. #[derive(Clone, Debug)] pub struct BacktraceSymbol { name: Option>, @@ -106,12 +103,9 @@ impl BacktraceSymbol { } } -/// A backtrace frame. This is similar to [`backtrace::BacktraceFrame`], -/// but is a separate struct to avoid public dependency issues. +/// A backtrace frame. /// -/// This struct is guaranteed to be pure data and operations involving -/// it will not call platform functions that take an unpredictable amount -/// of time to finish. +/// This struct represents one stack frame in a captured backtrace, similar to [`backtrace::BacktraceFrame`]. #[derive(Clone, Debug)] pub struct BacktraceFrame { ip: Address, @@ -154,12 +148,9 @@ impl BacktraceFrame { } } -/// A backtrace. This is similar to [`backtrace::Backtrace`], -/// but is a separate struct to avoid public dependency issues. +/// A captured backtrace. /// -/// This struct is guaranteed to be pure data and operations involving -/// it will not call platform functions that take an unpredictable amount -/// of time to finish. +/// This struct provides access to each backtrace frame, similar to [`backtrace::Backtrace`]. #[derive(Clone, Debug)] pub struct Backtrace { frames: Box<[BacktraceFrame]>, @@ -176,6 +167,7 @@ impl Backtrace { /// An execution trace of a task's last poll. /// ///
+/// /// Resolving a backtrace, either via the [`Display`][std::fmt::Display] impl or via /// [`resolve_backtraces`][Trace::resolve_backtraces], parses debuginfo, which is /// possibly a CPU-expensive operation that can take a platform-specific but