Skip to content

Commit 7051ab3

Browse files
authored
Debugging: add unique-within-store IDs for every entity. (bytecodealliance#12645)
When building an introspection API for use by a debugger, we need a way to expose *identity*: that is, to give some way of knowing that a given `Memory`, `Instance`, etc. is *this* one and not *that* one. Our handle types variously have either `Eq` implementations or e.g. `Module::same` for the ones that wrap an `Arc` under-the-covers; but that's not enough to allow a debugger to e.g. build a hashmap for whatever metadata that it might expose via whatever debugging protocol. For maximal generality and flexibility, we should expose the unique IDs that already more-or-less exist: for instances, that is their instance ID directly; for entities owned by instances, we can build a `u64` with the instance ID in the upper 32 bits and the defined-index in the lower 32 bits. IDs for all entities except modules are unique-within-a-Store (and this is all that is needed); IDs for modules happen to reuse the `CompiledModuleId` and so are unique-within-an-Engine. I've opted to name these `debug_index_within_store` to scope the feature and intended use-case clearly, but if there's a desire, I could easily rename them to simply `index`. I shied away from that here because I didn't want to give a notion that these indices are somehow canonical or correspond to some order or other.
1 parent 301dc71 commit 7051ab3

File tree

8 files changed

+189
-0
lines changed

8 files changed

+189
-0
lines changed

crates/wasmtime/src/runtime/externals/global.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,24 @@ impl Global {
397397
store.id() == self.store
398398
}
399399

400+
/// Returns a stable identifier for this global within its store.
401+
///
402+
/// This allows distinguishing globals when introspecting them
403+
/// e.g. via debug APIs.
404+
#[cfg(feature = "debug")]
405+
pub fn debug_index_in_store(&self) -> u64 {
406+
match self.kind {
407+
VMGlobalKind::Instance(idx) => u64::from(self.instance) << 32 | u64::from(idx.as_u32()),
408+
VMGlobalKind::Host(idx) => u64::from(u32::MAX) << 32 | u64::from(idx.as_u32()),
409+
#[cfg(feature = "component-model")]
410+
VMGlobalKind::ComponentFlags(idx) => {
411+
u64::from(self.instance) << 32 | u64::from(idx.as_u32())
412+
}
413+
#[cfg(feature = "component-model")]
414+
VMGlobalKind::TaskMayBlock => u64::from(self.instance) << 32 | u64::from(u32::MAX),
415+
}
416+
}
417+
400418
/// Get a stable hash key for this global.
401419
///
402420
/// Even if the same underlying global definition is added to the

crates/wasmtime/src/runtime/externals/table.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,15 @@ impl Table {
571571
store.id() == self.instance.store_id()
572572
}
573573

574+
/// Returns a stable identifier for this table within its store.
575+
///
576+
/// This allows distinguishing tables when introspecting them
577+
/// e.g. via debug APIs.
578+
#[cfg(feature = "debug")]
579+
pub fn debug_index_in_store(&self) -> u64 {
580+
u64::from(self.instance.instance().as_u32()) << 32 | u64::from(self.index.as_u32())
581+
}
582+
574583
/// Get a stable hash key for this table.
575584
///
576585
/// Even if the same underlying table definition is added to the

crates/wasmtime/src/runtime/externals/tag.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ impl Tag {
6060
store.id() == self.instance.store_id()
6161
}
6262

63+
/// Returns a stable identifier for this tag within its store.
64+
///
65+
/// This allows distinguishing tags when introspecting them
66+
/// e.g. via debug APIs.
67+
#[cfg(feature = "debug")]
68+
pub fn debug_index_in_store(&self) -> u64 {
69+
u64::from(self.instance.instance().as_u32()) << 32 | u64::from(self.index.as_u32())
70+
}
71+
6372
/// Determines whether this tag is reference equal to the other
6473
/// given tag in the given store.
6574
///

crates/wasmtime/src/runtime/instance.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,18 @@ impl Instance {
611611
self.id.instance()
612612
}
613613

614+
/// Return a unique-within-Store index for this `Instance`.
615+
///
616+
/// Allows distinguishing instance identities when introspecting
617+
/// the `Store`, e.g. via debug APIs.
618+
///
619+
/// This index will match the instance's position in the sequence
620+
/// returned by `Store::debug_all_instances()`.
621+
#[cfg(feature = "debug")]
622+
pub fn debug_index_in_store(&self) -> u32 {
623+
self.id.instance().as_u32()
624+
}
625+
614626
/// Get all globals within this instance.
615627
///
616628
/// Returns both import and defined globals.

crates/wasmtime/src/runtime/memory.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,15 @@ impl Memory {
671671
store.id() == self.instance.store_id()
672672
}
673673

674+
/// Returns a stable identifier for this memory within its store.
675+
///
676+
/// This allows distinguishing memories when introspecting them
677+
/// e.g. via debug APIs.
678+
#[cfg(feature = "debug")]
679+
pub fn debug_index_in_store(&self) -> u64 {
680+
u64::from(self.instance.instance().as_u32()) << 32 | u64::from(self.index.as_u32())
681+
}
682+
674683
/// Get a stable hash key for this memory.
675684
///
676685
/// Even if the same underlying memory definition is added to the

crates/wasmtime/src/runtime/module.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1113,6 +1113,15 @@ impl Module {
11131113
&self.inner.offsets
11141114
}
11151115

1116+
/// Return the unique-within-Engine ID for this module.
1117+
///
1118+
/// Allows distinguishing module identities when introspecting
1119+
/// modules, e.g. via debug APIs.
1120+
#[cfg(feature = "debug")]
1121+
pub fn debug_index_in_engine(&self) -> u64 {
1122+
self.id().as_u64()
1123+
}
1124+
11161125
/// Return the address, in memory, of the trampoline that allows Wasm to
11171126
/// call a array function of the given signature.
11181127
///

crates/wasmtime/src/runtime/vm/module_id.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,9 @@ impl CompiledModuleId {
1616
// uniqueness.
1717
CompiledModuleId(crate::store::StoreId::allocate().as_raw())
1818
}
19+
20+
/// Returns the inner unique integer contained in this ID.
21+
pub fn as_u64(self) -> u64 {
22+
self.0.get()
23+
}
1924
}

tests/all/debug.rs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,3 +1200,121 @@ fn component_bytecode() -> wasmtime::Result<()> {
12001200

12011201
Ok(())
12021202
}
1203+
1204+
#[test]
1205+
#[cfg_attr(miri, ignore)]
1206+
fn debug_ids() -> wasmtime::Result<()> {
1207+
let mut config = Config::default();
1208+
config.guest_debug(true);
1209+
config.wasm_exceptions(true);
1210+
let engine = Engine::new(&config)?;
1211+
let mut store = Store::new(&engine, ());
1212+
let module1 = Module::new(
1213+
&engine,
1214+
r#"
1215+
(module
1216+
(memory 1 1)
1217+
(memory 1 1)
1218+
(global (mut i32) (i32.const 0))
1219+
(global (mut i32) (i32.const 1))
1220+
(table 1 1 funcref)
1221+
(table 1 1 funcref)
1222+
(tag (param i32))
1223+
(tag (param i64)))
1224+
"#,
1225+
)?;
1226+
1227+
let module2 = Module::new(
1228+
&engine,
1229+
r#"
1230+
(module
1231+
(memory (export "m") 1 1))
1232+
"#,
1233+
)?;
1234+
1235+
let instance1 = Instance::new(&mut store, &module1, &[])?;
1236+
let instance2 = Instance::new(&mut store, &module2, &[])?;
1237+
let instance3 = Instance::new(&mut store, &module1, &[])?;
1238+
1239+
assert_ne!(
1240+
module1.debug_index_in_engine(),
1241+
module2.debug_index_in_engine()
1242+
);
1243+
assert_ne!(
1244+
instance1.debug_index_in_store(),
1245+
instance2.debug_index_in_store()
1246+
);
1247+
assert_ne!(
1248+
instance1
1249+
.debug_memory(&mut store, 0)
1250+
.unwrap()
1251+
.debug_index_in_store(),
1252+
instance1
1253+
.debug_memory(&mut store, 1)
1254+
.unwrap()
1255+
.debug_index_in_store()
1256+
);
1257+
assert_ne!(
1258+
instance1
1259+
.debug_memory(&mut store, 0)
1260+
.unwrap()
1261+
.debug_index_in_store(),
1262+
instance2
1263+
.debug_memory(&mut store, 0)
1264+
.unwrap()
1265+
.debug_index_in_store()
1266+
);
1267+
assert_ne!(
1268+
instance1
1269+
.debug_memory(&mut store, 0)
1270+
.unwrap()
1271+
.debug_index_in_store(),
1272+
instance3
1273+
.debug_memory(&mut store, 0)
1274+
.unwrap()
1275+
.debug_index_in_store()
1276+
);
1277+
assert_ne!(
1278+
instance1
1279+
.debug_global(&mut store, 0)
1280+
.unwrap()
1281+
.debug_index_in_store(),
1282+
instance3
1283+
.debug_global(&mut store, 0)
1284+
.unwrap()
1285+
.debug_index_in_store()
1286+
);
1287+
assert_ne!(
1288+
instance1
1289+
.debug_table(&mut store, 0)
1290+
.unwrap()
1291+
.debug_index_in_store(),
1292+
instance3
1293+
.debug_table(&mut store, 0)
1294+
.unwrap()
1295+
.debug_index_in_store()
1296+
);
1297+
assert_ne!(
1298+
instance1
1299+
.debug_tag(&mut store, 0)
1300+
.unwrap()
1301+
.debug_index_in_store(),
1302+
instance3
1303+
.debug_tag(&mut store, 0)
1304+
.unwrap()
1305+
.debug_index_in_store()
1306+
);
1307+
1308+
let m_via_export = instance2
1309+
.get_export(&mut store, "m")
1310+
.unwrap()
1311+
.into_memory()
1312+
.unwrap();
1313+
let m_via_introspection = instance2.debug_memory(&mut store, 0).unwrap();
1314+
assert_eq!(
1315+
m_via_export.debug_index_in_store(),
1316+
m_via_introspection.debug_index_in_store()
1317+
);
1318+
1319+
Ok(())
1320+
}

0 commit comments

Comments
 (0)