hydro_lang/compile/ir/
backtrace.rs1#[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 col_offset: usize, inner: RefCell<backtrace::Backtrace>,
22 resolved: RefCell<Option<Vec<BacktraceElement>>>,
23}
24
25#[cfg(stageleft_runtime)]
26#[cfg(feature = "build")]
27#[doc(hidden)]
28pub fn __macro_get_backtrace(col_offset: usize) -> Backtrace {
29 let mut out = Backtrace::get_backtrace(1);
30 out.col_offset = col_offset;
31 out
32}
33
34#[cfg(not(feature = "build"))]
35#[doc(hidden)]
36pub fn __macro_get_backtrace(_col_offset: usize) -> Backtrace {
37 panic!();
38}
39
40impl Backtrace {
41 #[cfg(feature = "build")]
42 #[inline(never)]
43 pub(crate) fn get_backtrace(skip_count: usize) -> Backtrace {
44 let backtrace = backtrace::Backtrace::new_unresolved();
45 Backtrace {
46 skip_count,
47 col_offset: 0,
48 inner: RefCell::new(backtrace),
49 resolved: RefCell::new(None),
50 }
51 }
52
53 #[cfg(not(feature = "build"))]
54 pub(crate) fn get_backtrace(_skip_count: usize) -> Backtrace {
55 panic!();
56 }
57
58 #[cfg(feature = "build")]
64 pub fn elements(&self) -> Vec<BacktraceElement> {
65 self.resolved
66 .borrow_mut()
67 .get_or_insert_with(|| {
68 let mut inner_borrow = self.inner.borrow_mut();
69 inner_borrow.resolve();
70 let mut collected: Vec<_> = inner_borrow
71 .frames()
72 .iter()
73 .skip_while(|f| {
74 !(std::ptr::eq(f.symbol_address(), Backtrace::get_backtrace as _)
75 || f.symbols()
76 .first()
77 .and_then(|s| s.name())
78 .and_then(|n| n.as_str())
79 .is_some_and(|n| n.contains("get_backtrace")))
80 })
81 .skip(1)
82 .take_while(|f| {
83 !f.symbols()
84 .last()
85 .and_then(|s| s.name())
86 .and_then(|n| n.as_str())
87 .is_some_and(|n| n.contains("__rust_begin_short_backtrace"))
88 })
89 .flat_map(|frame| frame.symbols())
90 .skip(self.skip_count)
91 .map(|symbol| {
92 let full_fn_name = symbol.name().unwrap().to_string();
93 BacktraceElement {
94 fn_name: full_fn_name
95 .rfind("::")
96 .map(|idx| full_fn_name.split_at(idx).0.to_string())
97 .unwrap_or(full_fn_name),
98 filename: symbol.filename().map(|f| f.display().to_string()),
99 lineno: symbol.lineno(),
100 colno: symbol.colno(),
101 addr: symbol.addr().map(|a| a as usize),
102 }
103 })
104 .collect();
105
106 if self.col_offset > 0
107 && let Some(first) = collected.first_mut()
108 {
109 first.colno = first
110 .colno
111 .map(|c| c.saturating_sub(self.col_offset as u32));
112 }
113
114 collected
115 })
116 .clone()
117 }
118}
119
120#[cfg(feature = "build")]
121#[derive(Clone)]
123pub struct BacktraceElement {
124 pub fn_name: String,
126 pub filename: Option<String>,
128 pub lineno: Option<u32>,
130 pub colno: Option<u32>,
132 pub addr: Option<usize>,
134}
135
136#[cfg(feature = "build")]
137impl Debug for BacktraceElement {
138 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139 f.debug_struct("BacktraceElement")
141 .field("fn_name", &self.fn_name)
142 .field("lineno", &self.lineno)
143 .field("colno", &self.colno)
144 .finish()
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 #[cfg(feature = "build")]
151 use super::*;
152
153 #[cfg(feature = "build")]
154 #[test]
155 fn test_backtrace() {
156 if cfg!(not(target_os = "linux")) && std::env::var_os("GITHUB_ACTIONS").is_some() {
157 eprintln!("Backtrace tests fail on non-linux Github Actions runners, skipping.");
158 return;
159 }
160
161 let backtrace = Backtrace::get_backtrace(0);
162 let elements = backtrace.elements();
163
164 hydro_build_utils::assert_debug_snapshot!(elements);
165 }
166}