1use crate::{FuncKey, ModulePC, StaticModuleIndex};
8use alloc::vec::Vec;
9use object::{Bytes, LittleEndian, U32};
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
13pub struct FrameStackShape(pub(crate) u32);
14impl FrameStackShape {
15 pub(crate) fn index(self) -> usize {
16 usize::try_from(self.0).unwrap()
17 }
18
19 pub fn raw(self) -> u32 {
22 self.0
23 }
24
25 pub fn from_raw(index: u32) -> FrameStackShape {
27 FrameStackShape(index)
28 }
29}
30
31#[derive(Clone, Copy, Debug)]
34pub struct FrameTableDescriptorIndex(pub(crate) u32);
35impl FrameTableDescriptorIndex {
36 fn index(self) -> usize {
37 usize::try_from(self.0).unwrap()
38 }
39}
40
41pub struct FrameTable<'a> {
47 frame_descriptor_ranges: &'a [U32<LittleEndian>],
48 frame_descriptor_data: &'a [u8],
49
50 frame_descriptor_fp_offsets: &'a [U32<LittleEndian>],
51
52 progpoint_pcs: &'a [U32<LittleEndian>],
53 progpoint_descriptor_offsets: &'a [U32<LittleEndian>],
54 progpoint_descriptor_data: &'a [U32<LittleEndian>],
55
56 breakpoint_modules: &'a [U32<LittleEndian>],
57 breakpoint_pcs: &'a [U32<LittleEndian>],
58 breakpoint_patch_offsets: &'a [U32<LittleEndian>],
59 breakpoint_patch_data_ends: &'a [U32<LittleEndian>],
60 breakpoint_patch_data: &'a [u8],
61
62 func_starts: &'a [U32<LittleEndian>],
63
64 original_text: &'a [u8],
65}
66
67impl<'a> FrameTable<'a> {
68 pub fn parse(data: &'a [u8], original_text: &'a [u8]) -> anyhow::Result<FrameTable<'a>> {
71 let mut data = Bytes(data);
72 let num_frame_descriptors = data
73 .read::<U32<LittleEndian>>()
74 .map_err(|_| anyhow::anyhow!("Unable to read frame descriptor count prefix"))?;
75 let num_frame_descriptors = usize::try_from(num_frame_descriptors.get(LittleEndian))?;
76 let num_progpoint_descriptors = data
77 .read::<U32<LittleEndian>>()
78 .map_err(|_| anyhow::anyhow!("Unable to read progpoint descriptor count prefix"))?;
79 let num_progpoint_descriptors =
80 usize::try_from(num_progpoint_descriptors.get(LittleEndian))?;
81 let num_breakpoints = data
82 .read::<U32<LittleEndian>>()
83 .map_err(|_| anyhow::anyhow!("Unable to read breakpoint count prefix"))?;
84 let num_breakpoints = usize::try_from(num_breakpoints.get(LittleEndian))?;
85 let num_funcs = data
86 .read::<U32<LittleEndian>>()
87 .map_err(|_| anyhow::anyhow!("Unable to read function count prefix"))?;
88 let num_funcs = usize::try_from(num_funcs.get(LittleEndian))?;
89
90 let frame_descriptor_pool_length = data
91 .read::<U32<LittleEndian>>()
92 .map_err(|_| anyhow::anyhow!("Unable to read frame descriptor pool length"))?;
93 let frame_descriptor_pool_length =
94 usize::try_from(frame_descriptor_pool_length.get(LittleEndian))?;
95 let progpoint_descriptor_pool_length = data
96 .read::<U32<LittleEndian>>()
97 .map_err(|_| anyhow::anyhow!("Unable to read progpoint descriptor pool length"))?;
98 let progpoint_descriptor_pool_length =
99 usize::try_from(progpoint_descriptor_pool_length.get(LittleEndian))?;
100 let breakpoint_patch_pool_length = data
101 .read::<U32<LittleEndian>>()
102 .map_err(|_| anyhow::anyhow!("Unable to read breakpoint patch pool length"))?;
103 let breakpoint_patch_pool_length =
104 usize::try_from(breakpoint_patch_pool_length.get(LittleEndian))?;
105
106 let (frame_descriptor_ranges, data) =
107 object::slice_from_bytes::<U32<LittleEndian>>(data.0, 2 * num_frame_descriptors)
108 .map_err(|_| anyhow::anyhow!("Unable to read frame descriptor ranges slice"))?;
109 let (frame_descriptor_fp_offsets, data) =
110 object::slice_from_bytes::<U32<LittleEndian>>(data, num_frame_descriptors)
111 .map_err(|_| anyhow::anyhow!("Unable to read frame descriptor FP offset slice"))?;
112
113 let (progpoint_pcs, data) =
114 object::slice_from_bytes::<U32<LittleEndian>>(data, num_progpoint_descriptors)
115 .map_err(|_| anyhow::anyhow!("Unable to read progpoint PC slice"))?;
116 let (progpoint_descriptor_offsets, data) =
117 object::slice_from_bytes::<U32<LittleEndian>>(data, num_progpoint_descriptors)
118 .map_err(|_| anyhow::anyhow!("Unable to read progpoint descriptor offset slice"))?;
119 let (breakpoint_modules, data) =
120 object::slice_from_bytes::<U32<LittleEndian>>(data, num_breakpoints)
121 .map_err(|_| anyhow::anyhow!("Unable to read breakpoint module slice"))?;
122 let (breakpoint_pcs, data) =
123 object::slice_from_bytes::<U32<LittleEndian>>(data, num_breakpoints)
124 .map_err(|_| anyhow::anyhow!("Unable to read breakpoint PC slice"))?;
125 let (breakpoint_patch_offsets, data) =
126 object::slice_from_bytes::<U32<LittleEndian>>(data, num_breakpoints)
127 .map_err(|_| anyhow::anyhow!("Unable to read breakpoint patch offsets slice"))?;
128 let (breakpoint_patch_data_ends, data) =
129 object::slice_from_bytes::<U32<LittleEndian>>(data, num_breakpoints)
130 .map_err(|_| anyhow::anyhow!("Unable to read breakpoint patch data ends slice"))?;
131 let (func_starts, data) = object::slice_from_bytes::<U32<LittleEndian>>(data, num_funcs)
132 .map_err(|_| anyhow::anyhow!("Unable to read function starts slice"))?;
133
134 let (frame_descriptor_data, data) = data
135 .split_at_checked(frame_descriptor_pool_length)
136 .ok_or_else(|| anyhow::anyhow!("Unable to read frame descriptor pool"))?;
137
138 let (progpoint_descriptor_data, data) =
139 object::slice_from_bytes::<U32<LittleEndian>>(data, progpoint_descriptor_pool_length)
140 .map_err(|_| anyhow::anyhow!("Unable to read progpoint descriptor pool"))?;
141
142 let (breakpoint_patch_data, _) = data
143 .split_at_checked(breakpoint_patch_pool_length)
144 .ok_or_else(|| anyhow::anyhow!("Unable to read breakpoint patch pool"))?;
145
146 Ok(FrameTable {
147 frame_descriptor_ranges,
148 frame_descriptor_data,
149 frame_descriptor_fp_offsets,
150 progpoint_pcs,
151 progpoint_descriptor_offsets,
152 progpoint_descriptor_data,
153 breakpoint_modules,
154 breakpoint_pcs,
155 breakpoint_patch_offsets,
156 breakpoint_patch_data_ends,
157 breakpoint_patch_data,
158 func_starts,
159 original_text,
160 })
161 }
162
163 pub fn frame_descriptor(
166 &self,
167 frame_descriptor: FrameTableDescriptorIndex,
168 ) -> Option<(&'a [u8], u32)> {
169 let range_start = self
170 .frame_descriptor_ranges
171 .get(frame_descriptor.index() * 2)?
172 .get(LittleEndian);
173 let range_end = self
174 .frame_descriptor_ranges
175 .get(frame_descriptor.index() * 2 + 1)?
176 .get(LittleEndian);
177 let range_start = usize::try_from(range_start).unwrap();
178 let range_end = usize::try_from(range_end).unwrap();
179 if range_end < range_start || range_end > self.frame_descriptor_data.len() {
180 return None;
181 }
182 let descriptor = &self.frame_descriptor_data[range_start..range_end];
183 let slot_to_fp_offset = self
184 .frame_descriptor_fp_offsets
185 .get(frame_descriptor.index())?
186 .get(LittleEndian);
187 Some((descriptor, slot_to_fp_offset))
188 }
189
190 pub fn find_program_point(
198 &self,
199 search_pc: u32,
200 search_pos: FrameInstPos,
201 ) -> Option<impl Iterator<Item = (ModulePC, FrameTableDescriptorIndex, FrameStackShape)>> {
202 let key = FrameInstPos::encode(search_pc, search_pos);
203 let index = match self
204 .progpoint_pcs
205 .binary_search_by_key(&key, |entry| entry.get(LittleEndian))
206 {
207 Ok(idx) => idx,
208 Err(idx) if idx > 0 => idx - 1,
209 Err(_) => return None,
210 };
211
212 let func_start = match self.func_starts.binary_search_by_key(&key, |entry| {
217 FrameInstPos::encode(entry.get(LittleEndian), FrameInstPos::Pre)
218 }) {
219 Ok(idx) => self.func_starts[idx].get(LittleEndian),
220 Err(idx) if idx > 0 => self.func_starts[idx - 1].get(LittleEndian),
221 Err(_) => return None,
222 };
223 let func_start_key = FrameInstPos::encode(func_start, FrameInstPos::Pre);
224 if self.progpoint_pcs[index].get(LittleEndian) < func_start_key {
225 return None;
226 }
227
228 Some(self.program_point_frame_iter(index))
229 }
230
231 pub fn into_program_points(
234 self,
235 ) -> impl Iterator<
236 Item = (
237 u32,
238 FrameInstPos,
239 Vec<(ModulePC, FrameTableDescriptorIndex, FrameStackShape)>,
240 ),
241 > + 'a {
242 self.progpoint_pcs.iter().enumerate().map(move |(i, pc)| {
243 let pc_and_pos = pc.get(LittleEndian);
244 let (pc, pos) = FrameInstPos::decode(pc_and_pos);
245 (
246 pc,
247 pos,
248 self.program_point_frame_iter(i).collect::<Vec<_>>(),
249 )
250 })
251 }
252
253 fn program_point_frame_iter(
254 &self,
255 index: usize,
256 ) -> impl Iterator<Item = (ModulePC, FrameTableDescriptorIndex, FrameStackShape)> {
257 let offset =
258 usize::try_from(self.progpoint_descriptor_offsets[index].get(LittleEndian)).unwrap();
259 let mut data = &self.progpoint_descriptor_data[offset..];
260
261 core::iter::from_fn(move || {
262 if data.len() < 3 {
263 return None;
264 }
265 let wasm_pc_raw = data[0].get(LittleEndian);
266 let frame_descriptor = FrameTableDescriptorIndex(data[1].get(LittleEndian));
267 let stack_shape = FrameStackShape(data[2].get(LittleEndian));
268 data = &data[3..];
269 let not_last = wasm_pc_raw & 0x8000_0000 != 0;
270 let wasm_pc = ModulePC::new(wasm_pc_raw & 0x7fff_ffff);
271 if !not_last {
272 data = &[];
273 }
274 Some((wasm_pc, frame_descriptor, stack_shape))
275 })
276 }
277
278 fn breakpoint_patch(&self, i: usize) -> FrameTableBreakpointData<'_> {
281 let patch_pool_start = if i == 0 {
282 0
283 } else {
284 self.breakpoint_patch_data_ends[i - 1].get(LittleEndian)
285 };
286 let patch_pool_end = self.breakpoint_patch_data_ends[i].get(LittleEndian);
287 let patch_pool_start = usize::try_from(patch_pool_start).unwrap();
288 let patch_pool_end = usize::try_from(patch_pool_end).unwrap();
289 let len = patch_pool_end - patch_pool_start;
290 let offset = self.breakpoint_patch_offsets[i].get(LittleEndian);
291 let offset = usize::try_from(offset).unwrap();
292 let original_data = &self.original_text[offset..offset + len];
293 FrameTableBreakpointData {
294 offset,
295 enable: &self.breakpoint_patch_data[patch_pool_start..patch_pool_end],
296 disable: original_data,
297 }
298 }
299
300 pub fn lookup_breakpoint_patches_by_pc(
302 &self,
303 pc: ModulePC,
304 ) -> impl Iterator<Item = FrameTableBreakpointData<'_>> + '_ {
305 let pc_raw = pc.raw();
308 let range = match self
309 .breakpoint_pcs
310 .binary_search_by_key(&pc_raw, |p| p.get(LittleEndian))
311 {
312 Ok(mut i) => {
313 while i > 0 && self.breakpoint_pcs[i - 1].get(LittleEndian) == pc_raw {
315 i -= 1;
316 }
317
318 let mut end = i;
320 while end < self.breakpoint_pcs.len()
321 && self.breakpoint_pcs[end].get(LittleEndian) == pc_raw
322 {
323 end += 1;
324 }
325
326 i..end
327 }
328 Err(_) => 0..0,
329 };
330
331 range.map(|i| self.breakpoint_patch(i))
332 }
333
334 pub fn nearest_breakpoint(&self, pc: ModulePC) -> Option<ModulePC> {
336 match self
337 .breakpoint_pcs
338 .binary_search_by_key(&pc.raw(), |p| p.get(LittleEndian))
339 {
340 Ok(_) => Some(pc),
341 Err(i) => {
342 if i < self.breakpoint_pcs.len() {
343 Some(ModulePC::new(self.breakpoint_pcs[i].get(LittleEndian)))
344 } else {
345 None
346 }
347 }
348 }
349 }
350
351 pub fn breakpoint_patches(
358 &self,
359 ) -> impl Iterator<Item = (StaticModuleIndex, ModulePC, FrameTableBreakpointData<'_>)> + '_
360 {
361 self.breakpoint_pcs.iter().enumerate().map(|(i, wasm_pc)| {
362 let module = StaticModuleIndex::from_u32(self.breakpoint_modules[i].get(LittleEndian));
363 let wasm_pc = ModulePC::new(wasm_pc.get(LittleEndian));
364 let data = self.breakpoint_patch(i);
365 (module, wasm_pc, data)
366 })
367 }
368}
369
370pub struct FrameTableBreakpointData<'a> {
373 pub offset: usize,
375 pub enable: &'a [u8],
377 pub disable: &'a [u8],
379}
380
381#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
420pub enum FrameInstPos {
421 Post,
424 Pre,
427}
428
429impl FrameInstPos {
430 pub(crate) fn encode(pc: u32, pos: FrameInstPos) -> u32 {
431 let lsb = match pos {
432 Self::Post => 0,
433 Self::Pre => 1,
434 };
435 debug_assert!(pc < 0x8000_0000);
436 (pc << 1) | lsb
437 }
438 pub(crate) fn decode(bits: u32) -> (u32, FrameInstPos) {
439 let pos = match bits & 1 {
440 0 => Self::Post,
441 1 => Self::Pre,
442 _ => unreachable!(),
443 };
444 let pc = bits >> 1;
445 (pc, pos)
446 }
447}
448
449#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
451pub struct FrameStateSlotOffset(pub(crate) u32);
452impl FrameStateSlotOffset {
453 #[cfg(feature = "compile")]
454 pub(crate) fn add(self, offset: u32) -> FrameStateSlotOffset {
455 FrameStateSlotOffset(self.0 + offset)
456 }
457
458 pub fn offset(self) -> i32 {
461 i32::try_from(self.0).unwrap()
462 }
463}
464
465#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
467#[allow(missing_docs, reason = "self-describing variants")]
468pub enum FrameValType {
469 I32,
470 I64,
471 F32,
472 F64,
473 V128,
474 AnyRef,
475 FuncRef,
476 ExternRef,
477 ExnRef,
478 ContRef,
479}
480
481impl FrameValType {
482 #[cfg(feature = "compile")]
483 pub(crate) fn storage_size(&self, pointer_size: u32) -> u32 {
484 match self {
485 FrameValType::I32 => 4,
486 FrameValType::I64 => 8,
487 FrameValType::F32 => 4,
488 FrameValType::F64 => 8,
489 FrameValType::V128 => 16,
490 FrameValType::AnyRef | FrameValType::ExternRef | FrameValType::ExnRef => 4,
491 FrameValType::FuncRef => pointer_size,
492 FrameValType::ContRef => 2 * pointer_size,
493 }
494 }
495}
496
497impl From<FrameValType> for u8 {
498 fn from(value: FrameValType) -> u8 {
499 match value {
500 FrameValType::I32 => 0,
501 FrameValType::I64 => 1,
502 FrameValType::F32 => 2,
503 FrameValType::F64 => 3,
504 FrameValType::V128 => 4,
505 FrameValType::AnyRef => 5,
506 FrameValType::FuncRef => 6,
507 FrameValType::ExternRef => 7,
508 FrameValType::ExnRef => 8,
509 FrameValType::ContRef => 9,
510 }
511 }
512}
513
514impl TryFrom<u8> for FrameValType {
515 type Error = anyhow::Error;
516 fn try_from(value: u8) -> anyhow::Result<Self> {
517 match value {
518 0 => Ok(Self::I32),
519 1 => Ok(Self::I64),
520 2 => Ok(Self::F32),
521 3 => Ok(Self::F64),
522 4 => Ok(Self::V128),
523 5 => Ok(Self::AnyRef),
524 6 => Ok(Self::FuncRef),
525 7 => Ok(Self::ExternRef),
526 8 => Ok(Self::ExnRef),
527 9 => Ok(Self::ContRef),
528 _ => Err(anyhow::anyhow!("Invalid type")),
529 }
530 }
531}
532
533pub struct FrameStateSlot<'a> {
538 func_key: FuncKey,
539 local_offsets: &'a [U32<LittleEndian>],
540 stack_shape_parents: &'a [U32<LittleEndian>],
541 stack_shape_offsets: &'a [U32<LittleEndian>],
542 local_types: &'a [u8],
543 stack_shape_types: &'a [u8],
544}
545
546impl<'a> FrameStateSlot<'a> {
547 pub fn parse(descriptor: &'a [u8]) -> anyhow::Result<FrameStateSlot<'a>> {
552 let mut data = Bytes(descriptor);
553 let func_key_namespace = data
554 .read::<U32<LittleEndian>>()
555 .map_err(|_| anyhow::anyhow!("Unable to read func key namespace"))?
556 .get(LittleEndian);
557 let func_key_index = data
558 .read::<U32<LittleEndian>>()
559 .map_err(|_| anyhow::anyhow!("Unable to read func key index"))?
560 .get(LittleEndian);
561 let func_key = FuncKey::from_raw_parts(func_key_namespace, func_key_index);
562
563 let num_locals = data
564 .read::<U32<LittleEndian>>()
565 .map_err(|_| anyhow::anyhow!("Unable to read num_locals"))?
566 .get(LittleEndian);
567 let num_locals = usize::try_from(num_locals)?;
568 let num_stack_shapes = data
569 .read::<U32<LittleEndian>>()
570 .map_err(|_| anyhow::anyhow!("Unable to read num_stack_shapes"))?
571 .get(LittleEndian);
572 let num_stack_shapes = usize::try_from(num_stack_shapes)?;
573
574 let (local_offsets, data) =
575 object::slice_from_bytes::<U32<LittleEndian>>(data.0, num_locals)
576 .map_err(|_| anyhow::anyhow!("Unable to read local_offsets slice"))?;
577 let (stack_shape_parents, data) =
578 object::slice_from_bytes::<U32<LittleEndian>>(data, num_stack_shapes)
579 .map_err(|_| anyhow::anyhow!("Unable to read stack_shape_parents slice"))?;
580 let (stack_shape_offsets, data) =
581 object::slice_from_bytes::<U32<LittleEndian>>(data, num_stack_shapes)
582 .map_err(|_| anyhow::anyhow!("Unable to read stack_shape_offsets slice"))?;
583 let (local_types, data) = data
584 .split_at_checked(num_locals)
585 .ok_or_else(|| anyhow::anyhow!("Unable to read local_types slice"))?;
586 let (stack_shape_types, _) = data
587 .split_at_checked(num_stack_shapes)
588 .ok_or_else(|| anyhow::anyhow!("Unable to read stack_shape_types slice"))?;
589
590 Ok(FrameStateSlot {
591 func_key,
592 local_offsets,
593 stack_shape_parents,
594 stack_shape_offsets,
595 local_types,
596 stack_shape_types,
597 })
598 }
599
600 pub fn func_key(&self) -> FuncKey {
603 self.func_key
604 }
605
606 pub fn locals(&self) -> impl Iterator<Item = (FrameStateSlotOffset, FrameValType)> {
608 (0..self.num_locals()).map(|i| self.local(i).unwrap())
609 }
610
611 pub fn local(&self, index: usize) -> Option<(FrameStateSlotOffset, FrameValType)> {
613 let offset = FrameStateSlotOffset(self.local_offsets.get(index)?.get(LittleEndian));
614 let ty = FrameValType::try_from(*self.local_types.get(index)?).expect("Invalid type");
615 Some((offset, ty))
616 }
617
618 pub fn num_locals(&self) -> usize {
620 self.local_offsets.len()
621 }
622
623 pub fn stack(
626 &self,
627 shape: FrameStackShape,
628 ) -> impl Iterator<Item = (FrameStateSlotOffset, FrameValType)> {
629 fn unpack_option_shape(shape: FrameStackShape) -> Option<FrameStackShape> {
630 if shape.0 == u32::MAX {
631 None
632 } else {
633 Some(shape)
634 }
635 }
636
637 let mut shape = unpack_option_shape(shape);
638 core::iter::from_fn(move || {
639 shape.map(|s| {
640 let parent = FrameStackShape(self.stack_shape_parents[s.index()].get(LittleEndian));
641 let parent = unpack_option_shape(parent);
642 let offset =
643 FrameStateSlotOffset(self.stack_shape_offsets[s.index()].get(LittleEndian));
644 let ty = FrameValType::try_from(self.stack_shape_types[s.index()])
645 .expect("Invalid type");
646 shape = parent;
647 (offset, ty)
648 })
649 })
650 }
651
652 pub fn stack_and_locals(
654 &self,
655 shape: FrameStackShape,
656 ) -> impl Iterator<Item = (FrameStateSlotOffset, FrameValType)> + '_ {
657 self.locals().chain(self.stack(shape))
658 }
659}