Coverage Report

Created: 2025-11-16 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/addr2line-0.24.2/src/frame.rs
Line
Count
Source
1
use alloc::borrow::Cow;
2
use alloc::string::String;
3
use core::iter;
4
5
use crate::{maybe_small, Error, Function, InlinedFunction, ResUnit};
6
7
/// A source location.
8
pub struct Location<'a> {
9
    /// The file name.
10
    pub file: Option<&'a str>,
11
    /// The line number.
12
    pub line: Option<u32>,
13
    /// The column number.
14
    ///
15
    /// A value of `Some(0)` indicates the left edge.
16
    pub column: Option<u32>,
17
}
18
19
/// A function frame.
20
pub struct Frame<'ctx, R: gimli::Reader> {
21
    /// The DWARF unit offset corresponding to the DIE of the function.
22
    pub dw_die_offset: Option<gimli::UnitOffset<R::Offset>>,
23
    /// The name of the function.
24
    pub function: Option<FunctionName<R>>,
25
    /// The source location corresponding to this frame.
26
    pub location: Option<Location<'ctx>>,
27
}
28
29
/// An iterator over function frames.
30
pub struct FrameIter<'ctx, R>(FrameIterState<'ctx, R>)
31
where
32
    R: gimli::Reader;
33
34
enum FrameIterState<'ctx, R>
35
where
36
    R: gimli::Reader,
37
{
38
    Empty,
39
    Location(Option<Location<'ctx>>),
40
    Frames(FrameIterFrames<'ctx, R>),
41
}
42
43
struct FrameIterFrames<'ctx, R>
44
where
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
54
impl<'ctx, R> FrameIter<'ctx, R>
55
where
56
    R: gimli::Reader + 'ctx,
57
{
58
0
    pub(crate) fn new_empty() -> Self {
59
0
        FrameIter(FrameIterState::Empty)
60
0
    }
Unexecuted instantiation: <addr2line::frame::FrameIter<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::new_empty
Unexecuted instantiation: <addr2line::frame::FrameIter<_>>::new_empty
61
62
0
    pub(crate) fn new_location(location: Location<'ctx>) -> Self {
63
0
        FrameIter(FrameIterState::Location(Some(location)))
64
0
    }
Unexecuted instantiation: <addr2line::frame::FrameIter<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::new_location
Unexecuted instantiation: <addr2line::frame::FrameIter<_>>::new_location
65
66
0
    pub(crate) fn new_frames(
67
0
        unit: &'ctx ResUnit<R>,
68
0
        sections: &'ctx gimli::Dwarf<R>,
69
0
        function: &'ctx Function<R>,
70
0
        inlined_functions: maybe_small::Vec<&'ctx InlinedFunction<R>>,
71
0
        location: Option<Location<'ctx>>,
72
0
    ) -> Self {
73
0
        FrameIter(FrameIterState::Frames(FrameIterFrames {
74
0
            unit,
75
0
            sections,
76
0
            function,
77
0
            inlined_functions: inlined_functions.into_iter().rev(),
78
0
            next: location,
79
0
        }))
80
0
    }
Unexecuted instantiation: <addr2line::frame::FrameIter<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::new_frames
Unexecuted instantiation: <addr2line::frame::FrameIter<_>>::new_frames
81
82
    /// Advances the iterator and returns the next frame.
83
    #[allow(clippy::should_implement_trait)]
84
0
    pub fn next(&mut self) -> Result<Option<Frame<'ctx, R>>, Error> {
85
0
        let frames = match &mut self.0 {
86
0
            FrameIterState::Empty => return Ok(None),
87
0
            FrameIterState::Location(location) => {
88
                // We can't move out of a mutable reference, so use `take` instead.
89
0
                let location = location.take();
90
0
                self.0 = FrameIterState::Empty;
91
0
                return Ok(Some(Frame {
92
0
                    dw_die_offset: None,
93
0
                    function: None,
94
0
                    location,
95
0
                }));
96
            }
97
0
            FrameIterState::Frames(frames) => frames,
98
        };
99
100
0
        let loc = frames.next.take();
101
0
        let func = match frames.inlined_functions.next() {
102
0
            Some(func) => func,
103
            None => {
104
0
                let frame = Frame {
105
0
                    dw_die_offset: Some(frames.function.dw_die_offset),
106
0
                    function: frames.function.name.clone().map(|name| FunctionName {
107
0
                        name,
108
0
                        language: frames.unit.lang,
109
0
                    }),
Unexecuted instantiation: <addr2line::frame::FrameIter<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::next::{closure#0}
Unexecuted instantiation: <addr2line::frame::FrameIter<_>>::next::{closure#0}
110
0
                    location: loc,
111
                };
112
0
                self.0 = FrameIterState::Empty;
113
0
                return Ok(Some(frame));
114
            }
115
        };
116
117
0
        let mut next = Location {
118
0
            file: None,
119
0
            line: if func.call_line != 0 {
120
0
                Some(func.call_line)
121
            } else {
122
0
                None
123
            },
124
0
            column: if func.call_column != 0 {
125
0
                Some(func.call_column)
126
            } else {
127
0
                None
128
            },
129
        };
130
0
        if let Some(call_file) = func.call_file {
131
0
            if let Some(lines) = frames.unit.parse_lines(frames.sections)? {
132
0
                next.file = lines.file(call_file);
133
0
            }
134
0
        }
135
0
        frames.next = Some(next);
136
137
        Ok(Some(Frame {
138
0
            dw_die_offset: Some(func.dw_die_offset),
139
0
            function: func.name.clone().map(|name| FunctionName {
140
0
                name,
141
0
                language: frames.unit.lang,
142
0
            }),
Unexecuted instantiation: <addr2line::frame::FrameIter<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::next::{closure#1}
Unexecuted instantiation: <addr2line::frame::FrameIter<_>>::next::{closure#1}
143
0
            location: loc,
144
        }))
145
0
    }
Unexecuted instantiation: <addr2line::frame::FrameIter<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::next
Unexecuted instantiation: <addr2line::frame::FrameIter<_>>::next
146
}
147
148
#[cfg(feature = "fallible-iterator")]
149
impl<'ctx, R> fallible_iterator::FallibleIterator for FrameIter<'ctx, R>
150
where
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
162
/// A function name.
163
pub struct FunctionName<R: gimli::Reader> {
164
    /// The name of the function.
165
    pub name: R,
166
    /// The language of the compilation unit containing this function.
167
    pub language: Option<gimli::DwLang>,
168
}
169
170
impl<R: gimli::Reader> FunctionName<R> {
171
    /// The raw name of this function before demangling.
172
0
    pub fn raw_name(&self) -> Result<Cow<'_, str>, Error> {
173
0
        self.name.to_string_lossy()
174
0
    }
175
176
    /// The name of this function after demangling (if applicable).
177
0
    pub fn demangle(&self) -> Result<Cow<'_, str>, Error> {
178
0
        self.raw_name().map(|x| demangle_auto(x, self.language))
179
0
    }
180
}
181
182
/// Demangle a symbol name using the demangling scheme for the given language.
183
///
184
/// Returns `None` if demangling failed or is not required.
185
#[allow(unused_variables)]
186
0
pub fn demangle(name: &str, language: gimli::DwLang) -> Option<String> {
187
0
    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
0
        _ => None,
201
    }
202
0
}
203
204
/// Apply 'best effort' demangling of a symbol name.
205
///
206
/// If `language` is given, then only the demangling scheme for that language
207
/// is used.
208
///
209
/// If `language` is `None`, then heuristics are used to determine how to
210
/// demangle the name. Currently, these heuristics are very basic.
211
///
212
/// If demangling fails or is not required, then `name` is returned unchanged.
213
0
pub fn demangle_auto(name: Cow<'_, str>, language: Option<gimli::DwLang>) -> Cow<'_, str> {
214
0
    match language {
215
0
        Some(language) => demangle(name.as_ref(), language),
216
0
        None => demangle(name.as_ref(), gimli::DW_LANG_Rust)
217
0
            .or_else(|| demangle(name.as_ref(), gimli::DW_LANG_C_plus_plus)),
218
    }
219
0
    .map(Cow::from)
220
0
    .unwrap_or(name)
221
0
}