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