hydro_lang/
backtrace.rs

1#[cfg(feature = "build")]
2use std::cell::RefCell;
3use std::fmt::Debug;
4
5#[derive(Clone)]
6pub struct Backtrace {
7    // TODO(shadaj): figure out how to make these not pub in Stageleft
8    #[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}