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