1 //! Debugging API.
2 
3 use crate::{
4     AnyRef, ExnRef, ExternRef, Func, Instance, Module, Val,
5     store::{AutoAssertNoGc, StoreOpaque},
6     vm::{CurrentActivationBacktrace, VMContext},
7 };
8 use alloc::vec::Vec;
9 use core::{ffi::c_void, ptr::NonNull};
10 use wasmtime_environ::{
11     DefinedFuncIndex, FrameInstPos, FrameStackShape, FrameStateSlot, FrameStateSlotOffset,
12     FrameTable, FrameTableDescriptorIndex, FrameValType, FuncKey,
13 };
14 use wasmtime_unwinder::Frame;
15 
16 use super::store::AsStoreOpaque;
17 
18 impl StoreOpaque {
19     /// Provide an object that captures Wasm stack state, including
20     /// Wasm VM-level values (locals and operand stack).
21     ///
22     /// This object views all activations for the current store that
23     /// are on the stack. An activation is a contiguous sequence of
24     /// Wasm frames (called functions) that were called from host code
25     /// and called back out to host code. If there are activations
26     /// from multiple stores on the stack, for example if Wasm code in
27     /// one store calls out to host code which invokes another Wasm
28     /// function in another store, then the other stores are "opaque"
29     /// to our view here in the same way that host code is.
30     ///
31     /// Returns `None` if debug instrumentation is not enabled for
32     /// the engine containing this store.
33     pub fn debug_frames(&mut self) -> Option<DebugFrameCursor<'_>> {
34         if !self.engine().tunables().debug_guest {
35             return None;
36         }
37 
38         // SAFETY: This takes a mutable borrow of `self` (the
39         // `StoreOpaque`), which owns all active stacks in the
40         // store. We do not provide any API that could mutate the
41         // frames that we are walking on the `DebugFrameCursor`.
42         let iter = unsafe { CurrentActivationBacktrace::new(self) };
43         let mut view = DebugFrameCursor {
44             iter,
45             is_trapping_frame: false,
46             frames: vec![],
47             current: None,
48         };
49         view.move_to_parent(); // Load the first frame.
50         Some(view)
51     }
52 }
53 
54 /// A view of an active stack frame, with the ability to move up the
55 /// stack.
56 ///
57 /// See the documentation on `Store::stack_value` for more information
58 /// about which frames this view will show.
59 pub struct DebugFrameCursor<'a> {
60     /// Iterator over frames.
61     ///
62     /// This iterator owns the store while the view exists (accessible
63     /// as `iter.store`).
64     iter: CurrentActivationBacktrace<'a>,
65 
66     /// Is the next frame to be visited by the iterator a trapping
67     /// frame?
68     ///
69     /// This alters how we interpret `pc`: for a trap, we look at the
70     /// instruction that *starts* at `pc`, while for all frames
71     /// further up the stack (i.e., at a callsite), we look at the
72     /// instruction that *ends* at `pc`.
73     is_trapping_frame: bool,
74 
75     /// Virtual frame queue: decoded from `iter`, not yet
76     /// yielded. Innermost frame on top (last).
77     ///
78     /// This is only non-empty when there is more than one virtual
79     /// frame in a physical frame (i.e., for inlining); thus, its size
80     /// is bounded by our inlining depth.
81     frames: Vec<VirtualFrame>,
82 
83     /// Currently focused virtual frame.
84     current: Option<FrameData>,
85 }
86 
87 impl<'a> DebugFrameCursor<'a> {
88     /// Move up to the next frame in the activation.
89     pub fn move_to_parent(&mut self) {
90         // If there are no virtual frames to yield, take and decode
91         // the next physical frame.
92         //
93         // Note that `if` rather than `while` here, and the assert
94         // that we get some virtual frames back, enforce the invariant
95         // that each physical frame decodes to at least one virtual
96         // frame (i.e., there are no physical frames for interstitial
97         // functions or other things that we completely ignore). If
98         // this ever changes, we can remove the assert and convert
99         // this to a loop that polls until it finds virtual frames.
100         self.current = None;
101         if self.frames.is_empty() {
102             let Some(next_frame) = self.iter.next() else {
103                 return;
104             };
105             self.frames =
106                 VirtualFrame::decode(&mut self.iter.store, next_frame, self.is_trapping_frame);
107             debug_assert!(!self.frames.is_empty());
108             self.is_trapping_frame = false;
109         }
110 
111         // Take a frame and focus it as the current one.
112         self.current = self.frames.pop().map(|vf| FrameData::compute(vf));
113     }
114 
115     /// Has the iterator reached the end of the activation?
116     pub fn done(&self) -> bool {
117         self.current.is_none()
118     }
119 
120     fn frame_data(&self) -> &FrameData {
121         self.current.as_ref().expect("No current frame")
122     }
123 
124     fn raw_instance(&self) -> &crate::vm::Instance {
125         // Read out the vmctx slot.
126 
127         // SAFETY: vmctx is always at offset 0 in the slot.
128         // (See crates/cranelift/src/func_environ.rs in `update_stack_slot_vmctx()`.)
129         let vmctx: *mut VMContext = unsafe { *(self.frame_data().slot_addr as *mut _) };
130         let vmctx = NonNull::new(vmctx).expect("null vmctx in debug state slot");
131         // SAFETY: the stored vmctx value is a valid instance in this
132         // store; we only visit frames from this store in the
133         // backtrace.
134         let instance = unsafe { crate::vm::Instance::from_vmctx(vmctx) };
135         // SAFETY: the instance pointer read above is valid.
136         unsafe { instance.as_ref() }
137     }
138 
139     /// Get the instance associated with the current frame.
140     pub fn instance(&mut self) -> Instance {
141         let instance = self.raw_instance();
142         Instance::from_wasmtime(instance.id(), self.iter.store.as_store_opaque())
143     }
144 
145     /// Get the module associated with the current frame, if any
146     /// (i.e., not a container instance for a host-created entity).
147     pub fn module(&self) -> Option<&Module> {
148         let instance = self.raw_instance();
149         instance.runtime_module()
150     }
151 
152     /// Get the raw function index associated with the current frame, and the
153     /// PC as an offset within its code section, if it is a Wasm
154     /// function directly from the given `Module` (rather than a
155     /// trampoline).
156     pub fn wasm_function_index_and_pc(&self) -> Option<(DefinedFuncIndex, u32)> {
157         let data = self.frame_data();
158         let FuncKey::DefinedWasmFunction(module, func) = data.func_key else {
159             return None;
160         };
161         debug_assert_eq!(
162             module,
163             self.module()
164                 .expect("module should be defined if this is a defined function")
165                 .env_module()
166                 .module_index
167         );
168         Some((func, data.wasm_pc))
169     }
170 
171     /// Get the number of locals in this frame.
172     pub fn num_locals(&self) -> u32 {
173         u32::try_from(self.frame_data().locals.len()).unwrap()
174     }
175 
176     /// Get the depth of the operand stack in this frame.
177     pub fn num_stacks(&self) -> u32 {
178         u32::try_from(self.frame_data().stack.len()).unwrap()
179     }
180 
181     /// Get the type and value of the given local in this frame.
182     ///
183     /// # Panics
184     ///
185     /// Panics if the index is out-of-range (greater than
186     /// `num_locals()`).
187     pub fn local(&mut self, index: u32) -> Val {
188         let data = self.frame_data();
189         let (offset, ty) = data.locals[usize::try_from(index).unwrap()];
190         let slot_addr = data.slot_addr;
191         // SAFETY: compiler produced metadata to describe this local
192         // slot and stored a value of the correct type into it.
193         unsafe { read_value(&mut self.iter.store, slot_addr, offset, ty) }
194     }
195 
196     /// Get the type and value of the given operand-stack value in
197     /// this frame.
198     ///
199     /// Index 0 corresponds to the bottom-of-stack, and higher indices
200     /// from there are more recently pushed values.  In other words,
201     /// index order reads the Wasm virtual machine's abstract stack
202     /// state left-to-right.
203     pub fn stack(&mut self, index: u32) -> Val {
204         let data = self.frame_data();
205         let (offset, ty) = data.stack[usize::try_from(index).unwrap()];
206         let slot_addr = data.slot_addr;
207         // SAFETY: compiler produced metadata to describe this
208         // operand-stack slot and stored a value of the correct type
209         // into it.
210         unsafe { read_value(&mut self.iter.store, slot_addr, offset, ty) }
211     }
212 }
213 
214 /// Internal data pre-computed for one stack frame.
215 ///
216 /// This combines physical frame info (pc, fp) with the module this PC
217 /// maps to (yielding a frame table) and one frame as produced by the
218 /// progpoint lookup (Wasm PC, frame descriptor index, stack shape).
219 struct VirtualFrame {
220     /// The frame pointer.
221     fp: *const u8,
222     /// The resolved module handle for the physical PC.
223     ///
224     /// The module for each inlined frame within the physical frame is
225     /// resolved from the vmctx reachable for each such frame; this
226     /// module isused only for looking up the frame table.
227     module: Module,
228     /// The Wasm PC for this frame.
229     wasm_pc: u32,
230     /// The frame descriptor for this frame.
231     frame_descriptor: FrameTableDescriptorIndex,
232     /// The stack shape for this frame.
233     stack_shape: FrameStackShape,
234 }
235 
236 impl VirtualFrame {
237     /// Return virtual frames corresponding to a physical frame, from
238     /// outermost to innermost.
239     fn decode(
240         store: &mut AutoAssertNoGc<'_>,
241         frame: Frame,
242         is_trapping_frame: bool,
243     ) -> Vec<VirtualFrame> {
244         let module = store
245             .modules()
246             .lookup_module_by_pc(frame.pc())
247             .expect("Wasm frame PC does not correspond to a module");
248         let base = module.code_object().code_memory().text().as_ptr() as usize;
249         let pc = frame.pc().wrapping_sub(base);
250         let table = module.frame_table().unwrap();
251         let pc = u32::try_from(pc).expect("PC offset too large");
252         let pos = if is_trapping_frame {
253             FrameInstPos::Pre
254         } else {
255             FrameInstPos::Post
256         };
257         let program_points = table.find_program_point(pc, pos).expect("There must be a program point record in every frame when debug instrumentation is enabled");
258 
259         program_points
260             .map(|(wasm_pc, frame_descriptor, stack_shape)| VirtualFrame {
261                 fp: core::ptr::with_exposed_provenance(frame.fp()),
262                 module: module.clone(),
263                 wasm_pc,
264                 frame_descriptor,
265                 stack_shape,
266             })
267             .collect()
268     }
269 }
270 
271 /// Data computed when we visit a given frame.
272 struct FrameData {
273     slot_addr: *const u8,
274     func_key: FuncKey,
275     wasm_pc: u32,
276     /// Shape of locals in this frame.
277     ///
278     /// We need to store this locally because `FrameView` cannot
279     /// borrow the store: it needs a mut borrow, and an iterator
280     /// cannot yield the same mut borrow multiple times because it
281     /// cannot control the lifetime of the values it yields (the
282     /// signature of `next()` does not bound the return value to the
283     /// `&mut self` arg).
284     locals: Vec<(FrameStateSlotOffset, FrameValType)>,
285     /// Shape of the stack slots at this program point in this frame.
286     ///
287     /// In addition to the borrowing-related reason above, we also
288     /// materialize this because we want to provide O(1) access to the
289     /// stack by depth, and the frame slot descriptor stores info in a
290     /// linked-list (actually DAG, with dedup'ing) way.
291     stack: Vec<(FrameStateSlotOffset, FrameValType)>,
292 }
293 
294 impl FrameData {
295     fn compute(frame: VirtualFrame) -> Self {
296         let frame_table = frame.module.frame_table().unwrap();
297         // Parse the frame descriptor.
298         let (data, slot_to_fp_offset) = frame_table
299             .frame_descriptor(frame.frame_descriptor)
300             .unwrap();
301         let frame_state_slot = FrameStateSlot::parse(data).unwrap();
302         let slot_addr = frame
303             .fp
304             .wrapping_sub(usize::try_from(slot_to_fp_offset).unwrap());
305 
306         // Materialize the stack shape so we have O(1) access to its
307         // elements, and so we don't need to keep the borrow to the
308         // module alive.
309         let mut stack = frame_state_slot
310             .stack(frame.stack_shape)
311             .collect::<Vec<_>>();
312         stack.reverse(); // Put top-of-stack last.
313 
314         // Materialize the local offsets/types so we don't need to
315         // keep the borrow to the module alive.
316         let locals = frame_state_slot.locals().collect::<Vec<_>>();
317 
318         FrameData {
319             slot_addr,
320             func_key: frame_state_slot.func_key(),
321             wasm_pc: frame.wasm_pc,
322             stack,
323             locals,
324         }
325     }
326 }
327 
328 /// Read the value at the given offset.
329 ///
330 /// # Safety
331 ///
332 /// The `offset` and `ty` must correspond to a valid value written
333 /// to the frame by generated code of the correct type. This will
334 /// be the case if this information comes from the frame tables
335 /// (as long as the frontend that generates the tables and
336 /// instrumentation is correct, and as long as the tables are
337 /// preserved through serialization).
338 unsafe fn read_value(
339     store: &mut AutoAssertNoGc<'_>,
340     slot_base: *const u8,
341     offset: FrameStateSlotOffset,
342     ty: FrameValType,
343 ) -> Val {
344     let address = unsafe { slot_base.offset(isize::try_from(offset.offset()).unwrap()) };
345 
346     // SAFETY: each case reads a value from memory that should be
347     // valid according to our safety condition.
348     match ty {
349         FrameValType::I32 => {
350             let value = unsafe { *(address as *const i32) };
351             Val::I32(value)
352         }
353         FrameValType::I64 => {
354             let value = unsafe { *(address as *const i64) };
355             Val::I64(value)
356         }
357         FrameValType::F32 => {
358             let value = unsafe { *(address as *const u32) };
359             Val::F32(value)
360         }
361         FrameValType::F64 => {
362             let value = unsafe { *(address as *const u64) };
363             Val::F64(value)
364         }
365         FrameValType::V128 => {
366             let value = unsafe { *(address as *const u128) };
367             Val::V128(value.into())
368         }
369         FrameValType::AnyRef => {
370             let mut nogc = AutoAssertNoGc::new(store);
371             let value = unsafe { *(address as *const u32) };
372             let value = AnyRef::_from_raw(&mut nogc, value);
373             Val::AnyRef(value)
374         }
375         FrameValType::ExnRef => {
376             let mut nogc = AutoAssertNoGc::new(store);
377             let value = unsafe { *(address as *const u32) };
378             let value = ExnRef::_from_raw(&mut nogc, value);
379             Val::ExnRef(value)
380         }
381         FrameValType::ExternRef => {
382             let mut nogc = AutoAssertNoGc::new(store);
383             let value = unsafe { *(address as *const u32) };
384             let value = ExternRef::_from_raw(&mut nogc, value);
385             Val::ExternRef(value)
386         }
387         FrameValType::FuncRef => {
388             let value = unsafe { *(address as *const *mut c_void) };
389             let value = unsafe { Func::_from_raw(store, value) };
390             Val::FuncRef(value)
391         }
392         FrameValType::ContRef => {
393             unimplemented!("contref values are not implemented in the host API yet")
394         }
395     }
396 }
397 
398 /// Compute raw pointers to all GC refs in the given frame.
399 // Note: ideally this would be an impl Iterator, but this is quite
400 // awkward because of the locally computed data (FrameStateSlot::parse
401 // structured result) within the closure borrowed by a nested closure.
402 pub(crate) fn gc_refs_in_frame<'a>(ft: FrameTable<'a>, pc: u32, fp: *mut usize) -> Vec<*mut u32> {
403     let fp = fp.cast::<u8>();
404     let mut ret = vec![];
405     if let Some(frames) = ft.find_program_point(pc, FrameInstPos::Post) {
406         for (_wasm_pc, frame_desc, stack_shape) in frames {
407             let (frame_desc_data, slot_to_fp_offset) = ft.frame_descriptor(frame_desc).unwrap();
408             let frame_base = unsafe { fp.offset(-isize::try_from(slot_to_fp_offset).unwrap()) };
409             let frame_desc = FrameStateSlot::parse(frame_desc_data).unwrap();
410             for (offset, ty) in frame_desc.stack_and_locals(stack_shape) {
411                 match ty {
412                     FrameValType::AnyRef | FrameValType::ExnRef | FrameValType::ExternRef => {
413                         let slot = unsafe {
414                             frame_base
415                                 .offset(isize::try_from(offset.offset()).unwrap())
416                                 .cast::<u32>()
417                         };
418                         ret.push(slot);
419                     }
420                     FrameValType::ContRef | FrameValType::FuncRef => {}
421                     FrameValType::I32
422                     | FrameValType::I64
423                     | FrameValType::F32
424                     | FrameValType::F64
425                     | FrameValType::V128 => {}
426                 }
427             }
428         }
429     }
430     ret
431 }
432