mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-29 00:00:11 +02:00
runtime: include task Id in taskdumps (#6328)
Task `Id`s provide a semi-stable identifier for monitoring task state across task dumps. Fixes #6313
This commit is contained in:
+3
-2
@@ -47,9 +47,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
// capture a dump, and print each trace
|
// capture a dump, and print each trace
|
||||||
println!("{:-<80}", "");
|
println!("{:-<80}", "");
|
||||||
if let Ok(dump) = timeout(Duration::from_secs(2), handle.dump()).await {
|
if let Ok(dump) = timeout(Duration::from_secs(2), handle.dump()).await {
|
||||||
for (i, task) in dump.tasks().iter().enumerate() {
|
for task in dump.tasks().iter() {
|
||||||
|
let id = task.id();
|
||||||
let trace = task.trace();
|
let trace = task.trace();
|
||||||
println!("TASK {i}:");
|
println!("TASK {id}:");
|
||||||
println!("{trace}\n");
|
println!("{trace}\n");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
//!
|
//!
|
||||||
//! See [Handle::dump][crate::runtime::Handle::dump].
|
//! See [Handle::dump][crate::runtime::Handle::dump].
|
||||||
|
|
||||||
|
use crate::task::Id;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
/// A snapshot of a runtime's state.
|
/// A snapshot of a runtime's state.
|
||||||
@@ -25,6 +26,7 @@ pub struct Tasks {
|
|||||||
/// 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,
|
||||||
trace: Trace,
|
trace: Trace,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,12 +59,28 @@ impl Tasks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Task {
|
impl Task {
|
||||||
pub(crate) fn new(trace: super::task::trace::Trace) -> Self {
|
pub(crate) fn new(id: Id, trace: super::task::trace::Trace) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
id,
|
||||||
trace: Trace { inner: trace },
|
trace: Trace { inner: trace },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a [task ID] that uniquely identifies this task relative to other
|
||||||
|
/// tasks spawned at the time of the dump.
|
||||||
|
///
|
||||||
|
/// **Note**: This is an [unstable API][unstable]. The public API of this type
|
||||||
|
/// may break in 1.x releases. See [the documentation on unstable
|
||||||
|
/// features][unstable] for details.
|
||||||
|
///
|
||||||
|
/// [task ID]: crate::task::Id
|
||||||
|
/// [unstable]: crate#unstable-features
|
||||||
|
#[cfg(tokio_unstable)]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(tokio_unstable)))]
|
||||||
|
pub fn id(&self) -> Id {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
|
||||||
/// A trace of this task's state.
|
/// A trace of this task's state.
|
||||||
pub fn trace(&self) -> &Trace {
|
pub fn trace(&self) -> &Trace {
|
||||||
&self.trace
|
&self.trace
|
||||||
|
|||||||
@@ -470,7 +470,7 @@ impl Handle {
|
|||||||
|
|
||||||
traces = trace_current_thread(&self.shared.owned, local, &self.shared.inject)
|
traces = trace_current_thread(&self.shared.owned, local, &self.shared.inject)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(dump::Task::new)
|
.map(|(id, trace)| dump::Task::new(id, trace))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Avoid double borrow panic
|
// Avoid double borrow panic
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ impl Handle {
|
|||||||
// was created with.
|
// was created with.
|
||||||
let traces = unsafe { trace_multi_thread(owned, &mut local, synced, injection) }
|
let traces = unsafe { trace_multi_thread(owned, &mut local, synced, injection) }
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(dump::Task::new)
|
.map(|(id, trace)| dump::Task::new(id, trace))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let result = dump::Dump::new(traces);
|
let result = dump::Dump::new(traces);
|
||||||
|
|||||||
@@ -376,6 +376,17 @@ impl<S: 'static> Task<S> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a [task ID] that uniquely identifies this task relative to other
|
||||||
|
/// currently spawned tasks.
|
||||||
|
///
|
||||||
|
/// [task ID]: crate::task::Id
|
||||||
|
#[cfg(tokio_unstable)]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(tokio_unstable)))]
|
||||||
|
pub(crate) fn id(&self) -> crate::task::Id {
|
||||||
|
// Safety: The header pointer is valid.
|
||||||
|
unsafe { Header::get_id(self.raw.header_ptr()) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::loom::sync::Arc;
|
use crate::loom::sync::Arc;
|
||||||
use crate::runtime::context;
|
use crate::runtime::context;
|
||||||
use crate::runtime::scheduler::{self, current_thread, Inject};
|
use crate::runtime::scheduler::{self, current_thread, Inject};
|
||||||
|
use crate::task::Id;
|
||||||
|
|
||||||
use backtrace::BacktraceFrame;
|
use backtrace::BacktraceFrame;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
@@ -270,7 +271,7 @@ pub(in crate::runtime) fn trace_current_thread(
|
|||||||
owned: &OwnedTasks<Arc<current_thread::Handle>>,
|
owned: &OwnedTasks<Arc<current_thread::Handle>>,
|
||||||
local: &mut VecDeque<Notified<Arc<current_thread::Handle>>>,
|
local: &mut VecDeque<Notified<Arc<current_thread::Handle>>>,
|
||||||
injection: &Inject<Arc<current_thread::Handle>>,
|
injection: &Inject<Arc<current_thread::Handle>>,
|
||||||
) -> Vec<Trace> {
|
) -> Vec<(Id, Trace)> {
|
||||||
// clear the local and injection queues
|
// clear the local and injection queues
|
||||||
|
|
||||||
let mut dequeued = Vec::new();
|
let mut dequeued = Vec::new();
|
||||||
@@ -303,7 +304,7 @@ cfg_rt_multi_thread! {
|
|||||||
local: &mut multi_thread::queue::Local<Arc<multi_thread::Handle>>,
|
local: &mut multi_thread::queue::Local<Arc<multi_thread::Handle>>,
|
||||||
synced: &Mutex<Synced>,
|
synced: &Mutex<Synced>,
|
||||||
injection: &Shared<Arc<multi_thread::Handle>>,
|
injection: &Shared<Arc<multi_thread::Handle>>,
|
||||||
) -> Vec<Trace> {
|
) -> Vec<(Id, Trace)> {
|
||||||
let mut dequeued = Vec::new();
|
let mut dequeued = Vec::new();
|
||||||
|
|
||||||
// clear the local queue
|
// clear the local queue
|
||||||
@@ -331,7 +332,7 @@ cfg_rt_multi_thread! {
|
|||||||
///
|
///
|
||||||
/// This helper presumes exclusive access to each task. The tasks must not exist
|
/// This helper presumes exclusive access to each task. The tasks must not exist
|
||||||
/// in any other queue.
|
/// in any other queue.
|
||||||
fn trace_owned<S: Schedule>(owned: &OwnedTasks<S>, dequeued: Vec<Notified<S>>) -> Vec<Trace> {
|
fn trace_owned<S: Schedule>(owned: &OwnedTasks<S>, dequeued: Vec<Notified<S>>) -> Vec<(Id, Trace)> {
|
||||||
let mut tasks = dequeued;
|
let mut tasks = dequeued;
|
||||||
// Notify and trace all un-notified tasks. The dequeued tasks are already
|
// Notify and trace all un-notified tasks. The dequeued tasks are already
|
||||||
// notified and so do not need to be re-notified.
|
// notified and so do not need to be re-notified.
|
||||||
@@ -351,8 +352,9 @@ fn trace_owned<S: Schedule>(owned: &OwnedTasks<S>, dequeued: Vec<Notified<S>>) -
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|task| {
|
.map(|task| {
|
||||||
let local_notified = owned.assert_owner(task);
|
let local_notified = owned.assert_owner(task);
|
||||||
|
let id = local_notified.task.id();
|
||||||
let ((), trace) = Trace::capture(|| local_notified.run());
|
let ((), trace) = Trace::capture(|| local_notified.run());
|
||||||
trace
|
(id, trace)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -41,8 +41,9 @@ fn current_thread() {
|
|||||||
assert_eq!(tasks.len(), 3);
|
assert_eq!(tasks.len(), 3);
|
||||||
|
|
||||||
for task in tasks {
|
for task in tasks {
|
||||||
|
let id = task.id();
|
||||||
let trace = task.trace().to_string();
|
let trace = task.trace().to_string();
|
||||||
eprintln!("\n\n{trace}\n\n");
|
eprintln!("\n\n{id}:\n{trace}\n\n");
|
||||||
assert!(trace.contains("dump::a"));
|
assert!(trace.contains("dump::a"));
|
||||||
assert!(trace.contains("dump::b"));
|
assert!(trace.contains("dump::b"));
|
||||||
assert!(trace.contains("dump::c"));
|
assert!(trace.contains("dump::c"));
|
||||||
@@ -78,8 +79,9 @@ fn multi_thread() {
|
|||||||
assert_eq!(tasks.len(), 3);
|
assert_eq!(tasks.len(), 3);
|
||||||
|
|
||||||
for task in tasks {
|
for task in tasks {
|
||||||
|
let id = task.id();
|
||||||
let trace = task.trace().to_string();
|
let trace = task.trace().to_string();
|
||||||
eprintln!("\n\n{trace}\n\n");
|
eprintln!("\n\n{id}:\n{trace}\n\n");
|
||||||
assert!(trace.contains("dump::a"));
|
assert!(trace.contains("dump::a"));
|
||||||
assert!(trace.contains("dump::b"));
|
assert!(trace.contains("dump::b"));
|
||||||
assert!(trace.contains("dump::c"));
|
assert!(trace.contains("dump::c"));
|
||||||
|
|||||||
Reference in New Issue
Block a user