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;
9use std::sync::atomic::{AtomicBool, Ordering};
10
11#[cfg(feature = "backtraces")]
12pub type Dep = MutexDep<Arc<Backtrace>>;
13#[cfg(not(feature = "backtraces"))]
14pub type Dep = MutexDep<()>;
15
16// Base message to be reported when cycle is detected
17const BASE_MESSAGE: &str = "Found cycle in mutex dependency graph:";
18
19static SHOULD_PANIC: AtomicBool = AtomicBool::new(true);
20
21/// Call this early in main() to suppress panics when a cycle is detected and print the lock cycles
22/// to stderr instead.
23///
24/// This is not as useful a mechanism for detecting issues as a panic, but it is useful for
25/// incrementally rolling out usage of tracing-mutex to many programs that use a shared crate.
26pub fn suppress_panics() {
27    SHOULD_PANIC.store(false, Ordering::Relaxed);
28}
29
30pub(crate) fn report_cycle(cycle: &[Dep]) {
31    let message = Dep::message(cycle);
32    if SHOULD_PANIC.load(Ordering::Relaxed) {
33        panic!("{message}");
34    } else {
35        eprintln!("{message}");
36    }
37}
38
39pub trait Reportable: Clone {
40    /// Capture the current state
41    fn capture() -> Self;
42
43    /// Format a trace of state for human readable consumption.
44    fn message(trace: &[Self]) -> Cow<'static, str>;
45}
46
47#[derive(Clone)]
48pub struct MutexDep<T>(T);
49
50/// Use a unit as tracing data: no tracing.
51///
52/// This should have no runtime overhead for capturing traces and should therefore be cheap enough
53/// for most purposes.
54impl Reportable for MutexDep<()> {
55    fn capture() -> Self {
56        Self(())
57    }
58
59    fn message(_trace: &[Self]) -> Cow<'static, str> {
60        Cow::Borrowed(BASE_MESSAGE)
61    }
62}
63
64/// Use a full backtrace as tracing data
65///
66/// Capture the entire backtrace which may be expensive. This implementation does not force capture
67/// in the event that backtraces are disabled at runtime, so the exact overhead can still be
68/// controlled a little.
69///
70/// N.B. the [`Backtrace`] needs to be wrapped in an Arc as backtraces are not [`Clone`].
71impl Reportable for MutexDep<Arc<Backtrace>> {
72    fn capture() -> Self {
73        Self(Arc::new(Backtrace::capture()))
74    }
75
76    fn message(trace: &[Self]) -> Cow<'static, str> {
77        let mut message = format!("{BASE_MESSAGE}\n");
78
79        for entry in trace {
80            let _ = writeln!(message, "{}", entry.0);
81        }
82
83        message.into()
84    }
85}