Skip to main content

ntex_error/
bt.rs

1//! Backtrace
2#![allow(warnings)]
3use std::collections::HashMap;
4use std::hash::{BuildHasher, Hasher};
5use std::panic::Location;
6use std::{cell::RefCell, fmt, fmt::Write, os, path, ptr, sync::Arc};
7
8use backtrace::{BacktraceFmt, BacktraceFrame, BytesOrWideString, Frame};
9
10thread_local! {
11    static FRAMES: RefCell<HashMap<*mut os::raw::c_void, BacktraceFrame>> = RefCell::new(HashMap::default());
12    static REPRS: RefCell<HashMap<u64, Arc<str>>> = RefCell::new(HashMap::default());
13}
14static mut START: Option<(&'static str, u32)> = None;
15static mut START_ALT: Option<(&'static str, u32)> = None;
16
17pub fn set_backtrace_start(file: &'static str, line: u32) {
18    unsafe {
19        START = Some((file, line));
20    }
21}
22
23#[doc(hidden)]
24pub fn set_backtrace_start_alt(file: &'static str, line: u32) {
25    unsafe {
26        START_ALT = Some((file, line));
27    }
28}
29
30#[derive(Clone)]
31/// Representation of a backtrace.
32///
33/// This structure can be used to capture a backtrace at various
34/// points in a program and later used to inspect what the backtrace
35/// was at that time.
36pub struct Backtrace(Arc<BacktraceInner>);
37
38struct BacktraceInner {
39    id: u64,
40    frames: [Option<Frame>; 80],
41    location: &'static Location<'static>,
42}
43
44impl Backtrace {
45    /// Create new backtrace
46    pub fn new(location: &'static Location<'static>) -> Self {
47        let mut st = foldhash::fast::FixedState::default().build_hasher();
48        let mut idx = 0;
49        let mut frames: [Option<Frame>; 80] = [const { None }; 80];
50
51        backtrace::trace(|frm| {
52            let ip = frm.ip();
53            st.write_usize(ip as usize);
54            frames[idx] = Some(frm.clone());
55            idx += 1;
56            idx < 80
57        });
58        let id = st.finish();
59
60        Self(Arc::new(BacktraceInner {
61            id,
62            frames,
63            location,
64        }))
65    }
66
67    /// Backtrace repr
68    pub fn repr(&self) -> Option<Arc<str>> {
69        REPRS.with(|r| r.borrow_mut().get(&self.0.id).cloned())
70    }
71
72    pub fn is_resolved(&self) -> bool {
73        REPRS.with(|r| r.borrow_mut().contains_key(&self.0.id))
74    }
75
76    pub fn resolve(&self) {
77        REPRS.with(|r| {
78            let mut reprs = r.borrow_mut();
79            if reprs.contains_key(&self.0.id) {
80                return;
81            }
82
83            FRAMES.with(|c| {
84                let mut cache = c.borrow_mut();
85
86                for frm in &self.0.frames {
87                    if let Some(frm) = frm {
88                        let ip = frm.ip();
89                        cache.entry(ip).or_insert_with(|| {
90                            let mut f = BacktraceFrame::from(frm.clone());
91                            f.resolve();
92                            f
93                        });
94                    }
95                }
96
97                let mut idx = 0;
98                let mut frames: [Option<&BacktraceFrame>; 80] = [None; 80];
99                for frm in &self.0.frames {
100                    if let Some(frm) = frm {
101                        frames[idx] = Some(&cache[&frm.ip()]);
102                        idx += 1;
103                    }
104                }
105
106                find_loc(self.0.location, &mut frames);
107
108                #[allow(static_mut_refs)]
109                {
110                    if let Some(start) = unsafe { START } {
111                        find_loc_start(start, &mut frames);
112                    }
113                    if let Some(start) = unsafe { START_ALT } {
114                        find_loc_start(start, &mut frames);
115                    }
116                    PATHS2.with(|paths| {
117                        for s in paths {
118                            find_loc_start((s.as_str(), 0), &mut frames);
119                        }
120                    });
121                }
122
123                let mut idx = 0;
124                for frm in &mut frames {
125                    if frm.is_some() {
126                        if idx > 10 {
127                            *frm = None;
128                        } else {
129                            idx += 1;
130                        }
131                    }
132                }
133
134                let bt = Bt(&frames[..]);
135                let mut buf = String::new();
136                let _ = write!(&mut buf, "\n{bt:?}");
137                let repr: Arc<str> = Arc::from(buf);
138                reprs.insert(self.0.id, repr.clone());
139                repr
140            });
141        });
142    }
143}
144
145fn find_loc(loc: &Location<'_>, frames: &mut [Option<&BacktraceFrame>]) {
146    let mut idx = 0;
147
148    'outter: for (i, frm) in frames.iter().enumerate() {
149        if let Some(f) = frm {
150            for sym in f.symbols() {
151                if let Some(fname) = sym.filename()
152                    && fname.ends_with(loc.file())
153                {
154                    idx = i;
155                    break 'outter;
156                }
157            }
158        } else {
159            break;
160        }
161    }
162
163    for f in frames.iter_mut().take(idx) {
164        *f = None;
165    }
166
167    PATHS.with(|paths| {
168        'outter: for frm in &mut frames[idx..] {
169            if let Some(f) = frm {
170                for sym in f.symbols() {
171                    if let Some(fname) = sym.filename() {
172                        for p in paths {
173                            if fname.ends_with(p) {
174                                *frm = None;
175                                continue 'outter;
176                            }
177                        }
178                    }
179                }
180            }
181        }
182    });
183}
184
185thread_local! {
186    static PATHS: Vec<String> = {
187        let mut paths = Vec::new();
188        for item in [
189            &["src", "ctx.rs"][..],
190            &["src", "map_err.rs"][..],
191            &["src", "and_then.rs"][..],
192            &["src", "fn_service.rs"][..],
193            &["src", "pipeline.rs"][..],
194            &["src", "net", "factory.rs"][..],
195            &["src", "future", "future.rs"][..],
196            &["src", "net", "service.rs"][..],
197            &["src", "boxed.rs"][..],
198            &["src", "error.rs"][..],
199            &["src", "wrk.rs"][..],
200            &["src", "future.rs"][..],
201            &["std", "src", "thread", "local.rs"][..],
202        ] {
203            paths.push(item.iter().collect::<path::PathBuf>().to_string_lossy().into_owned());
204        }
205        paths
206    };
207
208    static PATHS2: Vec<String> = {
209        let mut paths = Vec::new();
210        for item in [
211            &["src", "driver.rs"][..],
212            &["src", "rt_compio.rs"][..],
213            &["core", "src", "panic", "unwind_safe.rs"][..],
214            &["src", "runtime", "task", "core.rs"][..]
215        ] {
216            paths.push(item.iter().collect::<path::PathBuf>().to_string_lossy().into_owned());
217        }
218        paths
219    }
220}
221
222fn find_loc_start(loc: (&str, u32), frames: &mut [Option<&BacktraceFrame>]) {
223    let mut idx = 0;
224    while idx < frames.len() {
225        if let Some(frm) = &frames[idx] {
226            for sym in frm.symbols() {
227                if let Some(fname) = sym.filename()
228                    && let Some(lineno) = sym.lineno()
229                    && fname.ends_with(loc.0)
230                    && (loc.1 == 0 || lineno == loc.1)
231                {
232                    for f in frames.iter_mut().skip(idx) {
233                        if f.is_some() {
234                            *f = None;
235                        }
236                    }
237                    return;
238                }
239            }
240        }
241        idx += 1;
242    }
243}
244
245struct Bt<'a>(&'a [Option<&'a BacktraceFrame>]);
246
247impl fmt::Debug for Bt<'_> {
248    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
249        let cwd = std::env::current_dir();
250        let mut print_path =
251            move |fmt: &mut fmt::Formatter<'_>, path: BytesOrWideString<'_>| {
252                let path = path.into_path_buf();
253                if let Ok(cwd) = &cwd
254                    && let Ok(suffix) = path.strip_prefix(cwd)
255                {
256                    return fmt::Display::fmt(&suffix.display(), fmt);
257                }
258                fmt::Display::fmt(&path.display(), fmt)
259            };
260
261        let mut f = BacktraceFmt::new(fmt, backtrace::PrintFmt::Short, &mut print_path);
262        f.add_context()?;
263        for frm in self.0.iter().flatten() {
264            f.frame().backtrace_frame(frm)?;
265        }
266        f.finish()?;
267        Ok(())
268    }
269}
270
271impl fmt::Debug for Backtrace {
272    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
273        if let Some(repr) = self.repr() {
274            fmt::Display::fmt(repr.as_ref(), f)
275        } else {
276            Ok(())
277        }
278    }
279}
280
281impl fmt::Display for Backtrace {
282    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
283        if let Some(repr) = self.repr() {
284            fmt::Display::fmt(repr.as_ref(), f)
285        } else {
286            Ok(())
287        }
288    }
289}