symbolic_debuginfo/base.rs
1use std::borrow::Cow;
2use std::fmt;
3use std::ops::{Bound, Deref, RangeBounds};
4use std::str::FromStr;
5
6use symbolic_common::{clean_path, join_path, Arch, CodeId, DebugId, Name};
7
8use crate::sourcebundle::SourceFileDescriptor;
9
10pub(crate) trait Parse<'data>: Sized {
11 type Error;
12
13 fn parse(data: &'data [u8]) -> Result<Self, Self::Error>;
14
15 fn test(data: &'data [u8]) -> bool {
16 Self::parse(data).is_ok()
17 }
18}
19
20/// An error returned for unknown or invalid `ObjectKinds`.
21#[derive(Debug)]
22pub struct UnknownObjectKindError;
23
24impl fmt::Display for UnknownObjectKindError {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 write!(f, "unknown object class")
27 }
28}
29
30impl std::error::Error for UnknownObjectKindError {}
31
32/// Represents the designated use of the object file and hints at its contents.
33#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone)]
34pub enum ObjectKind {
35 /// There is no object class specified for this object file.
36 None,
37
38 /// The Relocatable file type is the format used for intermediate object
39 /// files. It is a very compact format containing all its sections in one
40 /// segment. The compiler and assembler usually create one Relocatable file
41 /// for each source code file. By convention, the file name extension for
42 /// this format is .o.
43 Relocatable,
44
45 /// The Executable file type is the format used by standard executable
46 /// programs.
47 Executable,
48
49 /// The Library file type is for dynamic shared libraries. It contains
50 /// some additional tables to support multiple modules. By convention, the
51 /// file name extension for this format is .dylib, except for the main
52 /// shared library of a framework, which does not usually have a file name
53 /// extension.
54 Library,
55
56 /// The Dump file type is used to store core files, which are
57 /// traditionally created when a program crashes. Core files store the
58 /// entire address space of a process at the time it crashed. You can
59 /// later run gdb on the core file to figure out why the crash occurred.
60 Dump,
61
62 /// The Debug file type designates files that store symbol information
63 /// for a corresponding binary file.
64 Debug,
65
66 /// A container that just stores source code files, but no other debug
67 /// information corresponding to the original object file.
68 Sources,
69
70 /// The Other type represents any valid object class that does not fit any
71 /// of the other classes. These are mostly CPU or OS dependent, or unique
72 /// to a single kind of object.
73 Other,
74}
75
76impl ObjectKind {
77 /// Returns the name of the object kind.
78 pub fn name(self) -> &'static str {
79 match self {
80 ObjectKind::None => "none",
81 ObjectKind::Relocatable => "rel",
82 ObjectKind::Executable => "exe",
83 ObjectKind::Library => "lib",
84 ObjectKind::Dump => "dump",
85 ObjectKind::Debug => "dbg",
86 ObjectKind::Sources => "src",
87 ObjectKind::Other => "other",
88 }
89 }
90
91 /// Returns a human readable name of the object kind.
92 ///
93 /// This is also used in alternate formatting:
94 ///
95 /// ```rust
96 /// # use symbolic_debuginfo::ObjectKind;
97 /// assert_eq!(format!("{:#}", ObjectKind::Executable), ObjectKind::Executable.human_name());
98 /// ```
99 pub fn human_name(self) -> &'static str {
100 match self {
101 ObjectKind::None => "file",
102 ObjectKind::Relocatable => "object",
103 ObjectKind::Executable => "executable",
104 ObjectKind::Library => "library",
105 ObjectKind::Dump => "memory dump",
106 ObjectKind::Debug => "debug companion",
107 ObjectKind::Sources => "sources",
108 ObjectKind::Other => "file",
109 }
110 }
111}
112
113impl fmt::Display for ObjectKind {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 if f.alternate() {
116 f.write_str(self.human_name())
117 } else {
118 f.write_str(self.name())
119 }
120 }
121}
122
123impl FromStr for ObjectKind {
124 type Err = UnknownObjectKindError;
125
126 fn from_str(string: &str) -> Result<ObjectKind, UnknownObjectKindError> {
127 Ok(match string {
128 "none" => ObjectKind::None,
129 "rel" => ObjectKind::Relocatable,
130 "exe" => ObjectKind::Executable,
131 "lib" => ObjectKind::Library,
132 "dump" => ObjectKind::Dump,
133 "dbg" => ObjectKind::Debug,
134 "src" => ObjectKind::Sources,
135 "other" => ObjectKind::Other,
136 _ => return Err(UnknownObjectKindError),
137 })
138 }
139}
140
141/// An error returned for unknown or invalid [`FileFormats`](enum.FileFormat.html).
142#[derive(Debug)]
143pub struct UnknownFileFormatError;
144
145impl fmt::Display for UnknownFileFormatError {
146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 write!(f, "unknown file format")
148 }
149}
150
151impl std::error::Error for UnknownFileFormatError {}
152
153/// Represents the physical object file format.
154#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone)]
155pub enum FileFormat {
156 /// An unknown file format.
157 Unknown,
158 /// Breakpad ASCII symbol.
159 Breakpad,
160 /// Executable and Linkable Format, used on Linux.
161 Elf,
162 /// Mach Objects, used on macOS and iOS derivatives.
163 MachO,
164 /// Program Database, the debug companion format on Windows.
165 Pdb,
166 /// Portable Executable, an extension of COFF used on Windows.
167 Pe,
168 /// Source code bundle ZIP.
169 SourceBundle,
170 /// WASM container.
171 Wasm,
172 /// Portable PDB
173 PortablePdb,
174}
175
176impl FileFormat {
177 /// Returns the name of the file format.
178 pub fn name(self) -> &'static str {
179 match self {
180 FileFormat::Unknown => "unknown",
181 FileFormat::Breakpad => "breakpad",
182 FileFormat::Elf => "elf",
183 FileFormat::MachO => "macho",
184 FileFormat::Pdb => "pdb",
185 FileFormat::Pe => "pe",
186 FileFormat::SourceBundle => "sourcebundle",
187 FileFormat::Wasm => "wasm",
188 FileFormat::PortablePdb => "portablepdb",
189 }
190 }
191}
192
193impl fmt::Display for FileFormat {
194 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195 f.write_str(self.name())
196 }
197}
198
199impl FromStr for FileFormat {
200 type Err = UnknownFileFormatError;
201
202 fn from_str(string: &str) -> Result<FileFormat, UnknownFileFormatError> {
203 Ok(match string {
204 "breakpad" => FileFormat::Breakpad,
205 "elf" => FileFormat::Elf,
206 "macho" => FileFormat::MachO,
207 "pdb" => FileFormat::Pdb,
208 "pe" => FileFormat::Pe,
209 "sourcebundle" => FileFormat::SourceBundle,
210 "wasm" => FileFormat::Wasm,
211 "portablepdb" => FileFormat::PortablePdb,
212 _ => return Err(UnknownFileFormatError),
213 })
214 }
215}
216
217/// A symbol from a symbol table.
218#[derive(Clone, Default, Eq, PartialEq)]
219pub struct Symbol<'data> {
220 /// The name of the symbol.
221 ///
222 /// This name is generally mangled. It can be demangled by constructing a `Name` instance and
223 /// calling demangle on it. Certain object files might only store demangled symbol names.
224 pub name: Option<Cow<'data, str>>,
225
226 /// The relative address of this symbol.
227 pub address: u64,
228
229 /// The size of this symbol, if known.
230 ///
231 /// When loading symbols from an object file, the size will generally not be known. Instead,
232 /// construct a [`SymbolMap`] from the object, which also fills in sizes.
233 ///
234 /// [`SymbolMap`]: struct.SymbolMap.html
235 pub size: u64,
236}
237
238impl Symbol<'_> {
239 /// Returns the name of this symbol as string.
240 pub fn name(&self) -> Option<&str> {
241 self.name.as_ref().map(Cow::as_ref)
242 }
243
244 /// Determines whether the given address is covered by this symbol.
245 ///
246 /// If the symbol size has not been computed, the address is assumed to be covered if it is
247 /// greated than the symbol address. Otherwise, the address must be in the half-open interval
248 /// `[address, address + size)`.
249 pub fn contains(&self, address: u64) -> bool {
250 address >= self.address && (self.size == 0 || address < self.address + self.size)
251 }
252}
253
254impl fmt::Debug for Symbol<'_> {
255 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256 f.debug_struct("Symbol")
257 .field("name", &self.name().unwrap_or("<unknown>"))
258 .field("address", &format_args!("{:#x}", self.address))
259 .field("size", &format_args!("{:#x}", self.size))
260 .finish()
261 }
262}
263
264/// IntoIterator type for [`SymbolMap`](struct.SymbolMap.html).
265pub type SymbolMapIter<'data> = std::vec::IntoIter<Symbol<'data>>;
266
267/// A sorted list of symbols, suitable for quick lookups.
268///
269/// This type can either be computed from a list or iterator of symbols, or preferrably directly
270/// by calling [`ObjectLike::symbol_map`] on any object. Symbols in the symbol map are guaranteed to
271/// have a `size` set, except for the last symbol, which is computed by taking the offset to the
272/// subsequent symbol.
273///
274/// `SymbolMap` also exposes a read-only view on the sorted slice of symbols. It can be converted to
275/// and from lists of symbols.
276///
277/// ## Example
278///
279/// ```rust
280/// # use symbolic_debuginfo::{Symbol, SymbolMap};
281/// let map = SymbolMap::from(vec![
282/// Symbol { name: Some("A".into()), address: 0x4400, size: 0 },
283/// Symbol { name: Some("B".into()), address: 0x4200, size: 0 },
284/// Symbol { name: Some("C".into()), address: 0x4000, size: 0 },
285/// ]);
286///
287/// assert_eq!(map[0], Symbol {
288/// name: Some("C".into()),
289/// address: 0x4000,
290/// size: 0x200,
291/// });
292/// ```
293///
294/// [`ObjectLike::symbol_map`]: trait.ObjectLike.html#tymethod.symbol_map
295#[derive(Clone, Debug, Default)]
296pub struct SymbolMap<'data> {
297 symbols: Vec<Symbol<'data>>,
298}
299
300impl<'data> SymbolMap<'data> {
301 /// Creates a new, empty symbol map.
302 pub fn new() -> Self {
303 SymbolMap {
304 symbols: Vec::new(),
305 }
306 }
307
308 /// Looks up the symbol covering the given address.
309 pub fn lookup(&self, address: u64) -> Option<&Symbol<'data>> {
310 match self.symbols.binary_search_by_key(&address, Self::key) {
311 Ok(index) => Some(&self.symbols[index]),
312 Err(0) => None,
313 Err(next_index) => {
314 let symbol = &self.symbols[next_index - 1];
315 if symbol.contains(address) {
316 Some(symbol)
317 } else {
318 None
319 }
320 }
321 }
322 }
323
324 /// Looks up a symbol by its start address.
325 pub fn lookup_exact(&self, address: u64) -> Option<&Symbol<'data>> {
326 let idx = self
327 .symbols
328 .binary_search_by_key(&address, Self::key)
329 .ok()?;
330 self.symbols.get(idx)
331 }
332
333 /// Looks up a symbol covering an entire range.
334 ///
335 /// This is similar to [`lookup`], but it only returns the symbol result if it _also_ covers the
336 /// inclusive end address of the range.
337 ///
338 /// [`lookup`]: struct.SymbolMap.html#method.lookup
339 pub fn lookup_range<R>(&self, range: R) -> Option<&Symbol<'data>>
340 where
341 R: RangeBounds<u64>,
342 {
343 let start = match range.start_bound() {
344 Bound::Included(start) => *start,
345 Bound::Excluded(start) => *start + 1,
346 Bound::Unbounded => 0,
347 };
348
349 let symbol = self.lookup(start)?;
350
351 let end = match range.end_bound() {
352 Bound::Included(end) => *end,
353 Bound::Excluded(end) => *end - 1,
354 Bound::Unbounded => u64::MAX,
355 };
356
357 if end <= start || symbol.contains(end) {
358 Some(symbol)
359 } else {
360 None
361 }
362 }
363
364 /// Returns the lookup key for a symbol, which is the symbol's address.
365 #[inline(always)]
366 fn key(symbol: &Symbol<'data>) -> u64 {
367 symbol.address
368 }
369}
370
371impl<'d> Deref for SymbolMap<'d> {
372 type Target = [Symbol<'d>];
373
374 fn deref(&self) -> &Self::Target {
375 &self.symbols
376 }
377}
378
379impl<'data> IntoIterator for SymbolMap<'data> {
380 type Item = Symbol<'data>;
381 type IntoIter = SymbolMapIter<'data>;
382
383 fn into_iter(self) -> Self::IntoIter {
384 self.symbols.into_iter()
385 }
386}
387
388impl<'data, 'a> IntoIterator for &'a SymbolMap<'data> {
389 type Item = &'a Symbol<'data>;
390 type IntoIter = std::slice::Iter<'a, Symbol<'data>>;
391
392 fn into_iter(self) -> Self::IntoIter {
393 self.symbols.iter()
394 }
395}
396
397impl<'d> AsRef<[Symbol<'d>]> for SymbolMap<'d> {
398 fn as_ref(&self) -> &[Symbol<'d>] {
399 &self.symbols
400 }
401}
402
403impl<'d> From<Vec<Symbol<'d>>> for SymbolMap<'d> {
404 fn from(mut symbols: Vec<Symbol<'d>>) -> Self {
405 if !symbols.is_empty() {
406 // NB: This might require stable sorting to ensure determinism if multiple symbols point
407 // at the same location. However, this only seems to happen for equivalent variants of
408 // the same function.
409 //
410 // An example would be destructors where D2 (base object destructor) and D1 (complete
411 // object destructor) might share the same code. Since those always demangle to the same
412 // name, we do not care which function to keep in this case.
413 //
414 // Inlined functions will generally not appear in this list, unless they _also_ have an
415 // explicit function body, in which case they will have a unique address, again.
416 symbols.sort_by_key(Self::key);
417
418 // Compute sizes of consecutive symbols if the size has not been provided by the symbol
419 // iterator. In the same go, drop all but the first symbols at any given address. We do
420 // not rely on the size of symbols in this case, since the ranges might still be
421 // overlapping.
422 symbols.dedup_by(|next, symbol| {
423 if symbol.size == 0 {
424 symbol.size = next.address - symbol.address;
425 }
426 symbol.address == next.address
427 })
428 }
429
430 SymbolMap { symbols }
431 }
432}
433
434impl<'d> FromIterator<Symbol<'d>> for SymbolMap<'d> {
435 fn from_iter<I>(iter: I) -> Self
436 where
437 I: IntoIterator<Item = Symbol<'d>>,
438 {
439 Vec::from_iter(iter).into()
440 }
441}
442
443/// File information referred by [`LineInfo`](struct.LineInfo.html) comprising a directory and name.
444///
445/// The file path is usually relative to a compilation directory. It might contain parent directory
446/// segments (`../`).
447#[derive(Clone, Default, Eq, PartialEq)]
448pub struct FileInfo<'data> {
449 /// The file's basename.
450 name: Cow<'data, [u8]>,
451 /// Path to the file.
452 dir: Cow<'data, [u8]>,
453}
454
455impl<'data> FileInfo<'data> {
456 /// Creates a `FileInfo` with a given directory and the file name.
457 #[cfg(feature = "dwarf")]
458 pub fn new(dir: Cow<'data, [u8]>, name: Cow<'data, [u8]>) -> Self {
459 FileInfo { name, dir }
460 }
461
462 /// Creates a `FileInfo` from a joined path by trying to split it.
463 #[cfg(any(feature = "breakpad", feature = "ms", feature = "sourcebundle"))]
464 pub fn from_path(path: &'data [u8]) -> Self {
465 let (dir, name) = symbolic_common::split_path_bytes(path);
466
467 FileInfo {
468 name: Cow::Borrowed(name),
469 dir: match dir {
470 Some(dir) => Cow::Borrowed(dir),
471 None => Cow::default(),
472 },
473 }
474 }
475
476 /// Creates a `FileInfo` from a joined path by trying to split it.
477 /// Unlike from_path(), copies the given data instead of referencing it.
478 #[cfg(feature = "ppdb")]
479 pub(crate) fn from_path_owned(path: &[u8]) -> Self {
480 let (dir, name) = symbolic_common::split_path_bytes(path);
481
482 FileInfo {
483 name: Cow::Owned(name.to_vec()),
484 dir: match dir {
485 Some(dir) => Cow::Owned(dir.to_vec()),
486 None => Cow::default(),
487 },
488 }
489 }
490
491 /// Creates a `FileInfo` with the file name.
492 pub fn from_filename(name: &'data [u8]) -> Self {
493 FileInfo {
494 name: Cow::Borrowed(name),
495 dir: Cow::default(),
496 }
497 }
498
499 /// The file name as UTF-8 string.
500 pub fn name_str(&self) -> Cow<'data, str> {
501 from_utf8_cow_lossy(&self.name)
502 }
503
504 /// Path to the file relative to the compilation directory.
505 pub fn dir_str(&self) -> Cow<'data, str> {
506 from_utf8_cow_lossy(&self.dir)
507 }
508
509 /// The full path to the file, relative to the compilation directory.
510 pub fn path_str(&self) -> String {
511 let joined = join_path(&self.dir_str(), &self.name_str());
512 clean_path(&joined).into_owned()
513 }
514}
515
516#[allow(clippy::ptr_arg)] // false positive https://github.com/rust-lang/rust-clippy/issues/9218
517pub(crate) fn from_utf8_cow_lossy<'data>(input: &Cow<'data, [u8]>) -> Cow<'data, str> {
518 // See https://github.com/rust-lang/rust/issues/32669
519 match input {
520 Cow::Borrowed(bytes) => String::from_utf8_lossy(bytes),
521 Cow::Owned(bytes) => match String::from_utf8_lossy(bytes) {
522 Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(bytes.to_vec()) }.into(),
523 Cow::Owned(s) => s.into(),
524 },
525 }
526}
527
528impl fmt::Debug for FileInfo<'_> {
529 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
530 f.debug_struct("FileInfo")
531 .field("name", &self.name_str())
532 .field("dir", &self.dir_str())
533 .finish()
534 }
535}
536
537/// File information comprising a compilation directory, relative path and name.
538pub struct FileEntry<'data> {
539 /// Path to the compilation directory. File paths are relative to this.
540 compilation_dir: Cow<'data, [u8]>,
541 /// File name and path.
542 pub info: FileInfo<'data>,
543}
544
545impl<'data> FileEntry<'data> {
546 /// Path to the compilation directory.
547 pub fn new(compilation_dir: Cow<'data, [u8]>, info: FileInfo<'data>) -> Self {
548 FileEntry {
549 compilation_dir,
550 info,
551 }
552 }
553
554 /// Path to the compilation directory.
555 pub fn compilation_dir_str(&self) -> Cow<'data, str> {
556 from_utf8_cow_lossy(&self.compilation_dir)
557 }
558
559 /// Absolute path to the file, including the compilation directory.
560 pub fn abs_path_str(&self) -> String {
561 let joined_path = join_path(&self.dir_str(), &self.name_str());
562 let joined = join_path(&self.compilation_dir_str(), &joined_path);
563 clean_path(&joined).into_owned()
564 }
565}
566
567impl fmt::Debug for FileEntry<'_> {
568 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
569 f.debug_struct("FileInfo")
570 .field("compilation_dir", &self.compilation_dir_str())
571 .field("name", &self.name_str())
572 .field("dir", &self.dir_str())
573 .finish()
574 }
575}
576
577impl<'data> Deref for FileEntry<'data> {
578 type Target = FileInfo<'data>;
579
580 fn deref(&self) -> &Self::Target {
581 &self.info
582 }
583}
584
585/// File and line number mapping for an instruction address.
586#[derive(Clone, Eq, PartialEq)]
587pub struct LineInfo<'data> {
588 /// The instruction address relative to the image base (load address).
589 pub address: u64,
590 /// Total code size covered by this line record.
591 pub size: Option<u64>,
592 /// File name and path.
593 pub file: FileInfo<'data>,
594 /// Absolute line number starting at 1. Zero means no line number.
595 pub line: u64,
596}
597
598#[cfg(test)]
599impl LineInfo<'static> {
600 pub(crate) fn new(address: u64, size: u64, file: &[u8], line: u64) -> LineInfo<'_> {
601 LineInfo {
602 address,
603 size: Some(size),
604 file: FileInfo::from_filename(file),
605 line,
606 }
607 }
608}
609
610impl fmt::Debug for LineInfo<'_> {
611 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
612 let mut s = f.debug_struct("LineInfo");
613 s.field("address", &format_args!("{:#x}", self.address));
614
615 match self.size {
616 Some(size) => s.field("size", &format_args!("{size:#x}")),
617 None => s.field("size", &self.size),
618 };
619
620 s.field("file", &self.file)
621 .field("line", &self.line)
622 .finish()
623 }
624}
625
626/// Debug information for a function.
627#[derive(Clone)]
628pub struct Function<'data> {
629 /// Relative instruction address of the start of the function.
630 pub address: u64,
631 /// Total code size covered by the function body, including inlined functions.
632 pub size: u64,
633 /// The name and language of the function symbol.
634 pub name: Name<'data>,
635 /// Path to the compilation directory. File paths are relative to this.
636 pub compilation_dir: &'data [u8],
637 /// Lines covered by this function, including inlined children.
638 pub lines: Vec<LineInfo<'data>>,
639 /// Functions that have been inlined into this function's body.
640 pub inlinees: Vec<Function<'data>>,
641 /// Specifies whether this function is inlined.
642 pub inline: bool,
643}
644
645impl Function<'_> {
646 /// End address of the entire function body, including inlined functions.
647 ///
648 /// This address points at the first instruction after the function body.
649 pub fn end_address(&self) -> u64 {
650 self.address.saturating_add(self.size)
651 }
652}
653
654impl fmt::Debug for Function<'_> {
655 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
656 f.debug_struct("Function")
657 .field("address", &format_args!("{:#x}", self.address))
658 .field("size", &format_args!("{:#x}", self.size))
659 .field("name", &self.name)
660 .field(
661 "compilation_dir",
662 &String::from_utf8_lossy(self.compilation_dir),
663 )
664 .field("lines", &self.lines)
665 .field("inlinees", &self.inlinees)
666 .field("inline", &self.inline)
667 .finish()
668 }
669}
670
671/// A dynamically dispatched iterator over items with the given lifetime.
672pub type DynIterator<'a, T> = Box<dyn Iterator<Item = T> + 'a>;
673
674/// A stateful session for interfacing with debug information.
675///
676/// Debug sessions can be obtained via [`ObjectLike::debug_session`]. Since computing a session may
677/// be a costly operation, try to reuse the session as much as possible.
678///
679/// ## Implementing DebugSession
680///
681/// Reading debug information from object files usually requires loading multiple sections into
682/// memory and computing maps for quick random access to certain information. Since this can be a
683/// quite costly process, this is encapsulated into a `DebugSession`. The session may hold whatever
684/// data and caches may be necessary for efficiently interfacing with the debug info.
685///
686/// All trait methods on a `DebugSession` receive `&mut self`, to allow mutation of internal cache
687/// structures. Lifetimes of returned types are tied to this session's lifetime, which allows to
688/// borrow data from the session.
689///
690/// Examples for things to compute when building a debug session are:
691///
692/// - Decompress debug information if it is stored with compression.
693/// - Build a symbol map for random access to public symbols.
694/// - Map string tables and other lookup tables.
695/// - Read headers of compilation units (compilands) to resolve cross-unit references.
696///
697/// [`ObjectLike::debug_session`]: trait.ObjectLike.html#tymethod.debug_session
698pub trait DebugSession<'session> {
699 /// The error returned when reading debug information fails.
700 type Error;
701
702 /// An iterator over all functions in this debug file.
703 type FunctionIterator: Iterator<Item = Result<Function<'session>, Self::Error>>;
704
705 /// An iterator over all source files referenced by this debug file.
706 type FileIterator: Iterator<Item = Result<FileEntry<'session>, Self::Error>>;
707
708 /// Returns an iterator over all functions in this debug file.
709 ///
710 /// Functions are iterated in the order they are declared in their compilation units. The
711 /// functions yielded by this iterator include all inlinees and line records resolved.
712 ///
713 /// Note that the iterator holds a mutable borrow on the debug session, which allows it to use
714 /// caches and optimize resources while resolving function and line information.
715 fn functions(&'session self) -> Self::FunctionIterator;
716
717 /// Returns an iterator over all source files referenced by this debug file.
718 fn files(&'session self) -> Self::FileIterator;
719
720 /// Looks up a file's source by its full canonicalized path.
721 ///
722 /// Returns a descriptor that has all the information available of the source. It can
723 /// either contain the source contents directly, if it was embedded, or a source link.
724 fn source_by_path(&self, path: &str) -> Result<Option<SourceFileDescriptor<'_>>, Self::Error>;
725}
726
727/// An object containing debug information.
728pub trait ObjectLike<'data, 'object> {
729 /// Errors thrown when reading information from this object.
730 type Error;
731
732 /// A session that allows optimized access to debugging information.
733 type Session: for<'session> DebugSession<'session, Error = Self::Error>;
734
735 /// The iterator over the symbols in the public symbol table.
736 type SymbolIterator: Iterator<Item = Symbol<'data>>;
737
738 /// The container format of this file.
739 fn file_format(&self) -> FileFormat;
740
741 /// The code identifier of this object.
742 ///
743 /// The identifier can be `None` if it cannot be determined from the object file, for instance,
744 /// because the identifier was stripped in the build process.
745 fn code_id(&self) -> Option<CodeId>;
746
747 /// The debug information identifier of this object.
748 fn debug_id(&self) -> DebugId;
749
750 /// The CPU architecture of this object.
751 fn arch(&self) -> Arch;
752
753 /// The kind of this object.
754 fn kind(&self) -> ObjectKind;
755
756 /// The address at which the image prefers to be loaded into memory.
757 fn load_address(&self) -> u64;
758
759 /// Determines whether this object exposes a public symbol table.
760 fn has_symbols(&self) -> bool;
761
762 /// Returns an iterator over symbols in the public symbol table.
763 fn symbols(&'object self) -> Self::SymbolIterator;
764
765 /// Returns an ordered map of symbols in the symbol table.
766 fn symbol_map(&self) -> SymbolMap<'data>;
767
768 /// Determines whether this object contains debug information.
769 fn has_debug_info(&self) -> bool;
770
771 /// Constructs a debugging session.
772 ///
773 /// A debugging session loads certain information from the object file and creates caches for
774 /// efficient access to various records in the debug information. Since this can be quite a
775 /// costly process, try to reuse the debugging session as long as possible.
776 ///
777 /// Constructing this session will also work if the object does not contain debugging
778 /// information, in which case the session will be a no-op. This can be checked via
779 /// [`has_debug_info`](trait.ObjectLike.html#tymethod.has_debug_info).
780 fn debug_session(&'object self) -> Result<Self::Session, Self::Error>;
781
782 /// Determines whether this object contains stack unwinding information.
783 fn has_unwind_info(&self) -> bool;
784
785 /// Determines whether this object contains embedded sources.
786 fn has_sources(&self) -> bool;
787
788 /// Determines whether this object is malformed and was only partially parsed
789 fn is_malformed(&self) -> bool;
790}
791
792mod derive_serde {
793 /// Helper macro to implement string based serialization and deserialization.
794 ///
795 /// If a type implements `FromStr` and `Display` then this automatically
796 /// implements a serializer/deserializer for that type that dispatches
797 /// appropriately.
798 macro_rules! impl_str_serde {
799 ($type:ty) => {
800 impl ::serde::ser::Serialize for $type {
801 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
802 where
803 S: ::serde::ser::Serializer,
804 {
805 serializer.serialize_str(self.name())
806 }
807 }
808
809 impl<'de> ::serde::de::Deserialize<'de> for $type {
810 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
811 where
812 D: ::serde::de::Deserializer<'de>,
813 {
814 <::std::borrow::Cow<'_, str>>::deserialize(deserializer)?
815 .parse()
816 .map_err(::serde::de::Error::custom)
817 }
818 }
819 };
820 }
821
822 impl_str_serde!(super::ObjectKind);
823 impl_str_serde!(super::FileFormat);
824}
825
826#[cfg(test)]
827mod tests {
828 use super::*;
829 use similar_asserts::assert_eq;
830
831 fn file_info<'a>(dir: &'a str, name: &'a str) -> FileInfo<'a> {
832 FileInfo::new(
833 Cow::Borrowed(dir.as_bytes()),
834 Cow::Borrowed(name.as_bytes()),
835 )
836 }
837
838 fn file_entry<'a>(compilation_dir: &'a str, dir: &'a str, name: &'a str) -> FileEntry<'a> {
839 FileEntry::new(
840 Cow::Borrowed(compilation_dir.as_bytes()),
841 file_info(dir, name),
842 )
843 }
844
845 #[test]
846 fn test_file_info() {
847 assert_eq!(file_info("", "foo.h").path_str(), "foo.h");
848 assert_eq!(
849 file_info("C:\\Windows", "foo.h").path_str(),
850 "C:\\Windows\\foo.h"
851 );
852 assert_eq!(
853 file_info("/usr/local", "foo.h").path_str(),
854 "/usr/local/foo.h"
855 );
856 assert_eq!(file_info("/usr/local", "../foo.h").path_str(), "/usr/foo.h");
857 assert_eq!(file_info("/usr/local", "/foo.h").path_str(), "/foo.h");
858 }
859
860 #[test]
861 fn test_file_entry() {
862 assert_eq!(file_entry("", "", "foo.h").abs_path_str(), "foo.h");
863 assert_eq!(
864 file_entry("C:\\Windows", "src", "foo.h").abs_path_str(),
865 "C:\\Windows\\src\\foo.h"
866 );
867 assert_eq!(
868 file_entry("/usr", "local", "foo.h").abs_path_str(),
869 "/usr/local/foo.h"
870 );
871 assert_eq!(
872 file_entry("/usr/local", "..", "foo.h").abs_path_str(),
873 "/usr/foo.h"
874 );
875 assert_eq!(
876 file_entry("/usr", "/src", "foo.h").abs_path_str(),
877 "/src/foo.h"
878 );
879 }
880}