mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-09 00:00:08 +02:00
tracing: replace future names with spawn locations in task spans (#3074)
## Motivation Currently, the per-task `tracing` spans generated by tokio's `tracing` feature flag include the `std::any::type_name` of the future that was spawned. When future combinators and/or libraries like Tower are in use, these future names can get _quite_ long. Furthermore, when formatting the `tracing` spans with their parent spans as context, any other task spans in the span context where the future was spawned from can _also_ include extremely long future names. In some cases, this can result in extremely high memory use just to store the future names. For example, in Linkerd, when we enable `tokio=trace` to enable the task spans, there's a spawned task whose future name is _232990 characters long_. A proxy with only 14 spawned tasks generates a task list that's over 690 KB. Enabling task spans under load results in the process getting OOM killed very quickly. ## Solution This branch removes future type names from the spans generated by `spawn`. As a replacement, to allow identifying which `spawn` call a span corresponds to, the task span now contains the source code location where `spawn` was called, when the compiler supports the `#[track_caller]` attribute. Since `track_caller` was stabilized in Rust 1.46.0, and our minimum supported Rust version is 1.45.0, we can't assume that `#[track_caller]` is always available. Instead, we have a RUSTFLAGS cfg, `tokio_track_caller`, that guards whether or not we use it. I've also added a `build.rs` that detects the compiler minor version, and sets the cfg flag automatically if the current compiler version is >= 1.46. This means users shouldn't have to enable `tokio_track_caller` manually. Here's the trace output from the `chat` example, before this change:  ...and after:  Closes #3073 Signed-off-by: Eliza Weisman <[email protected]>
This commit is contained in:
+4
-1
@@ -102,7 +102,7 @@ mio = { version = "0.7.3", optional = true }
|
|||||||
num_cpus = { version = "1.8.0", optional = true }
|
num_cpus = { version = "1.8.0", optional = true }
|
||||||
parking_lot = { version = "0.11.0", optional = true } # Not in full
|
parking_lot = { version = "0.11.0", optional = true } # Not in full
|
||||||
slab = { version = "0.4.1", optional = true }
|
slab = { version = "0.4.1", optional = true }
|
||||||
tracing = { version = "0.1.16", default-features = false, features = ["std"], optional = true } # Not in full
|
tracing = { version = "0.1.21", default-features = false, features = ["std"], optional = true } # Not in full
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc = { version = "0.2.42", optional = true }
|
libc = { version = "0.2.42", optional = true }
|
||||||
@@ -126,6 +126,9 @@ tempfile = "3.1.0"
|
|||||||
[target.'cfg(loom)'.dev-dependencies]
|
[target.'cfg(loom)'.dev-dependencies]
|
||||||
loom = { version = "0.3.5", features = ["futures", "checkpoint"] }
|
loom = { version = "0.3.5", features = ["futures", "checkpoint"] }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
autocfg = "1" # Needed for conditionally enabling `track-caller`
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
use autocfg::AutoCfg;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
match AutoCfg::new() {
|
||||||
|
Ok(ac) => {
|
||||||
|
// The #[track_caller] attribute was stabilized in rustc 1.46.0.
|
||||||
|
if ac.probe_rustc_version(1, 46) {
|
||||||
|
autocfg::emit("tokio_track_caller")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(e) => {
|
||||||
|
// If we couldn't detect the compiler version and features, just
|
||||||
|
// print a warning. This isn't a fatal error: we can still build
|
||||||
|
// Tokio, we just can't enable cfgs automatically.
|
||||||
|
println!(
|
||||||
|
"cargo:warning=tokio: failed to detect compiler features: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,13 +39,26 @@ impl Handle {
|
|||||||
// context::enter(self.clone(), f)
|
// context::enter(self.clone(), f)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
/// Run the provided function on an executor dedicated to blocking operations.
|
/// Run the provided function on an executor dedicated to blocking
|
||||||
|
/// operations.
|
||||||
|
#[cfg_attr(tokio_track_caller, track_caller)]
|
||||||
pub(crate) fn spawn_blocking<F, R>(&self, func: F) -> JoinHandle<R>
|
pub(crate) fn spawn_blocking<F, R>(&self, func: F) -> JoinHandle<R>
|
||||||
where
|
where
|
||||||
F: FnOnce() -> R + Send + 'static,
|
F: FnOnce() -> R + Send + 'static,
|
||||||
{
|
{
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
let func = {
|
let func = {
|
||||||
|
#[cfg(tokio_track_caller)]
|
||||||
|
let location = std::panic::Location::caller();
|
||||||
|
#[cfg(tokio_track_caller)]
|
||||||
|
let span = tracing::trace_span!(
|
||||||
|
target: "tokio::task",
|
||||||
|
"task",
|
||||||
|
kind = %"blocking",
|
||||||
|
function = %std::any::type_name::<F>(),
|
||||||
|
spawn.location = %format_args!("{}:{}:{}", location.file(), location.line(), location.column()),
|
||||||
|
);
|
||||||
|
#[cfg(not(tokio_track_caller))]
|
||||||
let span = tracing::trace_span!(
|
let span = tracing::trace_span!(
|
||||||
target: "tokio::task",
|
target: "tokio::task",
|
||||||
"task",
|
"task",
|
||||||
|
|||||||
@@ -357,11 +357,14 @@ cfg_rt! {
|
|||||||
/// });
|
/// });
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[cfg_attr(tokio_track_caller, track_caller)]
|
||||||
pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
|
pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
|
||||||
where
|
where
|
||||||
F: Future + Send + 'static,
|
F: Future + Send + 'static,
|
||||||
F::Output: Send + 'static,
|
F::Output: Send + 'static,
|
||||||
{
|
{
|
||||||
|
#[cfg(feature = "tracing")]
|
||||||
|
let future = crate::util::trace::task(future, "task");
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
#[cfg(feature = "rt-multi-thread")]
|
#[cfg(feature = "rt-multi-thread")]
|
||||||
Kind::ThreadPool(exec) => exec.spawn(future),
|
Kind::ThreadPool(exec) => exec.spawn(future),
|
||||||
@@ -385,6 +388,7 @@ cfg_rt! {
|
|||||||
/// println!("now running on a worker thread");
|
/// println!("now running on a worker thread");
|
||||||
/// });
|
/// });
|
||||||
/// # }
|
/// # }
|
||||||
|
#[cfg_attr(tokio_track_caller, track_caller)]
|
||||||
pub fn spawn_blocking<F, R>(&self, func: F) -> JoinHandle<R>
|
pub fn spawn_blocking<F, R>(&self, func: F) -> JoinHandle<R>
|
||||||
where
|
where
|
||||||
F: FnOnce() -> R + Send + 'static,
|
F: FnOnce() -> R + Send + 'static,
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ cfg_rt_multi_thread! {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[cfg_attr(tokio_track_caller, track_caller)]
|
||||||
pub fn spawn_blocking<F, R>(f: F) -> JoinHandle<R>
|
pub fn spawn_blocking<F, R>(f: F) -> JoinHandle<R>
|
||||||
where
|
where
|
||||||
F: FnOnce() -> R + Send + 'static,
|
F: FnOnce() -> R + Send + 'static,
|
||||||
|
|||||||
@@ -190,6 +190,7 @@ cfg_rt! {
|
|||||||
/// }).await;
|
/// }).await;
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[cfg_attr(tokio_track_caller, track_caller)]
|
||||||
pub fn spawn_local<F>(future: F) -> JoinHandle<F::Output>
|
pub fn spawn_local<F>(future: F) -> JoinHandle<F::Output>
|
||||||
where
|
where
|
||||||
F: Future + 'static,
|
F: Future + 'static,
|
||||||
@@ -273,6 +274,7 @@ impl LocalSet {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
/// [`spawn_local`]: fn@spawn_local
|
/// [`spawn_local`]: fn@spawn_local
|
||||||
|
#[cfg_attr(tokio_track_caller, track_caller)]
|
||||||
pub fn spawn_local<F>(&self, future: F) -> JoinHandle<F::Output>
|
pub fn spawn_local<F>(&self, future: F) -> JoinHandle<F::Output>
|
||||||
where
|
where
|
||||||
F: Future + 'static,
|
F: Future + 'static,
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ cfg_rt! {
|
|||||||
/// ```text
|
/// ```text
|
||||||
/// error[E0391]: cycle detected when processing `main`
|
/// error[E0391]: cycle detected when processing `main`
|
||||||
/// ```
|
/// ```
|
||||||
|
#[cfg_attr(tokio_track_caller, track_caller)]
|
||||||
pub fn spawn<T>(task: T) -> JoinHandle<T::Output>
|
pub fn spawn<T>(task: T) -> JoinHandle<T::Output>
|
||||||
where
|
where
|
||||||
T: Future + Send + 'static,
|
T: Future + Send + 'static,
|
||||||
|
|||||||
+14
-34
@@ -1,47 +1,27 @@
|
|||||||
cfg_trace! {
|
cfg_trace! {
|
||||||
cfg_rt! {
|
cfg_rt! {
|
||||||
use std::future::Future;
|
pub(crate) use tracing::instrument::Instrumented;
|
||||||
use std::pin::Pin;
|
|
||||||
use std::task::{Context, Poll};
|
|
||||||
use pin_project_lite::pin_project;
|
|
||||||
|
|
||||||
use tracing::Span;
|
|
||||||
|
|
||||||
pin_project! {
|
|
||||||
/// A future that has been instrumented with a `tracing` span.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub(crate) struct Instrumented<T> {
|
|
||||||
#[pin]
|
|
||||||
inner: T,
|
|
||||||
span: Span,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Future> Future for Instrumented<T> {
|
|
||||||
type Output = T::Output;
|
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
|
||||||
let this = self.project();
|
|
||||||
let _enter = this.span.enter();
|
|
||||||
this.inner.poll(cx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Instrumented<T> {
|
|
||||||
pub(crate) fn new(inner: T, span: Span) -> Self {
|
|
||||||
Self { inner, span }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[cfg_attr(tokio_track_caller, track_caller)]
|
||||||
pub(crate) fn task<F>(task: F, kind: &'static str) -> Instrumented<F> {
|
pub(crate) fn task<F>(task: F, kind: &'static str) -> Instrumented<F> {
|
||||||
|
use tracing::instrument::Instrument;
|
||||||
|
#[cfg(tokio_track_caller)]
|
||||||
|
let location = std::panic::Location::caller();
|
||||||
|
#[cfg(tokio_track_caller)]
|
||||||
let span = tracing::trace_span!(
|
let span = tracing::trace_span!(
|
||||||
target: "tokio::task",
|
target: "tokio::task",
|
||||||
"task",
|
"task",
|
||||||
%kind,
|
%kind,
|
||||||
future = %std::any::type_name::<F>(),
|
spawn.location = %format_args!("{}:{}:{}", location.file(), location.line(), location.column()),
|
||||||
);
|
);
|
||||||
Instrumented::new(task, span)
|
#[cfg(not(tokio_track_caller))]
|
||||||
|
let span = tracing::trace_span!(
|
||||||
|
target: "tokio::task",
|
||||||
|
"task",
|
||||||
|
%kind,
|
||||||
|
);
|
||||||
|
task.instrument(span)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user