1use 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)]
30pub struct Backtrace(Arc<str>);
36
37impl Backtrace {
38 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 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}