Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions crates/environ/src/compile/module_environ.rs
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,12 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> {
params: sig.params().into(),
});
}
if self.tunables.debug_guest {
// All functions are potentially reachable and
// callable by the guest debugger, so they must
// all be flagged as escaping.
self.flag_func_escaped(func_index);
}
self.result
.function_body_inputs
.push(FunctionBodyData { validator, body });
Expand Down
225 changes: 224 additions & 1 deletion crates/wasmtime/src/runtime/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ use core::{ffi::c_void, ptr::NonNull};
use wasmtime_environ::FrameTable;
use wasmtime_environ::{
DefinedFuncIndex, FrameInstPos, FrameStackShape, FrameStateSlot, FrameStateSlotOffset,
FrameTableBreakpointData, FrameTableDescriptorIndex, FrameValType, FuncKey, Trap,
FrameTableBreakpointData, FrameTableDescriptorIndex, FrameValType, FuncIndex, FuncKey,
GlobalIndex, MemoryIndex, TableIndex, TagIndex, Trap,
};
use wasmtime_unwinder::Frame;

Expand Down Expand Up @@ -65,6 +66,228 @@ impl<'a, T> StoreContextMut<'a, T> {
}
}

impl Instance {
/// Get access to a global within this instance's globals index
/// space.
///
/// This permits accessing globals whether they are exported or
/// not. However, it is only available for purposes of debugging,
/// and so is only permitted when `guest_debug` is enabled in the
/// Engine's configuration. The intent of the Wasmtime API is to
/// enforce the Wasm type system's encapsulation even in the host
/// API, except where necessary for developer tooling.
///
/// `None` is returned for any global index that is out-of-bounds.
///
/// `None` is returned if guest-debugging is not enabled in the
/// engine configuration for this Store.
pub fn debug_global(
&self,
mut store: impl AsContextMut,
global_index: u32,
) -> Option<crate::Global> {
let store = store.as_context_mut().0;
if !store.engine().tunables().debug_guest {
return None;
}

let instance = &store[self.id];
let env_module = self.module(&store).env_module();
// N.B.: `from_bits` here rather than `from_u32` so we don't
// panic on `u32::MAX`. A `u32::MAX` will become an invalid
// entity index which will properly return `None` below.
let global = GlobalIndex::from_bits(global_index);
if env_module.globals.is_valid(global) {
Some(instance.get_exported_global(store.id(), global))
} else {
None
}
}

/// Get access to a memory (unshared only) within this instance's
/// memory index space.
///
/// This permits accessing memories whether they are exported or
/// not. However, it is only available for purposes of debugging,
/// and so is only permitted when `guest_debug` is enabled in the
/// Engine's configuration. The intent of the Wasmtime API is to
/// enforce the Wasm type system's encapsulation even in the host
/// API, except where necessary for developer tooling.
///
/// `None` is returned for any memory index that is out-of-bounds.
///
/// `None` is returned for any shared memory (use
/// `debug_shared_memory` instead).
///
/// `None` is returned if guest-debugging is not enabled in the
/// engine configuration for this Store.
pub fn debug_memory(
&self,
mut store: impl AsContextMut,
memory_index: u32,
) -> Option<crate::Memory> {
let store = store.as_context_mut().0;
if !store.engine().tunables().debug_guest {
return None;
}

let instance = &store[self.id];
let env_module = self.module(&store).env_module();
let memory = MemoryIndex::from_bits(memory_index);
if env_module.memories.is_valid(memory) {
Some(
instance
.get_exported_memory(store.id(), memory)
.unshared()?,
)
} else {
None
}
}

/// Get access to a shared memory within this instance's memory
/// index space.
///
/// This permits accessing memories whether they are exported or
/// not. However, it is only available for purposes of debugging,
/// and so is only permitted when `guest_debug` is enabled in the
/// Engine's configuration. The intent of the Wasmtime API is to
/// enforce the Wasm type system's encapsulation even in the host
/// API, except where necessary for developer tooling.
///
/// `None` is returned for any memory index that is out-of-bounds.
///
/// `None` is returned for any unshared memory (use `debug_memory`
/// instead).
///
/// `None` is returned if guest-debugging is not enabled in the
/// engine configuration for this Store.
pub fn debug_shared_memory(
&self,
mut store: impl AsContextMut,
memory_index: u32,
) -> Option<crate::SharedMemory> {
let store = store.as_context_mut().0;
if !store.engine().tunables().debug_guest {
return None;
}

let instance = &store[self.id];
let env_module = self.module(&store).env_module();
let memory = MemoryIndex::from_bits(memory_index);
if env_module.memories.is_valid(memory) {
Some(crate::SharedMemory::from_raw(
instance.get_exported_memory(store.id(), memory).shared()?,
store.engine().clone(),
))
} else {
None
}
}

/// Get access to a table within this instance's table index
/// space.
///
/// This permits accessing tables whether they are exported or
/// not. However, it is only available for purposes of debugging,
/// and so is only permitted when `guest_debug` is enabled in the
/// Engine's configuration. The intent of the Wasmtime API is to
/// enforce the Wasm type system's encapsulation even in the host
/// API, except where necessary for developer tooling.
///
/// `None` is returned for any table index that is out-of-bounds.
///
/// `None` is returned if guest-debugging is not enabled in the
/// engine configuration for this Store.
pub fn debug_table(
&self,
mut store: impl AsContextMut,
table_index: u32,
) -> Option<crate::Table> {
let store = store.as_context_mut().0;
if !store.engine().tunables().debug_guest {
return None;
}

let instance = &store[self.id];
let env_module = self.module(&store).env_module();
let table = TableIndex::from_bits(table_index);
if env_module.tables.is_valid(table) {
Some(instance.get_exported_table(store.id(), table))
} else {
None
}
}

/// Get access to a function within this instance's function index
/// space.
///
/// This permits accessing functions whether they are exported or
/// not. However, it is only available for purposes of debugging,
/// and so is only permitted when `guest_debug` is enabled in the
/// Engine's configuration. The intent of the Wasmtime API is to
/// enforce the Wasm type system's encapsulation even in the host
/// API, except where necessary for developer tooling.
///
/// `None` is returned for any function index that is
/// out-of-bounds.
///
/// `None` is returned if guest-debugging is not enabled in the
/// engine configuration for this Store.
pub fn debug_function(
&self,
mut store: impl AsContextMut,
function_index: u32,
) -> Option<crate::Func> {
let store = store.as_context_mut().0;
if !store.engine().tunables().debug_guest {
return None;
}

let env_module = self.module(&store).env_module();
let func = FuncIndex::from_bits(function_index);
if env_module.functions.is_valid(func) {
let store_id = store.id();
let (instance, registry) = store.instance_and_module_registry_mut(self.id());
// SAFETY: the `store` and `registry` are associated with
// this instance as we fetched teh instance directly from
// the store above.
unsafe { Some(instance.get_exported_func(registry, store_id, func)) }
} else {
None
}
}

/// Get access to a tag within this instance's tag index space.
///
/// This permits accessing tags whether they are exported or
/// not. However, it is only available for purposes of debugging,
/// and so is only permitted when `guest_debug` is enabled in the
/// Engine's configuration. The intent of the Wasmtime API is to
/// enforce the Wasm type system's encapsulation even in the host
/// API, except where necessary for developer tooling.
///
/// `None` is returned for any tag index that is out-of-bounds.
///
/// `None` is returned if guest-debugging is not enabled in the
/// engine configuration for this Store.
pub fn debug_tag(&self, mut store: impl AsContextMut, tag_index: u32) -> Option<crate::Tag> {
let store = store.as_context_mut().0;
if !store.engine().tunables().debug_guest {
return None;
}

let instance = &store[self.id];
let env_module = self.module(&store).env_module();
let tag = TagIndex::from_bits(tag_index);
if env_module.tags.is_valid(tag) {
Some(instance.get_exported_tag(store.id(), tag))
} else {
None
}
}
}

impl<'a, T> StoreContext<'a, T> {
/// Return all breakpoints.
pub fn breakpoints(self) -> Option<impl Iterator<Item = Breakpoint> + 'a> {
Expand Down
2 changes: 1 addition & 1 deletion crates/wasmtime/src/runtime/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use wasmtime_environ::{
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(C)]
pub struct Instance {
id: StoreInstanceId,
pub(crate) id: StoreInstanceId,
}

// Double-check that the C representation in `instance.h` matches our in-Rust
Expand Down
6 changes: 6 additions & 0 deletions crates/wasmtime/src/runtime/vm/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,10 @@ impl ExportMemory {
ExportMemory::Shared(..) => None,
}
}
pub fn shared(self) -> Option<SharedMemory> {
match self {
ExportMemory::Unshared(_) => None,
ExportMemory::Shared(m, _) => Some(m),
}
}
}
Loading