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}