Repository: tokio-rs/tokio
File: tokio/src/runtime/dump.rs (Rust)

Code before:
/// 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.
/// </div>
///
/// 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<Vec<BacktraceFrame>> {
        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<Task>) -> Self {
        Self {
            tasks: Tasks { tasks },
        }
    }

    /// Tasks in this snapshot.
    pub fn tasks(&self) -> &Tasks {
        &self.tasks
    }
}

impl Tasks {
    /// Iterate over tasks.
    pub fn iter(&self) -> impl Iterator<Item = &Task> {
        self.tasks.iter()
    }

Review comment (suggestion):
Is it possible for one `Trace` to return multiple backtraces? If such a case does not exist, I feel it would be clearer to return `Vec<BacktraceFrame>`.

Code after:
///
/// This struct provides access to each backtrace frame, similar to [`backtrace::Backtrace`].
#[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<Item = &BacktraceFrame> {
        self.frames.iter()
    }
}

/// An execution trace of a task's last poll.
///
/// <div class="warning">
///
/// 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.
/// </div>
///
/// 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 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.
    ///
    /// [`poll`]: std::future::Future::poll
    /// [`join!`]: macro@join
    pub fn resolve_backtraces(&self) -> Vec<Backtrace> {
        self.inner
            .backtraces()