1use alloc::borrow::Cow;
2use alloc::string::String;
3use core::iter;
4
5use crate::{maybe_small, Error, Function, InlinedFunction, ResUnit};
6
7pub struct Location<'a> {
9 pub file: Option<&'a str>,
11 pub line: Option<u32>,
13 pub column: Option<u32>,
17}
18
19pub struct Frame<'ctx, R: gimli::Reader> {
21 pub dw_die_offset: Option<gimli::UnitOffset<R::Offset>>,
23 pub function: Option<FunctionName<R>>,
25 pub location: Option<Location<'ctx>>,
27}
28
29pub struct FrameIter<'ctx, R>(FrameIterState<'ctx, R>)
31where
32 R: gimli::Reader;
33
34enum FrameIterState<'ctx, R>
35where
36 R: gimli::Reader,
37{
38 Empty,
39 Location(Option<Location<'ctx>>),
40 Frames(FrameIterFrames<'ctx, R>),
41}
42
43struct FrameIterFrames<'ctx, R>
44where
45 R: gimli::Reader,
46{
47 unit: &'ctx ResUnit<R>,
48 sections: &'ctx gimli::Dwarf<R>,
49 function: &'ctx Function<R>,
50 inlined_functions: iter::Rev<maybe_small::IntoIter<&'ctx InlinedFunction<R>>>,
51 next: Option<Location<'ctx>>,
52}
53
54impl<'ctx, R> FrameIter<'ctx, R>
55where
56 R: gimli::Reader + 'ctx,
57{
58 pub(crate) fn new_empty() -> Self {
59 FrameIter(FrameIterState::Empty)
60 }
61
62 pub(crate) fn new_location(location: Location<'ctx>) -> Self {
63 FrameIter(FrameIterState::Location(Some(location)))
64 }
65
66 pub(crate) fn new_frames(
67 unit: &'ctx ResUnit<R>,
68 sections: &'ctx gimli::Dwarf<R>,
69 function: &'ctx Function<R>,
70 inlined_functions: maybe_small::Vec<&'ctx InlinedFunction<R>>,
71 location: Option<Location<'ctx>>,
72 ) -> Self {
73 FrameIter(FrameIterState::Frames(FrameIterFrames {
74 unit,
75 sections,
76 function,
77 inlined_functions: inlined_functions.into_iter().rev(),
78 next: location,
79 }))
80 }
81
82 #[allow(clippy::should_implement_trait)]
84 pub fn next(&mut self) -> Result<Option<Frame<'ctx, R>>, Error> {
85 let frames = match &mut self.0 {
86 FrameIterState::Empty => return Ok(None),
87 FrameIterState::Location(location) => {
88 let location = location.take();
90 self.0 = FrameIterState::Empty;
91 return Ok(Some(Frame {
92 dw_die_offset: None,
93 function: None,
94 location,
95 }));
96 }
97 FrameIterState::Frames(frames) => frames,
98 };
99
100 let loc = frames.next.take();
101 let func = match frames.inlined_functions.next() {
102 Some(func) => func,
103 None => {
104 let frame = Frame {
105 dw_die_offset: Some(frames.function.dw_die_offset),
106 function: frames.function.name.clone().map(|name| FunctionName {
107 name,
108 language: frames.unit.lang,
109 }),
110 location: loc,
111 };
112 self.0 = FrameIterState::Empty;
113 return Ok(Some(frame));
114 }
115 };
116
117 let mut next = Location {
118 file: None,
119 line: if func.call_line != 0 {
120 Some(func.call_line)
121 } else {
122 None
123 },
124 column: if func.call_column != 0 {
125 Some(func.call_column)
126 } else {
127 None
128 },
129 };
130 if let Some(call_file) = func.call_file {
131 if let Some(lines) = frames.unit.parse_lines(frames.sections)? {
132 next.file = lines.file(call_file);
133 }
134 }
135 frames.next = Some(next);
136
137 Ok(Some(Frame {
138 dw_die_offset: Some(func.dw_die_offset),
139 function: func.name.clone().map(|name| FunctionName {
140 name,
141 language: frames.unit.lang,
142 }),
143 location: loc,
144 }))
145 }
146}
147
148#[cfg(feature = "fallible-iterator")]
149impl<'ctx, R> fallible_iterator::FallibleIterator for FrameIter<'ctx, R>
150where
151 R: gimli::Reader + 'ctx,
152{
153 type Item = Frame<'ctx, R>;
154 type Error = Error;
155
156 #[inline]
157 fn next(&mut self) -> Result<Option<Frame<'ctx, R>>, Error> {
158 self.next()
159 }
160}
161
162pub struct FunctionName<R: gimli::Reader> {
164 pub name: R,
166 pub language: Option<gimli::DwLang>,
168}
169
170impl<R: gimli::Reader> FunctionName<R> {
171 pub fn raw_name(&self) -> Result<Cow<'_, str>, Error> {
173 self.name.to_string_lossy()
174 }
175
176 pub fn demangle(&self) -> Result<Cow<'_, str>, Error> {
178 self.raw_name().map(|x| demangle_auto(x, self.language))
179 }
180}
181
182#[allow(unused_variables)]
186pub fn demangle(name: &str, language: gimli::DwLang) -> Option<String> {
187 match language {
188 #[cfg(feature = "rustc-demangle")]
189 gimli::DW_LANG_Rust => rustc_demangle::try_demangle(name)
190 .ok()
191 .as_ref()
192 .map(|x| format!("{:#}", x)),
193 #[cfg(feature = "cpp_demangle")]
194 gimli::DW_LANG_C_plus_plus
195 | gimli::DW_LANG_C_plus_plus_03
196 | gimli::DW_LANG_C_plus_plus_11
197 | gimli::DW_LANG_C_plus_plus_14 => cpp_demangle::Symbol::new(name)
198 .ok()
199 .and_then(|x| x.demangle(&Default::default()).ok()),
200 _ => None,
201 }
202}
203
204pub fn demangle_auto(name: Cow<'_, str>, language: Option<gimli::DwLang>) -> Cow<'_, str> {
214 match language {
215 Some(language) => demangle(name.as_ref(), language),
216 None => demangle(name.as_ref(), gimli::DW_LANG_Rust)
217 .or_else(|| demangle(name.as_ref(), gimli::DW_LANG_C_plus_plus)),
218 }
219 .map(Cow::from)
220 .unwrap_or(name)
221}