1 use crate::debug::Compilation; 2 use crate::FunctionAddressMap; 3 use gimli::write; 4 use std::collections::BTreeMap; 5 use wasmtime_environ::{DefinedFuncIndex, FilePos, PrimaryMap, StaticModuleIndex}; 6 7 pub type GeneratedAddress = usize; 8 pub type WasmAddress = u64; 9 10 /// Contains mapping of the generated address to its original 11 /// source location. 12 #[derive(Debug)] 13 pub 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)] 21 pub 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)] 32 struct 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)] 41 struct Range { 42 wasm_start: WasmAddress, 43 wasm_end: WasmAddress, 44 gen_start: GeneratedAddress, 45 gen_end: GeneratedAddress, 46 positions: Box<[Position]>, 47 } 48 49 type 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)] 57 struct 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)] 64 struct 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)] 73 pub 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. 79 fn 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 86 fn 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 191 fn 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. 239 struct TransformRangeStartIter<'a> { 240 addr: WasmAddress, 241 indices: &'a [RangeIndex], 242 ranges: &'a [Range], 243 } 244 245 impl<'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 273 impl<'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. 302 struct TransformRangeEndIter<'a> { 303 addr: WasmAddress, 304 indices: &'a [RangeIndex], 305 ranges: &'a [Range], 306 } 307 308 impl<'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 336 impl<'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. 366 struct 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 375 impl<'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 392 impl<'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 461 impl 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)] 663 mod 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 } 846