symbolic_debuginfo/
ppdb.rs1use std::borrow::Cow;
3use std::collections::HashMap;
4use std::fmt;
5use std::iter;
6
7use once_cell::sync::OnceCell;
8use symbolic_common::{Arch, CodeId, DebugId};
9use symbolic_ppdb::EmbeddedSource;
10use symbolic_ppdb::{Document, FormatError, PortablePdb};
11
12use crate::base::*;
13use crate::sourcebundle::SourceFileDescriptor;
14
15pub type PortablePdbSymbolIterator<'data> = iter::Empty<Symbol<'data>>;
17pub type PortablePdbFunctionIterator<'session> =
19 iter::Empty<Result<Function<'session>, FormatError>>;
20
21pub struct PortablePdbObject<'data> {
23 data: &'data [u8],
24 ppdb: PortablePdb<'data>,
25}
26
27impl<'data> PortablePdbObject<'data> {
28 pub fn portable_pdb(&self) -> &PortablePdb<'_> {
30 &self.ppdb
31 }
32
33 pub fn data(&self) -> &'data [u8] {
35 self.data
36 }
37}
38
39impl<'data: 'object, 'object> ObjectLike<'data, 'object> for PortablePdbObject<'data> {
40 type Error = FormatError;
41 type Session = PortablePdbDebugSession<'data>;
42 type SymbolIterator = PortablePdbSymbolIterator<'data>;
43
44 fn debug_id(&self) -> DebugId {
46 self.ppdb.pdb_id().unwrap_or_default()
47 }
48
49 fn code_id(&self) -> Option<CodeId> {
53 None
54 }
55
56 fn arch(&self) -> Arch {
58 Arch::Unknown
59 }
60
61 fn kind(&self) -> ObjectKind {
63 ObjectKind::Debug
64 }
65
66 fn load_address(&self) -> u64 {
70 0
71 }
72
73 fn has_symbols(&self) -> bool {
75 false
76 }
77
78 fn symbols(&self) -> PortablePdbSymbolIterator<'data> {
80 iter::empty()
81 }
82
83 fn symbol_map(&self) -> SymbolMap<'data> {
85 SymbolMap::new()
86 }
87
88 fn has_debug_info(&self) -> bool {
90 self.ppdb.has_debug_info()
91 }
92
93 fn debug_session(&self) -> Result<PortablePdbDebugSession<'data>, FormatError> {
95 PortablePdbDebugSession::new(&self.ppdb)
96 }
97
98 fn has_unwind_info(&self) -> bool {
100 false
101 }
102
103 fn has_sources(&self) -> bool {
105 self.ppdb.has_source_links().unwrap_or(false)
106 || match self.ppdb.get_embedded_sources() {
107 Ok(mut iter) => iter.any(|v| v.is_ok()),
108 Err(_) => false,
109 }
110 }
111
112 fn is_malformed(&self) -> bool {
114 false
115 }
116
117 fn file_format(&self) -> FileFormat {
119 FileFormat::PortablePdb
120 }
121}
122
123impl<'data> Parse<'data> for PortablePdbObject<'data> {
124 type Error = FormatError;
125
126 fn test(data: &[u8]) -> bool {
127 PortablePdb::peek(data)
128 }
129
130 fn parse(data: &'data [u8]) -> Result<Self, Self::Error> {
131 let ppdb = PortablePdb::parse(data)?;
132 Ok(Self { data, ppdb })
133 }
134}
135
136impl fmt::Debug for PortablePdbObject<'_> {
137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 f.debug_struct("PortablePdbObject")
139 .field("portable_pdb", &self.portable_pdb())
140 .finish()
141 }
142}
143
144pub struct PortablePdbDebugSession<'data> {
146 ppdb: PortablePdb<'data>,
147 sources: OnceCell<HashMap<String, PPDBSource<'data>>>,
148}
149
150#[derive(Debug, Clone)]
151enum PPDBSource<'data> {
152 Embedded(EmbeddedSource<'data>),
153 Link(Document),
154}
155
156impl<'data> PortablePdbDebugSession<'data> {
157 fn new(ppdb: &'_ PortablePdb<'data>) -> Result<Self, FormatError> {
158 Ok(PortablePdbDebugSession {
159 ppdb: ppdb.clone(),
160 sources: OnceCell::new(),
161 })
162 }
163
164 fn init_sources(&self) -> HashMap<String, PPDBSource<'data>> {
165 let count = self.ppdb.get_documents_count().unwrap_or(0);
166 let mut result = HashMap::with_capacity(count);
167
168 if let Ok(iter) = self.ppdb.get_embedded_sources() {
169 for source in iter.flatten() {
170 result.insert(source.get_path().to_string(), PPDBSource::Embedded(source));
171 }
172 };
173
174 for i in 1..count + 1 {
175 if let Ok(doc) = self.ppdb.get_document(i) {
176 if !result.contains_key(&doc.name) {
177 result.insert(doc.name.clone(), PPDBSource::Link(doc));
178 }
179 }
180 }
181
182 result
183 }
184
185 pub fn functions(&self) -> PortablePdbFunctionIterator<'_> {
187 iter::empty()
188 }
189
190 pub fn files(&self) -> PortablePdbFileIterator<'_> {
192 PortablePdbFileIterator::new(&self.ppdb)
193 }
194
195 pub fn source_by_path(
197 &self,
198 path: &str,
199 ) -> Result<Option<SourceFileDescriptor<'_>>, FormatError> {
200 let sources = self.sources.get_or_init(|| self.init_sources());
201 match sources.get(path) {
202 None => Ok(None),
203 Some(PPDBSource::Embedded(source)) => source.get_contents().map(|bytes| {
204 Some(SourceFileDescriptor::new_embedded(
205 from_utf8_cow_lossy(&bytes),
206 None,
207 ))
208 }),
209 Some(PPDBSource::Link(document)) => Ok(self
210 .ppdb
211 .get_source_link(document)
212 .map(SourceFileDescriptor::new_remote)),
213 }
214 }
215}
216
217impl<'session> DebugSession<'session> for PortablePdbDebugSession<'_> {
218 type Error = FormatError;
219 type FunctionIterator = PortablePdbFunctionIterator<'session>;
220 type FileIterator = PortablePdbFileIterator<'session>;
221
222 fn functions(&'session self) -> Self::FunctionIterator {
223 self.functions()
224 }
225
226 fn files(&'session self) -> Self::FileIterator {
227 self.files()
228 }
229
230 fn source_by_path(&self, path: &str) -> Result<Option<SourceFileDescriptor<'_>>, Self::Error> {
231 self.source_by_path(path)
232 }
233}
234
235pub struct PortablePdbFileIterator<'s> {
237 ppdb: &'s PortablePdb<'s>,
238 row: usize,
239 size: usize,
240}
241
242impl<'s> PortablePdbFileIterator<'s> {
243 fn new(ppdb: &'s PortablePdb<'s>) -> Self {
244 PortablePdbFileIterator {
245 ppdb,
246 row: 1,
248 size: 0,
251 }
252 }
253}
254
255impl<'s> Iterator for PortablePdbFileIterator<'s> {
256 type Item = Result<FileEntry<'s>, FormatError>;
257
258 fn next(&mut self) -> Option<Self::Item> {
259 if self.size == 0 {
260 match self.ppdb.get_documents_count() {
261 Ok(size) => {
262 debug_assert!(size != usize::MAX);
263 self.size = size;
264 }
265 Err(e) => {
266 return Some(Err(e));
267 }
268 }
269 }
270
271 if self.row > self.size {
272 return None;
273 }
274
275 let index = self.row;
276 self.row += 1;
277
278 let document = match self.ppdb.get_document(index) {
279 Ok(doc) => doc,
280 Err(e) => {
281 return Some(Err(e));
282 }
283 };
284 Some(Ok(FileEntry::new(
285 Cow::default(),
286 FileInfo::from_path_owned(document.name.as_bytes()),
287 )))
288 }
289}