Coverage Report

Created: 2026-01-13 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/addr2line-0.25.1/src/lookup.rs
Line
Count
Source
1
use alloc::sync::Arc;
2
use core::marker::PhantomData;
3
use core::ops::ControlFlow;
4
5
/// This struct contains the information needed to find split DWARF data
6
/// and to produce a `gimli::Dwarf<R>` for it.
7
pub struct SplitDwarfLoad<R> {
8
    /// The dwo id, for looking up in a DWARF package, or for
9
    /// verifying an unpacked dwo found on the file system
10
    pub dwo_id: gimli::DwoId,
11
    /// The compilation directory `path` is relative to.
12
    pub comp_dir: Option<R>,
13
    /// A path on the filesystem, relative to `comp_dir` to find this dwo.
14
    pub path: Option<R>,
15
    /// Once the split DWARF data is loaded, the loader is expected
16
    /// to call [make_dwo(parent)](gimli::read::Dwarf::make_dwo) before
17
    /// returning the data.
18
    pub parent: Arc<gimli::Dwarf<R>>,
19
}
20
21
/// Operations that consult debug information may require additional files
22
/// to be loaded if split DWARF is being used. This enum returns the result
23
/// of the operation in the `Output` variant, or information about the split
24
/// DWARF that is required and a continuation to invoke once it is available
25
/// in the `Load` variant.
26
///
27
/// This enum is intended to be used in a loop like so:
28
/// ```no_run
29
///   # use addr2line::*;
30
///   # use std::sync::Arc;
31
///   # let ctx: Context<gimli::EndianSlice<gimli::RunTimeEndian>> = todo!();
32
///   # let do_split_dwarf_load = |load: SplitDwarfLoad<gimli::EndianSlice<gimli::RunTimeEndian>>| -> Option<Arc<gimli::Dwarf<gimli::EndianSlice<gimli::RunTimeEndian>>>> { None };
33
///   const ADDRESS: u64 = 0xdeadbeef;
34
///   let mut r = ctx.find_frames(ADDRESS);
35
///   let result = loop {
36
///     match r {
37
///       LookupResult::Output(result) => break result,
38
///       LookupResult::Load { load, continuation } => {
39
///         let dwo = do_split_dwarf_load(load);
40
///         r = continuation.resume(dwo);
41
///       }
42
///     }
43
///   };
44
/// ```
45
pub enum LookupResult<L: LookupContinuation> {
46
    /// The lookup requires split DWARF data to be loaded.
47
    Load {
48
        /// The information needed to find the split DWARF data.
49
        load: SplitDwarfLoad<<L as LookupContinuation>::Buf>,
50
        /// The continuation to resume with the loaded split DWARF data.
51
        continuation: L,
52
    },
53
    /// The lookup has completed and produced an output.
54
    Output(<L as LookupContinuation>::Output),
55
}
56
57
/// This trait represents a partially complete operation that can be resumed
58
/// once a load of needed split DWARF data is completed or abandoned by the
59
/// API consumer.
60
pub trait LookupContinuation: Sized {
61
    /// The final output of this operation.
62
    type Output;
63
    /// The type of reader used.
64
    type Buf: gimli::Reader;
65
66
    /// Resumes the operation with the provided data.
67
    ///
68
    /// After the caller loads the split DWARF data required, call this
69
    /// method to resume the operation. The return value of this method
70
    /// indicates if the computation has completed or if further data is
71
    /// required.
72
    ///
73
    /// If the additional data cannot be located, or the caller does not
74
    /// support split DWARF, `resume(None)` can be used to continue the
75
    /// operation with the data that is available.
76
    fn resume(self, input: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self>;
77
}
78
79
impl<L: LookupContinuation> LookupResult<L> {
80
    /// Callers that do not handle split DWARF can call `skip_all_loads`
81
    /// to fast-forward to the end result. This result is produced with
82
    /// the data that is available and may be less accurate than the
83
    /// the results that would be produced if the caller did properly
84
    /// support split DWARF.
85
0
    pub fn skip_all_loads(mut self) -> L::Output {
86
        loop {
87
0
            self = match self {
88
0
                LookupResult::Output(t) => return t,
89
0
                LookupResult::Load { continuation, .. } => continuation.resume(None),
90
            };
91
        }
92
0
    }
93
94
0
    pub(crate) fn map<T, F: FnOnce(L::Output) -> T>(
95
0
        self,
96
0
        f: F,
97
0
    ) -> LookupResult<MappedLookup<T, L, F>> {
98
0
        match self {
99
0
            LookupResult::Output(t) => LookupResult::Output(f(t)),
100
0
            LookupResult::Load { load, continuation } => LookupResult::Load {
101
0
                load,
102
0
                continuation: MappedLookup {
103
0
                    original: continuation,
104
0
                    mutator: f,
105
0
                },
106
0
            },
107
        }
108
0
    }
Unexecuted instantiation: <addr2line::lookup::LookupResult<addr2line::lookup::SimpleLookup<core::result::Result<(addr2line::DebugFile, gimli::read::dwarf::UnitRef<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>), gimli::read::Error>, gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>, <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::dwarf_and_unit::{closure#6}>>>::map::<core::result::Result<(core::option::Option<&addr2line::function::Function<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>, core::option::Option<addr2line::frame::Location>), gimli::read::Error>, <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::find_function_or_location::{closure#0}>
Unexecuted instantiation: <addr2line::lookup::LookupResult<_>>::map::<_, _>
109
110
0
    pub(crate) fn unwrap(self) -> L::Output {
111
0
        match self {
112
0
            LookupResult::Output(t) => t,
113
0
            LookupResult::Load { .. } => unreachable!("Internal API misuse"),
114
        }
115
0
    }
116
}
117
118
pub(crate) struct SimpleLookup<T, R, F>
119
where
120
    F: FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> T,
121
    R: gimli::Reader,
122
{
123
    f: F,
124
    phantom: PhantomData<(T, R)>,
125
}
126
127
impl<T, R, F> SimpleLookup<T, R, F>
128
where
129
    F: FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> T,
130
    R: gimli::Reader,
131
{
132
0
    pub(crate) fn new_complete(t: F::Output) -> LookupResult<SimpleLookup<T, R, F>> {
133
0
        LookupResult::Output(t)
134
0
    }
Unexecuted instantiation: <addr2line::lookup::SimpleLookup<core::result::Result<(addr2line::DebugFile, gimli::read::dwarf::UnitRef<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>), gimli::read::Error>, gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>, <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::dwarf_and_unit::{closure#6}>>::new_complete
Unexecuted instantiation: <addr2line::lookup::SimpleLookup<_, _, _>>::new_complete
135
136
0
    pub(crate) fn new_needs_load(
137
0
        load: SplitDwarfLoad<R>,
138
0
        f: F,
139
0
    ) -> LookupResult<SimpleLookup<T, R, F>> {
140
0
        LookupResult::Load {
141
0
            load,
142
0
            continuation: SimpleLookup {
143
0
                f,
144
0
                phantom: PhantomData,
145
0
            },
146
0
        }
147
0
    }
Unexecuted instantiation: <addr2line::lookup::SimpleLookup<core::result::Result<(addr2line::DebugFile, gimli::read::dwarf::UnitRef<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>), gimli::read::Error>, gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>, <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::dwarf_and_unit::{closure#6}>>::new_needs_load
Unexecuted instantiation: <addr2line::lookup::SimpleLookup<_, _, _>>::new_needs_load
148
}
149
150
impl<T, R, F> LookupContinuation for SimpleLookup<T, R, F>
151
where
152
    F: FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> T,
153
    R: gimli::Reader,
154
{
155
    type Output = T;
156
    type Buf = R;
157
158
0
    fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> {
159
0
        LookupResult::Output((self.f)(v))
160
0
    }
Unexecuted instantiation: <addr2line::lookup::SimpleLookup<core::result::Result<(addr2line::DebugFile, gimli::read::dwarf::UnitRef<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>), gimli::read::Error>, gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>, <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::dwarf_and_unit::{closure#6}> as addr2line::lookup::LookupContinuation>::resume
Unexecuted instantiation: <addr2line::lookup::SimpleLookup<_, _, _> as addr2line::lookup::LookupContinuation>::resume
161
}
162
163
pub(crate) struct MappedLookup<T, L, F>
164
where
165
    L: LookupContinuation,
166
    F: FnOnce(L::Output) -> T,
167
{
168
    original: L,
169
    mutator: F,
170
}
171
172
impl<T, L, F> LookupContinuation for MappedLookup<T, L, F>
173
where
174
    L: LookupContinuation,
175
    F: FnOnce(L::Output) -> T,
176
{
177
    type Output = T;
178
    type Buf = L::Buf;
179
180
0
    fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> {
181
0
        match self.original.resume(v) {
182
0
            LookupResult::Output(t) => LookupResult::Output((self.mutator)(t)),
183
0
            LookupResult::Load { load, continuation } => LookupResult::Load {
184
0
                load,
185
0
                continuation: MappedLookup {
186
0
                    original: continuation,
187
0
                    mutator: self.mutator,
188
0
                },
189
0
            },
190
        }
191
0
    }
Unexecuted instantiation: <addr2line::lookup::MappedLookup<core::result::Result<(core::option::Option<&addr2line::function::Function<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>, core::option::Option<addr2line::frame::Location>), gimli::read::Error>, addr2line::lookup::SimpleLookup<core::result::Result<(addr2line::DebugFile, gimli::read::dwarf::UnitRef<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>), gimli::read::Error>, gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>, <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::dwarf_and_unit::{closure#6}>, <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::find_function_or_location::{closure#0}> as addr2line::lookup::LookupContinuation>::resume
Unexecuted instantiation: <addr2line::lookup::MappedLookup<_, _, _> as addr2line::lookup::LookupContinuation>::resume
192
}
193
194
/// Some functions (e.g. `find_frames`) require considering multiple
195
/// compilation units, each of which might require their own split DWARF
196
/// lookup (and thus produce a continuation).
197
///
198
/// We store the underlying continuation here as well as a mutator function
199
/// that will either a) decide that the result of this continuation is
200
/// what is needed and mutate it to the final result or b) produce another
201
/// `LookupResult`. `new_lookup` will in turn eagerly drive any non-continuation
202
/// `LookupResult` with successive invocations of the mutator, until a new
203
/// continuation or a final result is produced. And finally, the impl of
204
/// `LookupContinuation::resume` will call `new_lookup` each time the
205
/// computation is resumed.
206
pub(crate) struct LoopingLookup<T, L, F>
207
where
208
    L: LookupContinuation,
209
    F: FnMut(L::Output) -> ControlFlow<T, LookupResult<L>>,
210
{
211
    continuation: L,
212
    mutator: F,
213
}
214
215
impl<T, L, F> LoopingLookup<T, L, F>
216
where
217
    L: LookupContinuation,
218
    F: FnMut(L::Output) -> ControlFlow<T, LookupResult<L>>,
219
{
220
0
    pub(crate) fn new_complete(t: T) -> LookupResult<Self> {
221
0
        LookupResult::Output(t)
222
0
    }
Unexecuted instantiation: <addr2line::lookup::LoopingLookup<core::result::Result<addr2line::frame::FrameIter<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>, gimli::read::Error>, addr2line::lookup::MappedLookup<core::result::Result<(core::option::Option<&addr2line::function::Function<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>, core::option::Option<addr2line::frame::Location>), gimli::read::Error>, addr2line::lookup::SimpleLookup<core::result::Result<(addr2line::DebugFile, gimli::read::dwarf::UnitRef<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>), gimli::read::Error>, gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>, <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::dwarf_and_unit::{closure#6}>, <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::find_function_or_location::{closure#0}>, <addr2line::Context<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::find_frames::{closure#0}>>::new_complete
Unexecuted instantiation: <addr2line::lookup::LoopingLookup<_, _, _>>::new_complete
223
224
0
    pub(crate) fn new_lookup(mut r: LookupResult<L>, mut mutator: F) -> LookupResult<Self> {
225
        // Drive the loop eagerly so that we only ever have to represent one state
226
        // (the r == ControlFlow::Continue state) in LoopingLookup.
227
        loop {
228
0
            match r {
229
0
                LookupResult::Output(l) => match mutator(l) {
230
0
                    ControlFlow::Break(t) => return LookupResult::Output(t),
231
0
                    ControlFlow::Continue(r2) => {
232
0
                        r = r2;
233
0
                    }
234
                },
235
0
                LookupResult::Load { load, continuation } => {
236
0
                    return LookupResult::Load {
237
0
                        load,
238
0
                        continuation: LoopingLookup {
239
0
                            continuation,
240
0
                            mutator,
241
0
                        },
242
0
                    };
243
                }
244
            }
245
        }
246
0
    }
Unexecuted instantiation: <addr2line::lookup::LoopingLookup<core::result::Result<addr2line::frame::FrameIter<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>, gimli::read::Error>, addr2line::lookup::MappedLookup<core::result::Result<(core::option::Option<&addr2line::function::Function<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>, core::option::Option<addr2line::frame::Location>), gimli::read::Error>, addr2line::lookup::SimpleLookup<core::result::Result<(addr2line::DebugFile, gimli::read::dwarf::UnitRef<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>), gimli::read::Error>, gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>, <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::dwarf_and_unit::{closure#6}>, <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::find_function_or_location::{closure#0}>, <addr2line::Context<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::find_frames::{closure#0}>>::new_lookup
Unexecuted instantiation: <addr2line::lookup::LoopingLookup<_, _, _>>::new_lookup
247
}
248
249
impl<T, L, F> LookupContinuation for LoopingLookup<T, L, F>
250
where
251
    L: LookupContinuation,
252
    F: FnMut(L::Output) -> ControlFlow<T, LookupResult<L>>,
253
{
254
    type Output = T;
255
    type Buf = L::Buf;
256
257
0
    fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> {
258
0
        let r = self.continuation.resume(v);
259
0
        LoopingLookup::new_lookup(r, self.mutator)
260
0
    }
Unexecuted instantiation: <addr2line::lookup::LoopingLookup<core::result::Result<addr2line::frame::FrameIter<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>, gimli::read::Error>, addr2line::lookup::MappedLookup<core::result::Result<(core::option::Option<&addr2line::function::Function<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>, core::option::Option<addr2line::frame::Location>), gimli::read::Error>, addr2line::lookup::SimpleLookup<core::result::Result<(addr2line::DebugFile, gimli::read::dwarf::UnitRef<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>), gimli::read::Error>, gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>, <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::dwarf_and_unit::{closure#6}>, <addr2line::unit::ResUnit<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::find_function_or_location::{closure#0}>, <addr2line::Context<gimli::read::endian_slice::EndianSlice<gimli::endianity::LittleEndian>>>::find_frames::{closure#0}> as addr2line::lookup::LookupContinuation>::resume
Unexecuted instantiation: <addr2line::lookup::LoopingLookup<_, _, _> as addr2line::lookup::LookupContinuation>::resume
261
}