mirror of
https://github.com/tokio-rs/tokio.git
synced 2025-10-04 12:24:36 +00:00
taskdump: add accessor methods for backtrace (#6975)
This commit is contained in:
parent
eb72ddde3b
commit
48e07a6d10
@ -1,4 +1,4 @@
|
|||||||
292
|
298
|
||||||
&
|
&
|
||||||
+
|
+
|
||||||
<
|
<
|
||||||
@ -30,6 +30,8 @@
|
|||||||
450ms
|
450ms
|
||||||
50ms
|
50ms
|
||||||
8MB
|
8MB
|
||||||
|
ABI
|
||||||
|
accessors
|
||||||
adaptor
|
adaptor
|
||||||
adaptors
|
adaptors
|
||||||
Adaptors
|
Adaptors
|
||||||
@ -75,8 +77,11 @@ datagrams
|
|||||||
deallocate
|
deallocate
|
||||||
deallocated
|
deallocated
|
||||||
Deallocates
|
Deallocates
|
||||||
|
debuginfo
|
||||||
decrementing
|
decrementing
|
||||||
|
demangled
|
||||||
dequeued
|
dequeued
|
||||||
|
dereferenced
|
||||||
deregister
|
deregister
|
||||||
deregistered
|
deregistered
|
||||||
deregistering
|
deregistering
|
||||||
@ -133,6 +138,7 @@ implementers
|
|||||||
implementor
|
implementor
|
||||||
implementors
|
implementors
|
||||||
incrementing
|
incrementing
|
||||||
|
inlining
|
||||||
interoperate
|
interoperate
|
||||||
invariants
|
invariants
|
||||||
Invariants
|
Invariants
|
||||||
@ -291,4 +297,3 @@ Wakers
|
|||||||
wakeup
|
wakeup
|
||||||
wakeups
|
wakeups
|
||||||
workstealing
|
workstealing
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
//! Snapshots of runtime state.
|
//! Snapshots of runtime state.
|
||||||
//!
|
//!
|
||||||
//! See [Handle::dump][crate::runtime::Handle::dump].
|
//! See [`Handle::dump`][crate::runtime::Handle::dump].
|
||||||
|
|
||||||
use crate::task::Id;
|
use crate::task::Id;
|
||||||
use std::fmt;
|
use std::{fmt, path::Path};
|
||||||
|
|
||||||
/// A snapshot of a runtime's state.
|
/// A snapshot of a runtime's state.
|
||||||
///
|
///
|
||||||
/// See [Handle::dump][crate::runtime::Handle::dump].
|
/// See [`Handle::dump`][crate::runtime::Handle::dump].
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Dump {
|
pub struct Dump {
|
||||||
tasks: Tasks,
|
tasks: Tasks,
|
||||||
@ -15,7 +15,7 @@ pub struct Dump {
|
|||||||
|
|
||||||
/// Snapshots of tasks.
|
/// Snapshots of tasks.
|
||||||
///
|
///
|
||||||
/// See [Handle::dump][crate::runtime::Handle::dump].
|
/// See [`Handle::dump`][crate::runtime::Handle::dump].
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Tasks {
|
pub struct Tasks {
|
||||||
tasks: Vec<Task>,
|
tasks: Vec<Task>,
|
||||||
@ -23,21 +23,199 @@ pub struct Tasks {
|
|||||||
|
|
||||||
/// A snapshot of a task.
|
/// A snapshot of a task.
|
||||||
///
|
///
|
||||||
/// See [Handle::dump][crate::runtime::Handle::dump].
|
/// See [`Handle::dump`][crate::runtime::Handle::dump].
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Task {
|
pub struct Task {
|
||||||
id: Id,
|
id: Id,
|
||||||
trace: Trace,
|
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 struct provides accessors for backtrace symbols, similar to [`backtrace::BacktraceSymbol`].
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct BacktraceSymbol {
|
||||||
|
name: Option<Box<[u8]>>,
|
||||||
|
name_demangled: Option<Box<str>>,
|
||||||
|
addr: Option<Address>,
|
||||||
|
filename: Option<std::path::PathBuf>,
|
||||||
|
lineno: Option<u32>,
|
||||||
|
colno: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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).into()),
|
||||||
|
addr: sym.addr().map(Address),
|
||||||
|
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.map(|addr| addr.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<u32> {
|
||||||
|
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<u32> {
|
||||||
|
self.colno
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A backtrace frame.
|
||||||
|
///
|
||||||
|
/// This struct represents one stack frame in a captured backtrace, similar to [`backtrace::BacktraceFrame`].
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct BacktraceFrame {
|
||||||
|
ip: Address,
|
||||||
|
symbol_address: Address,
|
||||||
|
symbols: Box<[BacktraceSymbol]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BacktraceFrame {
|
||||||
|
pub(crate) fn from_resolved_backtrace_frame(frame: &backtrace::BacktraceFrame) -> Self {
|
||||||
|
Self {
|
||||||
|
ip: Address(frame.ip()),
|
||||||
|
symbol_address: 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.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.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<Item = &BacktraceSymbol> {
|
||||||
|
self.symbols.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A captured backtrace.
|
||||||
|
///
|
||||||
|
/// 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.
|
/// An execution trace of a task's last poll.
|
||||||
///
|
///
|
||||||
/// See [Handle::dump][crate::runtime::Handle::dump].
|
/// <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)]
|
#[derive(Debug)]
|
||||||
pub struct Trace {
|
pub struct Trace {
|
||||||
inner: super::task::trace::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()
|
||||||
|
.iter()
|
||||||
|
.map(|backtrace| {
|
||||||
|
let mut backtrace = backtrace::Backtrace::from(backtrace.clone());
|
||||||
|
backtrace.resolve();
|
||||||
|
Backtrace {
|
||||||
|
frames: backtrace
|
||||||
|
.frames()
|
||||||
|
.iter()
|
||||||
|
.map(BacktraceFrame::from_resolved_backtrace_frame)
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Dump {
|
impl Dump {
|
||||||
pub(crate) fn new(tasks: Vec<Task>) -> Self {
|
pub(crate) fn new(tasks: Vec<Task>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -138,6 +138,10 @@ impl Trace {
|
|||||||
pub(crate) fn root<F>(future: F) -> Root<F> {
|
pub(crate) fn root<F>(future: F) -> Root<F> {
|
||||||
Root { future }
|
Root { future }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn backtraces(&self) -> &[Backtrace] {
|
||||||
|
&self.backtraces
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If this is a sub-invocation of [`Trace::capture`], capture a backtrace.
|
/// If this is a sub-invocation of [`Trace::capture`], capture a backtrace.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user