Files
ansi_term
backtrace
backtrace_sys
bincode
byteorder
cfg_if
crossbeam_channel
crossbeam_utils
difference
failure
failure_derive
lazy_static
libc
lz4
lz4_sys
min_max_heap
pretty_assertions
proc_macro2
quote
rustc_demangle
serde
serde_derive
shardio
smallvec
syn
synstructure
unicode_xid
  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
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use std::cell::UnsafeCell;
use std::env;
use std::ffi::OsString;
use std::fmt;
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use std::sync::Mutex;

pub use super::backtrace::Backtrace;

const GENERAL_BACKTRACE: &str = "RUST_BACKTRACE";
const FAILURE_BACKTRACE: &str = "RUST_FAILURE_BACKTRACE";

pub(super) struct InternalBacktrace {
    backtrace: Option<MaybeResolved>,
}

struct MaybeResolved {
    resolved: Mutex<bool>,
    backtrace: UnsafeCell<Backtrace>,
}

unsafe impl Send for MaybeResolved {}
unsafe impl Sync for MaybeResolved {}

impl InternalBacktrace {
    pub(super) fn new() -> InternalBacktrace {
        static ENABLED: AtomicUsize = ATOMIC_USIZE_INIT;

        match ENABLED.load(Ordering::SeqCst) {
            0 => {
                let enabled = is_backtrace_enabled(|var| env::var_os(var));
                ENABLED.store(enabled as usize + 1, Ordering::SeqCst);
                if !enabled {
                    return InternalBacktrace { backtrace: None }
                }
            }
            1 => return InternalBacktrace { backtrace: None },
            _ => {}
        }

        InternalBacktrace {
            backtrace: Some(MaybeResolved {
                resolved: Mutex::new(false),
                backtrace: UnsafeCell::new(Backtrace::new_unresolved()),
            }),
        }
    }

    pub(super) fn none() -> InternalBacktrace {
        InternalBacktrace { backtrace: None }
    }

    pub(super) fn as_backtrace(&self) -> Option<&Backtrace> {
        let bt = match self.backtrace {
            Some(ref bt) => bt,
            None => return None,
        };
        let mut resolved = bt.resolved.lock().unwrap();
        unsafe {
            if !*resolved {
                (*bt.backtrace.get()).resolve();
                *resolved = true;
            }
            Some(&*bt.backtrace.get())
        }
    }

    pub(super) fn is_none(&self) -> bool {
        self.backtrace.is_none()
    }
}

impl fmt::Debug for InternalBacktrace {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("InternalBacktrace")
            .field("backtrace", &self.as_backtrace())
            .finish()
    }
}

fn is_backtrace_enabled<F: Fn(&str) -> Option<OsString>>(get_var: F) -> bool {
    match get_var(FAILURE_BACKTRACE) {
        Some(ref val) if val != "0" => true,
        Some(ref val) if val == "0" => false,
        _ => match get_var(GENERAL_BACKTRACE) {
            Some(ref val) if val != "0" => true,
            _                           => false,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    const YEA: Option<&str> = Some("1");
    const NAY: Option<&str> = Some("0");
    const NOT_SET: Option<&str> = None;

    macro_rules! test_enabled {
        (failure: $failure:ident, general: $general:ident => $result:expr) => {{
            assert_eq!(is_backtrace_enabled(|var| match var {
                FAILURE_BACKTRACE   => $failure.map(OsString::from),
                GENERAL_BACKTRACE   => $general.map(OsString::from),
                _                   => panic!()
            }), $result);
        }}
    }

    #[test]
    fn always_enabled_if_failure_is_set_to_yes() {
        test_enabled!(failure: YEA, general: YEA => true);
        test_enabled!(failure: YEA, general: NOT_SET => true);
        test_enabled!(failure: YEA, general: NAY => true);
    }

    #[test]
    fn never_enabled_if_failure_is_set_to_no() {
        test_enabled!(failure: NAY, general: YEA => false);
        test_enabled!(failure: NAY, general: NOT_SET => false);
        test_enabled!(failure: NAY, general: NAY => false);
    }

    #[test]
    fn follows_general_if_failure_is_not_set() {
        test_enabled!(failure: NOT_SET, general: YEA => true);
        test_enabled!(failure: NOT_SET, general: NOT_SET => false);
        test_enabled!(failure: NOT_SET, general: NAY => false);
    }
}