wasmtime_cranelift/debug/transform/
address_transform.rs

1use crate::debug::Compilation;
2use crate::FunctionAddressMap;
3use gimli::write;
4use std::collections::BTreeMap;
5use wasmtime_environ::{DefinedFuncIndex, FilePos, PrimaryMap, StaticModuleIndex};
6
7pub type GeneratedAddress = usize;
8pub type WasmAddress = u64;
9
10/// Contains mapping of the generated address to its original
11/// source location.
12#[derive(Debug)]
13pub struct AddressMap {
14    pub generated: GeneratedAddress,
15    pub wasm: WasmAddress,
16}
17
18/// Information about generated function code: its body start,
19/// length, and instructions addresses.
20#[derive(Debug)]
21pub struct FunctionMap {
22    pub symbol: usize,
23    pub offset: GeneratedAddress,
24    pub len: GeneratedAddress,
25    pub wasm_start: WasmAddress,
26    pub wasm_end: WasmAddress,
27    pub addresses: Box<[AddressMap]>,
28}
29
30/// Mapping of the source location to its generated code range.
31#[derive(Debug)]
32struct Position {
33    wasm_pos: WasmAddress,
34    gen_start: GeneratedAddress,
35    gen_end: GeneratedAddress,
36}
37
38/// Mapping of continuous range of source location to its generated
39/// code. The positions are always in ascending order for search.
40#[derive(Debug)]
41struct Range {
42    wasm_start: WasmAddress,
43    wasm_end: WasmAddress,
44    gen_start: GeneratedAddress,
45    gen_end: GeneratedAddress,
46    positions: Box<[Position]>,
47}
48
49type RangeIndex = usize;
50
51/// Helper function address lookup data. Contains ranges start positions
52/// index and ranges data. The multiple ranges can include the same
53/// original source position. The index (B-Tree) uses range start
54/// position as a key. The index values reference the ranges array.
55/// The item are ordered RangeIndex.
56#[derive(Debug)]
57struct FuncLookup {
58    index: Vec<(WasmAddress, Box<[RangeIndex]>)>,
59    ranges: Box<[Range]>,
60}
61
62/// Mapping of original functions to generated code locations/ranges.
63#[derive(Debug)]
64struct FuncTransform {
65    start: WasmAddress,
66    end: WasmAddress,
67    index: DefinedFuncIndex,
68    lookup: FuncLookup,
69}
70
71/// Module functions mapping to generated code.
72#[derive(Debug)]
73pub struct AddressTransform {
74    map: PrimaryMap<DefinedFuncIndex, FunctionMap>,
75    func: Vec<(WasmAddress, FuncTransform)>,
76}
77
78/// Returns a wasm bytecode offset in the code section from SourceLoc.
79fn get_wasm_code_offset(loc: FilePos, code_section_offset: u64) -> WasmAddress {
80    // Code section size <= 4GB, allow wrapped SourceLoc to recover the overflow.
81    loc.file_offset()
82        .unwrap()
83        .wrapping_sub(code_section_offset as u32) as WasmAddress
84}
85
86fn build_function_lookup(
87    ft: &FunctionAddressMap,
88    code_section_offset: u64,
89) -> (WasmAddress, WasmAddress, FuncLookup) {
90    assert!(code_section_offset <= ft.start_srcloc.file_offset().unwrap().into());
91    let fn_start = get_wasm_code_offset(ft.start_srcloc, code_section_offset);
92    let fn_end = get_wasm_code_offset(ft.end_srcloc, code_section_offset);
93    assert!(fn_start <= fn_end);
94
95    // Build ranges of continuous source locations. The new ranges starts when
96    // non-descending order is interrupted. Assuming the same origin location can
97    // be present in multiple ranges.
98    let mut range_wasm_start = fn_start;
99    let mut range_gen_start = ft.body_offset;
100    let mut last_wasm_pos = range_wasm_start;
101    let mut ranges = Vec::new();
102    let mut ranges_index = BTreeMap::new();
103    let mut current_range = Vec::new();
104    let mut last_gen_inst_empty = false;
105    for (i, t) in ft.instructions.iter().enumerate() {
106        if t.srcloc.file_offset().is_none() {
107            continue;
108        }
109
110        let offset = get_wasm_code_offset(t.srcloc, code_section_offset);
111        assert!(fn_start <= offset);
112        assert!(offset <= fn_end);
113
114        let inst_gen_start = t.code_offset as usize;
115        let inst_gen_end = match ft.instructions.get(i + 1) {
116            Some(i) => i.code_offset as usize,
117            None => ft.body_len as usize,
118        };
119
120        if last_wasm_pos > offset {
121            // Start new range.
122            ranges_index.insert(range_wasm_start, ranges.len());
123            ranges.push(Range {
124                wasm_start: range_wasm_start,
125                wasm_end: last_wasm_pos,
126                gen_start: range_gen_start,
127                gen_end: inst_gen_start,
128                positions: current_range.into_boxed_slice(),
129            });
130            range_wasm_start = offset;
131            range_gen_start = inst_gen_start;
132            current_range = Vec::new();
133            last_gen_inst_empty = false;
134        }
135        if last_gen_inst_empty && current_range.last().unwrap().gen_start == inst_gen_start {
136            // It is possible that previous inst_gen_start == inst_gen_end, so
137            // make an attempt to merge all such positions with current one.
138            if inst_gen_start < inst_gen_end {
139                let last = current_range.last_mut().unwrap();
140                last.gen_end = inst_gen_end;
141                last_gen_inst_empty = false;
142            }
143        } else {
144            // Continue existing range: add new wasm->generated code position.
145            current_range.push(Position {
146                wasm_pos: offset,
147                gen_start: inst_gen_start,
148                gen_end: inst_gen_end,
149            });
150            // Track if last position was empty (see if-branch above).
151            last_gen_inst_empty = inst_gen_start == inst_gen_end;
152        }
153        last_wasm_pos = offset;
154    }
155    let last_gen_addr = ft.body_offset + ft.body_len as usize;
156    ranges_index.insert(range_wasm_start, ranges.len());
157    ranges.push(Range {
158        wasm_start: range_wasm_start,
159        wasm_end: fn_end,
160        gen_start: range_gen_start,
161        gen_end: last_gen_addr,
162        positions: current_range.into_boxed_slice(),
163    });
164
165    // Making ranges lookup faster by building index: B-tree with every range
166    // start position that maps into list of active ranges at this position.
167    let ranges = ranges.into_boxed_slice();
168    let mut active_ranges = Vec::new();
169    let mut index = BTreeMap::new();
170    let mut last_wasm_pos = None;
171    for (wasm_start, range_index) in ranges_index {
172        if Some(wasm_start) == last_wasm_pos {
173            active_ranges.push(range_index);
174            continue;
175        }
176        if let Some(position) = last_wasm_pos {
177            let mut sorted_ranges = active_ranges.clone();
178            sorted_ranges.sort();
179            index.insert(position, sorted_ranges.into_boxed_slice());
180        }
181        active_ranges.retain(|r| ranges[*r].wasm_end.cmp(&wasm_start) != std::cmp::Ordering::Less);
182        active_ranges.push(range_index);
183        last_wasm_pos = Some(wasm_start);
184    }
185    active_ranges.sort();
186    index.insert(last_wasm_pos.unwrap(), active_ranges.into_boxed_slice());
187    let index = Vec::from_iter(index.into_iter());
188    (fn_start, fn_end, FuncLookup { index, ranges })
189}
190
191fn build_function_addr_map(
192    compilation: &Compilation<'_>,
193    module: StaticModuleIndex,
194) -> PrimaryMap<DefinedFuncIndex, FunctionMap> {
195    let mut map = PrimaryMap::new();
196    for idx in compilation.translations[module]
197        .module
198        .defined_func_indices()
199    {
200        let (symbol, metadata) = compilation.function(module, idx);
201        let code_section_offset = compilation.translations[module]
202            .debuginfo
203            .wasm_file
204            .code_section_offset;
205        let ft = &metadata.address_map;
206        let mut fn_map = Vec::new();
207        for t in ft.instructions.iter() {
208            if t.srcloc.file_offset().is_none() {
209                continue;
210            }
211            let offset = get_wasm_code_offset(t.srcloc, code_section_offset);
212            fn_map.push(AddressMap {
213                generated: t.code_offset as usize,
214                wasm: offset,
215            });
216        }
217
218        if cfg!(debug_assertions) {
219            // fn_map is sorted by the generated field -- see FunctionAddressMap::instructions.
220            for i in 1..fn_map.len() {
221                assert!(fn_map[i - 1].generated <= fn_map[i].generated);
222            }
223        }
224
225        map.push(FunctionMap {
226            symbol,
227            offset: ft.body_offset,
228            len: ft.body_len as usize,
229            wasm_start: get_wasm_code_offset(ft.start_srcloc, code_section_offset),
230            wasm_end: get_wasm_code_offset(ft.end_srcloc, code_section_offset),
231            addresses: fn_map.into_boxed_slice(),
232        });
233    }
234    map
235}
236
237// Utility iterator to find all ranges starts for specific Wasm address.
238// The iterator returns generated addresses sorted by RangeIndex.
239struct TransformRangeStartIter<'a> {
240    addr: WasmAddress,
241    indices: &'a [RangeIndex],
242    ranges: &'a [Range],
243}
244
245impl<'a> TransformRangeStartIter<'a> {
246    fn new(func: &'a FuncTransform, addr: WasmAddress) -> Self {
247        let found = match func
248            .lookup
249            .index
250            .binary_search_by(|entry| entry.0.cmp(&addr))
251        {
252            Ok(i) => Some(&func.lookup.index[i].1),
253            Err(i) => {
254                if i > 0 {
255                    Some(&func.lookup.index[i - 1].1)
256                } else {
257                    None
258                }
259            }
260        };
261        if let Some(range_indices) = found {
262            TransformRangeStartIter {
263                addr,
264                indices: range_indices,
265                ranges: &func.lookup.ranges,
266            }
267        } else {
268            unreachable!();
269        }
270    }
271}
272
273impl<'a> Iterator for TransformRangeStartIter<'a> {
274    type Item = (GeneratedAddress, RangeIndex);
275    fn next(&mut self) -> Option<Self::Item> {
276        if let Some((first, tail)) = self.indices.split_first() {
277            let range_index = *first;
278            let range = &self.ranges[range_index];
279            self.indices = tail;
280            let address = match range
281                .positions
282                .binary_search_by(|a| a.wasm_pos.cmp(&self.addr))
283            {
284                Ok(i) => range.positions[i].gen_start,
285                Err(i) => {
286                    if i == 0 {
287                        range.gen_start
288                    } else {
289                        range.positions[i - 1].gen_end
290                    }
291                }
292            };
293            Some((address, range_index))
294        } else {
295            None
296        }
297    }
298}
299
300// Utility iterator to find all ranges ends for specific Wasm address.
301// The iterator returns generated addresses sorted by RangeIndex.
302struct TransformRangeEndIter<'a> {
303    addr: WasmAddress,
304    indices: &'a [RangeIndex],
305    ranges: &'a [Range],
306}
307
308impl<'a> TransformRangeEndIter<'a> {
309    fn new(func: &'a FuncTransform, addr: WasmAddress) -> Self {
310        let found = match func
311            .lookup
312            .index
313            .binary_search_by(|entry| entry.0.cmp(&addr))
314        {
315            Ok(i) => Some(&func.lookup.index[i].1),
316            Err(i) => {
317                if i > 0 {
318                    Some(&func.lookup.index[i - 1].1)
319                } else {
320                    None
321                }
322            }
323        };
324        if let Some(range_indices) = found {
325            TransformRangeEndIter {
326                addr,
327                indices: range_indices,
328                ranges: &func.lookup.ranges,
329            }
330        } else {
331            unreachable!();
332        }
333    }
334}
335
336impl<'a> Iterator for TransformRangeEndIter<'a> {
337    type Item = (GeneratedAddress, RangeIndex);
338    fn next(&mut self) -> Option<Self::Item> {
339        while let Some((first, tail)) = self.indices.split_first() {
340            let range_index = *first;
341            let range = &self.ranges[range_index];
342            self.indices = tail;
343            if range.wasm_start >= self.addr {
344                continue;
345            }
346            let address = match range
347                .positions
348                .binary_search_by(|a| a.wasm_pos.cmp(&self.addr))
349            {
350                Ok(i) => range.positions[i].gen_end,
351                Err(i) => {
352                    if i == range.positions.len() {
353                        range.gen_end
354                    } else {
355                        range.positions[i].gen_start
356                    }
357                }
358            };
359            return Some((address, range_index));
360        }
361        None
362    }
363}
364
365// Utility iterator to iterate by translated function ranges.
366struct TransformRangeIter<'a> {
367    func: &'a FuncTransform,
368    start_it: TransformRangeStartIter<'a>,
369    end_it: TransformRangeEndIter<'a>,
370    last_start: Option<(GeneratedAddress, RangeIndex)>,
371    last_end: Option<(GeneratedAddress, RangeIndex)>,
372    last_item: Option<(GeneratedAddress, GeneratedAddress)>,
373}
374
375impl<'a> TransformRangeIter<'a> {
376    fn new(func: &'a FuncTransform, start: WasmAddress, end: WasmAddress) -> Self {
377        let mut start_it = TransformRangeStartIter::new(func, start);
378        let last_start = start_it.next();
379        let mut end_it = TransformRangeEndIter::new(func, end);
380        let last_end = end_it.next();
381        TransformRangeIter {
382            func,
383            start_it,
384            end_it,
385            last_start,
386            last_end,
387            last_item: None,
388        }
389    }
390}
391
392impl<'a> Iterator for TransformRangeIter<'a> {
393    type Item = (GeneratedAddress, GeneratedAddress);
394    fn next(&mut self) -> Option<Self::Item> {
395        loop {
396            // Merge TransformRangeStartIter and TransformRangeEndIter data using
397            // FuncLookup index's field property to be sorted by RangeIndex.
398            let (start, end, range_index): (
399                Option<GeneratedAddress>,
400                Option<GeneratedAddress>,
401                RangeIndex,
402            ) = {
403                match (self.last_start.as_ref(), self.last_end.as_ref()) {
404                    (Some((s, sri)), Some((e, eri))) => {
405                        if sri == eri {
406                            // Start and end RangeIndex matched.
407                            (Some(*s), Some(*e), *sri)
408                        } else if sri < eri {
409                            (Some(*s), None, *sri)
410                        } else {
411                            (None, Some(*e), *eri)
412                        }
413                    }
414                    (Some((s, sri)), None) => (Some(*s), None, *sri),
415                    (None, Some((e, eri))) => (None, Some(*e), *eri),
416                    (None, None) => {
417                        // Reached ends for start and end iterators.
418                        return None;
419                    }
420                }
421            };
422            let range_start = match start {
423                Some(range_start) => {
424                    // Consume start iterator.
425                    self.last_start = self.start_it.next();
426                    range_start
427                }
428                None => {
429                    let range = &self.func.lookup.ranges[range_index];
430                    range.gen_start
431                }
432            };
433            let range_end = match end {
434                Some(range_end) => {
435                    // Consume end iterator.
436                    self.last_end = self.end_it.next();
437                    range_end
438                }
439                None => {
440                    let range = &self.func.lookup.ranges[range_index];
441                    range.gen_end
442                }
443            };
444
445            if cfg!(debug_assertions) {
446                match self.last_item.replace((range_start, range_end)) {
447                    Some((_, last_end)) => debug_assert!(last_end <= range_start),
448                    None => (),
449                }
450            }
451
452            if range_start < range_end {
453                return Some((range_start, range_end));
454            }
455            // Throw away empty ranges.
456            debug_assert!(range_start == range_end);
457        }
458    }
459}
460
461impl AddressTransform {
462    pub fn new(compilation: &Compilation<'_>, module: StaticModuleIndex) -> Self {
463        let mut func = BTreeMap::new();
464        let code_section_offset = compilation.translations[module]
465            .debuginfo
466            .wasm_file
467            .code_section_offset;
468
469        for idx in compilation.translations[module]
470            .module
471            .defined_func_indices()
472        {
473            let (_, metadata) = compilation.function(module, idx);
474            let (fn_start, fn_end, lookup) =
475                build_function_lookup(&metadata.address_map, code_section_offset);
476
477            func.insert(
478                fn_start,
479                FuncTransform {
480                    start: fn_start,
481                    end: fn_end,
482                    index: idx,
483                    lookup,
484                },
485            );
486        }
487
488        let map = build_function_addr_map(compilation, module);
489        let func = Vec::from_iter(func.into_iter());
490        AddressTransform { map, func }
491    }
492
493    #[cfg(test)]
494    pub fn mock(
495        module_map: &wasmtime_environ::PrimaryMap<
496            wasmtime_environ::DefinedFuncIndex,
497            &crate::CompiledFunctionMetadata,
498        >,
499        wasm_file: wasmtime_environ::WasmFileInfo,
500    ) -> Self {
501        let mut translations = wasmtime_environ::PrimaryMap::new();
502        let mut translation = wasmtime_environ::ModuleTranslation::default();
503        translation.debuginfo.wasm_file = wasm_file;
504        translation
505            .module
506            .push_function(wasmtime_environ::ModuleInternedTypeIndex::from_u32(0));
507        translations.push(translation);
508
509        let mut dummy_obj = object::write::Object::new(
510            object::BinaryFormat::Elf,
511            object::Architecture::Wasm32,
512            object::Endianness::Little,
513        );
514        let dummy_symbol = dummy_obj.add_file_symbol(Vec::new());
515        let func_lookup = move |_, f| (dummy_symbol, module_map[f]);
516        let tunables = wasmtime_environ::Tunables::default_host();
517        let compile = Compilation::new(
518            &*cranelift_codegen::isa::lookup(target_lexicon::Triple::host())
519                .unwrap()
520                .finish(cranelift_codegen::settings::Flags::new(
521                    cranelift_codegen::settings::builder(),
522                ))
523                .unwrap(),
524            &translations,
525            &func_lookup,
526            None,
527            &tunables,
528        );
529        Self::new(&compile, StaticModuleIndex::from_u32(0))
530    }
531
532    fn find_func(&self, addr: u64) -> Option<&FuncTransform> {
533        // TODO check if we need to include end address
534        let func = match self.func.binary_search_by(|entry| entry.0.cmp(&addr)) {
535            Ok(i) => &self.func[i].1,
536            Err(i) => {
537                if i > 0 {
538                    &self.func[i - 1].1
539                } else {
540                    return None;
541                }
542            }
543        };
544        if addr >= func.start {
545            return Some(func);
546        }
547        None
548    }
549
550    pub fn find_func_index(&self, addr: u64) -> Option<DefinedFuncIndex> {
551        self.find_func(addr).map(|f| f.index)
552    }
553
554    fn translate_raw(&self, addr: u64) -> Option<(usize, GeneratedAddress)> {
555        if addr == 0 {
556            // It's normally 0 for debug info without the linked code.
557            return None;
558        }
559        if let Some(func) = self.find_func(addr) {
560            let map = &self.map[func.index];
561            if addr == func.end {
562                // Clamp last address to the end to extend translation to the end
563                // of the function.
564                return Some((map.symbol, map.len));
565            }
566            let first_result = TransformRangeStartIter::new(func, addr).next();
567            first_result.map(|(address, _)| (map.symbol, address))
568        } else {
569            // Address was not found: function was not compiled?
570            None
571        }
572    }
573
574    pub fn can_translate_address(&self, addr: u64) -> bool {
575        self.translate(addr).is_some()
576    }
577
578    pub fn translate(&self, addr: u64) -> Option<write::Address> {
579        self.translate_raw(addr)
580            .map(|(symbol, address)| write::Address::Symbol {
581                symbol,
582                addend: address as i64,
583            })
584    }
585
586    pub fn translate_ranges_raw<'a>(
587        &'a self,
588        start: u64,
589        end: u64,
590    ) -> Option<(usize, impl Iterator<Item = (usize, usize)> + 'a)> {
591        if start == 0 {
592            // It's normally 0 for debug info without the linked code.
593            return None;
594        }
595        if let Some(func) = self.find_func(start) {
596            let result = TransformRangeIter::new(func, start, end);
597            let symbol = self.map[func.index].symbol;
598            return Some((symbol, result));
599        }
600        // Address was not found: function was not compiled?
601        None
602    }
603
604    pub fn translate_ranges<'a>(
605        &'a self,
606        start: u64,
607        end: u64,
608    ) -> impl Iterator<Item = (write::Address, u64)> + 'a {
609        enum TranslateRangesResult<'a> {
610            Empty,
611            Raw {
612                symbol: usize,
613                it: Box<dyn Iterator<Item = (usize, usize)> + 'a>,
614            },
615        }
616        impl<'a> Iterator for TranslateRangesResult<'a> {
617            type Item = (write::Address, u64);
618            fn next(&mut self) -> Option<Self::Item> {
619                match self {
620                    TranslateRangesResult::Empty => None,
621                    TranslateRangesResult::Raw { symbol, it } => match it.next() {
622                        Some((start, end)) => {
623                            debug_assert!(start < end);
624                            Some((
625                                write::Address::Symbol {
626                                    symbol: *symbol,
627                                    addend: start as i64,
628                                },
629                                (end - start) as u64,
630                            ))
631                        }
632                        None => None,
633                    },
634                }
635            }
636        }
637
638        match self.translate_ranges_raw(start, end) {
639            Some((symbol, ranges)) => TranslateRangesResult::Raw {
640                symbol,
641                it: Box::new(ranges),
642            },
643            None => TranslateRangesResult::Empty,
644        }
645    }
646
647    pub fn map(&self) -> &PrimaryMap<DefinedFuncIndex, FunctionMap> {
648        &self.map
649    }
650
651    pub fn func_range(&self, index: DefinedFuncIndex) -> (GeneratedAddress, GeneratedAddress) {
652        let map = &self.map[index];
653        (map.offset, map.offset + map.len)
654    }
655
656    pub fn func_source_range(&self, index: DefinedFuncIndex) -> (WasmAddress, WasmAddress) {
657        let map = &self.map[index];
658        (map.wasm_start, map.wasm_end)
659    }
660}
661
662#[cfg(test)]
663mod tests {
664    use super::{build_function_lookup, get_wasm_code_offset, AddressTransform};
665    use crate::{CompiledFunctionMetadata, FunctionAddressMap};
666    use cranelift_entity::PrimaryMap;
667    use gimli::write::Address;
668    use std::mem;
669    use wasmtime_environ::{FilePos, InstructionAddressMap, WasmFileInfo};
670
671    #[test]
672    fn test_get_wasm_code_offset() {
673        let offset = get_wasm_code_offset(FilePos::new(3), 1);
674        assert_eq!(2, offset);
675        let offset = get_wasm_code_offset(FilePos::new(16), 0xF000_0000);
676        assert_eq!(0x1000_0010, offset);
677        let offset = get_wasm_code_offset(FilePos::new(1), 0x20_8000_0000);
678        assert_eq!(0x8000_0001, offset);
679    }
680
681    fn create_simple_func(wasm_offset: u32) -> FunctionAddressMap {
682        FunctionAddressMap {
683            instructions: vec![
684                InstructionAddressMap {
685                    srcloc: FilePos::new(wasm_offset + 2),
686                    code_offset: 5,
687                },
688                InstructionAddressMap {
689                    srcloc: FilePos::default(),
690                    code_offset: 8,
691                },
692                InstructionAddressMap {
693                    srcloc: FilePos::new(wasm_offset + 7),
694                    code_offset: 15,
695                },
696                InstructionAddressMap {
697                    srcloc: FilePos::default(),
698                    code_offset: 23,
699                },
700            ]
701            .into(),
702            start_srcloc: FilePos::new(wasm_offset),
703            end_srcloc: FilePos::new(wasm_offset + 10),
704            body_offset: 0,
705            body_len: 30,
706        }
707    }
708
709    #[test]
710    fn test_build_function_lookup_simple() {
711        let input = create_simple_func(11);
712        let (start, end, lookup) = build_function_lookup(&input, 1);
713        assert_eq!(10, start);
714        assert_eq!(20, end);
715
716        assert_eq!(1, lookup.index.len());
717        let index_entry = lookup.index.into_iter().next().unwrap();
718        assert_eq!((10u64, vec![0].into_boxed_slice()), index_entry);
719        assert_eq!(1, lookup.ranges.len());
720        let range = &lookup.ranges[0];
721        assert_eq!(10, range.wasm_start);
722        assert_eq!(20, range.wasm_end);
723        assert_eq!(0, range.gen_start);
724        assert_eq!(30, range.gen_end);
725        let positions = &range.positions;
726        assert_eq!(2, positions.len());
727        assert_eq!(12, positions[0].wasm_pos);
728        assert_eq!(5, positions[0].gen_start);
729        assert_eq!(8, positions[0].gen_end);
730        assert_eq!(17, positions[1].wasm_pos);
731        assert_eq!(15, positions[1].gen_start);
732        assert_eq!(23, positions[1].gen_end);
733    }
734
735    #[test]
736    fn test_build_function_lookup_two_ranges() {
737        let mut input = create_simple_func(11);
738        // append instruction with same srcloc as input.instructions[0]
739        let mut list = Vec::from(mem::take(&mut input.instructions));
740        list.push(InstructionAddressMap {
741            srcloc: FilePos::new(11 + 2),
742            code_offset: 23,
743        });
744        list.push(InstructionAddressMap {
745            srcloc: FilePos::default(),
746            code_offset: 26,
747        });
748        input.instructions = list.into();
749        let (start, end, lookup) = build_function_lookup(&input, 1);
750        assert_eq!(10, start);
751        assert_eq!(20, end);
752
753        assert_eq!(2, lookup.index.len());
754        let index_entries = Vec::from_iter(lookup.index.into_iter());
755        assert_eq!((10u64, vec![0].into_boxed_slice()), index_entries[0]);
756        assert_eq!((12u64, vec![0, 1].into_boxed_slice()), index_entries[1]);
757        assert_eq!(2, lookup.ranges.len());
758
759        let range = &lookup.ranges[0];
760        assert_eq!(10, range.wasm_start);
761        assert_eq!(17, range.wasm_end);
762        assert_eq!(0, range.gen_start);
763        assert_eq!(23, range.gen_end);
764        let positions = &range.positions;
765        assert_eq!(2, positions.len());
766        assert_eq!(12, positions[0].wasm_pos);
767        assert_eq!(5, positions[0].gen_start);
768        assert_eq!(8, positions[0].gen_end);
769        assert_eq!(17, positions[1].wasm_pos);
770        assert_eq!(15, positions[1].gen_start);
771        assert_eq!(23, positions[1].gen_end);
772
773        let range = &lookup.ranges[1];
774        assert_eq!(12, range.wasm_start);
775        assert_eq!(20, range.wasm_end);
776        assert_eq!(23, range.gen_start);
777        assert_eq!(30, range.gen_end);
778        let positions = &range.positions;
779        assert_eq!(1, positions.len());
780        assert_eq!(12, positions[0].wasm_pos);
781        assert_eq!(23, positions[0].gen_start);
782        assert_eq!(26, positions[0].gen_end);
783    }
784
785    #[test]
786    fn test_addr_translate() {
787        // Ignore this test if cranelift doesn't support the native platform.
788        if cranelift_native::builder().is_err() {
789            return;
790        }
791        let func = CompiledFunctionMetadata {
792            address_map: create_simple_func(11),
793            ..Default::default()
794        };
795        let input = PrimaryMap::from_iter([&func]);
796        let at = AddressTransform::mock(
797            &input,
798            WasmFileInfo {
799                path: None,
800                code_section_offset: 1,
801                imported_func_count: 0,
802                funcs: Vec::new(),
803            },
804        );
805
806        let addr = at.translate(10);
807        assert_eq!(
808            Some(Address::Symbol {
809                symbol: 0,
810                addend: 0,
811            }),
812            addr
813        );
814
815        let addr = at.translate(20);
816        assert_eq!(
817            Some(Address::Symbol {
818                symbol: 0,
819                addend: 30,
820            }),
821            addr
822        );
823
824        let addr = at.translate(0);
825        assert_eq!(None, addr);
826
827        let addr = at.translate(12);
828        assert_eq!(
829            Some(Address::Symbol {
830                symbol: 0,
831                addend: 5,
832            }),
833            addr
834        );
835
836        let addr = at.translate(18);
837        assert_eq!(
838            Some(Address::Symbol {
839                symbol: 0,
840                addend: 23,
841            }),
842            addr
843        );
844    }
845}