1use crate::FuncKey;
8use alloc::vec::Vec;
9use object::{Bytes, LittleEndian, U32Bytes};
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 [U32Bytes<LittleEndian>],
48 frame_descriptor_data: &'a [u8],
49
50 frame_descriptor_fp_offsets: &'a [U32Bytes<LittleEndian>],
51
52 progpoint_pcs: &'a [U32Bytes<LittleEndian>],
53 progpoint_descriptor_offsets: &'a [U32Bytes<LittleEndian>],
54 progpoint_descriptor_data: &'a [U32Bytes<LittleEndian>],
55
56 breakpoint_pcs: &'a [U32Bytes<LittleEndian>],
57 breakpoint_patch_offsets: &'a [U32Bytes<LittleEndian>],
58 breakpoint_patch_data_ends: &'a [U32Bytes<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::<U32Bytes<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::<U32Bytes<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::<U32Bytes<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::<U32Bytes<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::<U32Bytes<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::<U32Bytes<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::<U32Bytes<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::<U32Bytes<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::<U32Bytes<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::<U32Bytes<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::<U32Bytes<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::<U32Bytes<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::<U32Bytes<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) = object::slice_from_bytes::<U32Bytes<LittleEndian>>(
127 data,
128 progpoint_descriptor_pool_length,
129 )
130 .map_err(|_| anyhow::anyhow!("Unable to read progpoint descriptor pool"))?;
131
132 let (breakpoint_patch_data, _) = data
133 .split_at_checked(breakpoint_patch_pool_length)
134 .ok_or_else(|| anyhow::anyhow!("Unable to read breakpoint patch pool"))?;
135
136 Ok(FrameTable {
137 frame_descriptor_ranges,
138 frame_descriptor_data,
139 frame_descriptor_fp_offsets,
140 progpoint_pcs,
141 progpoint_descriptor_offsets,
142 progpoint_descriptor_data,
143 breakpoint_pcs,
144 breakpoint_patch_offsets,
145 breakpoint_patch_data_ends,
146 breakpoint_patch_data,
147 original_text,
148 })
149 }
150
151 pub fn frame_descriptor(
154 &self,
155 frame_descriptor: FrameTableDescriptorIndex,
156 ) -> Option<(&'a [u8], u32)> {
157 let range_start = self
158 .frame_descriptor_ranges
159 .get(frame_descriptor.index() * 2)?
160 .get(LittleEndian);
161 let range_end = self
162 .frame_descriptor_ranges
163 .get(frame_descriptor.index() * 2 + 1)?
164 .get(LittleEndian);
165 let range_start = usize::try_from(range_start).unwrap();
166 let range_end = usize::try_from(range_end).unwrap();
167 if range_end < range_start || range_end > self.frame_descriptor_data.len() {
168 return None;
169 }
170 let descriptor = &self.frame_descriptor_data[range_start..range_end];
171 let slot_to_fp_offset = self
172 .frame_descriptor_fp_offsets
173 .get(frame_descriptor.index())?
174 .get(LittleEndian);
175 Some((descriptor, slot_to_fp_offset))
176 }
177
178 pub fn find_program_point(
181 &self,
182 search_pc: u32,
183 search_pos: FrameInstPos,
184 ) -> Option<impl Iterator<Item = (u32, FrameTableDescriptorIndex, FrameStackShape)>> {
185 let key = FrameInstPos::encode(search_pc, search_pos);
186 let index = match self
187 .progpoint_pcs
188 .binary_search_by_key(&key, |entry| entry.get(LittleEndian))
189 {
190 Ok(idx) => idx,
191 Err(idx) if idx > 0 => idx - 1,
192 Err(_) => return None,
193 };
194
195 Some(self.program_point_frame_iter(index))
196 }
197
198 pub fn into_program_points(
201 self,
202 ) -> impl Iterator<
203 Item = (
204 u32,
205 FrameInstPos,
206 Vec<(u32, FrameTableDescriptorIndex, FrameStackShape)>,
207 ),
208 > + 'a {
209 self.progpoint_pcs.iter().enumerate().map(move |(i, pc)| {
210 let pc_and_pos = pc.get(LittleEndian);
211 let (pc, pos) = FrameInstPos::decode(pc_and_pos);
212 (
213 pc,
214 pos,
215 self.program_point_frame_iter(i).collect::<Vec<_>>(),
216 )
217 })
218 }
219
220 fn program_point_frame_iter(
221 &self,
222 index: usize,
223 ) -> impl Iterator<Item = (u32, FrameTableDescriptorIndex, FrameStackShape)> {
224 let offset =
225 usize::try_from(self.progpoint_descriptor_offsets[index].get(LittleEndian)).unwrap();
226 let mut data = &self.progpoint_descriptor_data[offset..];
227
228 core::iter::from_fn(move || {
229 if data.len() < 3 {
230 return None;
231 }
232 let wasm_pc = data[0].get(LittleEndian);
233 let frame_descriptor = FrameTableDescriptorIndex(data[1].get(LittleEndian));
234 let stack_shape = FrameStackShape(data[2].get(LittleEndian));
235 data = &data[3..];
236 let not_last = wasm_pc & 0x8000_0000 != 0;
237 let wasm_pc = wasm_pc & 0x7fff_ffff;
238 if !not_last {
239 data = &[];
240 }
241 Some((wasm_pc, frame_descriptor, stack_shape))
242 })
243 }
244
245 fn breakpoint_patch(&self, i: usize) -> FrameTableBreakpointData<'_> {
248 let patch_pool_start = if i == 0 {
249 0
250 } else {
251 self.breakpoint_patch_data_ends[i - 1].get(LittleEndian)
252 };
253 let patch_pool_end = self.breakpoint_patch_data_ends[i].get(LittleEndian);
254 let patch_pool_start = usize::try_from(patch_pool_start).unwrap();
255 let patch_pool_end = usize::try_from(patch_pool_end).unwrap();
256 let len = patch_pool_end - patch_pool_start;
257 let offset = self.breakpoint_patch_offsets[i].get(LittleEndian);
258 let offset = usize::try_from(offset).unwrap();
259 let original_data = &self.original_text[offset..offset + len];
260 FrameTableBreakpointData {
261 offset,
262 enable: &self.breakpoint_patch_data[patch_pool_start..patch_pool_end],
263 disable: original_data,
264 }
265 }
266
267 pub fn lookup_breakpoint_patches_by_pc(
269 &self,
270 pc: u32,
271 ) -> impl Iterator<Item = FrameTableBreakpointData<'_>> + '_ {
272 let range = match self
275 .breakpoint_pcs
276 .binary_search_by_key(&pc, |p| p.get(LittleEndian))
277 {
278 Ok(mut i) => {
279 while i > 0 && self.breakpoint_pcs[i - 1].get(LittleEndian) == pc {
281 i -= 1;
282 }
283
284 let mut end = i;
286 while end < self.breakpoint_pcs.len()
287 && self.breakpoint_pcs[end].get(LittleEndian) == pc
288 {
289 end += 1;
290 }
291
292 i..end
293 }
294 Err(_) => 0..0,
295 };
296
297 range.map(|i| self.breakpoint_patch(i))
298 }
299
300 pub fn nearest_breakpoint(&self, pc: u32) -> Option<u32> {
302 match self
303 .breakpoint_pcs
304 .binary_search_by_key(&pc, |p| p.get(LittleEndian))
305 {
306 Ok(_) => Some(pc),
307 Err(i) => {
308 if i < self.breakpoint_pcs.len() {
309 Some(self.breakpoint_pcs[i].get(LittleEndian))
310 } else {
311 None
312 }
313 }
314 }
315 }
316
317 pub fn breakpoint_patches(
321 &self,
322 ) -> impl Iterator<Item = (u32, FrameTableBreakpointData<'_>)> + '_ {
323 self.breakpoint_pcs.iter().enumerate().map(|(i, wasm_pc)| {
324 let wasm_pc = wasm_pc.get(LittleEndian);
325 let data = self.breakpoint_patch(i);
326 (wasm_pc, data)
327 })
328 }
329}
330
331pub struct FrameTableBreakpointData<'a> {
334 pub offset: usize,
336 pub enable: &'a [u8],
338 pub disable: &'a [u8],
340}
341
342#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
381pub enum FrameInstPos {
382 Post,
385 Pre,
388}
389
390impl FrameInstPos {
391 pub(crate) fn encode(pc: u32, pos: FrameInstPos) -> u32 {
392 let lsb = match pos {
393 Self::Post => 0,
394 Self::Pre => 1,
395 };
396 debug_assert!(pc < 0x8000_0000);
397 (pc << 1) | lsb
398 }
399 pub(crate) fn decode(bits: u32) -> (u32, FrameInstPos) {
400 let pos = match bits & 1 {
401 0 => Self::Post,
402 1 => Self::Pre,
403 _ => unreachable!(),
404 };
405 let pc = bits >> 1;
406 (pc, pos)
407 }
408}
409
410#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
412pub struct FrameStateSlotOffset(pub(crate) u32);
413impl FrameStateSlotOffset {
414 #[cfg(feature = "compile")]
415 pub(crate) fn add(self, offset: u32) -> FrameStateSlotOffset {
416 FrameStateSlotOffset(self.0 + offset)
417 }
418
419 pub fn offset(self) -> i32 {
422 i32::try_from(self.0).unwrap()
423 }
424}
425
426#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
428#[allow(missing_docs, reason = "self-describing variants")]
429pub enum FrameValType {
430 I32,
431 I64,
432 F32,
433 F64,
434 V128,
435 AnyRef,
436 FuncRef,
437 ExternRef,
438 ExnRef,
439 ContRef,
440}
441
442impl FrameValType {
443 #[cfg(feature = "compile")]
444 pub(crate) fn storage_size(&self, pointer_size: u32) -> u32 {
445 match self {
446 FrameValType::I32 => 4,
447 FrameValType::I64 => 8,
448 FrameValType::F32 => 4,
449 FrameValType::F64 => 8,
450 FrameValType::V128 => 16,
451 FrameValType::AnyRef | FrameValType::ExternRef | FrameValType::ExnRef => 4,
452 FrameValType::FuncRef => pointer_size,
453 FrameValType::ContRef => 2 * pointer_size,
454 }
455 }
456}
457
458impl From<FrameValType> for u8 {
459 fn from(value: FrameValType) -> u8 {
460 match value {
461 FrameValType::I32 => 0,
462 FrameValType::I64 => 1,
463 FrameValType::F32 => 2,
464 FrameValType::F64 => 3,
465 FrameValType::V128 => 4,
466 FrameValType::AnyRef => 5,
467 FrameValType::FuncRef => 6,
468 FrameValType::ExternRef => 7,
469 FrameValType::ExnRef => 8,
470 FrameValType::ContRef => 9,
471 }
472 }
473}
474
475impl TryFrom<u8> for FrameValType {
476 type Error = anyhow::Error;
477 fn try_from(value: u8) -> anyhow::Result<Self> {
478 match value {
479 0 => Ok(Self::I32),
480 1 => Ok(Self::I64),
481 2 => Ok(Self::F32),
482 3 => Ok(Self::F64),
483 4 => Ok(Self::V128),
484 5 => Ok(Self::AnyRef),
485 6 => Ok(Self::FuncRef),
486 7 => Ok(Self::ExternRef),
487 8 => Ok(Self::ExnRef),
488 9 => Ok(Self::ContRef),
489 _ => Err(anyhow::anyhow!("Invalid type")),
490 }
491 }
492}
493
494pub struct FrameStateSlot<'a> {
499 func_key: FuncKey,
500 local_offsets: &'a [U32Bytes<LittleEndian>],
501 stack_shape_parents: &'a [U32Bytes<LittleEndian>],
502 stack_shape_offsets: &'a [U32Bytes<LittleEndian>],
503 local_types: &'a [u8],
504 stack_shape_types: &'a [u8],
505}
506
507impl<'a> FrameStateSlot<'a> {
508 pub fn parse(descriptor: &'a [u8]) -> anyhow::Result<FrameStateSlot<'a>> {
513 let mut data = Bytes(descriptor);
514 let func_key_namespace = data
515 .read::<U32Bytes<LittleEndian>>()
516 .map_err(|_| anyhow::anyhow!("Unable to read func key namespace"))?
517 .get(LittleEndian);
518 let func_key_index = data
519 .read::<U32Bytes<LittleEndian>>()
520 .map_err(|_| anyhow::anyhow!("Unable to read func key index"))?
521 .get(LittleEndian);
522 let func_key = FuncKey::from_raw_parts(func_key_namespace, func_key_index);
523
524 let num_locals = data
525 .read::<U32Bytes<LittleEndian>>()
526 .map_err(|_| anyhow::anyhow!("Unable to read num_locals"))?
527 .get(LittleEndian);
528 let num_locals = usize::try_from(num_locals)?;
529 let num_stack_shapes = data
530 .read::<U32Bytes<LittleEndian>>()
531 .map_err(|_| anyhow::anyhow!("Unable to read num_stack_shapes"))?
532 .get(LittleEndian);
533 let num_stack_shapes = usize::try_from(num_stack_shapes)?;
534
535 let (local_offsets, data) =
536 object::slice_from_bytes::<U32Bytes<LittleEndian>>(data.0, num_locals)
537 .map_err(|_| anyhow::anyhow!("Unable to read local_offsets slice"))?;
538 let (stack_shape_parents, data) =
539 object::slice_from_bytes::<U32Bytes<LittleEndian>>(data, num_stack_shapes)
540 .map_err(|_| anyhow::anyhow!("Unable to read stack_shape_parents slice"))?;
541 let (stack_shape_offsets, data) =
542 object::slice_from_bytes::<U32Bytes<LittleEndian>>(data, num_stack_shapes)
543 .map_err(|_| anyhow::anyhow!("Unable to read stack_shape_offsets slice"))?;
544 let (local_types, data) = data
545 .split_at_checked(num_locals)
546 .ok_or_else(|| anyhow::anyhow!("Unable to read local_types slice"))?;
547 let (stack_shape_types, _) = data
548 .split_at_checked(num_stack_shapes)
549 .ok_or_else(|| anyhow::anyhow!("Unable to read stack_shape_types slice"))?;
550
551 Ok(FrameStateSlot {
552 func_key,
553 local_offsets,
554 stack_shape_parents,
555 stack_shape_offsets,
556 local_types,
557 stack_shape_types,
558 })
559 }
560
561 pub fn func_key(&self) -> FuncKey {
564 self.func_key
565 }
566
567 pub fn locals(&self) -> impl Iterator<Item = (FrameStateSlotOffset, FrameValType)> {
569 (0..self.num_locals()).map(|i| self.local(i).unwrap())
570 }
571
572 pub fn local(&self, index: usize) -> Option<(FrameStateSlotOffset, FrameValType)> {
574 let offset = FrameStateSlotOffset(self.local_offsets.get(index)?.get(LittleEndian));
575 let ty = FrameValType::try_from(*self.local_types.get(index)?).expect("Invalid type");
576 Some((offset, ty))
577 }
578
579 pub fn num_locals(&self) -> usize {
581 self.local_offsets.len()
582 }
583
584 pub fn stack(
587 &self,
588 shape: FrameStackShape,
589 ) -> impl Iterator<Item = (FrameStateSlotOffset, FrameValType)> {
590 fn unpack_option_shape(shape: FrameStackShape) -> Option<FrameStackShape> {
591 if shape.0 == u32::MAX {
592 None
593 } else {
594 Some(shape)
595 }
596 }
597
598 let mut shape = unpack_option_shape(shape);
599 core::iter::from_fn(move || {
600 shape.map(|s| {
601 let parent = FrameStackShape(self.stack_shape_parents[s.index()].get(LittleEndian));
602 let parent = unpack_option_shape(parent);
603 let offset =
604 FrameStateSlotOffset(self.stack_shape_offsets[s.index()].get(LittleEndian));
605 let ty = FrameValType::try_from(self.stack_shape_types[s.index()])
606 .expect("Invalid type");
607 shape = parent;
608 (offset, ty)
609 })
610 })
611 }
612
613 pub fn stack_and_locals(
615 &self,
616 shape: FrameStackShape,
617 ) -> impl Iterator<Item = (FrameStateSlotOffset, FrameValType)> + '_ {
618 self.locals().chain(self.stack(shape))
619 }
620}