samply_symbols/
symbol_map.rs

1use std::borrow::Cow;
2use std::sync::Arc;
3
4use debugid::DebugId;
5
6use crate::shared::LookupAddress;
7use crate::{
8    AddressInfo, ExternalFileAddressRef, ExternalFileRef, FileAndPathHelper, FileLocation,
9    FrameDebugInfo, FramesLookupResult, SyncAddressInfo,
10};
11
12pub trait SymbolMapTrait {
13    fn debug_id(&self) -> DebugId;
14
15    fn symbol_count(&self) -> usize;
16
17    fn iter_symbols(&self) -> Box<dyn Iterator<Item = (u32, Cow<'_, str>)> + '_>;
18
19    /// Look up information for an address synchronously.
20    ///
21    /// If the information is known to be in an external file, and this file is
22    /// already cached within this symbol map, then that cached information is
23    /// consulted as part of this lookup_sync invocation. This method only returns
24    /// `FramesLookupResult::External` if the caller actually needs to supply new
25    /// file contents with a follow-up call to `try_lookup_external_with_file_contents`.
26    fn lookup_sync(&self, address: LookupAddress) -> Option<SyncAddressInfo>;
27}
28
29pub trait SymbolMapTraitWithExternalFileSupport<FC>: SymbolMapTrait {
30    fn get_as_symbol_map(&self) -> &dyn SymbolMapTrait;
31    fn try_lookup_external(&self, external: &ExternalFileAddressRef) -> Option<FramesLookupResult>;
32    fn try_lookup_external_with_file_contents(
33        &self,
34        external: &ExternalFileAddressRef,
35        file_contents: Option<FC>,
36    ) -> Option<FramesLookupResult>;
37}
38
39pub trait GetInnerSymbolMap {
40    fn get_inner_symbol_map<'a>(&'a self) -> &'a (dyn SymbolMapTrait + 'a);
41}
42
43pub trait GetInnerSymbolMapWithLookupFramesExt<FC> {
44    fn get_inner_symbol_map<'a>(
45        &'a self,
46    ) -> &'a (dyn SymbolMapTraitWithExternalFileSupport<FC> + Send + Sync + 'a);
47}
48
49enum InnerSymbolMap<FC> {
50    WithoutAddFile(Box<dyn GetInnerSymbolMap + Send + Sync>),
51    WithAddFile(Box<dyn GetInnerSymbolMapWithLookupFramesExt<FC> + Send + Sync>),
52    Direct(Arc<dyn SymbolMapTrait + Send + Sync>),
53}
54
55pub struct SymbolMap<H: FileAndPathHelper> {
56    debug_file_location: H::FL,
57    inner: InnerSymbolMap<H::F>,
58    helper: Option<Arc<H>>,
59}
60
61impl<H: FileAndPathHelper> SymbolMap<H> {
62    pub(crate) fn new_plain(
63        debug_file_location: H::FL,
64        inner: Box<dyn GetInnerSymbolMap + Send + Sync>,
65    ) -> Self {
66        Self {
67            debug_file_location,
68            inner: InnerSymbolMap::WithoutAddFile(inner),
69            helper: None,
70        }
71    }
72
73    pub(crate) fn new_with_external_file_support(
74        debug_file_location: H::FL,
75        inner: Box<dyn GetInnerSymbolMapWithLookupFramesExt<H::F> + Send + Sync>,
76        helper: Arc<H>,
77    ) -> Self {
78        Self {
79            debug_file_location,
80            inner: InnerSymbolMap::WithAddFile(inner),
81            helper: Some(helper),
82        }
83    }
84
85    pub fn with_symbol_map_trait(
86        debug_file_location: H::FL,
87        inner: Arc<dyn SymbolMapTrait + Send + Sync>,
88    ) -> Self {
89        Self {
90            debug_file_location,
91            inner: InnerSymbolMap::Direct(inner),
92            helper: None,
93        }
94    }
95
96    fn inner(&self) -> &dyn SymbolMapTrait {
97        match &self.inner {
98            InnerSymbolMap::WithoutAddFile(inner) => inner.get_inner_symbol_map(),
99            InnerSymbolMap::WithAddFile(inner) => inner.get_inner_symbol_map().get_as_symbol_map(),
100            InnerSymbolMap::Direct(inner) => inner.as_ref(),
101        }
102    }
103
104    pub fn debug_file_location(&self) -> &H::FL {
105        &self.debug_file_location
106    }
107
108    pub fn debug_id(&self) -> debugid::DebugId {
109        self.inner().debug_id()
110    }
111
112    pub fn symbol_count(&self) -> usize {
113        self.inner().symbol_count()
114    }
115
116    pub fn iter_symbols(&self) -> Box<dyn Iterator<Item = (u32, Cow<'_, str>)> + '_> {
117        self.inner().iter_symbols()
118    }
119
120    pub fn lookup_sync(&self, address: LookupAddress) -> Option<SyncAddressInfo> {
121        self.inner().lookup_sync(address)
122    }
123
124    pub async fn lookup(&self, address: LookupAddress) -> Option<AddressInfo> {
125        let address_info = self.inner().lookup_sync(address)?;
126        let symbol = address_info.symbol;
127        let (mut external, inner) = match (address_info.frames, &self.inner) {
128            (Some(FramesLookupResult::Available(frames)), _) => {
129                return Some(AddressInfo {
130                    symbol,
131                    frames: Some(frames),
132                });
133            }
134            (None, _) | (_, InnerSymbolMap::WithoutAddFile(_)) | (_, InnerSymbolMap::Direct(_)) => {
135                return Some(AddressInfo {
136                    symbol,
137                    frames: None,
138                });
139            }
140            (Some(FramesLookupResult::External(external)), InnerSymbolMap::WithAddFile(inner)) => {
141                (external, inner.get_inner_symbol_map())
142            }
143        };
144        let helper = self.helper.as_deref()?;
145        loop {
146            let maybe_file_location = match &external.file_ref {
147                ExternalFileRef::MachoExternalObject { file_path } => self
148                    .debug_file_location
149                    .location_for_external_object_file(file_path),
150                ExternalFileRef::ElfExternalDwo { comp_dir, path } => {
151                    self.debug_file_location.location_for_dwo(comp_dir, path)
152                }
153            };
154            let file_contents = match maybe_file_location {
155                Some(location) => helper.load_file(location).await.ok(),
156                None => None,
157            };
158            let lookup_result =
159                inner.try_lookup_external_with_file_contents(&external, file_contents);
160            external = match lookup_result {
161                Some(FramesLookupResult::Available(frames)) => {
162                    return Some(AddressInfo {
163                        symbol,
164                        frames: Some(frames),
165                    });
166                }
167                None => {
168                    return Some(AddressInfo {
169                        symbol,
170                        frames: None,
171                    });
172                }
173                Some(FramesLookupResult::External(external)) => external,
174            };
175        }
176    }
177
178    /// Resolve a debug info lookup for which `SymbolMap::lookup_*` returned a
179    /// `FramesLookupResult::External`.
180    ///
181    /// This method is asynchronous because it may load a new external file.
182    ///
183    /// This keeps the most recent external file cached, so that repeated lookups
184    /// for the same external file are fast.
185    pub async fn lookup_external(
186        &self,
187        external: &ExternalFileAddressRef,
188    ) -> Option<Vec<FrameDebugInfo>> {
189        let helper = self.helper.as_deref()?;
190        let inner = match &self.inner {
191            InnerSymbolMap::WithoutAddFile(_) | InnerSymbolMap::Direct(_) => return None,
192            InnerSymbolMap::WithAddFile(inner) => inner.get_inner_symbol_map(),
193        };
194
195        let mut lookup_result: Option<FramesLookupResult> = inner.try_lookup_external(external);
196        loop {
197            let external = match lookup_result {
198                Some(FramesLookupResult::Available(frames)) => return Some(frames),
199                None => return None,
200                Some(FramesLookupResult::External(external)) => external,
201            };
202            let maybe_file_location = match &external.file_ref {
203                ExternalFileRef::MachoExternalObject { file_path } => self
204                    .debug_file_location
205                    .location_for_external_object_file(file_path),
206                ExternalFileRef::ElfExternalDwo { comp_dir, path } => {
207                    self.debug_file_location.location_for_dwo(comp_dir, path)
208                }
209            };
210            let file_contents = match maybe_file_location {
211                Some(location) => helper.load_file(location).await.ok(),
212                None => None,
213            };
214            lookup_result = inner.try_lookup_external_with_file_contents(&external, file_contents);
215        }
216    }
217}