addr2line/lookup.rs
1use alloc::sync::Arc;
2use core::marker::PhantomData;
3use 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.
7pub 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/// ```
45pub 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.
60pub 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
79impl<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 pub fn skip_all_loads(mut self) -> L::Output {
86 loop {
87 self = match self {
88 LookupResult::Output(t) => return t,
89 LookupResult::Load { continuation, .. } => continuation.resume(None),
90 };
91 }
92 }
93
94 pub(crate) fn map<T, F: FnOnce(L::Output) -> T>(
95 self,
96 f: F,
97 ) -> LookupResult<MappedLookup<T, L, F>> {
98 match self {
99 LookupResult::Output(t) => LookupResult::Output(f(t)),
100 LookupResult::Load { load, continuation } => LookupResult::Load {
101 load,
102 continuation: MappedLookup {
103 original: continuation,
104 mutator: f,
105 },
106 },
107 }
108 }
109
110 pub(crate) fn unwrap(self) -> L::Output {
111 match self {
112 LookupResult::Output(t) => t,
113 LookupResult::Load { .. } => unreachable!("Internal API misuse"),
114 }
115 }
116}
117
118pub(crate) struct SimpleLookup<T, R, F>
119where
120 F: FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> T,
121 R: gimli::Reader,
122{
123 f: F,
124 phantom: PhantomData<(T, R)>,
125}
126
127impl<T, R, F> SimpleLookup<T, R, F>
128where
129 F: FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> T,
130 R: gimli::Reader,
131{
132 pub(crate) fn new_complete(t: F::Output) -> LookupResult<SimpleLookup<T, R, F>> {
133 LookupResult::Output(t)
134 }
135
136 pub(crate) fn new_needs_load(
137 load: SplitDwarfLoad<R>,
138 f: F,
139 ) -> LookupResult<SimpleLookup<T, R, F>> {
140 LookupResult::Load {
141 load,
142 continuation: SimpleLookup {
143 f,
144 phantom: PhantomData,
145 },
146 }
147 }
148}
149
150impl<T, R, F> LookupContinuation for SimpleLookup<T, R, F>
151where
152 F: FnOnce(Option<Arc<gimli::Dwarf<R>>>) -> T,
153 R: gimli::Reader,
154{
155 type Output = T;
156 type Buf = R;
157
158 fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> {
159 LookupResult::Output((self.f)(v))
160 }
161}
162
163pub(crate) struct MappedLookup<T, L, F>
164where
165 L: LookupContinuation,
166 F: FnOnce(L::Output) -> T,
167{
168 original: L,
169 mutator: F,
170}
171
172impl<T, L, F> LookupContinuation for MappedLookup<T, L, F>
173where
174 L: LookupContinuation,
175 F: FnOnce(L::Output) -> T,
176{
177 type Output = T;
178 type Buf = L::Buf;
179
180 fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> {
181 match self.original.resume(v) {
182 LookupResult::Output(t) => LookupResult::Output((self.mutator)(t)),
183 LookupResult::Load { load, continuation } => LookupResult::Load {
184 load,
185 continuation: MappedLookup {
186 original: continuation,
187 mutator: self.mutator,
188 },
189 },
190 }
191 }
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.
206pub(crate) struct LoopingLookup<T, L, F>
207where
208 L: LookupContinuation,
209 F: FnMut(L::Output) -> ControlFlow<T, LookupResult<L>>,
210{
211 continuation: L,
212 mutator: F,
213}
214
215impl<T, L, F> LoopingLookup<T, L, F>
216where
217 L: LookupContinuation,
218 F: FnMut(L::Output) -> ControlFlow<T, LookupResult<L>>,
219{
220 pub(crate) fn new_complete(t: T) -> LookupResult<Self> {
221 LookupResult::Output(t)
222 }
223
224 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 match r {
229 LookupResult::Output(l) => match mutator(l) {
230 ControlFlow::Break(t) => return LookupResult::Output(t),
231 ControlFlow::Continue(r2) => {
232 r = r2;
233 }
234 },
235 LookupResult::Load { load, continuation } => {
236 return LookupResult::Load {
237 load,
238 continuation: LoopingLookup {
239 continuation,
240 mutator,
241 },
242 };
243 }
244 }
245 }
246 }
247}
248
249impl<T, L, F> LookupContinuation for LoopingLookup<T, L, F>
250where
251 L: LookupContinuation,
252 F: FnMut(L::Output) -> ControlFlow<T, LookupResult<L>>,
253{
254 type Output = T;
255 type Buf = L::Buf;
256
257 fn resume(self, v: Option<Arc<gimli::Dwarf<Self::Buf>>>) -> LookupResult<Self> {
258 let r = self.continuation.resume(v);
259 LoopingLookup::new_lookup(r, self.mutator)
260 }
261}