use std::future::Future; use std::pin::Pin; use std::ptr; use std::sync::{Arc, Mutex}; use std::task::{Context, Poll}; use std::time::{Duration, Instant};
use tokio::runtime::dump::{trace_with, Root, Trace, TraceMeta};
if above_leaf && below_root {
frames.push(frame.to_owned().into());
}
if ptr::eq(frame.symbol_address(), meta.trace_leaf_addr) {
above_leaf = true;
}
below_root
});
}
// Resolve frames into human-readable symbol names with hashes stripped. letmut bt = backtrace::Backtrace::from(frames);
bt.resolve(); letmut names = vec![]; for frame in bt.frames() { for symbol in frame.symbols() { iflet Some(name) = symbol.name() {
names.push(strip_symbol_hash(&format!("{name}")).to_owned());
}
}
}
log.push(names);
}
/// Strip the trailing `::h<hex>` hash that rustc appends to symbol names. fn strip_symbol_hash(s: &str) -> &str { // Symbols end with "::h" followed by hex digits. Find the last "::h". iflet Some(pos) = s.rfind("::h") { let suffix = &s[pos + 3..]; if !suffix.is_empty() && suffix.chars().all(|c| c.is_ascii_hexdigit()) { return &s[..pos];
}
}
s
}
pin_project_lite::pin_project! { /// A future wrapper that uses `trace_with` to capture a backtrace on every /// poll. /// The captured backtraces are stored in `logs`. pubstruct TaskDump<F: Future> { #[pin]
f: Root<F>,
logs: Arc<Mutex<Vec<Vec<String>>>>,
}
}
// Poll the future with a real waker. if it returns Ready, exit immediately match this.f.as_mut().poll(cx) {
Poll::Ready(result) => return Poll::Ready(result),
Poll::Pending => {}
};
letmut logs = Vec::new();
// Tracing poll with a noop waker. If the future is at a yield // point, trace_leaf fires our callback and returns Pending. We discard // the result — this poll is purely for capturing the backtrace. let noop = futures::task::noop_waker(); letmut noop_cx = Context::from_waker(&noop); let trace_poll = trace_with(
|| this.f.as_mut().poll(&mut noop_cx),
|meta| trace_leaf_for_test(meta, &mut logs),
); // trace should always produce poll pending
assert!(
matches!(trace_poll, Poll::Pending), "expected trace to produce Poll::Pending but it was ready"
);
// Drain any frames captured by trace_leaf_for_test into our log.
this.logs.lock().unwrap().extend(logs);
Poll::Pending
}
}
/// Validates that `trace_with` (via the `TaskDump` wrapper): /// 1. Invokes the trace-leaf callback when the wrapped future is at a yield point. /// 2. Produces a backtrace (via `backtrace::trace`) that contains the expected /// intermediate symbols between the root and the leaf. /// 3. Does not produce spurious callbacks when the future returns `Ready`. #[tokio::test] asyncfn trace_with_callback_and_backtrace() { let logs: Arc<Mutex<Vec<Vec<String>>>> = Arc::new(Mutex::new(vec![]));
let result = TaskDump::new( async {
inner_yield_point().await; 42
},
logs.clone(),
)
.await;
// These symbols should appear in the trace in this exact order (substring match). // trace_leaf itself should NOT appear — it's the boundary frame. let expected_in_order = [ "tokio::task::yield_now::yield_now", "core::future::poll_fn::PollFn", "tokio::task::yield_now::yield_now", "task_trace_self::inner_yield_point", "task_trace_self::trace_with_callback_and_backtrace",
]; let trace = &captured[0];
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.