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, path, 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                        PATHS2.with(|paths| {
85                            for s in paths {
86                                find_loc_start((s.as_str(), 0), &mut frames);
87                            }
88                        });
89                    }
90
91                    let mut idx = 0;
92                    for frm in &mut frames {
93                        if frm.is_some() {
94                            if idx > 10 {
95                                *frm = None;
96                            } else {
97                                idx += 1;
98                            }
99                        }
100                    }
101
102                    let bt = Bt(&frames[..]);
103                    let mut buf = String::new();
104                    let _ = write!(&mut buf, "\n{bt:?}");
105                    let repr: Arc<str> = Arc::from(buf);
106                    reprs.insert(id, repr.clone());
107                    repr
108                }
109            })
110        });
111
112        Self(repr)
113    }
114
115    /// Backtrace repr
116    pub fn repr(&self) -> &str {
117        &self.0
118    }
119}
120
121fn find_loc(loc: &Location<'_>, frames: &mut [Option<&BacktraceFrame>]) {
122    let mut idx = 0;
123
124    'outter: for (i, frm) in frames.iter().enumerate() {
125        if let Some(f) = frm {
126            for sym in f.symbols() {
127                if let Some(fname) = sym.filename()
128                    && fname.ends_with(loc.file())
129                {
130                    idx = i;
131                    break 'outter;
132                }
133            }
134        } else {
135            break;
136        }
137    }
138
139    for f in frames.iter_mut().take(idx) {
140        *f = None;
141    }
142
143    PATHS.with(|paths| {
144        'outter: for frm in &mut frames[idx..] {
145            if let Some(f) = frm {
146                for sym in f.symbols() {
147                    if let Some(fname) = sym.filename() {
148                        for p in paths {
149                            if fname.ends_with(p) {
150                                *frm = None;
151                                continue 'outter;
152                            }
153                        }
154                    }
155                }
156            }
157        }
158    });
159}
160
161thread_local! {
162    static PATHS: Vec<String> = {
163        let mut paths = Vec::new();
164        for item in [
165            &["src", "ctx.rs"][..],
166            &["src", "map_err.rs"][..],
167            &["src", "and_then.rs"][..],
168            &["src", "fn_service.rs"][..],
169            &["src", "pipeline.rs"][..],
170            &["src", "net", "factory.rs"][..],
171            &["src", "future", "future.rs"][..],
172            &["src", "net", "service.rs"][..],
173            &["src", "boxed.rs"][..],
174            &["src", "error.rs"][..],
175            &["src", "wrk.rs"][..],
176            &["src", "future.rs"][..],
177            &["std", "src", "thread", "local.rs"][..],
178        ] {
179            paths.push(item.iter().collect::<path::PathBuf>().to_string_lossy().into_owned());
180        }
181        paths
182    };
183
184    static PATHS2: Vec<String> = {
185        let mut paths = Vec::new();
186        for item in [
187            &["src", "driver.rs"][..],
188            &["src", "rt_compio.rs"][..],
189            &["core", "src", "panic", "unwind_safe.rs"][..],
190            &["src", "runtime", "task", "core.rs"][..]
191        ] {
192            paths.push(item.iter().collect::<path::PathBuf>().to_string_lossy().into_owned());
193        }
194        paths
195    }
196}
197
198fn find_loc_start(loc: (&str, u32), frames: &mut [Option<&BacktraceFrame>]) {
199    let mut idx = 0;
200    while idx < frames.len() {
201        if let Some(frm) = &frames[idx] {
202            for sym in frm.symbols() {
203                if let Some(fname) = sym.filename()
204                    && let Some(lineno) = sym.lineno()
205                    && fname.ends_with(loc.0)
206                    && (loc.1 == 0 || lineno == loc.1)
207                {
208                    for f in frames.iter_mut().skip(idx) {
209                        if f.is_some() {
210                            *f = None;
211                        }
212                    }
213                    return;
214                }
215            }
216        }
217        idx += 1;
218    }
219}
220
221struct Bt<'a>(&'a [Option<&'a BacktraceFrame>]);
222
223impl fmt::Debug for Bt<'_> {
224    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
225        let cwd = std::env::current_dir();
226        let mut print_path =
227            move |fmt: &mut fmt::Formatter<'_>, path: BytesOrWideString<'_>| {
228                let path = path.into_path_buf();
229                if let Ok(cwd) = &cwd
230                    && let Ok(suffix) = path.strip_prefix(cwd)
231                {
232                    return fmt::Display::fmt(&suffix.display(), fmt);
233                }
234                fmt::Display::fmt(&path.display(), fmt)
235            };
236
237        let mut f = BacktraceFmt::new(fmt, backtrace::PrintFmt::Short, &mut print_path);
238        f.add_context()?;
239        for frm in self.0.iter().flatten() {
240            f.frame().backtrace_frame(frm)?;
241        }
242        f.finish()?;
243        Ok(())
244    }
245}
246
247impl fmt::Debug for Backtrace {
248    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249        fmt::Display::fmt(&self.0, f)
250    }
251}
252
253impl fmt::Display for Backtrace {
254    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
255        fmt::Display::fmt(&self.0, f)
256    }
257}