Skip to content
Open
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
2 changes: 1 addition & 1 deletion crates/debugger/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ rust-version.workspace = true
workspace = true

[dependencies]
wasmtime = { workspace = true, features = ["debug", "std", "async", "component-model", "gc", "gc-drc"] }
wasmtime = { workspace = true, features = ["debug", "std", "async", "component-model", "gc", "gc-drc", "threads"] }
wasmtime-wasi = { workspace = true }
wasmtime-wasi-io = { workspace = true }
tokio = { workspace = true, features = ["rt", "sync", "macros"] }
Expand Down
138 changes: 121 additions & 17 deletions crates/debugger/src/host/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,32 @@ use std::pin::Pin;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use wasmtime::{
Engine, ExnRef, FrameHandle, Func, Global, Instance, Memory, Module, OwnedRooted, Result,
Table, Tag, Val, component::Resource, component::ResourceTable,
Engine, ExnRef, FrameHandle, Func, Global, Instance, Module, OwnedRooted, Result, Table, Tag,
Val, component::Resource, component::ResourceTable,
};
use wasmtime_wasi::p2::{DynPollable, Pollable, subscribe};

/// A memory exposed through the debugger API.
///
/// The component-level resource is deliberately shared by both kinds of
/// Wasmtime linear memory. Shared memories use their own host API and do not
/// belong to a `Store`, unlike ordinary memories.
#[derive(Clone)]
pub enum Memory {
Unshared(wasmtime::Memory),
Shared(wasmtime::SharedMemory),
}

impl Memory {
fn unique_id(&self) -> u64 {
match self {
Memory::Unshared(memory) => memory.debug_index_in_store(),
// A shared memory's base address is stable for its lifetime.
Memory::Shared(memory) => memory.data().as_ptr() as usize as u64,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer not to expose an internal pointer to the debug component like this -- it feels like an encapsulation violation. Instead we should add debug_index_in_store to SharedMemory as we did for Memory.

}
}
}

/// Representation of one debuggee: a store with debugged code inside,
/// under the control of the debugger.
pub struct Debuggee {
Expand Down Expand Up @@ -446,7 +467,7 @@ impl wit::HostModule for ResourceTable {

impl wit::HostMemory for ResourceTable {
async fn size_bytes(&mut self, self_: Resource<Memory>, d: Resource<Debuggee>) -> Result<u64> {
let memory = *self.get(&self_)?;
let memory = self.get(&self_)?.clone();
let d = debugger(self, &d)?;
d.memory_size_bytes(memory).await
}
Expand All @@ -456,7 +477,7 @@ impl wit::HostMemory for ResourceTable {
self_: Resource<Memory>,
d: Resource<Debuggee>,
) -> Result<u64> {
let memory = *self.get(&self_)?;
let memory = self.get(&self_)?.clone();
let d = debugger(self, &d)?;
d.memory_page_size(memory).await
}
Expand All @@ -467,7 +488,7 @@ impl wit::HostMemory for ResourceTable {
d: Resource<Debuggee>,
delta_bytes: u64,
) -> Result<u64> {
let memory = *self.get(&self_)?;
let memory = self.get(&self_)?.clone();
let d = debugger(self, &d)?;
d.memory_grow(memory, delta_bytes).await
}
Expand All @@ -479,7 +500,7 @@ impl wit::HostMemory for ResourceTable {
addr: u64,
len: u64,
) -> Result<Vec<u8>> {
let memory = *self.get(&self_)?;
let memory = self.get(&self_)?.clone();
let d = debugger(self, &d)?;
Ok(d.memory_read_bytes(memory, addr, len)
.await?
Expand All @@ -493,7 +514,7 @@ impl wit::HostMemory for ResourceTable {
addr: u64,
bytes: Vec<u8>,
) -> Result<()> {
let memory = *self.get(&self_)?;
let memory = self.get(&self_)?.clone();
let d = debugger(self, &d)?;
d.memory_write_bytes(memory, addr, bytes)
.await?
Expand All @@ -507,7 +528,7 @@ impl wit::HostMemory for ResourceTable {
d: Resource<Debuggee>,
addr: u64,
) -> Result<u8> {
let memory = *self.get(&self_)?;
let memory = self.get(&self_)?.clone();
let d = debugger(self, &d)?;
Ok(d.memory_read_u8(memory, addr)
.await?
Expand All @@ -520,7 +541,7 @@ impl wit::HostMemory for ResourceTable {
d: Resource<Debuggee>,
addr: u64,
) -> Result<u16> {
let memory = *self.get(&self_)?;
let memory = self.get(&self_)?.clone();
let d = debugger(self, &d)?;
Ok(d.memory_read_u16(memory, addr)
.await?
Expand All @@ -533,7 +554,7 @@ impl wit::HostMemory for ResourceTable {
d: Resource<Debuggee>,
addr: u64,
) -> Result<u32> {
let memory = *self.get(&self_)?;
let memory = self.get(&self_)?.clone();
let d = debugger(self, &d)?;
Ok(d.memory_read_u32(memory, addr)
.await?
Expand All @@ -546,7 +567,7 @@ impl wit::HostMemory for ResourceTable {
d: Resource<Debuggee>,
addr: u64,
) -> Result<u64> {
let memory = *self.get(&self_)?;
let memory = self.get(&self_)?.clone();
let d = debugger(self, &d)?;
Ok(d.memory_read_u64(memory, addr)
.await?
Expand All @@ -560,7 +581,7 @@ impl wit::HostMemory for ResourceTable {
addr: u64,
value: u8,
) -> Result<()> {
let memory = *self.get(&self_)?;
let memory = self.get(&self_)?.clone();
let d = debugger(self, &d)?;
d.memory_write_u8(memory, addr, value)
.await?
Expand All @@ -575,7 +596,7 @@ impl wit::HostMemory for ResourceTable {
addr: u64,
value: u16,
) -> Result<()> {
let memory = *self.get(&self_)?;
let memory = self.get(&self_)?.clone();
let d = debugger(self, &d)?;
d.memory_write_u16(memory, addr, value)
.await?
Expand All @@ -590,7 +611,7 @@ impl wit::HostMemory for ResourceTable {
addr: u64,
value: u32,
) -> Result<()> {
let memory = *self.get(&self_)?;
let memory = self.get(&self_)?.clone();
let d = debugger(self, &d)?;
d.memory_write_u32(memory, addr, value)
.await?
Expand All @@ -605,7 +626,7 @@ impl wit::HostMemory for ResourceTable {
addr: u64,
value: u64,
) -> Result<()> {
let memory = *self.get(&self_)?;
let memory = self.get(&self_)?.clone();
let d = debugger(self, &d)?;
d.memory_write_u64(memory, addr, value)
.await?
Expand All @@ -614,12 +635,12 @@ impl wit::HostMemory for ResourceTable {
}

async fn clone(&mut self, self_: Resource<Memory>) -> Result<Resource<Memory>> {
let memory = *self.get(&self_)?;
let memory = self.get(&self_)?.clone();
Ok(self.push(memory)?)
}

async fn unique_id(&mut self, self_: Resource<Memory>) -> Result<u64> {
Ok(self.get(&self_)?.debug_index_in_store())
Ok(self.get(&self_)?.unique_id())
}

async fn drop(&mut self, rep: Resource<Memory>) -> Result<()> {
Expand Down Expand Up @@ -1049,3 +1070,86 @@ impl wit::Host for ResourceTable {
err.downcast()
}
}

#[cfg(test)]
mod tests {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the wrong level at which to test; it's extremely verbose and a lot of machinery to poke inside resource-tables and such.

We have an integration test that starts up Wasmtime with the gdbstub component and runs LLDB against it. Perhaps you can write a test there with a custom-in-WAT debuggee that has a shared memory, and then show that you can print its memory contents with LLDB?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(On reading further, I see that you did that -- so, please just delete this whole unit test)

use super::*;
use crate::host::add_debuggee;
use wasmtime::{Config, Engine, Instance, Module, Store};

#[cfg_attr(miri, ignore)]
#[cfg(target_pointer_width = "64")]
#[tokio::test]
async fn shared_memory_is_exposed_to_debugger() -> wasmtime::Result<()> {
let mut config = Config::new();
config.wasm_threads(true);
config.shared_memory(true);
config.guest_debug(true);
let engine = Engine::new(&config)?;
let module = Module::new(&engine, r#"(module (memory (export "memory") 1 2 shared))"#)?;
let mut store = Store::new(&engine, ());
let instance = Instance::new_async(&mut store, &module, &[]).await?;

let debuggee = crate::Debuggee::new(store, |_store| Box::pin(async { Ok(()) }));
let mut table = ResourceTable::new();
let debuggee = add_debuggee(&mut table, debuggee)?;
let instance = table.push(instance)?;
let memory = <ResourceTable as wit::HostInstance>::get_memory(
&mut table,
instance,
Resource::new_borrow(debuggee.rep()),
0,
)
.await?;
let size = <ResourceTable as wit::HostMemory>::size_bytes(
&mut table,
Resource::new_borrow(memory.rep()),
Resource::new_borrow(debuggee.rep()),
)
.await?;
assert_eq!(size, 65536);

let page_size = <ResourceTable as wit::HostMemory>::page_size_bytes(
&mut table,
Resource::new_borrow(memory.rep()),
Resource::new_borrow(debuggee.rep()),
)
.await?;
assert_eq!(page_size, 65536);
let old_size = <ResourceTable as wit::HostMemory>::grow_to_bytes(
&mut table,
Resource::new_borrow(memory.rep()),
Resource::new_borrow(debuggee.rep()),
page_size,
)
.await?;
assert_eq!(old_size, size);
let size = <ResourceTable as wit::HostMemory>::size_bytes(
&mut table,
Resource::new_borrow(memory.rep()),
Resource::new_borrow(debuggee.rep()),
)
.await?;
assert_eq!(size, 2 * page_size);

<ResourceTable as wit::HostMemory>::set_u32(
&mut table,
Resource::new_borrow(memory.rep()),
Resource::new_borrow(debuggee.rep()),
0,
0x1234_5678,
)
.await?;
let value = <ResourceTable as wit::HostMemory>::get_u32(
&mut table,
Resource::new_borrow(memory.rep()),
Resource::new_borrow(debuggee.rep()),
0,
)
.await?;
assert_eq!(value, 0x1234_5678);

<ResourceTable as wit::HostDebuggee>::drop(&mut table, debuggee).await?;
Ok(())
}
}
2 changes: 1 addition & 1 deletion crates/debugger/src/host/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ wasmtime::component::bindgen!({
"bytecodealliance:wasmtime/debuggee.module": wasmtime::Module,
"bytecodealliance:wasmtime/debuggee.table": wasmtime::Table,
"bytecodealliance:wasmtime/debuggee.global": wasmtime::Global,
"bytecodealliance:wasmtime/debuggee.memory": wasmtime::Memory,
"bytecodealliance:wasmtime/debuggee.memory": super::api::Memory,
"bytecodealliance:wasmtime/debuggee.wasm-tag": wasmtime::Tag,
"bytecodealliance:wasmtime/debuggee.wasm-func": wasmtime::Func,
"bytecodealliance:wasmtime/debuggee.wasm-exception": super::api::WasmException,
Expand Down
Loading