Path: blob/main/crates/wizer/src/instrument.rs
2458 views
//! The initial instrumentation pass.12use crate::info::ModuleContext;3use wasm_encoder::SectionId;4use wasm_encoder::reencode::{Reencode, RoundtripReencoder};56/// Instrument the input Wasm so that it exports its memories and globals,7/// allowing us to inspect their state after the module is instantiated and8/// initialized.9///10/// For example, given this input module:11///12/// ```wat13/// (module $A14/// (module $B15/// (memory $B_mem)16/// (global $B_glob (mut i32))17/// )18///19/// (instance $x (instantiate $B))20/// (instance $y (instantiate $B))21///22/// (memory $A_mem)23/// (global $A_glob (mut i32))24/// )25/// ```26///27/// this pass will produce the following instrumented module:28///29/// ```wat30/// (module $A31/// (module $B32/// (memory $B_mem)33/// (global $B_glob (mut i32))34///35/// ;; Export all state.36/// (export "__wizer_memory_0" (memory $B_mem))37/// (export "__wizer_global_0" (global $B_glob))38/// )39///40/// (instance $x (instantiate $B))41/// (instance $y (instantiate $B))42///43/// (memory $A_mem)44/// (global $A_glob (mut i32))45///46/// ;; Export of all state (including transitively re-exporting nested47/// ;; instantiations' state).48/// (export "__wizer_memory_0" (memory $A_mem))49/// (export "__wizer_global_0" (global $A_glob))50/// (export "__wizer_instance_0" (instance $x))51/// (export "__wizer_instance_1" (instance $y))52/// )53/// ```54///55/// NB: we re-export nested instantiations as a whole instance export because we56/// can do this without disturbing existing instances' indices. If we were to57/// export their memories and globals individually, that would disturb the58/// modules locally defined memoryies' and globals' indices, which would require59/// rewriting the code section, which would break debug info offsets.60pub(crate) fn instrument(module: &mut ModuleContext<'_>) -> Vec<u8> {61log::debug!("Instrumenting the input Wasm");6263let mut encoder = wasm_encoder::Module::new();64let mut defined_global_exports = Vec::new();65let mut defined_memory_exports = Vec::new();6667for section in module.raw_sections() {68match section.id {69// For the exports section, we need to transitively export internal70// state so that we can read the initialized state after we call the71// initialization function.72id if id == u8::from(SectionId::Export) => {73let mut exports = wasm_encoder::ExportSection::new();7475// First, copy over all the original exports.76for export in module.exports() {77RoundtripReencoder78.parse_export(&mut exports, *export)79.unwrap();80}8182// Now export all of this module's defined globals, memories,83// and instantiations under well-known names so we can inspect84// them after initialization.85for (i, ty, _) in module.defined_globals() {86if !ty.mutable {87continue;88}89let name = format!("__wizer_global_{i}");90exports.export(&name, wasm_encoder::ExportKind::Global, i);91defined_global_exports.push((i, name));92}93for (i, (j, _)) in module.defined_memories().enumerate() {94let name = format!("__wizer_memory_{i}");95exports.export(&name, wasm_encoder::ExportKind::Memory, j);96defined_memory_exports.push(name);97}9899encoder.section(&exports);100}101102// All other sections don't need instrumentation and can be copied103// over directly.104_other => {105encoder.section(section);106}107}108}109110module.defined_global_exports = Some(defined_global_exports);111module.defined_memory_exports = Some(defined_memory_exports);112113encoder.finish()114}115116117