1use crate::{CodeTransform, Function, InstrLocId, ModuleFunctions};
2use id_arena::Id;
3use std::{cmp::Ordering, ops::Range};
4
5use super::dwarf::AddressSearchPreference;
6
7#[derive(Debug, PartialEq)]
9pub(crate) enum CodeAddress {
10 InstrInFunction { instr_id: InstrLocId },
12 InstrEdge { instr_id: InstrLocId },
14 OffsetInFunction { id: Id<Function>, offset: usize },
16 FunctionEdge { id: Id<Function> },
18 Unknown,
20}
21
22pub(crate) struct CodeAddressGenerator {
24 address_convert_table: Vec<(Range<usize>, Id<Function>)>,
26 instrument_address_convert_table: Vec<(usize, InstrLocId)>,
28}
29
30impl CodeAddressGenerator {
31 pub(crate) fn new(funcs: &ModuleFunctions) -> Self {
32 let mut address_convert_table = funcs
33 .iter_local()
34 .filter_map(|(func_id, func)| func.original_range.clone().map(|range| (range, func_id)))
35 .collect::<Vec<_>>();
36
37 let mut instrument_address_convert_table = funcs
38 .iter_local()
39 .flat_map(|(_, func)| &func.instruction_mapping)
40 .copied()
41 .collect::<Vec<_>>();
42
43 address_convert_table.sort_by_key(|i| i.0.start);
44 instrument_address_convert_table.sort_by_key(|i| i.0);
45
46 Self {
47 address_convert_table,
48 instrument_address_convert_table,
49 }
50 }
51
52 pub(crate) fn find_address(
53 &self,
54 address: usize,
55 search_preference: AddressSearchPreference,
56 ) -> CodeAddress {
57 match self
58 .instrument_address_convert_table
59 .binary_search_by_key(&address, |i| i.0)
60 {
61 Ok(id) => {
62 return CodeAddress::InstrInFunction {
63 instr_id: self.instrument_address_convert_table[id].1,
64 }
65 }
66 Err(id) => {
67 if id < self.instrument_address_convert_table.len()
68 && self.instrument_address_convert_table[id].0 - 1 == address
69 {
70 return CodeAddress::InstrEdge {
71 instr_id: self.instrument_address_convert_table[id].1,
72 };
73 }
74 }
75 };
76
77 let inclusive_range_comparor = |range: &(Range<usize>, Id<Function>)| {
79 if range.0.end < address {
81 Ordering::Less
82 } else if address <= range.0.start {
83 Ordering::Greater
84 } else {
85 Ordering::Equal
86 }
87 };
88 let exclusive_range_comparor = |range: &(Range<usize>, Id<Function>)| {
89 if range.0.end <= address {
91 Ordering::Less
92 } else if address < range.0.start {
93 Ordering::Greater
94 } else {
95 Ordering::Equal
96 }
97 };
98 let range_comparor: &dyn Fn(_) -> Ordering = match search_preference {
99 AddressSearchPreference::InclusiveFunctionEnd => &inclusive_range_comparor,
100 AddressSearchPreference::ExclusiveFunctionEnd => &exclusive_range_comparor,
101 };
102
103 match self.address_convert_table.binary_search_by(range_comparor) {
104 Ok(i) => {
105 let entry = &self.address_convert_table[i];
106 let code_offset_from_function_start = address - entry.0.start;
107
108 if address == entry.0.end {
109 CodeAddress::FunctionEdge { id: entry.1 }
110 } else {
111 CodeAddress::OffsetInFunction {
112 id: entry.1,
113 offset: code_offset_from_function_start,
114 }
115 }
116 }
117 Err(_) => CodeAddress::Unknown,
118 }
119 }
120}
121
122pub(crate) struct CodeAddressConverter<'a> {
124 code_transform: &'a CodeTransform,
125}
126
127impl<'a> CodeAddressConverter<'a> {
128 pub(crate) fn new(code_transform: &'a CodeTransform) -> Self {
129 Self { code_transform }
130 }
131
132 pub(crate) fn find_address(&self, code: CodeAddress) -> Option<usize> {
133 match code {
134 CodeAddress::InstrInFunction { instr_id } => {
135 match self
136 .code_transform
137 .instruction_map
138 .binary_search_by_key(&instr_id, |i| i.0)
139 {
140 Ok(id) => Some(self.code_transform.instruction_map[id].1),
141 Err(_) => None,
142 }
143 }
144 CodeAddress::InstrEdge { instr_id } => {
145 match self
146 .code_transform
147 .instruction_map
148 .binary_search_by_key(&instr_id, |i| i.0)
149 {
150 Ok(id) => Some(self.code_transform.instruction_map[id].1 - 1),
151 Err(_) => None,
152 }
153 }
154 CodeAddress::OffsetInFunction { id, offset } => {
155 match self
156 .code_transform
157 .function_ranges
158 .binary_search_by_key(&id, |i| i.0)
159 {
160 Ok(id) => Some(self.code_transform.function_ranges[id].1.start + offset),
161 Err(_) => None,
162 }
163 }
164 CodeAddress::FunctionEdge { id } => {
165 match self
166 .code_transform
167 .function_ranges
168 .binary_search_by_key(&id, |i| i.0)
169 {
170 Ok(id) => Some(self.code_transform.function_ranges[id].1.end),
171 Err(_) => None,
172 }
173 }
174 CodeAddress::Unknown => None,
175 }
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182
183 #[test]
184 fn test_code_address_generator() {
185 let mut module = crate::Module::default();
186
187 let mut func1 = crate::LocalFunction::new(
188 Vec::new(),
189 crate::FunctionBuilder::new(&mut module.types, &[], &[]),
190 );
191
192 func1.original_range = Some(Range { start: 20, end: 30 });
193
194 let id1 = module.funcs.add_local(func1);
195
196 let mut func2 = crate::LocalFunction::new(
197 Vec::new(),
198 crate::FunctionBuilder::new(&mut module.types, &[], &[]),
199 );
200
201 func2.original_range = Some(Range { start: 30, end: 50 });
202
203 let id2 = module.funcs.add_local(func2);
204
205 let address_converter = CodeAddressGenerator::new(&module.funcs);
206
207 assert_eq!(
208 address_converter.find_address(10, AddressSearchPreference::InclusiveFunctionEnd),
209 CodeAddress::Unknown
210 );
211 assert_eq!(
212 address_converter.find_address(20, AddressSearchPreference::ExclusiveFunctionEnd),
213 CodeAddress::OffsetInFunction { id: id1, offset: 0 }
214 );
215 assert_eq!(
216 address_converter.find_address(20, AddressSearchPreference::InclusiveFunctionEnd),
217 CodeAddress::Unknown
218 );
219 assert_eq!(
220 address_converter.find_address(25, AddressSearchPreference::ExclusiveFunctionEnd),
221 CodeAddress::OffsetInFunction { id: id1, offset: 5 }
222 );
223 assert_eq!(
224 address_converter.find_address(29, AddressSearchPreference::ExclusiveFunctionEnd),
225 CodeAddress::OffsetInFunction { id: id1, offset: 9 }
226 );
227 assert_eq!(
228 address_converter.find_address(29, AddressSearchPreference::InclusiveFunctionEnd),
229 CodeAddress::OffsetInFunction { id: id1, offset: 9 }
230 );
231 assert_eq!(
232 address_converter.find_address(30, AddressSearchPreference::InclusiveFunctionEnd),
233 CodeAddress::FunctionEdge { id: id1 }
234 );
235 assert_eq!(
236 address_converter.find_address(30, AddressSearchPreference::ExclusiveFunctionEnd),
237 CodeAddress::OffsetInFunction { id: id2, offset: 0 }
238 );
239 assert_eq!(
240 address_converter.find_address(50, AddressSearchPreference::InclusiveFunctionEnd),
241 CodeAddress::FunctionEdge { id: id2 }
242 );
243 assert_eq!(
244 address_converter.find_address(50, AddressSearchPreference::ExclusiveFunctionEnd),
245 CodeAddress::Unknown
246 );
247 }
248
249 #[test]
250 fn test_code_address_converter() {
251 let mut module = crate::Module::default();
252
253 let func1 = crate::LocalFunction::new(
254 Vec::new(),
255 crate::FunctionBuilder::new(&mut module.types, &[], &[]),
256 );
257 let id1 = module.funcs.add_local(func1);
258 let instr_id1 = InstrLocId::new(1);
259 let instr_id2 = InstrLocId::new(2);
260
261 let mut code_transform = CodeTransform::default();
262 {
263 code_transform
264 .function_ranges
265 .push((id1, Range { start: 50, end: 80 }));
266 code_transform.instruction_map.push((instr_id1, 60));
267 code_transform.instruction_map.push((instr_id2, 65));
268 }
269
270 let converter = CodeAddressConverter::new(&code_transform);
271
272 assert_eq!(
273 converter.find_address(CodeAddress::OffsetInFunction { id: id1, offset: 5 }),
274 Some(55)
275 );
276 assert_eq!(
277 converter.find_address(CodeAddress::InstrInFunction {
278 instr_id: instr_id1
279 }),
280 Some(60)
281 );
282 assert_eq!(
283 converter.find_address(CodeAddress::InstrEdge {
284 instr_id: instr_id2
285 }),
286 Some(64)
287 );
288 assert_eq!(
289 converter.find_address(CodeAddress::InstrInFunction {
290 instr_id: instr_id2
291 }),
292 Some(65)
293 );
294 assert_eq!(
295 converter.find_address(CodeAddress::FunctionEdge { id: id1 }),
296 Some(80)
297 );
298 assert_eq!(converter.find_address(CodeAddress::Unknown), None);
299 }
300}