symbolic_ppdb/cache/
writer.rs

1use std::collections::BTreeMap;
2use std::io::Write;
3
4use indexmap::IndexSet;
5use symbolic_common::{DebugId, Language};
6use watto::{Pod, StringTable};
7
8use super::{raw, CacheError};
9use crate::PortablePdb;
10
11/// The PortablePdbCache Converter.
12///
13/// This can extract data from a [`PortablePdb`] struct and
14/// serialize it to disk via its [`serialize`](PortablePdbCacheConverter::serialize) method.
15#[derive(Debug, Default)]
16pub struct PortablePdbCacheConverter {
17    /// A byte sequence uniquely representing the debugging metadata blob content.
18    pdb_id: DebugId,
19    /// The set of all [`raw::File`]s that have been added to this `Converter`.
20    files: IndexSet<raw::File>,
21    /// A map from [`raw::Range`]s to the [`raw::SourceLocation`]s they correspond to.
22    ///
23    /// Only the starting address of a range is saved, the end address is given implicitly
24    /// by the start address of the next range.
25    ranges: BTreeMap<raw::Range, raw::SourceLocation>,
26    string_table: StringTable,
27}
28
29impl PortablePdbCacheConverter {
30    /// Creates a new Converter.
31    pub fn new() -> Self {
32        Self::default()
33    }
34
35    /// Processes a Portable PDB file, inserting its sequence point information into this converter.
36    pub fn process_portable_pdb(&mut self, portable_pdb: &PortablePdb) -> Result<(), CacheError> {
37        if let Some(id) = portable_pdb.pdb_id() {
38            self.set_pdb_id(id);
39        }
40
41        for (function, sequence_points) in portable_pdb.get_all_sequence_points().enumerate() {
42            let func_idx = (function + 1) as u32;
43            let sequence_points = sequence_points?;
44
45            for sp in sequence_points.iter() {
46                if sp.is_hidden() {
47                    continue;
48                }
49                let range = raw::Range {
50                    func_idx,
51                    il_offset: sp.il_offset,
52                };
53
54                let doc = portable_pdb.get_document(sp.document_id as usize)?;
55                let file_idx = self.insert_file(&doc.name, doc.lang);
56                let source_location = raw::SourceLocation {
57                    line: sp.start_line,
58                    file_idx,
59                };
60
61                self.ranges.insert(range, source_location);
62            }
63        }
64
65        Ok(())
66    }
67
68    /// Serialize the converted data.
69    ///
70    /// This writes the PortablePdbCache binary format into the given [`Write`].
71    pub fn serialize<W: Write>(self, writer: &mut W) -> std::io::Result<()> {
72        let mut writer = watto::Writer::new(writer);
73
74        let num_ranges = self.ranges.len() as u32;
75        let num_files = self.files.len() as u32;
76        let string_bytes = self.string_table.into_bytes();
77
78        let header = raw::Header {
79            magic: raw::PPDBCACHE_MAGIC,
80            version: super::PPDBCACHE_VERSION,
81
82            pdb_id: self.pdb_id,
83
84            num_files,
85            num_ranges,
86            string_bytes: string_bytes.len() as u32,
87            _reserved: [0; 16],
88        };
89
90        writer.write_all(header.as_bytes())?;
91        writer.align_to(8)?;
92
93        for file in self.files.into_iter() {
94            writer.write_all(file.as_bytes())?;
95        }
96        writer.align_to(8)?;
97
98        for sl in self.ranges.values() {
99            writer.write_all(sl.as_bytes())?;
100        }
101        writer.align_to(8)?;
102
103        for r in self.ranges.keys() {
104            writer.write_all(r.as_bytes())?;
105        }
106        writer.align_to(8)?;
107
108        writer.write_all(&string_bytes)?;
109
110        Ok(())
111    }
112
113    fn set_pdb_id(&mut self, id: DebugId) {
114        self.pdb_id = id;
115    }
116
117    fn insert_file(&mut self, name: &str, lang: Language) -> u32 {
118        let name_offset = self.string_table.insert(name) as u32;
119        let file = raw::File {
120            name_offset,
121            lang: lang as u32,
122        };
123
124        self.files.insert_full(file).0 as u32
125    }
126}