Skip to main content

ntex_error/
bt.rs

1//! Backtrace
2use std::collections::HashMap;
3use std::hash::{BuildHasher, Hasher};
4use std::panic::Location;
5use std::{cell::RefCell, fmt, fmt::Write, os, ptr, sync::Arc};
6
7use backtrace::{BacktraceFmt, BacktraceFrame, BytesOrWideString};
8
9thread_local! {
10    static FRAMES: RefCell<HashMap<*mut os::raw::c_void, BacktraceFrame>> = RefCell::new(HashMap::default());
11    static REPRS: RefCell<HashMap<u64, Arc<str>>> = RefCell::new(HashMap::default());
12}
13static mut START: Option<(&'static str, u32)> = None;
14static mut START_ALT: Option<(&'static str, u32)> = None;
15
16pub fn set_backtrace_start(file: &'static str, line: u32) {
17    unsafe {
18        START = Some((file, line));
19    }
20}
21
22#[doc(hidden)]
23pub fn set_backtrace_start_alt(file: &'static str, line: u32) {
24    unsafe {
25        START_ALT = Some((file, line));
26    }
27}
28
29#[derive(Clone)]
30/// Representation of a backtrace.
31///
32/// This structure can be used to capture a backtrace at various
33/// points in a program and later used to inspect what the backtrace
34/// was at that time.
35pub struct Backtrace(Arc<str>);
36
37impl Backtrace {
38    /// Create new backtrace
39    pub fn new(loc: &Location<'_>) -> Self {
40        let repr = FRAMES.with(|c| {
41            let mut cache = c.borrow_mut();
42            let mut idx = 0;
43            let mut st = foldhash::fast::FixedState::default().build_hasher();
44            let mut idxs: [*mut os::raw::c_void; 128] = [ptr::null_mut(); 128];
45
46            backtrace::trace(|frm| {
47                let ip = frm.ip();
48                st.write_usize(ip as usize);
49                cache.entry(ip).or_insert_with(|| {
50                    let mut f = BacktraceFrame::from(frm.clone());
51                    f.resolve();
52                    f
53                });
54                idxs[idx] = ip;
55                idx += 1;
56
57                idx < 128
58            });
59
60            let id = st.finish();
61
62            REPRS.with(|r| {
63                let mut reprs = r.borrow_mut();
64                if let Some(repr) = reprs.get(&id) {
65                    repr.clone()
66                } else {
67                    let mut frames: [Option<&BacktraceFrame>; 128] = [None; 128];
68                    for (idx, ip) in idxs.as_ref().iter().enumerate() {
69                        if !ip.is_null() {
70                            frames[idx] = Some(&cache[ip]);
71                        }
72                    }
73
74                    find_loc(loc, &mut frames);
75
76                    #[allow(static_mut_refs)]
77                    {
78                        if let Some(start) = unsafe { START } {
79                            find_loc_start(start, &mut frames);
80                        }
81                        if let Some(start) = unsafe { START_ALT } {
82                            find_loc_start(start, &mut frames);
83                        }
84                    }
85
86                    let bt = Bt(&frames[..]);
87                    let mut buf = String::new();
88                    let _ = write!(&mut buf, "\n{bt:?}");
89                    let repr: Arc<str> = Arc::from(buf);
90                    reprs.insert(id, repr.clone());
91                    repr
92                }
93            })
94        });
95
96        Self(repr)
97    }
98
99    /// Backtrace repr
100    pub fn repr(&self) -> &str {
101        &self.0
102    }
103}
104
105fn find_loc(loc: &Location<'_>, frames: &mut [Option<&BacktraceFrame>]) {
106    for (idx, frm) in frames.iter_mut().enumerate() {
107        if let Some(f) = frm {
108            for sym in f.symbols() {
109                if let Some(fname) = sym.filename()
110                    && let Some(lineno) = sym.lineno()
111                    && fname.ends_with(loc.file())
112                    && lineno == loc.line()
113                {
114                    for f in frames.iter_mut().take(idx) {
115                        *f = None;
116                    }
117                    return;
118                }
119            }
120        } else {
121            break;
122        }
123    }
124}
125
126fn find_loc_start(loc: (&str, u32), frames: &mut [Option<&BacktraceFrame>]) {
127    let mut idx = 0;
128    while idx < frames.len() {
129        if let Some(frm) = &frames[idx] {
130            for sym in frm.symbols() {
131                if let Some(fname) = sym.filename()
132                    && let Some(lineno) = sym.lineno()
133                    && fname.ends_with(loc.0)
134                    && (loc.1 == 0 || lineno == loc.1)
135                {
136                    for f in frames.iter_mut().skip(idx) {
137                        if f.is_some() {
138                            *f = None;
139                        } else {
140                            return;
141                        }
142                    }
143                    break;
144                }
145            }
146        }
147        idx += 1;
148    }
149}
150
151struct Bt<'a>(&'a [Option<&'a BacktraceFrame>]);
152
153impl fmt::Debug for Bt<'_> {
154    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
155        let cwd = std::env::current_dir();
156        let mut print_path =
157            move |fmt: &mut fmt::Formatter<'_>, path: BytesOrWideString<'_>| {
158                let path = path.into_path_buf();
159                if let Ok(cwd) = &cwd
160                    && let Ok(suffix) = path.strip_prefix(cwd)
161                {
162                    return fmt::Display::fmt(&suffix.display(), fmt);
163                }
164                fmt::Display::fmt(&path.display(), fmt)
165            };
166
167        let mut f = BacktraceFmt::new(fmt, backtrace::PrintFmt::Short, &mut print_path);
168        f.add_context()?;
169        for frm in self.0.iter().flatten() {
170            f.frame().backtrace_frame(frm)?;
171        }
172        f.finish()?;
173        Ok(())
174    }
175}
176
177impl fmt::Debug for Backtrace {
178    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179        fmt::Display::fmt(&self.0, f)
180    }
181}
182
183impl fmt::Display for Backtrace {
184    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185        fmt::Display::fmt(&self.0, f)
186    }
187}