alt_failure/backtrace/
internal.rs1use std::cell::UnsafeCell;
2use std::env;
3use std::ffi::OsString;
4use std::fmt;
5#[allow(deprecated)] use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
7use std::sync::Mutex;
8
9pub use super::backtrace::Backtrace;
10
11const GENERAL_BACKTRACE: &str = "RUST_BACKTRACE";
12const FAILURE_BACKTRACE: &str = "RUST_FAILURE_BACKTRACE";
13
14pub(super) struct InternalBacktrace {
15 backtrace: Option<MaybeResolved>,
16}
17
18struct MaybeResolved {
19 resolved: Mutex<bool>,
20 backtrace: UnsafeCell<Backtrace>,
21}
22
23unsafe impl Send for MaybeResolved {}
24unsafe impl Sync for MaybeResolved {}
25
26impl InternalBacktrace {
27 pub(super) fn new() -> InternalBacktrace {
28 #[allow(deprecated)] static ENABLED: AtomicUsize = ATOMIC_USIZE_INIT;
30
31 match ENABLED.load(Ordering::SeqCst) {
32 0 => {
33 let enabled = is_backtrace_enabled(|var| env::var_os(var));
34 ENABLED.store(enabled as usize + 1, Ordering::SeqCst);
35 if !enabled {
36 return InternalBacktrace { backtrace: None }
37 }
38 }
39 1 => return InternalBacktrace { backtrace: None },
40 _ => {}
41 }
42
43 InternalBacktrace {
44 backtrace: Some(MaybeResolved {
45 resolved: Mutex::new(false),
46 backtrace: UnsafeCell::new(Backtrace::new_unresolved()),
47 }),
48 }
49 }
50
51 pub(super) fn none() -> InternalBacktrace {
52 InternalBacktrace { backtrace: None }
53 }
54
55 pub(super) fn as_backtrace(&self) -> Option<&Backtrace> {
56 let bt = match self.backtrace {
57 Some(ref bt) => bt,
58 None => return None,
59 };
60 let mut resolved = bt.resolved.lock().unwrap();
61 unsafe {
62 if !*resolved {
63 (*bt.backtrace.get()).resolve();
64 *resolved = true;
65 }
66 Some(&*bt.backtrace.get())
67 }
68 }
69
70 pub(super) fn is_none(&self) -> bool {
71 self.backtrace.is_none()
72 }
73}
74
75impl fmt::Debug for InternalBacktrace {
76 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77 f.debug_struct("InternalBacktrace")
78 .field("backtrace", &self.as_backtrace())
79 .finish()
80 }
81}
82
83fn is_backtrace_enabled<F: Fn(&str) -> Option<OsString>>(get_var: F) -> bool {
84 match get_var(FAILURE_BACKTRACE) {
85 Some(ref val) if val != "0" => true,
86 Some(ref val) if val == "0" => false,
87 _ => match get_var(GENERAL_BACKTRACE) {
88 Some(ref val) if val != "0" => true,
89 _ => false,
90 }
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 const YEA: Option<&str> = Some("1");
99 const NAY: Option<&str> = Some("0");
100 const NOT_SET: Option<&str> = None;
101
102 macro_rules! test_enabled {
103 (failure: $failure:ident, general: $general:ident => $result:expr) => {{
104 assert_eq!(is_backtrace_enabled(|var| match var {
105 FAILURE_BACKTRACE => $failure.map(OsString::from),
106 GENERAL_BACKTRACE => $general.map(OsString::from),
107 _ => panic!()
108 }), $result);
109 }}
110 }
111
112 #[test]
113 fn always_enabled_if_failure_is_set_to_yes() {
114 test_enabled!(failure: YEA, general: YEA => true);
115 test_enabled!(failure: YEA, general: NOT_SET => true);
116 test_enabled!(failure: YEA, general: NAY => true);
117 }
118
119 #[test]
120 fn never_enabled_if_failure_is_set_to_no() {
121 test_enabled!(failure: NAY, general: YEA => false);
122 test_enabled!(failure: NAY, general: NOT_SET => false);
123 test_enabled!(failure: NAY, general: NAY => false);
124 }
125
126 #[test]
127 fn follows_general_if_failure_is_not_set() {
128 test_enabled!(failure: NOT_SET, general: YEA => true);
129 test_enabled!(failure: NOT_SET, general: NOT_SET => false);
130 test_enabled!(failure: NOT_SET, general: NAY => false);
131 }
132}