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, sync::LazyLock};
7
8use backtrace::{BacktraceFmt, BacktraceFrame, BytesOrWideString, Frame};
9
10thread_local! {
11    static FRAMES: RefCell<HashMap<usize, Arc<BacktraceFrame>>> = RefCell::new(HashMap::default());
12    static REPRS: RefCell<HashMap<u64, Arc<str>>> = RefCell::new(HashMap::default());
13    static DEFAULT: Arc<str> = Arc::from("Unresolved");
14}
15
16static mut START: Option<(&'static str, u32)> = None;
17static mut START_ALT: Option<(&'static str, u32)> = None;
18
19pub fn set_backtrace_start(file: &'static str, line: u32) {
20    unsafe {
21        START = Some((file, line));
22    }
23}
24
25#[doc(hidden)]
26pub fn set_backtrace_start_alt(file: &'static str, line: u32) {
27    unsafe {
28        START_ALT = Some((file, line));
29    }
30}
31
32#[derive(Clone)]
33/// Representation of a backtrace.
34///
35/// This structure can be used to capture a backtrace at various
36/// points in a program and later used to inspect what the backtrace
37/// was at that time.
38pub struct Backtrace(Arc<BacktraceInner>);
39
40#[derive(Debug)]
41/// Backtrace resolver.
42///
43/// Symbol resolution may require filesystem access and can be blocking.
44/// In asynchronous contexts, this work should be offloaded to a thread
45/// pool.
46///
47/// **Note:** Once resolution is complete, control must return to the
48/// originating thread to ensure caching is performed correctly.
49pub struct BacktraceResolver {
50    bt: Arc<BacktraceInner>,
51    repr: Option<Arc<str>>,
52    resolved: bool,
53    frames: HashMap<usize, Arc<BacktraceFrame>>,
54}
55
56#[derive(Debug)]
57struct BacktraceInner {
58    id: u64,
59    frames: [Option<Frame>; 80],
60    location: &'static Location<'static>,
61}
62
63impl Backtrace {
64    /// Create new backtrace
65    pub fn new(location: &'static Location<'static>) -> Self {
66        let mut st = foldhash::fast::FixedState::default().build_hasher();
67        let mut idx = 0;
68        let mut frames: [Option<Frame>; 80] = [const { None }; 80];
69
70        backtrace::trace(|frm| {
71            let ip = frm.ip();
72            st.write_usize(ip as usize);
73            frames[idx] = Some(frm.clone());
74            idx += 1;
75            idx < 80
76        });
77        let id = st.finish();
78
79        Self(Arc::new(BacktraceInner {
80            id,
81            frames,
82            location,
83        }))
84    }
85
86    /// Backtrace repr
87    pub fn repr(&self) -> Option<Arc<str>> {
88        REPRS.with(|r| r.borrow_mut().get(&self.0.id).cloned())
89    }
90
91    pub fn is_resolved(&self) -> bool {
92        REPRS.with(|r| r.borrow_mut().contains_key(&self.0.id))
93    }
94
95    pub fn resolver(&self) -> BacktraceResolver {
96        REPRS.with(|r| {
97            let mut reprs = r.borrow_mut();
98            if let Some(repr) = reprs.get(&self.0.id) {
99                BacktraceResolver {
100                    repr: None,
101                    resolved: true,
102                    bt: self.0.clone(),
103                    frames: HashMap::default(),
104                }
105            } else {
106                DEFAULT.with(|s| {
107                    reprs.insert(self.0.id, s.clone());
108                });
109
110                let mut frames = HashMap::default();
111
112                FRAMES.with(|c| {
113                    let mut cache = c.borrow();
114
115                    for frm in &self.0.frames {
116                        if let Some(frm) = frm {
117                            let ip = frm.ip() as usize;
118                            if let Some(frame) = cache.get(&ip) {
119                                frames.insert(ip, frame.clone());
120                            }
121                        }
122                    }
123                });
124
125                BacktraceResolver {
126                    frames,
127                    resolved: false,
128                    repr: None,
129                    bt: self.0.clone(),
130                }
131            }
132        })
133    }
134}
135
136impl BacktraceResolver {
137    #[allow(clippy::return_self_not_must_use)]
138    pub fn resolve(mut self) -> Self {
139        if self.resolved {
140            return self;
141        }
142
143        for frm in &self.bt.frames {
144            if let Some(frm) = frm {
145                let ip = frm.ip() as usize;
146                if self.frames.contains_key(&ip) {
147                    continue;
148                }
149
150                let mut f = BacktraceFrame::from(frm.clone());
151                f.resolve();
152                self.frames.insert(ip, Arc::new(f));
153            }
154        }
155
156        let mut idx = 0;
157        let mut frames: [Option<&BacktraceFrame>; 80] = [None; 80];
158        for frm in &self.bt.frames {
159            if let Some(frm) = frm {
160                let ip = frm.ip() as usize;
161                frames[idx] = Some(self.frames[&ip].as_ref());
162                idx += 1;
163            }
164        }
165
166        find_loc(self.bt.location, &mut frames);
167
168        #[allow(static_mut_refs)]
169        {
170            if let Some(start) = unsafe { START } {
171                find_loc_start(start, &mut frames);
172            }
173            if let Some(start) = unsafe { START_ALT } {
174                find_loc_start(start, &mut frames);
175            }
176            PATHS2.with(|paths| {
177                for s in paths {
178                    find_loc_start((s.as_str(), 0), &mut frames);
179                }
180            });
181        }
182
183        let mut idx = 0;
184        for frm in &mut frames {
185            if frm.is_some() {
186                if idx > 10 {
187                    *frm = None;
188                } else {
189                    idx += 1;
190                }
191            }
192        }
193
194        let bt = Bt(&frames[..]);
195        let mut buf = String::new();
196        let _ = write!(&mut buf, "\n{bt:?}");
197        self.repr = Some(Arc::from(buf));
198
199        self
200    }
201}
202
203impl Drop for BacktraceResolver {
204    fn drop(&mut self) {
205        if !self.resolved {
206            if let Some(repr) = self.repr.take() {
207                REPRS.with(|r| {
208                    r.borrow_mut().insert(self.bt.id, repr);
209                });
210            }
211
212            FRAMES.with(|c| {
213                let mut cache = c.borrow_mut();
214
215                for (ip, frm) in &self.frames {
216                    let ip = frm.ip() as usize;
217                    if !cache.contains_key(&ip) {
218                        cache.insert(ip, frm.clone());
219                    }
220                }
221            });
222        }
223    }
224}
225
226fn find_loc(loc: &Location<'_>, frames: &mut [Option<&BacktraceFrame>]) {
227    let mut idx = 0;
228
229    'outter: for (i, frm) in frames.iter().enumerate() {
230        if let Some(f) = frm {
231            for sym in f.symbols() {
232                if let Some(fname) = sym.filename()
233                    && fname.ends_with(loc.file())
234                {
235                    idx = i;
236                    break 'outter;
237                }
238            }
239        } else {
240            break;
241        }
242    }
243
244    for f in frames.iter_mut().take(idx) {
245        *f = None;
246    }
247
248    PATHS.with(|paths| {
249        'outter: for frm in &mut frames[idx..] {
250            if let Some(f) = frm {
251                for sym in f.symbols() {
252                    if let Some(fname) = sym.filename() {
253                        for p in paths {
254                            if fname.ends_with(p) {
255                                *frm = None;
256                                continue 'outter;
257                            }
258                        }
259                    }
260                }
261            }
262        }
263    });
264}
265
266thread_local! {
267    static PATHS: Vec<String> = {
268        let mut paths = Vec::new();
269        for item in [
270            &["src", "ctx.rs"][..],
271            &["src", "map_err.rs"][..],
272            &["src", "and_then.rs"][..],
273            &["src", "fn_service.rs"][..],
274            &["src", "pipeline.rs"][..],
275            &["src", "net", "factory.rs"][..],
276            &["src", "future", "future.rs"][..],
277            &["src", "net", "service.rs"][..],
278            &["src", "boxed.rs"][..],
279            &["src", "error.rs"][..],
280            &["src", "wrk.rs"][..],
281            &["src", "future.rs"][..],
282            &["std", "src", "thread", "local.rs"][..],
283        ] {
284            paths.push(item.iter().collect::<path::PathBuf>().to_string_lossy().into_owned());
285        }
286        paths
287    };
288
289    static PATHS2: Vec<String> = {
290        let mut paths = Vec::new();
291        for item in [
292            &["src", "driver.rs"][..],
293            &["src", "rt_compio.rs"][..],
294            &["core", "src", "panic", "unwind_safe.rs"][..],
295            &["src", "runtime", "task", "core.rs"][..]
296        ] {
297            paths.push(item.iter().collect::<path::PathBuf>().to_string_lossy().into_owned());
298        }
299        paths
300    }
301}
302
303fn find_loc_start(loc: (&str, u32), frames: &mut [Option<&BacktraceFrame>]) {
304    let mut idx = 0;
305    while idx < frames.len() {
306        if let Some(frm) = &frames[idx] {
307            for sym in frm.symbols() {
308                if let Some(fname) = sym.filename()
309                    && let Some(lineno) = sym.lineno()
310                    && fname.ends_with(loc.0)
311                    && (loc.1 == 0 || lineno == loc.1)
312                {
313                    for f in frames.iter_mut().skip(idx) {
314                        if f.is_some() {
315                            *f = None;
316                        }
317                    }
318                    return;
319                }
320            }
321        }
322        idx += 1;
323    }
324}
325
326struct Bt<'a>(&'a [Option<&'a BacktraceFrame>]);
327
328impl fmt::Debug for Bt<'_> {
329    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
330        let cwd = std::env::current_dir();
331        let mut print_path =
332            move |fmt: &mut fmt::Formatter<'_>, path: BytesOrWideString<'_>| {
333                let path = path.into_path_buf();
334                if let Ok(cwd) = &cwd
335                    && let Ok(suffix) = path.strip_prefix(cwd)
336                {
337                    return fmt::Display::fmt(&suffix.display(), fmt);
338                }
339                fmt::Display::fmt(&path.display(), fmt)
340            };
341
342        let mut f = BacktraceFmt::new(fmt, backtrace::PrintFmt::Short, &mut print_path);
343        f.add_context()?;
344        for frm in self.0.iter().flatten() {
345            f.frame().backtrace_frame(frm)?;
346        }
347        f.finish()?;
348        Ok(())
349    }
350}
351
352impl fmt::Debug for Backtrace {
353    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354        if let Some(repr) = self.repr() {
355            fmt::Display::fmt(repr.as_ref(), f)
356        } else {
357            Ok(())
358        }
359    }
360}
361
362impl fmt::Display for Backtrace {
363    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
364        if let Some(repr) = self.repr() {
365            fmt::Display::fmt(repr.as_ref(), f)
366        } else {
367            Ok(())
368        }
369    }
370}