1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/// specify whether we want to capture backtraces, and if they should be resolved
#[derive(Debug, Clone, Copy)]
pub enum BacktwrapCaptureStrategy {
    /// don't capture... Backtwrap will contain None
    DoNotCapture,
    /// capture unresolved backtraces... you can resolve them before printing
    CaptureUnresolved,
    /// capture resolved backtraces... they will have debug symbols
    CaptureResolved,
}

use BacktwrapCaptureStrategy::*;

lazy_static! {
    static ref CAPTURE_STRATEGY: std::sync::Mutex<BacktwrapCaptureStrategy> = {
        let out = std::sync::Mutex::new(match std::env::var("BACKTRACE_STRATEGY") {
            Ok(s) => match s.as_str() {
                "CAPTURE_RESOLVED" => CaptureResolved,
                "CAPTURE_UNRESOLVED" => CaptureUnresolved,
                _ => DoNotCapture,
            },
            _ => DoNotCapture,
        });
        warn!("Using Backtrace Capture Strategy: {:?}", out);
        out
    };
}

/// it seems as though
/// - linux can caputure resolved backtraces w/o much overhead
/// - macOs can capture UNresolved backtraces (fn pointers but no debug symbols)
///   without much overhead
/// - windows cannot capture backtraces at all without major slowdowns
#[derive(Shrinkwrap, Debug, Clone)]
#[shrinkwrap(mutable)]
pub struct Backtwrap(pub Option<backtrace::Backtrace>);

impl Backtwrap {
    /// Capture (or doesn't capture) backtraces based on environment variable
    ///  - default - DoNotCapture
    ///  - BACKTRACE_STRATEGY=CAPTURE_RESOLVED - CaptureResolved
    ///  - BACKTRACE_STRATEGY=CAPTURE_UNRESOLVED - CaptureUnresolved
    pub fn new() -> Self {
        Self(
            match *CAPTURE_STRATEGY.lock().expect("failed to lock mutex") {
                CaptureResolved => Some(backtrace::Backtrace::new()),
                CaptureUnresolved => Some(backtrace::Backtrace::new_unresolved()),
                DoNotCapture => None,
            },
        )
    }

    /// get the current capture strategy
    pub fn get_capture_strategy() -> BacktwrapCaptureStrategy {
        *CAPTURE_STRATEGY.lock().expect("failed to lock mutex")
    }

    /// explicitly set the current capture strategy
    pub fn set_capture_strategy(strategy: BacktwrapCaptureStrategy) {
        *CAPTURE_STRATEGY.lock().expect("failed to lock mutex") = strategy;
    }
}

impl PartialEq for Backtwrap {
    fn eq(&self, other: &Backtwrap) -> bool {
        format!("{:?}", self) == format!("{:?}", other)
    }
}

impl Eq for Backtwrap {}

impl std::hash::Hash for Backtwrap {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        format!("{:?}", self).hash(state);
    }
}

impl std::convert::From<backtrace::Backtrace> for Backtwrap {
    fn from(bt: backtrace::Backtrace) -> Backtwrap {
        Self(Some(bt))
    }
}

impl std::convert::From<Option<backtrace::Backtrace>> for Backtwrap {
    fn from(bt: Option<backtrace::Backtrace>) -> Backtwrap {
        Self(bt)
    }
}

impl std::convert::From<Backtwrap> for Option<backtrace::Backtrace> {
    fn from(bt: Backtwrap) -> Option<backtrace::Backtrace> {
        bt.0
    }
}