fxprof_processed_profile/
lib_mappings.rs

1use std::collections::BTreeMap;
2
3/// Keeps track of mapped libraries in an address space. Stores a value
4/// for each mapping, and allows efficient lookup of that value based on
5/// an address.
6///
7/// A "library" here is a loose term; it could be a normal shared library,
8/// or the main binary, but it could also be a synthetic library for JIT
9/// code. For normal libraries, there's usually just one mapping per library.
10/// For JIT code, you could have many small mappings, one per JIT function,
11/// all pointing to the synthetic JIT "library".
12#[derive(Debug, Clone)]
13pub struct LibMappings<T> {
14    /// A BTreeMap of non-overlapping Mappings. The key is the start_avma of the mapping.
15    ///
16    /// When a new mapping is added, overlapping mappings are removed.
17    map: BTreeMap<u64, Mapping<T>>,
18}
19
20impl<T> Default for LibMappings<T> {
21    fn default() -> Self {
22        Self::new()
23    }
24}
25
26impl<T> LibMappings<T> {
27    /// Creates a new empty instance.
28    pub fn new() -> Self {
29        Self {
30            map: BTreeMap::new(),
31        }
32    }
33
34    /// Add a mapping to this address space. Any existing mappings which overlap with the
35    /// new mapping are removed.
36    ///
37    /// `start_avma` and `end_avma` describe the address range that this mapping
38    /// occupies.
39    ///
40    /// AVMA = "actual virtual memory address"
41    ///
42    /// `relative_address_at_start` is the "relative address" which corresponds
43    /// to `start_avma`, in the library that is mapped in this mapping. This is zero if
44    /// `start_avm` is the base address of the library.
45    ///
46    /// A relative address is a `u32` value which is relative to the library base address.
47    /// So you will usually set `relative_address_at_start` to `start_avma - base_avma`.
48    ///
49    /// For ELF binaries, the base address is the AVMA of the first segment, i.e. the
50    /// start_avma of the mapping created by the first ELF `LOAD` command.
51    ///
52    /// For mach-O binaries, the base address is the vmaddr of the `__TEXT` segment.
53    ///
54    /// For Windows binaries, the base address is the image load address.
55    pub fn add_mapping(
56        &mut self,
57        start_avma: u64,
58        end_avma: u64,
59        relative_address_at_start: u32,
60        value: T,
61    ) {
62        let removal_avma_range_start =
63            if let Some(mapping_overlapping_with_start_avma) = self.lookup_impl(start_avma) {
64                mapping_overlapping_with_start_avma.start_avma
65            } else {
66                start_avma
67            };
68        // self.map.drain(removal_avma_range_start..end_avma);
69        let overlapping_keys: Vec<u64> = self
70            .map
71            .range(removal_avma_range_start..end_avma)
72            .map(|(start_avma, _)| *start_avma)
73            .collect();
74        for key in overlapping_keys {
75            self.map.remove(&key);
76        }
77
78        self.map.insert(
79            start_avma,
80            Mapping {
81                start_avma,
82                end_avma,
83                relative_address_at_start,
84                value,
85            },
86        );
87    }
88
89    /// Remove a mapping which starts at the given address. If found, this returns
90    /// the `relative_address_at_start` and the associated value of the mapping.
91    pub fn remove_mapping(&mut self, start_avma: u64) -> Option<(u32, T)> {
92        self.map
93            .remove(&start_avma)
94            .map(|m| (m.relative_address_at_start, m.value))
95    }
96
97    /// Clear all mappings.
98    pub fn clear(&mut self) {
99        self.map.clear();
100    }
101
102    /// Look up the mapping which covers the given address and return
103    /// the stored value.
104    pub fn lookup(&self, avma: u64) -> Option<&T> {
105        self.lookup_impl(avma).map(|m| &m.value)
106    }
107
108    /// Look up the mapping which covers the given address and return
109    /// its `Mapping<T>``.
110    fn lookup_impl(&self, avma: u64) -> Option<&Mapping<T>> {
111        let (_start_avma, last_mapping_starting_at_or_before_avma) =
112            self.map.range(..=avma).next_back()?;
113        if avma < last_mapping_starting_at_or_before_avma.end_avma {
114            Some(last_mapping_starting_at_or_before_avma)
115        } else {
116            None
117        }
118    }
119
120    /// Converts an absolute address (AVMA, actual virtual memory address) into
121    /// a relative address and the mapping's associated value.
122    pub fn convert_address(&self, avma: u64) -> Option<(u32, &T)> {
123        let mapping = self.lookup_impl(avma)?;
124        let offset_from_mapping_start = (avma - mapping.start_avma) as u32;
125        let relative_address = mapping.relative_address_at_start + offset_from_mapping_start;
126        Some((relative_address, &mapping.value))
127    }
128}
129
130#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)]
131struct Mapping<T> {
132    start_avma: u64,
133    end_avma: u64,
134    relative_address_at_start: u32,
135    value: T,
136}
137
138#[cfg(test)]
139mod test {
140    use super::*;
141
142    #[test]
143    fn test_lib_mappings() {
144        let mut m = LibMappings::new();
145        m.add_mapping(100, 200, 100, "100..200");
146        m.add_mapping(200, 250, 200, "200..250");
147        assert_eq!(m.lookup(200), Some(&"200..250"));
148        m.add_mapping(180, 220, 180, "180..220");
149        assert_eq!(m.lookup(200), Some(&"180..220"));
150        assert_eq!(m.lookup(170), None);
151        assert_eq!(m.lookup(220), None);
152        m.add_mapping(225, 250, 225, "225..250");
153        m.add_mapping(255, 270, 255, "255..270");
154        m.add_mapping(100, 150, 100, "100..150");
155        assert_eq!(m.lookup(90), None);
156        assert_eq!(m.lookup(150), None);
157        assert_eq!(m.lookup(149), Some(&"100..150"));
158        assert_eq!(m.lookup(200), Some(&"180..220"));
159        assert_eq!(m.lookup(260), Some(&"255..270"));
160    }
161}