1#[cfg(feature = "build")]
2use std::cell::RefCell;
3use std::fmt::Debug;
4
5#[derive(Clone)]
6pub struct Backtrace {
7 #[cfg(feature = "build")]
9 pub skip_count: usize,
10 #[cfg(feature = "build")]
11 pub inner: RefCell<backtrace::Backtrace>,
12 #[cfg(feature = "build")]
13 pub resolved: RefCell<Option<Vec<BacktraceElement>>>,
14}
15
16impl Backtrace {
17 #[cfg(feature = "build")]
18 pub fn elements(&self) -> Vec<BacktraceElement> {
19 self.resolved
20 .borrow_mut()
21 .get_or_insert_with(|| {
22 let mut inner_borrow = self.inner.borrow_mut();
23 inner_borrow.resolve();
24 inner_borrow
25 .frames()
26 .iter()
27 .skip_while(|f| {
28 !(f.symbol_address() as usize == get_backtrace as usize
29 || f.symbols()
30 .first()
31 .and_then(|s| s.name())
32 .and_then(|n| n.as_str())
33 .is_some_and(|n| n.contains("get_backtrace")))
34 })
35 .skip(1)
36 .take_while(|f| {
37 !f.symbols()
38 .last()
39 .and_then(|s| s.name())
40 .and_then(|n| n.as_str())
41 .is_some_and(|n| n.contains("__rust_begin_short_backtrace"))
42 })
43 .flat_map(|frame| frame.symbols())
44 .skip(self.skip_count)
45 .map(|symbol| {
46 let full_fn_name = symbol.name().unwrap().to_string();
47 BacktraceElement {
48 fn_name: full_fn_name
49 .rfind("::")
50 .map(|idx| full_fn_name.split_at(idx).0.to_string())
51 .unwrap_or(full_fn_name),
52 filename: symbol.filename().map(|f| f.display().to_string()),
53 lineno: symbol.lineno(),
54 addr: symbol.addr().map(|a| a as usize),
55 }
56 })
57 .collect()
58 })
59 .clone()
60 }
61}
62
63#[derive(Clone)]
64pub struct BacktraceElement {
65 pub fn_name: String,
66 pub filename: Option<String>,
67 pub lineno: Option<u32>,
68 pub addr: Option<usize>,
69}
70
71impl Debug for BacktraceElement {
72 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73 f.write_str(&self.fn_name)
74 }
75}
76
77#[cfg(feature = "build")]
78#[inline(never)]
79pub(crate) fn get_backtrace(skip_count: usize) -> Backtrace {
80 let backtrace = backtrace::Backtrace::new_unresolved();
81 Backtrace {
82 skip_count,
83 inner: RefCell::new(backtrace),
84 resolved: RefCell::new(None),
85 }
86}
87
88#[cfg(not(feature = "build"))]
89pub(crate) fn get_backtrace(_skip_count: usize) -> Backtrace {
90 panic!();
91}
92
93#[cfg(test)]
94mod tests {
95 #[cfg(unix)]
96 use super::*;
97
98 #[cfg(unix)]
99 #[test]
100 fn test_backtrace() {
101 let backtrace = get_backtrace(0);
102 let elements = backtrace.elements();
103
104 hydro_build_utils::assert_debug_snapshot!(elements);
105 }
106}