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