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