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 original_text: &'a [u8],
62}
63
64impl<'a> FrameTable<'a> {
65 pub fn parse(data: &'a [u8], original_text: &'a [u8]) -> anyhow::Result<FrameTable<'a>> {
68 let mut data = Bytes(data);
69 let num_frame_descriptors = data
70 .read::<U32<LittleEndian>>()
71 .map_err(|_| anyhow::anyhow!("Unable to read frame descriptor count prefix"))?;
72 let num_frame_descriptors = usize::try_from(num_frame_descriptors.get(LittleEndian))?;
73 let num_progpoint_descriptors = data
74 .read::<U32<LittleEndian>>()
75 .map_err(|_| anyhow::anyhow!("Unable to read progpoint descriptor count prefix"))?;
76 let num_progpoint_descriptors =
77 usize::try_from(num_progpoint_descriptors.get(LittleEndian))?;
78 let num_breakpoints = data
79 .read::<U32<LittleEndian>>()
80 .map_err(|_| anyhow::anyhow!("Unable to read breakpoint count prefix"))?;
81 let num_breakpoints = usize::try_from(num_breakpoints.get(LittleEndian))?;
82
83 let frame_descriptor_pool_length = data
84 .read::<U32<LittleEndian>>()
85 .map_err(|_| anyhow::anyhow!("Unable to read frame descriptor pool length"))?;
86 let frame_descriptor_pool_length =
87 usize::try_from(frame_descriptor_pool_length.get(LittleEndian))?;
88 let progpoint_descriptor_pool_length = data
89 .read::<U32<LittleEndian>>()
90 .map_err(|_| anyhow::anyhow!("Unable to read progpoint descriptor pool length"))?;
91 let progpoint_descriptor_pool_length =
92 usize::try_from(progpoint_descriptor_pool_length.get(LittleEndian))?;
93 let breakpoint_patch_pool_length = data
94 .read::<U32<LittleEndian>>()
95 .map_err(|_| anyhow::anyhow!("Unable to read breakpoint patch pool length"))?;
96 let breakpoint_patch_pool_length =
97 usize::try_from(breakpoint_patch_pool_length.get(LittleEndian))?;
98
99 let (frame_descriptor_ranges, data) =
100 object::slice_from_bytes::<U32<LittleEndian>>(data.0, 2 * num_frame_descriptors)
101 .map_err(|_| anyhow::anyhow!("Unable to read frame descriptor ranges slice"))?;
102 let (frame_descriptor_fp_offsets, data) =
103 object::slice_from_bytes::<U32<LittleEndian>>(data, num_frame_descriptors)
104 .map_err(|_| anyhow::anyhow!("Unable to read frame descriptor FP offset slice"))?;
105
106 let (progpoint_pcs, data) =
107 object::slice_from_bytes::<U32<LittleEndian>>(data, num_progpoint_descriptors)
108 .map_err(|_| anyhow::anyhow!("Unable to read progpoint PC slice"))?;
109 let (progpoint_descriptor_offsets, data) =
110 object::slice_from_bytes::<U32<LittleEndian>>(data, num_progpoint_descriptors)
111 .map_err(|_| anyhow::anyhow!("Unable to read progpoint descriptor offset slice"))?;
112 let (breakpoint_pcs, data) =
113 object::slice_from_bytes::<U32<LittleEndian>>(data, num_breakpoints)
114 .map_err(|_| anyhow::anyhow!("Unable to read breakpoint PC slice"))?;
115 let (breakpoint_patch_offsets, data) =
116 object::slice_from_bytes::<U32<LittleEndian>>(data, num_breakpoints)
117 .map_err(|_| anyhow::anyhow!("Unable to read breakpoint patch offsets slice"))?;
118 let (breakpoint_patch_data_ends, data) =
119 object::slice_from_bytes::<U32<LittleEndian>>(data, num_breakpoints)
120 .map_err(|_| anyhow::anyhow!("Unable to read breakpoint patch data ends slice"))?;
121
122 let (frame_descriptor_data, data) = data
123 .split_at_checked(frame_descriptor_pool_length)
124 .ok_or_else(|| anyhow::anyhow!("Unable to read frame descriptor pool"))?;
125
126 let (progpoint_descriptor_data, data) =
127 object::slice_from_bytes::<U32<LittleEndian>>(data, progpoint_descriptor_pool_length)
128 .map_err(|_| anyhow::anyhow!("Unable to read progpoint descriptor pool"))?;
129
130 let (breakpoint_patch_data, _) = data
131 .split_at_checked(breakpoint_patch_pool_length)
132 .ok_or_else(|| anyhow::anyhow!("Unable to read breakpoint patch pool"))?;
133
134 Ok(FrameTable {
135 frame_descriptor_ranges,
136 frame_descriptor_data,
137 frame_descriptor_fp_offsets,
138 progpoint_pcs,
139 progpoint_descriptor_offsets,
140 progpoint_descriptor_data,
141 breakpoint_pcs,
142 breakpoint_patch_offsets,
143 breakpoint_patch_data_ends,
144 breakpoint_patch_data,
145 original_text,
146 })
147 }
148
149 pub fn frame_descriptor(
152 &self,
153 frame_descriptor: FrameTableDescriptorIndex,
154 ) -> Option<(&'a [u8], u32)> {
155 let range_start = self
156 .frame_descriptor_ranges
157 .get(frame_descriptor.index() * 2)?
158 .get(LittleEndian);
159 let range_end = self
160 .frame_descriptor_ranges
161 .get(frame_descriptor.index() * 2 + 1)?
162 .get(LittleEndian);
163 let range_start = usize::try_from(range_start).unwrap();
164 let range_end = usize::try_from(range_end).unwrap();
165 if range_end < range_start || range_end > self.frame_descriptor_data.len() {
166 return None;
167 }
168 let descriptor = &self.frame_descriptor_data[range_start..range_end];
169 let slot_to_fp_offset = self
170 .frame_descriptor_fp_offsets
171 .get(frame_descriptor.index())?
172 .get(LittleEndian);
173 Some((descriptor, slot_to_fp_offset))
174 }
175
176 pub fn find_program_point(
179 &self,
180 search_pc: u32,
181 search_pos: FrameInstPos,
182 ) -> Option<impl Iterator<Item = (ModulePC, FrameTableDescriptorIndex, FrameStackShape)>> {
183 let key = FrameInstPos::encode(search_pc, search_pos);
184 let index = match self
185 .progpoint_pcs
186 .binary_search_by_key(&key, |entry| entry.get(LittleEndian))
187 {
188 Ok(idx) => idx,
189 Err(idx) if idx > 0 => idx - 1,
190 Err(_) => return None,
191 };
192
193 Some(self.program_point_frame_iter(index))
194 }
195
196 pub fn into_program_points(
199 self,
200 ) -> impl Iterator<
201 Item = (
202 u32,
203 FrameInstPos,
204 Vec<(ModulePC, FrameTableDescriptorIndex, FrameStackShape)>,
205 ),
206 > + 'a {
207 self.progpoint_pcs.iter().enumerate().map(move |(i, pc)| {
208 let pc_and_pos = pc.get(LittleEndian);
209 let (pc, pos) = FrameInstPos::decode(pc_and_pos);
210 (
211 pc,
212 pos,
213 self.program_point_frame_iter(i).collect::<Vec<_>>(),
214 )
215 })
216 }
217
218 fn program_point_frame_iter(
219 &self,
220 index: usize,
221 ) -> impl Iterator<Item = (ModulePC, FrameTableDescriptorIndex, FrameStackShape)> {
222 let offset =
223 usize::try_from(self.progpoint_descriptor_offsets[index].get(LittleEndian)).unwrap();
224 let mut data = &self.progpoint_descriptor_data[offset..];
225
226 core::iter::from_fn(move || {
227 if data.len() < 3 {
228 return None;
229 }
230 let wasm_pc_raw = data[0].get(LittleEndian);
231 let frame_descriptor = FrameTableDescriptorIndex(data[1].get(LittleEndian));
232 let stack_shape = FrameStackShape(data[2].get(LittleEndian));
233 data = &data[3..];
234 let not_last = wasm_pc_raw & 0x8000_0000 != 0;
235 let wasm_pc = ModulePC::new(wasm_pc_raw & 0x7fff_ffff);
236 if !not_last {
237 data = &[];
238 }
239 Some((wasm_pc, frame_descriptor, stack_shape))
240 })
241 }
242
243 fn breakpoint_patch(&self, i: usize) -> FrameTableBreakpointData<'_> {
246 let patch_pool_start = if i == 0 {
247 0
248 } else {
249 self.breakpoint_patch_data_ends[i - 1].get(LittleEndian)
250 };
251 let patch_pool_end = self.breakpoint_patch_data_ends[i].get(LittleEndian);
252 let patch_pool_start = usize::try_from(patch_pool_start).unwrap();
253 let patch_pool_end = usize::try_from(patch_pool_end).unwrap();
254 let len = patch_pool_end - patch_pool_start;
255 let offset = self.breakpoint_patch_offsets[i].get(LittleEndian);
256 let offset = usize::try_from(offset).unwrap();
257 let original_data = &self.original_text[offset..offset + len];
258 FrameTableBreakpointData {
259 offset,
260 enable: &self.breakpoint_patch_data[patch_pool_start..patch_pool_end],
261 disable: original_data,
262 }
263 }
264
265 pub fn lookup_breakpoint_patches_by_pc(
267 &self,
268 pc: ModulePC,
269 ) -> impl Iterator<Item = FrameTableBreakpointData<'_>> + '_ {
270 let pc_raw = pc.raw();
273 let range = match self
274 .breakpoint_pcs
275 .binary_search_by_key(&pc_raw, |p| p.get(LittleEndian))
276 {
277 Ok(mut i) => {
278 while i > 0 && self.breakpoint_pcs[i - 1].get(LittleEndian) == pc_raw {
280 i -= 1;
281 }
282
283 let mut end = i;
285 while end < self.breakpoint_pcs.len()
286 && self.breakpoint_pcs[end].get(LittleEndian) == pc_raw
287 {
288 end += 1;
289 }
290
291 i..end
292 }
293 Err(_) => 0..0,
294 };
295
296 range.map(|i| self.breakpoint_patch(i))
297 }
298
299 pub fn nearest_breakpoint(&self, pc: ModulePC) -> Option<ModulePC> {
301 match self
302 .breakpoint_pcs
303 .binary_search_by_key(&pc.raw(), |p| p.get(LittleEndian))
304 {
305 Ok(_) => Some(pc),
306 Err(i) => {
307 if i < self.breakpoint_pcs.len() {
308 Some(ModulePC::new(self.breakpoint_pcs[i].get(LittleEndian)))
309 } else {
310 None
311 }
312 }
313 }
314 }
315
316 pub fn breakpoint_patches(
320 &self,
321 ) -> impl Iterator<Item = (ModulePC, FrameTableBreakpointData<'_>)> + '_ {
322 self.breakpoint_pcs.iter().enumerate().map(|(i, wasm_pc)| {
323 let wasm_pc = ModulePC::new(wasm_pc.get(LittleEndian));
324 let data = self.breakpoint_patch(i);
325 (wasm_pc, data)
326 })
327 }
328}
329
330pub struct FrameTableBreakpointData<'a> {
333 pub offset: usize,
335 pub enable: &'a [u8],
337 pub disable: &'a [u8],
339}
340
341#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
380pub enum FrameInstPos {
381 Post,
384 Pre,
387}
388
389impl FrameInstPos {
390 pub(crate) fn encode(pc: u32, pos: FrameInstPos) -> u32 {
391 let lsb = match pos {
392 Self::Post => 0,
393 Self::Pre => 1,
394 };
395 debug_assert!(pc < 0x8000_0000);
396 (pc << 1) | lsb
397 }
398 pub(crate) fn decode(bits: u32) -> (u32, FrameInstPos) {
399 let pos = match bits & 1 {
400 0 => Self::Post,
401 1 => Self::Pre,
402 _ => unreachable!(),
403 };
404 let pc = bits >> 1;
405 (pc, pos)
406 }
407}
408
409#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
411pub struct FrameStateSlotOffset(pub(crate) u32);
412impl FrameStateSlotOffset {
413 #[cfg(feature = "compile")]
414 pub(crate) fn add(self, offset: u32) -> FrameStateSlotOffset {
415 FrameStateSlotOffset(self.0 + offset)
416 }
417
418 pub fn offset(self) -> i32 {
421 i32::try_from(self.0).unwrap()
422 }
423}
424
425#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
427#[allow(missing_docs, reason = "self-describing variants")]
428pub enum FrameValType {
429 I32,
430 I64,
431 F32,
432 F64,
433 V128,
434 AnyRef,
435 FuncRef,
436 ExternRef,
437 ExnRef,
438 ContRef,
439}
440
441impl FrameValType {
442 #[cfg(feature = "compile")]
443 pub(crate) fn storage_size(&self, pointer_size: u32) -> u32 {
444 match self {
445 FrameValType::I32 => 4,
446 FrameValType::I64 => 8,
447 FrameValType::F32 => 4,
448 FrameValType::F64 => 8,
449 FrameValType::V128 => 16,
450 FrameValType::AnyRef | FrameValType::ExternRef | FrameValType::ExnRef => 4,
451 FrameValType::FuncRef => pointer_size,
452 FrameValType::ContRef => 2 * pointer_size,
453 }
454 }
455}
456
457impl From<FrameValType> for u8 {
458 fn from(value: FrameValType) -> u8 {
459 match value {
460 FrameValType::I32 => 0,
461 FrameValType::I64 => 1,
462 FrameValType::F32 => 2,
463 FrameValType::F64 => 3,
464 FrameValType::V128 => 4,
465 FrameValType::AnyRef => 5,
466 FrameValType::FuncRef => 6,
467 FrameValType::ExternRef => 7,
468 FrameValType::ExnRef => 8,
469 FrameValType::ContRef => 9,
470 }
471 }
472}
473
474impl TryFrom<u8> for FrameValType {
475 type Error = anyhow::Error;
476 fn try_from(value: u8) -> anyhow::Result<Self> {
477 match value {
478 0 => Ok(Self::I32),
479 1 => Ok(Self::I64),
480 2 => Ok(Self::F32),
481 3 => Ok(Self::F64),
482 4 => Ok(Self::V128),
483 5 => Ok(Self::AnyRef),
484 6 => Ok(Self::FuncRef),
485 7 => Ok(Self::ExternRef),
486 8 => Ok(Self::ExnRef),
487 9 => Ok(Self::ContRef),
488 _ => Err(anyhow::anyhow!("Invalid type")),
489 }
490 }
491}
492
493pub struct FrameStateSlot<'a> {
498 func_key: FuncKey,
499 local_offsets: &'a [U32<LittleEndian>],
500 stack_shape_parents: &'a [U32<LittleEndian>],
501 stack_shape_offsets: &'a [U32<LittleEndian>],
502 local_types: &'a [u8],
503 stack_shape_types: &'a [u8],
504}
505
506impl<'a> FrameStateSlot<'a> {
507 pub fn parse(descriptor: &'a [u8]) -> anyhow::Result<FrameStateSlot<'a>> {
512 let mut data = Bytes(descriptor);
513 let func_key_namespace = data
514 .read::<U32<LittleEndian>>()
515 .map_err(|_| anyhow::anyhow!("Unable to read func key namespace"))?
516 .get(LittleEndian);
517 let func_key_index = data
518 .read::<U32<LittleEndian>>()
519 .map_err(|_| anyhow::anyhow!("Unable to read func key index"))?
520 .get(LittleEndian);
521 let func_key = FuncKey::from_raw_parts(func_key_namespace, func_key_index);
522
523 let num_locals = data
524 .read::<U32<LittleEndian>>()
525 .map_err(|_| anyhow::anyhow!("Unable to read num_locals"))?
526 .get(LittleEndian);
527 let num_locals = usize::try_from(num_locals)?;
528 let num_stack_shapes = data
529 .read::<U32<LittleEndian>>()
530 .map_err(|_| anyhow::anyhow!("Unable to read num_stack_shapes"))?
531 .get(LittleEndian);
532 let num_stack_shapes = usize::try_from(num_stack_shapes)?;
533
534 let (local_offsets, data) =
535 object::slice_from_bytes::<U32<LittleEndian>>(data.0, num_locals)
536 .map_err(|_| anyhow::anyhow!("Unable to read local_offsets slice"))?;
537 let (stack_shape_parents, data) =
538 object::slice_from_bytes::<U32<LittleEndian>>(data, num_stack_shapes)
539 .map_err(|_| anyhow::anyhow!("Unable to read stack_shape_parents slice"))?;
540 let (stack_shape_offsets, data) =
541 object::slice_from_bytes::<U32<LittleEndian>>(data, num_stack_shapes)
542 .map_err(|_| anyhow::anyhow!("Unable to read stack_shape_offsets slice"))?;
543 let (local_types, data) = data
544 .split_at_checked(num_locals)
545 .ok_or_else(|| anyhow::anyhow!("Unable to read local_types slice"))?;
546 let (stack_shape_types, _) = data
547 .split_at_checked(num_stack_shapes)
548 .ok_or_else(|| anyhow::anyhow!("Unable to read stack_shape_types slice"))?;
549
550 Ok(FrameStateSlot {
551 func_key,
552 local_offsets,
553 stack_shape_parents,
554 stack_shape_offsets,
555 local_types,
556 stack_shape_types,
557 })
558 }
559
560 pub fn func_key(&self) -> FuncKey {
563 self.func_key
564 }
565
566 pub fn locals(&self) -> impl Iterator<Item = (FrameStateSlotOffset, FrameValType)> {
568 (0..self.num_locals()).map(|i| self.local(i).unwrap())
569 }
570
571 pub fn local(&self, index: usize) -> Option<(FrameStateSlotOffset, FrameValType)> {
573 let offset = FrameStateSlotOffset(self.local_offsets.get(index)?.get(LittleEndian));
574 let ty = FrameValType::try_from(*self.local_types.get(index)?).expect("Invalid type");
575 Some((offset, ty))
576 }
577
578 pub fn num_locals(&self) -> usize {
580 self.local_offsets.len()
581 }
582
583 pub fn stack(
586 &self,
587 shape: FrameStackShape,
588 ) -> impl Iterator<Item = (FrameStateSlotOffset, FrameValType)> {
589 fn unpack_option_shape(shape: FrameStackShape) -> Option<FrameStackShape> {
590 if shape.0 == u32::MAX {
591 None
592 } else {
593 Some(shape)
594 }
595 }
596
597 let mut shape = unpack_option_shape(shape);
598 core::iter::from_fn(move || {
599 shape.map(|s| {
600 let parent = FrameStackShape(self.stack_shape_parents[s.index()].get(LittleEndian));
601 let parent = unpack_option_shape(parent);
602 let offset =
603 FrameStateSlotOffset(self.stack_shape_offsets[s.index()].get(LittleEndian));
604 let ty = FrameValType::try_from(self.stack_shape_types[s.index()])
605 .expect("Invalid type");
606 shape = parent;
607 (offset, ty)
608 })
609 })
610 }
611
612 pub fn stack_and_locals(
614 &self,
615 shape: FrameStackShape,
616 ) -> impl Iterator<Item = (FrameStateSlotOffset, FrameValType)> + '_ {
617 self.locals().chain(self.stack(shape))
618 }
619}