1 //! Debugging API.
2 
3 use super::store::AsStoreOpaque;
4 use crate::store::StoreId;
5 use crate::vm::{Activation, Backtrace};
6 use crate::{
7     AnyRef, AsContextMut, CodeMemory, ExnRef, Extern, ExternRef, Func, Instance, Module,
8     OwnedRooted, StoreContext, StoreContextMut, Val,
9     code::StoreCodePC,
10     module::ModuleRegistry,
11     store::{AutoAssertNoGc, StoreOpaque},
12     vm::{CompiledModuleId, VMContext},
13 };
14 use crate::{Caller, Result, Store};
15 use alloc::collections::{BTreeMap, BTreeSet, btree_map::Entry};
16 use alloc::vec;
17 use alloc::vec::Vec;
18 use core::{ffi::c_void, ptr::NonNull};
19 #[cfg(feature = "gc")]
20 use wasmtime_environ::FrameTable;
21 use wasmtime_environ::{
22     DefinedFuncIndex, EntityIndex, FrameInstPos, FrameStackShape, FrameStateSlot,
23     FrameStateSlotOffset, FrameTableBreakpointData, FrameTableDescriptorIndex, FrameValType,
24     FuncIndex, FuncKey, GlobalIndex, MemoryIndex, TableIndex, TagIndex, Trap,
25 };
26 use wasmtime_unwinder::{Frame, FrameCursor};
27 
28 impl<T> Store<T> {
29     /// Provide a frame handle for all activations, in order from
30     /// innermost (most recently called) to outermost on the stack.
31     ///
32     /// An activation is a contiguous sequence of Wasm frames (called
33     /// functions) that were called from host code and called back out
34     /// to host code. If there are activations from multiple stores on
35     /// the stack, for example if Wasm code in one store calls out to
36     /// host code which invokes another Wasm function in another
37     /// store, then the other stores are "opaque" to our view here in
38     /// the same way that host code is.
39     ///
40     /// Returns an empty list if debug instrumentation is not enabled
41     /// for the engine containing this store.
42     pub fn debug_exit_frames(&mut self) -> impl Iterator<Item = FrameHandle> {
43         self.as_store_opaque().debug_exit_frames()
44     }
45 
46     /// Start an edit session to update breakpoints.
47     pub fn edit_breakpoints<'a>(&'a mut self) -> Option<BreakpointEdit<'a>> {
48         self.as_store_opaque().edit_breakpoints()
49     }
50 
51     /// Get a vector of all Instances held in the Store, for debug
52     /// purposes.
53     ///
54     /// Guest debugging must be enabled for this accessor to return
55     /// any instances. If it is not, an empty vector is returend.
56     pub fn debug_all_instances(&mut self) -> Vec<Instance> {
57         self.as_store_opaque().debug_all_instances()
58     }
59 
60     /// Get a vector of all Modules held in the Store, for debug
61     /// purposes.
62     ///
63     /// Guest debugging must be enabled for this accessor to return
64     /// any modules. If it is not, an empty vector is returend.
65     pub fn debug_all_modules(&mut self) -> Vec<Module> {
66         self.as_store_opaque().debug_all_modules()
67     }
68 }
69 
70 impl<'a, T> StoreContextMut<'a, T> {
71     /// Provide a frame handle for all activations, in order from
72     /// innermost (most recently called) to outermost on the stack.
73     ///
74     /// See [`Store::debug_exit_frames`] for more details.
75     pub fn debug_exit_frames(&mut self) -> impl Iterator<Item = FrameHandle> {
76         self.0.as_store_opaque().debug_exit_frames()
77     }
78 
79     /// Start an edit session to update breakpoints.
80     pub fn edit_breakpoints(self) -> Option<BreakpointEdit<'a>> {
81         self.0.as_store_opaque().edit_breakpoints()
82     }
83 
84     /// Get a vector of all Instances held in the Store, for debug
85     /// purposes.
86     ///
87     /// See [`Store::debug_all_instances`] for more details.
88     pub fn debug_all_instances(self) -> Vec<Instance> {
89         self.0.as_store_opaque().debug_all_instances()
90     }
91 
92     /// Get a vector of all Modules held in the Store, for debug
93     /// purposes.
94     ///
95     /// See [`Store::debug_all_modules`] for more details.
96     pub fn debug_all_modules(self) -> Vec<Module> {
97         self.0.as_store_opaque().debug_all_modules()
98     }
99 }
100 
101 impl<'a, T> Caller<'a, T> {
102     /// Provide a frame handle for all activations, in order from
103     /// innermost (most recently called) to outermost on the stack.
104     ///
105     /// See [`Store::debug_exit_frames`] for more details.
106     pub fn debug_exit_frames(&mut self) -> impl Iterator<Item = FrameHandle> {
107         self.store.0.as_store_opaque().debug_exit_frames()
108     }
109 
110     /// Start an edit session to update breakpoints.
111     pub fn edit_breakpoints<'b>(&'b mut self) -> Option<BreakpointEdit<'b>> {
112         self.store.0.as_store_opaque().edit_breakpoints()
113     }
114 
115     /// Get a vector of all Instances held in the Store, for debug
116     /// purposes.
117     ///
118     /// See [`Store::debug_all_instances`] for more details.
119     pub fn debug_all_instances(&mut self) -> Vec<Instance> {
120         self.store.0.as_store_opaque().debug_all_instances()
121     }
122 
123     /// Get a vector of all Modules held in the Store, for debug
124     /// purposes.
125     ///
126     /// See [`Store::debug_all_modules`] for more details.
127     pub fn debug_all_modules(&mut self) -> Vec<Module> {
128         self.store.0.as_store_opaque().debug_all_modules()
129     }
130 }
131 
132 impl StoreOpaque {
133     fn debug_exit_frames(&mut self) -> impl Iterator<Item = FrameHandle> {
134         let activations = if self.engine().tunables().debug_guest {
135             Backtrace::activations(self)
136         } else {
137             vec![]
138         };
139 
140         activations
141             .into_iter()
142             // SAFETY: each activation is currently active and will
143             // remain so (we have a mutable borrow of the store).
144             .filter_map(|act| unsafe { FrameHandle::exit_frame(self, act) })
145     }
146 
147     fn edit_breakpoints<'a>(&'a mut self) -> Option<BreakpointEdit<'a>> {
148         if !self.engine().tunables().debug_guest {
149             return None;
150         }
151 
152         let (breakpoints, registry) = self.breakpoints_and_registry_mut();
153         Some(breakpoints.edit(registry))
154     }
155 
156     fn debug_all_instances(&mut self) -> Vec<Instance> {
157         if !self.engine().tunables().debug_guest {
158             return vec![];
159         }
160 
161         self.all_instances().collect()
162     }
163 
164     fn debug_all_modules(&self) -> Vec<Module> {
165         if !self.engine().tunables().debug_guest {
166             return vec![];
167         }
168 
169         self.modules().all_modules().cloned().collect()
170     }
171 }
172 
173 impl Instance {
174     /// Get access to a global within this instance's globals index
175     /// space.
176     ///
177     /// This permits accessing globals whether they are exported or
178     /// not. However, it is only available for purposes of debugging,
179     /// and so is only permitted when `guest_debug` is enabled in the
180     /// Engine's configuration. The intent of the Wasmtime API is to
181     /// enforce the Wasm type system's encapsulation even in the host
182     /// API, except where necessary for developer tooling.
183     ///
184     /// `None` is returned for any global index that is out-of-bounds.
185     ///
186     /// `None` is returned if guest-debugging is not enabled in the
187     /// engine configuration for this Store.
188     pub fn debug_global(
189         &self,
190         mut store: impl AsContextMut,
191         global_index: u32,
192     ) -> Option<crate::Global> {
193         self.debug_export(
194             store.as_context_mut().0,
195             GlobalIndex::from_bits(global_index).into(),
196         )
197         .and_then(|s| s.into_global())
198     }
199 
200     /// Get access to a memory (unshared only) within this instance's
201     /// memory index space.
202     ///
203     /// This permits accessing memories whether they are exported or
204     /// not. However, it is only available for purposes of debugging,
205     /// and so is only permitted when `guest_debug` is enabled in the
206     /// Engine's configuration. The intent of the Wasmtime API is to
207     /// enforce the Wasm type system's encapsulation even in the host
208     /// API, except where necessary for developer tooling.
209     ///
210     /// `None` is returned for any memory index that is out-of-bounds.
211     ///
212     /// `None` is returned for any shared memory (use
213     /// `debug_shared_memory` instead).
214     ///
215     /// `None` is returned if guest-debugging is not enabled in the
216     /// engine configuration for this Store.
217     pub fn debug_memory(
218         &self,
219         mut store: impl AsContextMut,
220         memory_index: u32,
221     ) -> Option<crate::Memory> {
222         self.debug_export(
223             store.as_context_mut().0,
224             MemoryIndex::from_bits(memory_index).into(),
225         )
226         .and_then(|s| s.into_memory())
227     }
228 
229     /// Get access to a shared memory within this instance's memory
230     /// index space.
231     ///
232     /// This permits accessing memories whether they are exported or
233     /// not. However, it is only available for purposes of debugging,
234     /// and so is only permitted when `guest_debug` is enabled in the
235     /// Engine's configuration. The intent of the Wasmtime API is to
236     /// enforce the Wasm type system's encapsulation even in the host
237     /// API, except where necessary for developer tooling.
238     ///
239     /// `None` is returned for any memory index that is out-of-bounds.
240     ///
241     /// `None` is returned for any unshared memory (use `debug_memory`
242     /// instead).
243     ///
244     /// `None` is returned if guest-debugging is not enabled in the
245     /// engine configuration for this Store.
246     pub fn debug_shared_memory(
247         &self,
248         mut store: impl AsContextMut,
249         memory_index: u32,
250     ) -> Option<crate::SharedMemory> {
251         self.debug_export(
252             store.as_context_mut().0,
253             MemoryIndex::from_bits(memory_index).into(),
254         )
255         .and_then(|s| s.into_shared_memory())
256     }
257 
258     /// Get access to a table within this instance's table index
259     /// space.
260     ///
261     /// This permits accessing tables whether they are exported or
262     /// not. However, it is only available for purposes of debugging,
263     /// and so is only permitted when `guest_debug` is enabled in the
264     /// Engine's configuration. The intent of the Wasmtime API is to
265     /// enforce the Wasm type system's encapsulation even in the host
266     /// API, except where necessary for developer tooling.
267     ///
268     /// `None` is returned for any table index that is out-of-bounds.
269     ///
270     /// `None` is returned if guest-debugging is not enabled in the
271     /// engine configuration for this Store.
272     pub fn debug_table(
273         &self,
274         mut store: impl AsContextMut,
275         table_index: u32,
276     ) -> Option<crate::Table> {
277         self.debug_export(
278             store.as_context_mut().0,
279             TableIndex::from_bits(table_index).into(),
280         )
281         .and_then(|s| s.into_table())
282     }
283 
284     /// Get access to a function within this instance's function index
285     /// space.
286     ///
287     /// This permits accessing functions whether they are exported or
288     /// not. However, it is only available for purposes of debugging,
289     /// and so is only permitted when `guest_debug` is enabled in the
290     /// Engine's configuration. The intent of the Wasmtime API is to
291     /// enforce the Wasm type system's encapsulation even in the host
292     /// API, except where necessary for developer tooling.
293     ///
294     /// `None` is returned for any function index that is
295     /// out-of-bounds.
296     ///
297     /// `None` is returned if guest-debugging is not enabled in the
298     /// engine configuration for this Store.
299     pub fn debug_function(
300         &self,
301         mut store: impl AsContextMut,
302         function_index: u32,
303     ) -> Option<crate::Func> {
304         self.debug_export(
305             store.as_context_mut().0,
306             FuncIndex::from_bits(function_index).into(),
307         )
308         .and_then(|s| s.into_func())
309     }
310 
311     /// Get access to a tag within this instance's tag index space.
312     ///
313     /// This permits accessing tags whether they are exported or
314     /// not. However, it is only available for purposes of debugging,
315     /// and so is only permitted when `guest_debug` is enabled in the
316     /// Engine's configuration. The intent of the Wasmtime API is to
317     /// enforce the Wasm type system's encapsulation even in the host
318     /// API, except where necessary for developer tooling.
319     ///
320     /// `None` is returned for any tag index that is out-of-bounds.
321     ///
322     /// `None` is returned if guest-debugging is not enabled in the
323     /// engine configuration for this Store.
324     pub fn debug_tag(&self, mut store: impl AsContextMut, tag_index: u32) -> Option<crate::Tag> {
325         self.debug_export(
326             store.as_context_mut().0,
327             TagIndex::from_bits(tag_index).into(),
328         )
329         .and_then(|s| s.into_tag())
330     }
331 
332     fn debug_export(&self, store: &mut StoreOpaque, index: EntityIndex) -> Option<Extern> {
333         if !store.engine().tunables().debug_guest {
334             return None;
335         }
336 
337         let env_module = self._module(store).env_module();
338         if !env_module.is_valid(index) {
339             return None;
340         }
341         let store_id = store.id();
342         let (instance, registry) = store.instance_and_module_registry_mut(self.id());
343         // SAFETY: the `store` and `registry` are associated with
344         // this instance as we fetched the instance directly from
345         // the store above.
346         let export = unsafe { instance.get_export_by_index_mut(registry, store_id, index) };
347         Some(Extern::from_wasmtime_export(export, store))
348     }
349 }
350 
351 impl<'a, T> StoreContext<'a, T> {
352     /// Return all breakpoints.
353     pub fn breakpoints(self) -> Option<impl Iterator<Item = Breakpoint> + 'a> {
354         if !self.engine().tunables().debug_guest {
355             return None;
356         }
357 
358         let (breakpoints, registry) = self.0.breakpoints_and_registry();
359         Some(breakpoints.breakpoints(registry))
360     }
361 
362     /// Indicate whether single-step mode is enabled.
363     pub fn is_single_step(&self) -> bool {
364         let (breakpoints, _) = self.0.breakpoints_and_registry();
365         breakpoints.is_single_step()
366     }
367 }
368 
369 /// A handle to a stack frame, valid as long as execution is not
370 /// resumed in the associated `Store`.
371 ///
372 /// This handle can be held and cloned and used to refer to a frame
373 /// within a paused store. It is cheap: it internally consists of a
374 /// pointer to the actual frame, together with some metadata to
375 /// determine when that pointer has gone stale.
376 ///
377 /// At the API level, any usage of this frame handle requires a
378 /// mutable borrow of the `Store`, because the `Store` logically owns
379 /// the stack(s) for any execution within it. However, the existence
380 /// of the handle itself does not hold a borrow on the `Store`; hence,
381 /// the `Store` can continue to be used and queried, and some state
382 /// (e.g. memories, tables, GC objects) can even be mutated, as long
383 /// as execution is not resumed. The intent of this API is to allow a
384 /// wide variety of debugger implementation strategies that expose
385 /// stack frames and also allow other commands/actions at the same
386 /// time.
387 ///
388 /// The user can use [`FrameHandle::is_valid`] to determine if the
389 /// handle is still valid and usable.
390 #[derive(Clone)]
391 pub struct FrameHandle {
392     /// The unwinder cursor at this frame.
393     cursor: FrameCursor,
394 
395     /// The index of the virtual frame within the physical frame.
396     virtual_frame_idx: usize,
397 
398     /// The unique Store this frame came from, to ensure the handle is
399     /// used with the correct Store.
400     store_id: StoreId,
401 
402     /// Store `execution_version`.
403     store_version: u64,
404 }
405 
406 impl FrameHandle {
407     /// Create a new FrameHandle at the exit frame of an activation.
408     ///
409     /// # Safety
410     ///
411     /// The provided activation must be valid currently.
412     unsafe fn exit_frame(store: &mut StoreOpaque, activation: Activation) -> Option<FrameHandle> {
413         // SAFETY: activation is valid as per our safety condition.
414         let mut cursor = unsafe { activation.cursor() };
415 
416         // Find the first virtual frame. Each physical frame may have
417         // zero or more virtual frames.
418         while !cursor.done() {
419             let (cache, registry) = store.frame_data_cache_mut_and_registry();
420             let frames = cache.lookup_or_compute(registry, cursor.frame());
421             if frames.len() > 0 {
422                 return Some(FrameHandle {
423                     cursor,
424                     virtual_frame_idx: 0,
425                     store_id: store.id(),
426                     store_version: store.vm_store_context().execution_version,
427                 });
428             }
429             // SAFETY: activation is still valid (valid on entry per
430             // our safety condition, and we have not returned control
431             // since above).
432             unsafe {
433                 cursor.advance(store.unwinder());
434             }
435         }
436 
437         None
438     }
439 
440     /// Determine whether this handle can still be used to refer to a
441     /// frame.
442     pub fn is_valid(&self, mut store: impl AsContextMut) -> bool {
443         let store = store.as_context_mut();
444         self.is_valid_impl(store.0.as_store_opaque())
445     }
446 
447     fn is_valid_impl(&self, store: &StoreOpaque) -> bool {
448         let id = store.id();
449         let version = store.vm_store_context().execution_version;
450         self.store_id == id && self.store_version == version
451     }
452 
453     /// Get a handle to the next frame up the activation (the one that
454     /// called this frame), if any.
455     pub fn parent(&self, mut store: impl AsContextMut) -> Result<Option<FrameHandle>> {
456         let mut store = store.as_context_mut();
457         if !self.is_valid(&mut store) {
458             crate::error::bail!("Frame handle is no longer valid.");
459         }
460 
461         let mut parent = self.clone();
462         parent.virtual_frame_idx += 1;
463 
464         while !parent.cursor.done() {
465             let (cache, registry) = store
466                 .0
467                 .as_store_opaque()
468                 .frame_data_cache_mut_and_registry();
469             let frames = cache.lookup_or_compute(registry, parent.cursor.frame());
470             if parent.virtual_frame_idx < frames.len() {
471                 return Ok(Some(parent));
472             }
473             parent.virtual_frame_idx = 0;
474             // SAFETY: activation is valid because we checked validity
475             // wrt execution version at the top of this function, and
476             // we have not returned since.
477             unsafe {
478                 parent.cursor.advance(store.0.as_store_opaque().unwinder());
479             }
480         }
481 
482         Ok(None)
483     }
484 
485     fn frame_data<'a>(&self, store: &'a mut StoreOpaque) -> Result<&'a FrameData> {
486         if !self.is_valid_impl(store) {
487             crate::error::bail!("Frame handle is no longer valid.");
488         }
489         let (cache, registry) = store.frame_data_cache_mut_and_registry();
490         let frames = cache.lookup_or_compute(registry, self.cursor.frame());
491         // `virtual_frame_idx` counts up for ease of iteration
492         // behavior, while the frames are stored in outer-to-inner
493         // (i.e., caller to callee) order, so we need to reverse here.
494         Ok(&frames[frames.len() - 1 - self.virtual_frame_idx])
495     }
496 
497     fn raw_instance<'a>(&self, store: &mut StoreOpaque) -> Result<&'a crate::vm::Instance> {
498         let frame_data = self.frame_data(store)?;
499 
500         // Read out the vmctx slot.
501 
502         // SAFETY: vmctx is always at offset 0 in the slot.  (See
503         // crates/cranelift/src/func_environ.rs in
504         // `update_stack_slot_vmctx()`.)  The frame/activation is
505         // still valid because we verified this in `frame_data` above.
506         let vmctx: usize =
507             unsafe { *(frame_data.slot_addr(self.cursor.frame().fp()) as *mut usize) };
508         let vmctx: *mut VMContext = core::ptr::with_exposed_provenance_mut(vmctx);
509         let vmctx = NonNull::new(vmctx).expect("null vmctx in debug state slot");
510         // SAFETY: the stored vmctx value is a valid instance in this
511         // store; we only visit frames from this store in the
512         // backtrace.
513         let instance = unsafe { crate::vm::Instance::from_vmctx(vmctx) };
514         // SAFETY: the instance pointer read above is valid.
515         Ok(unsafe { instance.as_ref() })
516     }
517 
518     /// Get the instance associated with the current frame.
519     pub fn instance(&self, mut store: impl AsContextMut) -> Result<Instance> {
520         let store = store.as_context_mut();
521         let instance = self.raw_instance(store.0.as_store_opaque())?;
522         let id = instance.id();
523         Ok(Instance::from_wasmtime(id, store.0.as_store_opaque()))
524     }
525 
526     /// Get the module associated with the current frame, if any
527     /// (i.e., not a container instance for a host-created entity).
528     pub fn module<'a, T: 'static>(
529         &self,
530         store: impl Into<StoreContextMut<'a, T>>,
531     ) -> Result<Option<&'a Module>> {
532         let store = store.into();
533         let instance = self.raw_instance(store.0.as_store_opaque())?;
534         Ok(instance.runtime_module())
535     }
536 
537     /// Get the raw function index associated with the current frame, and the
538     /// PC as an offset within its code section, if it is a Wasm
539     /// function directly from the given `Module` (rather than a
540     /// trampoline).
541     pub fn wasm_function_index_and_pc(
542         &self,
543         mut store: impl AsContextMut,
544     ) -> Result<Option<(DefinedFuncIndex, u32)>> {
545         let mut store = store.as_context_mut();
546         let frame_data = self.frame_data(store.0.as_store_opaque())?;
547         let FuncKey::DefinedWasmFunction(module, func) = frame_data.func_key else {
548             return Ok(None);
549         };
550         let wasm_pc = frame_data.wasm_pc;
551         debug_assert_eq!(
552             module,
553             self.module(&mut store)?
554                 .expect("module should be defined if this is a defined function")
555                 .env_module()
556                 .module_index
557         );
558         Ok(Some((func, wasm_pc)))
559     }
560 
561     /// Get the number of locals in this frame.
562     pub fn num_locals(&self, mut store: impl AsContextMut) -> Result<u32> {
563         let store = store.as_context_mut();
564         let frame_data = self.frame_data(store.0.as_store_opaque())?;
565         Ok(u32::try_from(frame_data.locals.len()).unwrap())
566     }
567 
568     /// Get the depth of the operand stack in this frame.
569     pub fn num_stacks(&self, mut store: impl AsContextMut) -> Result<u32> {
570         let store = store.as_context_mut();
571         let frame_data = self.frame_data(store.0.as_store_opaque())?;
572         Ok(u32::try_from(frame_data.stack.len()).unwrap())
573     }
574 
575     /// Get the type and value of the given local in this frame.
576     ///
577     /// # Panics
578     ///
579     /// Panics if the index is out-of-range (greater than
580     /// `num_locals()`).
581     pub fn local(&self, mut store: impl AsContextMut, index: u32) -> Result<Val> {
582         let store = store.as_context_mut();
583         let frame_data = self.frame_data(store.0.as_store_opaque())?;
584         let (offset, ty) = frame_data.locals[usize::try_from(index).unwrap()];
585         let slot_addr = frame_data.slot_addr(self.cursor.frame().fp());
586         // SAFETY: compiler produced metadata to describe this local
587         // slot and stored a value of the correct type into it. Slot
588         // address is valid because we checked liveness of the
589         // activation/frame via `frame_data` above.
590         Ok(unsafe { read_value(store.0.as_store_opaque(), slot_addr, offset, ty) })
591     }
592 
593     /// Get the type and value of the given operand-stack value in
594     /// this frame.
595     ///
596     /// Index 0 corresponds to the bottom-of-stack, and higher indices
597     /// from there are more recently pushed values.  In other words,
598     /// index order reads the Wasm virtual machine's abstract stack
599     /// state left-to-right.
600     pub fn stack(&self, mut store: impl AsContextMut, index: u32) -> Result<Val> {
601         let store = store.as_context_mut();
602         let frame_data = self.frame_data(store.0.as_store_opaque())?;
603         let (offset, ty) = frame_data.stack[usize::try_from(index).unwrap()];
604         let slot_addr = frame_data.slot_addr(self.cursor.frame().fp());
605         // SAFETY: compiler produced metadata to describe this
606         // operand-stack slot and stored a value of the correct type
607         // into it. Slot address is valid because we checked liveness
608         // of the activation/frame via `frame_data` above.
609         Ok(unsafe { read_value(store.0.as_store_opaque(), slot_addr, offset, ty) })
610     }
611 }
612 
613 /// A cache from `StoreCodePC`s for modules' private code within a
614 /// store to pre-computed layout data for the virtual stack frame(s)
615 /// present at that physical PC.
616 pub(crate) struct FrameDataCache {
617     /// For a given physical PC, the list of virtual frames, from
618     /// inner (most recently called/inlined) to outer.
619     by_pc: BTreeMap<StoreCodePC, Vec<FrameData>>,
620 }
621 
622 impl FrameDataCache {
623     pub(crate) fn new() -> FrameDataCache {
624         FrameDataCache {
625             by_pc: BTreeMap::new(),
626         }
627     }
628 
629     /// Look up (or compute) the list of `FrameData`s from a physical
630     /// `Frame`.
631     fn lookup_or_compute<'a>(
632         &'a mut self,
633         registry: &ModuleRegistry,
634         frame: Frame,
635     ) -> &'a [FrameData] {
636         let pc = StoreCodePC::from_raw(frame.pc());
637         match self.by_pc.entry(pc) {
638             Entry::Occupied(frames) => frames.into_mut(),
639             Entry::Vacant(v) => {
640                 // Although inlining can mix modules, `module` is the
641                 // module that actually contains the physical PC
642                 // (i.e., the outermost function that inlined the
643                 // others).
644                 let (module, frames) = VirtualFrame::decode(registry, frame.pc());
645                 let frames = frames
646                     .into_iter()
647                     .map(|frame| FrameData::compute(frame, &module))
648                     .collect::<Vec<_>>();
649                 v.insert(frames)
650             }
651         }
652     }
653 }
654 
655 /// Internal data pre-computed for one stack frame.
656 ///
657 /// This represents one frame as produced by the progpoint lookup
658 /// (Wasm PC, frame descriptor index, stack shape).
659 struct VirtualFrame {
660     /// The Wasm PC for this frame.
661     wasm_pc: u32,
662     /// The frame descriptor for this frame.
663     frame_descriptor: FrameTableDescriptorIndex,
664     /// The stack shape for this frame.
665     stack_shape: FrameStackShape,
666 }
667 
668 impl VirtualFrame {
669     /// Return virtual frames corresponding to a physical frame, from
670     /// outermost to innermost.
671     fn decode(registry: &ModuleRegistry, pc: usize) -> (Module, Vec<VirtualFrame>) {
672         let (module_with_code, pc) = registry
673             .module_and_code_by_pc(pc)
674             .expect("Wasm frame PC does not correspond to a module");
675         let module = module_with_code.module();
676         let table = module.frame_table().unwrap();
677         let pc = u32::try_from(pc).expect("PC offset too large");
678         let program_points = table.find_program_point(pc, FrameInstPos::Post)
679             .expect("There must be a program point record in every frame when debug instrumentation is enabled");
680 
681         (
682             module.clone(),
683             program_points
684                 .map(|(wasm_pc, frame_descriptor, stack_shape)| VirtualFrame {
685                     wasm_pc,
686                     frame_descriptor,
687                     stack_shape,
688                 })
689                 .collect(),
690         )
691     }
692 }
693 
694 /// Data computed when we visit a given frame.
695 struct FrameData {
696     slot_to_fp_offset: usize,
697     func_key: FuncKey,
698     wasm_pc: u32,
699     /// Shape of locals in this frame.
700     ///
701     /// We need to store this locally because `FrameView` cannot
702     /// borrow the store: it needs a mut borrow, and an iterator
703     /// cannot yield the same mut borrow multiple times because it
704     /// cannot control the lifetime of the values it yields (the
705     /// signature of `next()` does not bound the return value to the
706     /// `&mut self` arg).
707     locals: Vec<(FrameStateSlotOffset, FrameValType)>,
708     /// Shape of the stack slots at this program point in this frame.
709     ///
710     /// In addition to the borrowing-related reason above, we also
711     /// materialize this because we want to provide O(1) access to the
712     /// stack by depth, and the frame slot descriptor stores info in a
713     /// linked-list (actually DAG, with dedup'ing) way.
714     stack: Vec<(FrameStateSlotOffset, FrameValType)>,
715 }
716 
717 impl FrameData {
718     fn compute(frame: VirtualFrame, module: &Module) -> Self {
719         let frame_table = module.frame_table().unwrap();
720         // Parse the frame descriptor.
721         let (data, slot_to_fp_offset) = frame_table
722             .frame_descriptor(frame.frame_descriptor)
723             .unwrap();
724         let frame_state_slot = FrameStateSlot::parse(data).unwrap();
725         let slot_to_fp_offset = usize::try_from(slot_to_fp_offset).unwrap();
726 
727         // Materialize the stack shape so we have O(1) access to its
728         // elements, and so we don't need to keep the borrow to the
729         // module alive.
730         let mut stack = frame_state_slot
731             .stack(frame.stack_shape)
732             .collect::<Vec<_>>();
733         stack.reverse(); // Put top-of-stack last.
734 
735         // Materialize the local offsets/types so we don't need to
736         // keep the borrow to the module alive.
737         let locals = frame_state_slot.locals().collect::<Vec<_>>();
738 
739         FrameData {
740             slot_to_fp_offset,
741             func_key: frame_state_slot.func_key(),
742             wasm_pc: frame.wasm_pc,
743             stack,
744             locals,
745         }
746     }
747 
748     fn slot_addr(&self, fp: usize) -> *mut u8 {
749         let fp: *mut u8 = core::ptr::with_exposed_provenance_mut(fp);
750         fp.wrapping_sub(self.slot_to_fp_offset)
751     }
752 }
753 
754 /// Read the value at the given offset.
755 ///
756 /// # Safety
757 ///
758 /// The `offset` and `ty` must correspond to a valid value written
759 /// to the frame by generated code of the correct type. This will
760 /// be the case if this information comes from the frame tables
761 /// (as long as the frontend that generates the tables and
762 /// instrumentation is correct, and as long as the tables are
763 /// preserved through serialization).
764 unsafe fn read_value(
765     store: &mut StoreOpaque,
766     slot_base: *const u8,
767     offset: FrameStateSlotOffset,
768     ty: FrameValType,
769 ) -> Val {
770     let address = unsafe { slot_base.offset(isize::try_from(offset.offset()).unwrap()) };
771 
772     // SAFETY: each case reads a value from memory that should be
773     // valid according to our safety condition.
774     match ty {
775         FrameValType::I32 => {
776             let value = unsafe { *(address as *const i32) };
777             Val::I32(value)
778         }
779         FrameValType::I64 => {
780             let value = unsafe { *(address as *const i64) };
781             Val::I64(value)
782         }
783         FrameValType::F32 => {
784             let value = unsafe { *(address as *const u32) };
785             Val::F32(value)
786         }
787         FrameValType::F64 => {
788             let value = unsafe { *(address as *const u64) };
789             Val::F64(value)
790         }
791         FrameValType::V128 => {
792             // Vectors are always stored as little-endian.
793             let value = unsafe { u128::from_le_bytes(*(address as *const [u8; 16])) };
794             Val::V128(value.into())
795         }
796         FrameValType::AnyRef => {
797             let mut nogc = AutoAssertNoGc::new(store);
798             let value = unsafe { *(address as *const u32) };
799             let value = AnyRef::_from_raw(&mut nogc, value);
800             Val::AnyRef(value)
801         }
802         FrameValType::ExnRef => {
803             let mut nogc = AutoAssertNoGc::new(store);
804             let value = unsafe { *(address as *const u32) };
805             let value = ExnRef::_from_raw(&mut nogc, value);
806             Val::ExnRef(value)
807         }
808         FrameValType::ExternRef => {
809             let mut nogc = AutoAssertNoGc::new(store);
810             let value = unsafe { *(address as *const u32) };
811             let value = ExternRef::_from_raw(&mut nogc, value);
812             Val::ExternRef(value)
813         }
814         FrameValType::FuncRef => {
815             let value = unsafe { *(address as *const *mut c_void) };
816             let value = unsafe { Func::_from_raw(store, value) };
817             Val::FuncRef(value)
818         }
819         FrameValType::ContRef => {
820             unimplemented!("contref values are not implemented in the host API yet")
821         }
822     }
823 }
824 
825 /// Compute raw pointers to all GC refs in the given frame.
826 // Note: ideally this would be an impl Iterator, but this is quite
827 // awkward because of the locally computed data (FrameStateSlot::parse
828 // structured result) within the closure borrowed by a nested closure.
829 #[cfg(feature = "gc")]
830 pub(crate) fn gc_refs_in_frame<'a>(ft: FrameTable<'a>, pc: u32, fp: *mut usize) -> Vec<*mut u32> {
831     let fp = fp.cast::<u8>();
832     let mut ret = vec![];
833     if let Some(frames) = ft.find_program_point(pc, FrameInstPos::Post) {
834         for (_wasm_pc, frame_desc, stack_shape) in frames {
835             let (frame_desc_data, slot_to_fp_offset) = ft.frame_descriptor(frame_desc).unwrap();
836             let frame_base = unsafe { fp.offset(-isize::try_from(slot_to_fp_offset).unwrap()) };
837             let frame_desc = FrameStateSlot::parse(frame_desc_data).unwrap();
838             for (offset, ty) in frame_desc.stack_and_locals(stack_shape) {
839                 match ty {
840                     FrameValType::AnyRef | FrameValType::ExnRef | FrameValType::ExternRef => {
841                         let slot = unsafe {
842                             frame_base
843                                 .offset(isize::try_from(offset.offset()).unwrap())
844                                 .cast::<u32>()
845                         };
846                         ret.push(slot);
847                     }
848                     FrameValType::ContRef | FrameValType::FuncRef => {}
849                     FrameValType::I32
850                     | FrameValType::I64
851                     | FrameValType::F32
852                     | FrameValType::F64
853                     | FrameValType::V128 => {}
854                 }
855             }
856         }
857     }
858     ret
859 }
860 
861 /// One debug event that occurs when running Wasm code on a store with
862 /// a debug handler attached.
863 #[derive(Debug)]
864 pub enum DebugEvent<'a> {
865     /// A [`wasmtime::Error`](crate::Error) was raised by a hostcall.
866     HostcallError(&'a crate::Error),
867     /// An exception is thrown and caught by Wasm. The current state
868     /// is at the throw-point.
869     CaughtExceptionThrown(OwnedRooted<ExnRef>),
870     /// An exception was not caught and is escaping to the host.
871     UncaughtExceptionThrown(OwnedRooted<ExnRef>),
872     /// A Wasm trap occurred.
873     Trap(Trap),
874     /// A breakpoint was reached.
875     Breakpoint,
876     /// An epoch yield occurred.
877     EpochYield,
878 }
879 
880 /// A handler for debug events.
881 ///
882 /// This is an async callback that is invoked directly within the
883 /// context of a debug event that occurs, i.e., with the Wasm code
884 /// still on the stack. The callback can thus observe that stack, up
885 /// to the most recent entry to Wasm.[^1]
886 ///
887 /// Because this callback receives a `StoreContextMut`, it has full
888 /// access to any state that any other hostcall has, including the
889 /// `T`. In that way, it is like an epoch-deadline callback or a
890 /// call-hook callback. It also "freezes" the entire store for the
891 /// duration of the debugger callback future.
892 ///
893 /// In the future, we expect to provide an "externally async" API on
894 /// the `Store` that allows receiving a stream of debug events and
895 /// accessing the store mutably while frozen; that will need to
896 /// integrate with [`Store::run_concurrent`] to properly timeslice and
897 /// scope the mutable access to the store, and has not been built
898 /// yet. In the meantime, it should be possible to build a fully
899 /// functional debugger with this async-callback API by channeling
900 /// debug events out, and requests to read the store back in, over
901 /// message-passing channels between the callback and an external
902 /// debugger main loop.
903 ///
904 /// Note that the `handle` hook may use its mutable store access to
905 /// invoke another Wasm. Debug events will also be caught and will
906 /// cause further `handle` invocations during this recursive
907 /// invocation. It is up to the debugger to handle any implications of
908 /// this reentrancy (e.g., implications on a duplex channel protocol
909 /// with an event/continue handshake) if it does so.
910 ///
911 /// Note also that this trait has `Clone` as a supertrait, and the
912 /// handler is cloned at every invocation as an artifact of the
913 /// internal ownership structure of Wasmtime: the handler itself is
914 /// owned by the store, but also receives a mutable borrow to the
915 /// whole store, so we need to clone it out to invoke it. It is
916 /// recommended that this trait be implemented by a type that is cheap
917 /// to clone: for example, a single `Arc` handle to debugger state.
918 ///
919 /// [^1]: Providing visibility further than the most recent entry to
920 ///       Wasm is not directly possible because it could see into
921 ///       another async stack, and the stack that polls the future
922 ///       running a particular Wasm invocation could change after each
923 ///       suspend point in the handler.
924 ///
925 /// [`Store::run_concurrent`]: crate::Store::run_concurrent
926 pub trait DebugHandler: Clone + Send + Sync + 'static {
927     /// The data expected on the store that this handler is attached
928     /// to.
929     type Data;
930 
931     /// Handle a debug event.
932     fn handle(
933         &self,
934         store: StoreContextMut<'_, Self::Data>,
935         event: DebugEvent<'_>,
936     ) -> impl Future<Output = ()> + Send;
937 }
938 
939 /// Breakpoint state for modules within a store.
940 #[derive(Default)]
941 pub(crate) struct BreakpointState {
942     /// Single-step mode.
943     single_step: bool,
944     /// Breakpoints added individually.
945     breakpoints: BTreeSet<BreakpointKey>,
946 }
947 
948 /// A breakpoint.
949 pub struct Breakpoint {
950     /// Reference to the module in which we are setting the breakpoint.
951     pub module: Module,
952     /// Wasm PC offset within the module.
953     pub pc: u32,
954 }
955 
956 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
957 struct BreakpointKey(CompiledModuleId, u32);
958 
959 impl BreakpointKey {
960     fn from_raw(module: &Module, pc: u32) -> BreakpointKey {
961         BreakpointKey(module.id(), pc)
962     }
963 
964     fn get(&self, registry: &ModuleRegistry) -> Breakpoint {
965         let module = registry
966             .module_by_compiled_id(self.0)
967             .expect("Module should not have been removed from Store")
968             .clone();
969         Breakpoint { module, pc: self.1 }
970     }
971 }
972 
973 /// A breakpoint-editing session.
974 ///
975 /// This enables updating breakpoint state (setting or unsetting
976 /// individual breakpoints or the store-global single-step flag) in a
977 /// batch. It is more efficient to batch these updates because
978 /// "re-publishing" the newly patched code, with update breakpoint
979 /// settings, typically requires a syscall to re-enable execute
980 /// permissions.
981 pub struct BreakpointEdit<'a> {
982     state: &'a mut BreakpointState,
983     registry: &'a mut ModuleRegistry,
984     /// Modules that have been edited.
985     ///
986     /// Invariant: each of these modules' CodeMemory objects is
987     /// *unpublished* when in the dirty set.
988     dirty_modules: BTreeSet<StoreCodePC>,
989 }
990 
991 impl BreakpointState {
992     pub(crate) fn edit<'a>(&'a mut self, registry: &'a mut ModuleRegistry) -> BreakpointEdit<'a> {
993         BreakpointEdit {
994             state: self,
995             registry,
996             dirty_modules: BTreeSet::new(),
997         }
998     }
999 
1000     pub(crate) fn breakpoints<'a>(
1001         &'a self,
1002         registry: &'a ModuleRegistry,
1003     ) -> impl Iterator<Item = Breakpoint> + 'a {
1004         self.breakpoints.iter().map(|key| key.get(registry))
1005     }
1006 
1007     pub(crate) fn is_single_step(&self) -> bool {
1008         self.single_step
1009     }
1010 }
1011 
1012 impl<'a> BreakpointEdit<'a> {
1013     fn get_code_memory<'b>(
1014         registry: &'b mut ModuleRegistry,
1015         dirty_modules: &mut BTreeSet<StoreCodePC>,
1016         module: &Module,
1017     ) -> Result<&'b mut CodeMemory> {
1018         let store_code_pc = registry.store_code_base_or_register(module)?;
1019         let code_memory = registry
1020             .store_code_mut(store_code_pc)
1021             .expect("Just checked presence above")
1022             .code_memory_mut()
1023             .expect("Must have unique ownership of StoreCode in guest-debug mode");
1024         if dirty_modules.insert(store_code_pc) {
1025             code_memory.unpublish()?;
1026         }
1027         Ok(code_memory)
1028     }
1029 
1030     fn patch<'b>(
1031         patches: impl Iterator<Item = FrameTableBreakpointData<'b>> + 'b,
1032         mem: &mut CodeMemory,
1033         enable: bool,
1034     ) {
1035         let mem = mem.text_mut();
1036         for patch in patches {
1037             let data = if enable { patch.enable } else { patch.disable };
1038             let mem = &mut mem[patch.offset..patch.offset + data.len()];
1039             log::trace!(
1040                 "patch: offset 0x{:x} with enable={enable}: data {data:?} replacing {mem:?}",
1041                 patch.offset
1042             );
1043             mem.copy_from_slice(data);
1044         }
1045     }
1046 
1047     /// Add a breakpoint in the given module at the given PC in that
1048     /// module.
1049     ///
1050     /// No effect if the breakpoint is already set.
1051     pub fn add_breakpoint(&mut self, module: &Module, pc: u32) -> Result<()> {
1052         let key = BreakpointKey::from_raw(module, pc);
1053         self.state.breakpoints.insert(key);
1054         log::trace!("patching in breakpoint {key:?}");
1055         let mem = Self::get_code_memory(self.registry, &mut self.dirty_modules, module)?;
1056         let frame_table = module
1057             .frame_table()
1058             .expect("Frame table must be present when guest-debug is enabled");
1059         let patches = frame_table.lookup_breakpoint_patches_by_pc(pc);
1060         Self::patch(patches, mem, true);
1061         Ok(())
1062     }
1063 
1064     /// Remove a breakpoint in the given module at the given PC in
1065     /// that module.
1066     ///
1067     /// No effect if the breakpoint was not set.
1068     pub fn remove_breakpoint(&mut self, module: &Module, pc: u32) -> Result<()> {
1069         let key = BreakpointKey::from_raw(module, pc);
1070         self.state.breakpoints.remove(&key);
1071         if !self.state.single_step {
1072             let mem = Self::get_code_memory(self.registry, &mut self.dirty_modules, module)?;
1073             let frame_table = module
1074                 .frame_table()
1075                 .expect("Frame table must be present when guest-debug is enabled");
1076             let patches = frame_table.lookup_breakpoint_patches_by_pc(pc);
1077             Self::patch(patches, mem, false);
1078         }
1079         Ok(())
1080     }
1081 
1082     /// Turn on or off single-step mode.
1083     ///
1084     /// In single-step mode, a breakpoint event is emitted at every
1085     /// Wasm PC.
1086     pub fn single_step(&mut self, enabled: bool) -> Result<()> {
1087         log::trace!(
1088             "single_step({enabled}) with breakpoint set {:?}",
1089             self.state.breakpoints
1090         );
1091         let modules = self.registry.all_modules().cloned().collect::<Vec<_>>();
1092         for module in modules {
1093             let mem = Self::get_code_memory(self.registry, &mut self.dirty_modules, &module)?;
1094             let table = module
1095                 .frame_table()
1096                 .expect("Frame table must be present when guest-debug is enabled");
1097             for (wasm_pc, patch) in table.breakpoint_patches() {
1098                 let key = BreakpointKey::from_raw(&module, wasm_pc);
1099                 let this_enabled = enabled || self.state.breakpoints.contains(&key);
1100                 log::trace!(
1101                     "single_step: enabled {enabled} key {key:?} -> this_enabled {this_enabled}"
1102                 );
1103                 Self::patch(core::iter::once(patch), mem, this_enabled);
1104             }
1105         }
1106 
1107         self.state.single_step = enabled;
1108 
1109         Ok(())
1110     }
1111 }
1112 
1113 impl<'a> Drop for BreakpointEdit<'a> {
1114     fn drop(&mut self) {
1115         for &store_code_base in &self.dirty_modules {
1116             let store_code = self.registry.store_code_mut(store_code_base).unwrap();
1117             if let Err(e) = store_code
1118                 .code_memory_mut()
1119                 .expect("Must have unique ownership of StoreCode in guest-debug mode")
1120                 .publish()
1121             {
1122                 abort_on_republish_error(e);
1123             }
1124         }
1125     }
1126 }
1127 
1128 /// Abort when we cannot re-publish executable code.
1129 ///
1130 /// Note that this puts us in quite a conundrum. Typically we will
1131 /// have been editing breakpoints from within a hostcall context
1132 /// (e.g. inside a debugger hook while execution is paused) with JIT
1133 /// code on the stack. Wasmtime's usual path to return errors is back
1134 /// through that JIT code: we do not panic-unwind across the JIT code,
1135 /// we return into the exit trampoline and that then re-enters the
1136 /// raise libcall to use a Cranelift exception-throw to cross most of
1137 /// the JIT frames to the entry trampoline. When even trampolines are
1138 /// no longer executable, we have no way out. Even an ordinary
1139 /// `panic!` cannot work, because we catch panics and carry them
1140 /// across JIT code using that trampoline-based error path. Our only
1141 /// way out is to directly abort the whole process.
1142 ///
1143 /// This is not without precedent: other engines have similar failure
1144 /// paths. For example, SpiderMonkey directly aborts the process when
1145 /// failing to re-apply executable permissions (see [1]).
1146 ///
1147 /// Note that we don't really expect to ever hit this case in
1148 /// practice: it's unlikely that `mprotect` applying `PROT_EXEC` would
1149 /// fail due to, e.g., resource exhaustion in the kernel, because we
1150 /// will have the same net number of virtual memory areas before and
1151 /// after the permissions change. Nevertheless, we have to account for
1152 /// the possibility of error.
1153 ///
1154 /// [1]: https://searchfox.org/firefox-main/rev/7496c8515212669451d7e775a00c2be07da38ca5/js/src/jit/AutoWritableJitCode.h#26-56
1155 #[cfg(feature = "std")]
1156 fn abort_on_republish_error(e: crate::Error) -> ! {
1157     log::error!(
1158         "Failed to re-publish executable code: {e:?}. Wasmtime cannot return through JIT code on the stack and cannot even panic; aborting the process."
1159     );
1160     std::process::abort();
1161 }
1162 
1163 /// In the `no_std` case, we don't have a concept of a "process
1164 /// abort", so rely on `panic!`. Typically an embedded scenario that
1165 /// uses `no_std` will build with `panic=abort` so the effect is the
1166 /// same. If it doesn't, there is truly nothing we can do here so
1167 /// let's panic anyway; the panic propagation through the trampolines
1168 /// will at least deterministically crash.
1169 #[cfg(not(feature = "std"))]
1170 fn abort_on_republish_error(e: crate::Error) -> ! {
1171     panic!("Failed to re-publish executable code: {e:?}");
1172 }
1173