1use 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)]
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 }
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 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}