async_backtrace/lib.rs
1//! Efficient, logical 'stack' traces of async functions.
2//!
3//! ## Usage
4//! To use, annotate your async functions with `#[async_backtrace::framed]`,
5//! like so:
6//!
7//! ```rust
8//! #[tokio::main]
9//! async fn main() {
10//! tokio::select! {
11//! _ = tokio::spawn(async_backtrace::frame!(pending())) => {}
12//! _ = foo() => {}
13//! };
14//! }
15//!
16//! #[async_backtrace::framed]
17//! async fn pending() {
18//! std::future::pending::<()>().await
19//! }
20//!
21//! #[async_backtrace::framed]
22//! async fn foo() {
23//! bar().await;
24//! }
25//!
26//! #[async_backtrace::framed]
27//! async fn bar() {
28//! futures::join!(fiz(), buz());
29//! }
30//!
31//! #[async_backtrace::framed]
32//! async fn fiz() {
33//! tokio::task::yield_now().await;
34//! }
35//!
36//! #[async_backtrace::framed]
37//! async fn buz() {
38//! println!("{}", baz().await);
39//! }
40//!
41//! #[async_backtrace::framed]
42//! async fn baz() -> String {
43//! async_backtrace::taskdump_tree(true)
44//! }
45//! ```
46//!
47//! This example program will print out something along the lines of:
48//!
49//! ```text
50//! ╼ taskdump::foo::{{closure}} at backtrace/examples/taskdump.rs:20:1
51//! └╼ taskdump::bar::{{closure}} at backtrace/examples/taskdump.rs:25:1
52//! ├╼ taskdump::buz::{{closure}} at backtrace/examples/taskdump.rs:35:1
53//! │ └╼ taskdump::baz::{{closure}} at backtrace/examples/taskdump.rs:40:1
54//! └╼ taskdump::fiz::{{closure}} at backtrace/examples/taskdump.rs:30:1
55//! ╼ taskdump::pending::{{closure}} at backtrace/examples/taskdump.rs:15:1
56//! ```
57//!
58//! ## Minimizing Overhead
59//! To minimize overhead, ensure that futures you spawn with your async runtime
60//! are marked with `#[framed]`.
61//!
62//! In other words, avoid doing this:
63//! ```rust
64//! # #[tokio::main] async fn main() {
65//! tokio::spawn(async {
66//! foo().await;
67//! bar().await;
68//! }).await;
69//! # }
70//!
71//! #[async_backtrace::framed] async fn foo() {}
72//! #[async_backtrace::framed] async fn bar() {}
73//! ```
74//! ...and prefer doing this:
75//! ```rust
76//! # #[tokio::main] async fn main() {
77//! tokio::spawn(async_backtrace::location!().frame(async {
78//! foo().await;
79//! bar().await;
80//! })).await;
81//! # }
82//!
83//! #[async_backtrace::framed] async fn foo() {}
84//! #[async_backtrace::framed] async fn bar() {}
85//! ```
86//!
87//! ## Estimating Overhead
88//! To estimate the overhead of adopting `#[framed]` in your application, refer
89//! to the benchmarks and interpretive guidance in
90//! `./backtrace/benches/frame_overhead.rs`. You can run these benchmarks with
91//! `cargo bench`.
92
93pub(crate) mod frame;
94pub(crate) mod framed;
95pub(crate) mod linked_list;
96pub(crate) mod location;
97pub(crate) mod tasks;
98
99pub(crate) use frame::Frame;
100pub(crate) use framed::Framed;
101pub use location::Location;
102pub use tasks::{tasks, Task};
103
104/// Include the annotated async function in backtraces and taskdumps.
105///
106/// This, for instance:
107/// ```
108/// # async fn bar() {}
109/// # async fn baz() {}
110/// #[async_backtrace::framed]
111/// async fn foo() {
112/// bar().await;
113/// baz().await;
114/// }
115/// ```
116/// ...expands, roughly, to:
117/// ```
118/// # async fn bar() {}
119/// # async fn baz() {}
120/// async fn foo() {
121/// async_backtrace::frame!(async move {
122/// bar().await;
123/// baz().await;
124/// }).await;
125/// }
126/// ```
127pub use async_backtrace_attributes::framed;
128
129/// Include the annotated async expression in backtraces and taskdumps.
130///
131/// This, for instance:
132/// ```
133/// # #[tokio::main] async fn main() {
134/// # async fn foo() {}
135/// # async fn bar() {}
136/// tokio::spawn(async_backtrace::frame!(async {
137/// foo().await;
138/// bar().await;
139/// })).await;
140/// # }
141/// ```
142/// ...expands, roughly, to:
143/// ```
144/// # #[tokio::main] async fn main() {
145/// # async fn foo() {}
146/// # async fn bar() {}
147/// tokio::spawn(async_backtrace::location!().frame(async {
148/// foo().await;
149/// bar().await;
150/// })).await;
151/// # }
152/// ```
153#[macro_export]
154macro_rules! frame {
155 ($async_expr:expr) => {
156 $crate::location!().frame($async_expr)
157 };
158}
159
160/// Produces a human-readable tree of task states.
161///
162/// If `wait_for_running_tasks` is `false`, this routine will display only the
163/// top-level location of currently-running tasks and a note that they are
164/// "POLLING". Otherwise, this routine will wait for currently-running tasks to
165/// become idle.
166///
167/// # Safety
168/// If `wait_for_running_tasks` is `true`, this routine may deadlock if any
169/// non-async lock is held which may also be held by a Framed task.
170pub fn taskdump_tree(wait_for_running_tasks: bool) -> String {
171 tasks()
172 .map(|task| task.pretty_tree(wait_for_running_tasks))
173 .collect::<Vec<String>>()
174 .join("\n")
175}
176
177/// Produces a backtrace starting at the currently-active frame (if any).
178///
179/// ## Example
180/// ```
181/// use async_backtrace::{framed, backtrace, Location};
182///
183/// #[tokio::main]
184/// async fn main() {
185/// foo().await;
186/// }
187///
188/// #[async_backtrace::framed]
189/// async fn foo() {
190/// bar().await;
191/// }
192///
193/// #[async_backtrace::framed]
194/// async fn bar() {
195/// baz().await;
196/// }
197///
198/// #[async_backtrace::framed]
199/// async fn baz() {
200/// # macro_rules! assert_eq { ($l:expr, $r:expr) => { debug_assert_eq!($l.len(), $r.len());} }
201/// assert_eq!(&async_backtrace::backtrace().unwrap().iter().map(|l| l.to_string()).collect::<Vec<_>>()[..], &[
202/// "rust_out::baz::{{closure}} at src/lib.rs:19:1",
203/// "rust_out::bar::{{closure}} at src/lib.rs:14:1",
204/// "rust_out::foo::{{closure}} at src/lib.rs:9:1",
205/// ]);
206/// }
207/// ```
208pub fn backtrace() -> Option<Box<[Location]>> {
209 Frame::with_active(|maybe_frame| maybe_frame.map(Frame::backtrace_locations))
210}
211
212pub(crate) mod sync {
213 #[cfg(loom)]
214 pub(crate) use loom::sync::Mutex;
215
216 #[cfg(not(loom))]
217 pub(crate) use std::sync::Mutex;
218
219 pub(crate) use std::sync::TryLockError;
220}
221
222pub(crate) mod cell {
223 #[cfg(loom)]
224 pub(crate) use loom::cell::{Cell, UnsafeCell};
225
226 #[cfg(not(loom))]
227 pub(crate) use std::cell::Cell;
228
229 #[cfg(not(loom))]
230 #[derive(Debug)]
231 #[repr(transparent)]
232 pub(crate) struct UnsafeCell<T>(std::cell::UnsafeCell<T>);
233
234 #[cfg(not(loom))]
235 impl<T> UnsafeCell<T> {
236 pub(crate) fn new(data: T) -> UnsafeCell<T> {
237 UnsafeCell(std::cell::UnsafeCell::new(data))
238 }
239
240 pub(crate) fn with<R>(&self, f: impl FnOnce(*const T) -> R) -> R {
241 f(self.0.get())
242 }
243
244 pub(crate) fn with_mut<R>(&self, f: impl FnOnce(*mut T) -> R) -> R {
245 f(self.0.get())
246 }
247 }
248}
249
250pub(crate) fn defer<F: FnOnce() -> R, R>(f: F) -> impl Drop {
251 struct Defer<F: FnOnce() -> R, R>(Option<F>);
252
253 impl<F: FnOnce() -> R, R> Drop for Defer<F, R> {
254 fn drop(&mut self) {
255 self.0.take().unwrap()();
256 }
257 }
258
259 Defer(Some(f))
260}
261
262#[doc(hidden)]
263/** NOT STABLE! DO NOT USE! */
264pub mod ඞ {
265 // ^ kudos to Daniel Henry-Mantilla
266 pub use crate::frame::Frame;
267}