tracing_mutex/
reporting.rs

1//! Cycle reporting primitives
2//!
3//! This module exposes [`Dep`], which resolves to either something that tracks dependencies or to
4//! something that doesn't.  It should only be assumed to implement the [`Reportable`] trait.
5use std::backtrace::Backtrace;
6use std::borrow::Cow;
7use std::fmt::Write;
8use std::sync::Arc;
9
10#[cfg(feature = "backtraces")]
11pub type Dep = MutexDep<Arc<Backtrace>>;
12#[cfg(not(feature = "backtraces"))]
13pub type Dep = MutexDep<()>;
14
15// Base message to be reported when cycle is detected
16const BASE_MESSAGE: &str = "Found cycle in mutex dependency graph:";
17
18pub trait Reportable: Clone {
19    /// Capture the current state
20    fn capture() -> Self;
21
22    /// Format a trace of state for human readable consumption.
23    fn panic_message(trace: &[Self]) -> Cow<'static, str>;
24}
25
26#[derive(Clone)]
27pub struct MutexDep<T>(T);
28
29/// Use a unit as tracing data: no tracing.
30///
31/// This should have no runtime overhead for capturing traces and should therefore be cheap enough
32/// for most purposes.
33impl Reportable for MutexDep<()> {
34    fn capture() -> Self {
35        Self(())
36    }
37
38    fn panic_message(_trace: &[Self]) -> Cow<'static, str> {
39        Cow::Borrowed(BASE_MESSAGE)
40    }
41}
42
43/// Use a full backtrace as tracing data
44///
45/// Capture the entire backtrace which may be expensive. This implementation does not force capture
46/// in the event that backtraces are disabled at runtime, so the exact overhead can still be
47/// controlled a little.
48///
49/// N.B. the [`Backtrace`] needs to be wrapped in an Arc as backtraces are not [`Clone`].
50impl Reportable for MutexDep<Arc<Backtrace>> {
51    fn capture() -> Self {
52        Self(Arc::new(Backtrace::capture()))
53    }
54
55    fn panic_message(trace: &[Self]) -> Cow<'static, str> {
56        let mut message = format!("{BASE_MESSAGE}\n");
57
58        for entry in trace {
59            let _ = writeln!(message, "{}", entry.0);
60        }
61
62        message.into()
63    }
64}