diff --git a/crates/cranelift/src/compiled_function.rs b/crates/cranelift/src/compiled_function.rs index 451b41f3ab8f..b28f2d5349a6 100644 --- a/crates/cranelift/src/compiled_function.rs +++ b/crates/cranelift/src/compiled_function.rs @@ -6,7 +6,7 @@ use cranelift_codegen::{ isa::unwind::CfaUnwindInfo, isa::unwind::UnwindInfo, }; use wasmtime_environ::{ - FilePos, FrameStateSlotBuilder, InstructionAddressMap, PrimaryMap, TrapInformation, + FilePos, FrameStateSlotBuilder, InstructionAddressMap, ModulePC, PrimaryMap, TrapInformation, }; #[derive(Debug, Clone, PartialEq, Eq, Default)] @@ -67,8 +67,8 @@ pub struct CompiledFunction { metadata: CompiledFunctionMetadata, /// Debug metadata for the top-level function's state slot. pub debug_slot_descriptor: Option, - /// Debug breakpoint patches: Wasm PC, offset range in buffer. - pub breakpoint_patch_points: Vec<(u32, Range)>, + /// Debug breakpoint patches: module-relative Wasm PC, offset range in buffer. + pub breakpoint_patch_points: Vec<(ModulePC, Range)>, } impl CompiledFunction { @@ -120,7 +120,7 @@ impl CompiledFunction { // second-to-last tag will get the innermost Wasm PC (if // there are multiple nested frames due to inlining). assert!(tag.tags.len() >= 3); - let ir::DebugTag::User(wasm_pc) = tag.tags[tag.tags.len() - 2] else { + let ir::DebugTag::User(wasm_pc_raw) = tag.tags[tag.tags.len() - 2] else { panic!("invalid tag") }; @@ -128,7 +128,7 @@ impl CompiledFunction { let patchable_end = patchable_callsite.ret_addr; self.breakpoint_patch_points - .push((wasm_pc, patchable_start..patchable_end)); + .push((ModulePC::new(wasm_pc_raw), patchable_start..patchable_end)); tags.next(); patchable_callsites.next(); @@ -218,7 +218,7 @@ impl CompiledFunction { /// Returns an iterator over breakpoint patches for this function. /// /// Each tuple is (wasm PC, buffer offset range). - pub fn breakpoint_patches(&self) -> impl Iterator)> + '_ { + pub fn breakpoint_patches(&self) -> impl Iterator)> + '_ { self.breakpoint_patch_points.iter().cloned() } } diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs index cb0b22526e2b..b1ce4028cd33 100644 --- a/crates/cranelift/src/compiler.rs +++ b/crates/cranelift/src/compiler.rs @@ -37,7 +37,7 @@ use wasmtime_environ::{ Abi, AddressMapSection, BuiltinFunctionIndex, CacheStore, CompileError, CompiledFunctionBody, DefinedFuncIndex, FlagValue, FrameInstPos, FrameStackShape, FrameStateSlotBuilder, FrameTableBuilder, FuncKey, FunctionBodyData, FunctionLoc, HostCall, InliningCompiler, - ModuleTranslation, ModuleTypesBuilder, PtrSize, StackMapSection, StaticModuleIndex, + ModulePC, ModuleTranslation, ModuleTypesBuilder, PtrSize, StackMapSection, StaticModuleIndex, TrapEncodingBuilder, TrapSentinel, TripleExt, Tunables, WasmFuncType, WasmValType, prelude::*, }; use wasmtime_unwinder::ExceptionTableBuilder; @@ -520,7 +520,7 @@ impl wasmtime_environ::Compiler for Compiler { } } - let mut breakpoint_table = Vec::new(); + let mut breakpoint_table: Vec<(ModulePC, Range)> = Vec::new(); let mut nop_units = None; let mut ret = Vec::with_capacity(funcs.len()); @@ -1623,7 +1623,7 @@ fn clif_to_env_frame_tables<'a>( for frame_tags in tag_site.tags.chunks_exact(3) { let &[ ir::DebugTag::StackSlot(slot), - ir::DebugTag::User(wasm_pc), + ir::DebugTag::User(wasm_pc_raw), ir::DebugTag::User(stack_shape), ] = frame_tags else { @@ -1645,7 +1645,7 @@ fn clif_to_env_frame_tables<'a>( }); frames.push(( - wasm_pc, + ModulePC::new(wasm_pc_raw), frame_descriptor, FrameStackShape::from_raw(stack_shape), )); @@ -1669,8 +1669,8 @@ fn clif_to_env_frame_tables<'a>( /// Wasmtime's serialized metadata. fn clif_to_env_breakpoints( range: Range, - breakpoint_patches: impl Iterator)>, - patch_table: &mut Vec<(u32, Range)>, + breakpoint_patches: impl Iterator)>, + patch_table: &mut Vec<(ModulePC, Range)>, ) -> Result<()> { patch_table.extend(breakpoint_patches.map(|(wasm_pc, offset_range)| { let start = offset_range.start + u32::try_from(range.start).unwrap(); diff --git a/crates/cranelift/src/func_environ.rs b/crates/cranelift/src/func_environ.rs index b829988f8a8d..71d743d4792d 100644 --- a/crates/cranelift/src/func_environ.rs +++ b/crates/cranelift/src/func_environ.rs @@ -27,12 +27,12 @@ use std::mem; use wasmparser::{FuncValidator, Operator, WasmFeatures, WasmModuleResources}; use wasmtime_core::math::f64_cvt_to_int_bounds; use wasmtime_environ::{ - BuiltinFunctionIndex, DataIndex, DefinedFuncIndex, ElemIndex, EngineOrModuleTypeIndex, - FrameStateSlotBuilder, FrameValType, FuncIndex, FuncKey, GlobalConstValue, GlobalIndex, - IndexType, Memory, MemoryIndex, Module, ModuleInternedTypeIndex, ModuleTranslation, - ModuleTypesBuilder, PtrSize, Table, TableIndex, TagIndex, Tunables, TypeConvert, TypeIndex, - VMOffsets, WasmCompositeInnerType, WasmFuncType, WasmHeapTopType, WasmHeapType, WasmRefType, - WasmResult, WasmValType, + BuiltinFunctionIndex, ComponentPC, DataIndex, DefinedFuncIndex, ElemIndex, + EngineOrModuleTypeIndex, FrameStateSlotBuilder, FrameValType, FuncIndex, FuncKey, + GlobalConstValue, GlobalIndex, IndexType, Memory, MemoryIndex, Module, ModuleInternedTypeIndex, + ModuleTranslation, ModuleTypesBuilder, PtrSize, Table, TableIndex, TagIndex, Tunables, + TypeConvert, TypeIndex, VMOffsets, WasmCompositeInnerType, WasmFuncType, WasmHeapTopType, + WasmHeapType, WasmRefType, WasmResult, WasmValType, }; use wasmtime_environ::{FUNCREF_INIT_BIT, FUNCREF_MASK}; @@ -137,6 +137,11 @@ pub struct FuncEnvironment<'module_environment> { needs_gc_heap: bool, entities: WasmEntities, + /// The byte offset of the module's wasm binary within the outer + /// binary (e.g. a component). Used to make source locations in + /// guest-debug frame tables module-relative. + pub(crate) wasm_module_offset: u64, + /// Translation state at the given point. pub(crate) stacks: FuncTranslationStacks, @@ -291,6 +296,7 @@ impl<'module_environment> FuncEnvironment<'module_environment> { state_slot: None, next_srcloc: ir::SourceLoc::default(), + wasm_module_offset: translation.wasm_module_offset, } } @@ -1318,10 +1324,18 @@ impl<'module_environment> FuncEnvironment<'module_environment> { .last() .map(|s| s.raw()) .unwrap_or(u32::MAX); - let pc = srcloc.bits(); + // Convert component-relative srcloc to module-relative + // Wasm PC for the frame table. The srcloc on the builder + // remains component-relative for native DWARF and other + // purposes, but the frame table must be module-relative + // because the guest-debug API presents a purely core-Wasm + // view of the world where components are deconstructed + // into core Wasm modules. + let component_pc = ComponentPC::new(srcloc.bits()); + let module_pc = component_pc.to_module_pc(self.wasm_module_offset); vec![ ir::DebugTag::StackSlot(*slot), - ir::DebugTag::User(pc), + ir::DebugTag::User(module_pc.raw()), ir::DebugTag::User(stack_shape), ] } else { diff --git a/crates/debugger/src/host/opaque.rs b/crates/debugger/src/host/opaque.rs index 2c4d0013a45a..cce93b9d96c5 100644 --- a/crates/debugger/src/host/opaque.rs +++ b/crates/debugger/src/host/opaque.rs @@ -503,7 +503,7 @@ impl OpaqueDebugger for crate::Debuggee { .wasm_function_index_and_pc(&mut store) .map_err(|_| wit::Error::InvalidFrame)? .ok_or(wit::Error::NonWasmFrame)?; - Ok((func.as_u32(), pc)) + Ok((func.as_u32(), pc.raw())) }) .await? } @@ -556,7 +556,7 @@ impl OpaqueDebugger for crate::Debuggee { store .edit_breakpoints() .expect("guest debugging is enabled") - .add_breakpoint(&module, pc) + .add_breakpoint(&module, wasmtime::ModulePC::new(pc)) .map_err(|_| wit::Error::InvalidPc)?; Ok(()) }) @@ -568,7 +568,7 @@ impl OpaqueDebugger for crate::Debuggee { store .edit_breakpoints() .expect("guest debugging is enabled") - .remove_breakpoint(&module, pc) + .remove_breakpoint(&module, wasmtime::ModulePC::new(pc)) .map_err(|_| wit::Error::InvalidPc)?; Ok(()) }) diff --git a/crates/debugger/src/lib.rs b/crates/debugger/src/lib.rs index 7877cb35720f..5aad12d0fed3 100644 --- a/crates/debugger/src/lib.rs +++ b/crates/debugger/src/lib.rs @@ -586,7 +586,8 @@ mod test { .wasm_function_index_and_pc(&mut store) .unwrap() .unwrap() - .1, + .1 + .raw(), 36 ); assert_eq!(frame.num_locals(&mut store).unwrap(), 2); @@ -618,7 +619,8 @@ mod test { .wasm_function_index_and_pc(&mut store) .unwrap() .unwrap() - .1, + .1 + .raw(), 38 ); assert_eq!(frame.num_locals(&mut store).unwrap(), 2); @@ -651,7 +653,8 @@ mod test { .wasm_function_index_and_pc(&mut store) .unwrap() .unwrap() - .1, + .1 + .raw(), 40 ); assert_eq!(frame.num_locals(&mut store).unwrap(), 2); @@ -685,7 +688,8 @@ mod test { .wasm_function_index_and_pc(&mut store) .unwrap() .unwrap() - .1, + .1 + .raw(), 41 ); assert_eq!(frame.num_locals(&mut store).unwrap(), 2); diff --git a/crates/environ/src/address_map.rs b/crates/environ/src/address_map.rs index 5dda8e634047..8d1fd2d48bfd 100644 --- a/crates/environ/src/address_map.rs +++ b/crates/environ/src/address_map.rs @@ -1,5 +1,6 @@ //! Data structures to provide transformation of the source +use core::fmt; use object::{Bytes, LittleEndian, U32}; use serde_derive::{Deserialize, Serialize}; @@ -61,6 +62,85 @@ impl Default for FilePos { } } +/// A Wasm bytecode offset relative to the start of a component (or +/// top-level module) binary. +/// +/// When compiling a component, the Wasm parser returns source +/// positions relative to the entire component binary. This type +/// captures that convention. Use +/// [`ComponentPC::to_module_pc`] to convert to a +/// [`ModulePC`] given the byte offset of the module within the +/// component. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ComponentPC(u32); + +impl ComponentPC { + /// Create a new component-relative PC from a raw offset. + pub fn new(offset: u32) -> Self { + Self(offset) + } + + /// Get the raw u32 offset. + pub fn raw(self) -> u32 { + self.0 + } + + /// Convert to a module-relative PC by subtracting the byte offset + /// of the module within the component binary. + pub fn to_module_pc(self, wasm_module_offset: u64) -> ModulePC { + let offset = u32::try_from(wasm_module_offset).unwrap(); + ModulePC(self.0 - offset) + } +} + +impl fmt::Debug for ComponentPC { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ComponentPC({:#x})", self.0) + } +} + +impl fmt::Display for ComponentPC { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:#x}", self.0) + } +} + +/// A Wasm bytecode offset relative to the start of a core Wasm +/// module binary. +/// +/// In the guest-debug system, PCs are always module-relative because +/// the debugger presents a core-Wasm view of the world where +/// components are deconstructed into individual core Wasm modules. +/// +/// For standalone (non-component) modules, `ModulePC` and +/// [`ComponentPC`] values are numerically identical. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ModulePC(u32); + +impl ModulePC { + /// Create a new module-relative PC from a raw offset. + pub fn new(offset: u32) -> Self { + Self(offset) + } + + /// Get the raw u32 offset. + pub fn raw(self) -> u32 { + self.0 + } +} + +impl fmt::Debug for ModulePC { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ModulePC({:#x})", self.0) + } +} + +impl fmt::Display for ModulePC { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:#x}", self.0) + } +} + /// Parse an `ELF_WASMTIME_ADDRMAP` section, returning the slice of code offsets /// and the slice of associated file positions for each offset. fn parse_address_map(section: &[u8]) -> Option<(&[U32], &[U32])> { diff --git a/crates/environ/src/compile/frame_table.rs b/crates/environ/src/compile/frame_table.rs index 68ed97bc2e7c..f3288f08f121 100644 --- a/crates/environ/src/compile/frame_table.rs +++ b/crates/environ/src/compile/frame_table.rs @@ -10,7 +10,7 @@ use crate::{ FrameInstPos, FrameStackShape, FrameStateSlotOffset, FrameTableDescriptorIndex, FrameValType, - FuncKey, WasmHeapTopType, WasmValType, prelude::*, + FuncKey, ModulePC, WasmHeapTopType, WasmValType, prelude::*, }; use object::{LittleEndian, U32}; use std::collections::{HashMap, hash_map::Entry}; @@ -278,9 +278,9 @@ impl FrameTableBuilder { &mut self, native_pc: u32, pos: FrameInstPos, - // For each frame: Wasm PC, frame descriptor, stack shape - // within the frame descriptor. - frames: &[(u32, FrameTableDescriptorIndex, FrameStackShape)], + // For each frame: module-relative Wasm PC, frame descriptor, + // stack shape within the frame descriptor. + frames: &[(ModulePC, FrameTableDescriptorIndex, FrameStackShape)], ) { let pc_and_pos = FrameInstPos::encode(native_pc, pos); // If we already have a program point record at this PC, @@ -300,11 +300,12 @@ impl FrameTableBuilder { .push(U32::new(LittleEndian, start)); for (i, &(wasm_pc, frame_descriptor, stack_shape)) in frames.iter().enumerate() { - debug_assert!(wasm_pc < 0x8000_0000); + let wasm_pc_raw = wasm_pc.raw(); + debug_assert!(wasm_pc_raw < 0x8000_0000); let not_last = i < (frames.len() - 1); - let wasm_pc = wasm_pc | if not_last { 0x8000_0000 } else { 0 }; + let wasm_pc_raw = wasm_pc_raw | if not_last { 0x8000_0000 } else { 0 }; self.progpoint_descriptor_data - .push(U32::new(LittleEndian, wasm_pc)); + .push(U32::new(LittleEndian, wasm_pc_raw)); self.progpoint_descriptor_data .push(U32::new(LittleEndian, frame_descriptor.0)); self.progpoint_descriptor_data @@ -313,8 +314,14 @@ impl FrameTableBuilder { } /// Add one breakpoint patch. - pub fn add_breakpoint_patch(&mut self, wasm_pc: u32, patch_start_native_pc: u32, patch: &[u8]) { - self.breakpoint_pcs.push(U32::new(LittleEndian, wasm_pc)); + pub fn add_breakpoint_patch( + &mut self, + wasm_pc: ModulePC, + patch_start_native_pc: u32, + patch: &[u8], + ) { + self.breakpoint_pcs + .push(U32::new(LittleEndian, wasm_pc.raw())); self.breakpoint_patch_offsets .push(U32::new(LittleEndian, patch_start_native_pc)); self.breakpoint_patch_data.extend(patch.iter().cloned()); diff --git a/crates/environ/src/compile/module_environ.rs b/crates/environ/src/compile/module_environ.rs index 2ecacbb21d71..59ddb758cf83 100644 --- a/crates/environ/src/compile/module_environ.rs +++ b/crates/environ/src/compile/module_environ.rs @@ -53,6 +53,12 @@ pub struct ModuleTranslation<'data> { /// themselves. pub wasm: &'data [u8], + /// The byte offset of this module's Wasm binary within the outer + /// binary (e.g. a component). For standalone modules this is 0. + /// This is used to convert component-relative source locations to + /// module-relative source locations. + pub wasm_module_offset: u64, + /// References to the function bodies. pub function_body_inputs: PrimaryMap>, @@ -118,6 +124,7 @@ impl<'data> ModuleTranslation<'data> { Self { module: Module::new(module_index), wasm: &[], + wasm_module_offset: 0, function_body_inputs: PrimaryMap::default(), known_imported_functions: SecondaryMap::default(), exported_signatures: Vec::default(), diff --git a/crates/environ/src/component/translate.rs b/crates/environ/src/component/translate.rs index 8855a848a660..9fef9cce7bf8 100644 --- a/crates/environ/src/component/translate.rs +++ b/crates/environ/src/component/translate.rs @@ -1199,7 +1199,7 @@ impl<'a, 'data> Translator<'a, 'data> { let index = self.validator.types(0).unwrap().module_count(); self.validator.module_section(&unchecked_range)?; let static_module_index = self.static_modules.next_key(); - let translation = ModuleEnvironment::new( + let mut translation = ModuleEnvironment::new( self.tunables, self.validator, self.types.module_types_builder(), @@ -1219,6 +1219,8 @@ impl<'a, 'data> Translator<'a, 'data> { .context("wasm component contains an invalid module section") })?, )?; + + translation.wasm_module_offset = u64::try_from(unchecked_range.start).unwrap(); let static_module_index2 = self.static_modules.push(translation); assert_eq!(static_module_index, static_module_index2); let types = self.validator.types(0).unwrap(); diff --git a/crates/environ/src/frame_table.rs b/crates/environ/src/frame_table.rs index 76bc8538caa2..c23a746b2653 100644 --- a/crates/environ/src/frame_table.rs +++ b/crates/environ/src/frame_table.rs @@ -4,7 +4,7 @@ //! section in a compiled artifact as produced by //! [`crate::compile::FrameTableBuilder`]. -use crate::FuncKey; +use crate::{FuncKey, ModulePC}; use alloc::vec::Vec; use object::{Bytes, LittleEndian, U32}; @@ -179,7 +179,7 @@ impl<'a> FrameTable<'a> { &self, search_pc: u32, search_pos: FrameInstPos, - ) -> Option> { + ) -> Option> { let key = FrameInstPos::encode(search_pc, search_pos); let index = match self .progpoint_pcs @@ -201,7 +201,7 @@ impl<'a> FrameTable<'a> { Item = ( u32, FrameInstPos, - Vec<(u32, FrameTableDescriptorIndex, FrameStackShape)>, + Vec<(ModulePC, FrameTableDescriptorIndex, FrameStackShape)>, ), > + 'a { self.progpoint_pcs.iter().enumerate().map(move |(i, pc)| { @@ -218,7 +218,7 @@ impl<'a> FrameTable<'a> { fn program_point_frame_iter( &self, index: usize, - ) -> impl Iterator { + ) -> impl Iterator { let offset = usize::try_from(self.progpoint_descriptor_offsets[index].get(LittleEndian)).unwrap(); let mut data = &self.progpoint_descriptor_data[offset..]; @@ -227,12 +227,12 @@ impl<'a> FrameTable<'a> { if data.len() < 3 { return None; } - let wasm_pc = data[0].get(LittleEndian); + let wasm_pc_raw = data[0].get(LittleEndian); let frame_descriptor = FrameTableDescriptorIndex(data[1].get(LittleEndian)); let stack_shape = FrameStackShape(data[2].get(LittleEndian)); data = &data[3..]; - let not_last = wasm_pc & 0x8000_0000 != 0; - let wasm_pc = wasm_pc & 0x7fff_ffff; + let not_last = wasm_pc_raw & 0x8000_0000 != 0; + let wasm_pc = ModulePC::new(wasm_pc_raw & 0x7fff_ffff); if !not_last { data = &[]; } @@ -265,24 +265,25 @@ impl<'a> FrameTable<'a> { /// Find a list of breakpoint patches for a given Wasm PC. pub fn lookup_breakpoint_patches_by_pc( &self, - pc: u32, + pc: ModulePC, ) -> impl Iterator> + '_ { // Find *some* entry with a matching Wasm PC. Note that there // may be multiple entries for one PC. + let pc_raw = pc.raw(); let range = match self .breakpoint_pcs - .binary_search_by_key(&pc, |p| p.get(LittleEndian)) + .binary_search_by_key(&pc_raw, |p| p.get(LittleEndian)) { Ok(mut i) => { // Scan backward to first index with this PC. - while i > 0 && self.breakpoint_pcs[i - 1].get(LittleEndian) == pc { + while i > 0 && self.breakpoint_pcs[i - 1].get(LittleEndian) == pc_raw { i -= 1; } // Scan forward to find the end of the range. let mut end = i; while end < self.breakpoint_pcs.len() - && self.breakpoint_pcs[end].get(LittleEndian) == pc + && self.breakpoint_pcs[end].get(LittleEndian) == pc_raw { end += 1; } @@ -296,15 +297,15 @@ impl<'a> FrameTable<'a> { } /// Find the nearest breakpoint PC at or after the given PC. - pub fn nearest_breakpoint(&self, pc: u32) -> Option { + pub fn nearest_breakpoint(&self, pc: ModulePC) -> Option { match self .breakpoint_pcs - .binary_search_by_key(&pc, |p| p.get(LittleEndian)) + .binary_search_by_key(&pc.raw(), |p| p.get(LittleEndian)) { Ok(_) => Some(pc), Err(i) => { if i < self.breakpoint_pcs.len() { - Some(self.breakpoint_pcs[i].get(LittleEndian)) + Some(ModulePC::new(self.breakpoint_pcs[i].get(LittleEndian))) } else { None } @@ -314,12 +315,12 @@ impl<'a> FrameTable<'a> { /// Return an iterator over all breakpoint patches. /// - /// Returned tuples are (Wasm PC, breakpoint data). + /// Returned tuples are (module-relative Wasm PC, breakpoint data). pub fn breakpoint_patches( &self, - ) -> impl Iterator)> + '_ { + ) -> impl Iterator)> + '_ { self.breakpoint_pcs.iter().enumerate().map(|(i, wasm_pc)| { - let wasm_pc = wasm_pc.get(LittleEndian); + let wasm_pc = ModulePC::new(wasm_pc.get(LittleEndian)); let data = self.breakpoint_patch(i); (wasm_pc, data) }) diff --git a/crates/gdbstub-component/src/target.rs b/crates/gdbstub-component/src/target.rs index 58b2c1fd70f6..c54cf9e58e3a 100644 --- a/crates/gdbstub-component/src/target.rs +++ b/crates/gdbstub-component/src/target.rs @@ -306,9 +306,12 @@ impl<'a> Libraries for Debugger<'a> { buf: &mut [u8], ) -> TargetResult { let mut xml = String::from(""); - for addr in self.addr_space.module_base_addrs() { + for (i, addr) in self.addr_space.module_base_addrs().enumerate() { + // Each library entry needs a unique name; LLDB's + // DynamicLoader deduplicates by name, so using the same + // name for all modules causes only the first to be loaded. xml.push_str(&format!( - "
", + "
", addr.as_raw() )); } diff --git a/crates/wasmtime/src/runtime/debug.rs b/crates/wasmtime/src/runtime/debug.rs index 9888d6582b4a..2f40b92a0de0 100644 --- a/crates/wasmtime/src/runtime/debug.rs +++ b/crates/wasmtime/src/runtime/debug.rs @@ -20,6 +20,8 @@ use alloc::vec::Vec; use core::{ffi::c_void, ptr::NonNull}; #[cfg(feature = "gc")] use wasmtime_environ::FrameTable; +// Re-export ModulePC so downstream crates can use it. +pub use wasmtime_environ::ModulePC; use wasmtime_environ::{ DefinedFuncIndex, EntityIndex, FrameInstPos, FrameStackShape, FrameStateSlot, FrameStateSlotOffset, FrameTableBreakpointData, FrameTableDescriptorIndex, FrameValType, @@ -537,13 +539,13 @@ impl FrameHandle { } /// Get the raw function index associated with the current frame, and the - /// PC as an offset within its code section, if it is a Wasm - /// function directly from the given `Module` (rather than a - /// trampoline). + /// module-relative PC as an offset within the module binary, if + /// this is a Wasm function directly from the given `Module` + /// (rather than a trampoline). pub fn wasm_function_index_and_pc( &self, mut store: impl AsContextMut, - ) -> Result> { + ) -> Result> { let mut store = store.as_context_mut(); let frame_data = self.frame_data(store.0.as_store_opaque())?; let FuncKey::DefinedWasmFunction(module, func) = frame_data.func_key else { @@ -659,8 +661,8 @@ impl FrameDataCache { /// This represents one frame as produced by the progpoint lookup /// (Wasm PC, frame descriptor index, stack shape). struct VirtualFrame { - /// The Wasm PC for this frame. - wasm_pc: u32, + /// The module-relative Wasm PC for this frame. + wasm_pc: ModulePC, /// The frame descriptor for this frame. frame_descriptor: FrameTableDescriptorIndex, /// The stack shape for this frame. @@ -697,7 +699,7 @@ impl VirtualFrame { struct FrameData { slot_to_fp_offset: usize, func_key: FuncKey, - wasm_pc: u32, + wasm_pc: ModulePC, /// Shape of locals in this frame. /// /// We need to store this locally because `FrameView` cannot @@ -959,15 +961,15 @@ pub(crate) struct BreakpointState { pub struct Breakpoint { /// Reference to the module in which we are setting the breakpoint. pub module: Module, - /// Wasm PC offset within the module. - pub pc: u32, + /// Module-relative Wasm PC offset. + pub pc: ModulePC, } #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -struct BreakpointKey(CompiledModuleId, u32); +struct BreakpointKey(CompiledModuleId, ModulePC); impl BreakpointKey { - fn from_raw(module: &Module, pc: u32) -> BreakpointKey { + fn from_raw(module: &Module, pc: ModulePC) -> BreakpointKey { BreakpointKey(module.id(), pc) } @@ -1081,7 +1083,7 @@ impl<'a> BreakpointEdit<'a> { /// available opcode PC. /// /// No effect if the breakpoint is already set. - pub fn add_breakpoint(&mut self, module: &Module, pc: u32) -> Result<()> { + pub fn add_breakpoint(&mut self, module: &Module, pc: ModulePC) -> Result<()> { let frame_table = module .frame_table() .expect("Frame table must be present when guest-debug is enabled"); @@ -1113,7 +1115,7 @@ impl<'a> BreakpointEdit<'a> { /// that module. /// /// No effect if the breakpoint was not set. - pub fn remove_breakpoint(&mut self, module: &Module, pc: u32) -> Result<()> { + pub fn remove_breakpoint(&mut self, module: &Module, pc: ModulePC) -> Result<()> { let requested_key = BreakpointKey::from_raw(module, pc); let actual_key = self .state diff --git a/src/commands/objdump.rs b/src/commands/objdump.rs index f85e513340d7..8cc28e57e155 100644 --- a/src/commands/objdump.rs +++ b/src/commands/objdump.rs @@ -16,7 +16,7 @@ use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; use wasmtime::{Engine, Result, bail, error::Context as _}; use wasmtime_environ::{ FilePos, FrameInstPos, FrameStackShape, FrameStateSlot, FrameTable, FrameTableDescriptorIndex, - StackMap, Trap, obj, + ModulePC, StackMap, Trap, obj, }; use wasmtime_unwinder::{ExceptionHandler, ExceptionTable}; @@ -573,7 +573,7 @@ struct Decorator<'a> { Item = ( u32, FrameInstPos, - Vec<(u32, FrameTableDescriptorIndex, FrameStackShape)>, + Vec<(ModulePC, FrameTableDescriptorIndex, FrameStackShape)>, ), > + 'a, >, @@ -583,7 +583,7 @@ struct Decorator<'a> { // Breakpoint table, sorted by native offset instead so we can // display inline with disassembly (the table in the image is // sorted by Wasm PC). - breakpoints: Peekable)>>>, + breakpoints: Peekable)>>>, frame_table_descriptors: Option>, } diff --git a/tests/all/debug.rs b/tests/all/debug.rs index 6f4ce8342eac..7f8b4b2062af 100644 --- a/tests/all/debug.rs +++ b/tests/all/debug.rs @@ -4,7 +4,8 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; use wasmtime::{ AsContextMut, Caller, Config, DebugEvent, DebugHandler, Engine, Extern, FrameHandle, Func, - Global, GlobalType, Instance, Module, Mutability, Store, StoreContextMut, Val, ValType, + Global, GlobalType, Instance, Module, ModulePC, Mutability, Store, StoreContextMut, Val, + ValType, }; use crate::async_functions::PollOnce; @@ -108,7 +109,11 @@ fn stack_values_two_frames() -> wasmtime::Result<()> { 1 ); assert_eq!( - stack.wasm_function_index_and_pc(&mut caller)?.unwrap().1, + stack + .wasm_function_index_and_pc(&mut caller)? + .unwrap() + .1 + .raw(), 67 ); @@ -129,7 +134,11 @@ fn stack_values_two_frames() -> wasmtime::Result<()> { 0 ); assert_eq!( - stack.wasm_function_index_and_pc(&mut caller)?.unwrap().1, + stack + .wasm_function_index_and_pc(&mut caller)? + .unwrap() + .1 + .raw(), 57 ); @@ -289,7 +298,11 @@ fn stack_values_two_activations() -> wasmtime::Result<()> { 0 ); assert_eq!( - stack.wasm_function_index_and_pc(&mut caller)?.unwrap().1, + stack + .wasm_function_index_and_pc(&mut caller)? + .unwrap() + .1 + .raw(), 58 ); assert!(Module::same( @@ -317,7 +330,11 @@ fn stack_values_two_activations() -> wasmtime::Result<()> { 0 ); assert_eq!( - stack.wasm_function_index_and_pc(&mut caller)?.unwrap().1, + stack + .wasm_function_index_and_pc(&mut caller)? + .unwrap() + .1 + .raw(), 58 ); assert!(Module::same( @@ -709,7 +726,7 @@ async fn hostcall_trap_events() -> wasmtime::Result<()> { wasmtime::DebugEvent::Trap(wasmtime_environ::Trap::IntegerDivisionByZero) => { let frame = store.debug_exit_frames().next().unwrap(); let (_func, pc) = frame.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); - assert_eq!(pc, 0x26); + assert_eq!(pc.raw(), 0x26); } } ); @@ -799,7 +816,7 @@ async fn breakpoint_events() -> wasmtime::Result<()> { assert_eq!(stack.local(&mut store, 1).unwrap().unwrap_i32(), 2); let (func, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); assert_eq!(func.as_u32(), 0); - assert_eq!(pc, 0x28); + assert_eq!(pc.raw(), 0x28); let stack = stack.parent(&mut store).unwrap(); assert!(stack.is_none()); } @@ -811,7 +828,7 @@ async fn breakpoint_events() -> wasmtime::Result<()> { store .edit_breakpoints() .unwrap() - .add_breakpoint(&module, 0x28)?; + .add_breakpoint(&module, ModulePC::new(0x28))?; let instance = Instance::new_async(&mut store, &module, &[]).await?; let func = instance.get_func(&mut store, "main").unwrap(); @@ -824,12 +841,12 @@ async fn breakpoint_events() -> wasmtime::Result<()> { let breakpoints = store.breakpoints().unwrap().collect::>(); assert_eq!(breakpoints.len(), 1); assert!(Module::same(&breakpoints[0].module, &module)); - assert_eq!(breakpoints[0].pc, 0x28); + assert_eq!(breakpoints[0].pc, ModulePC::new(0x28)); store .edit_breakpoints() .unwrap() - .remove_breakpoint(&module, 0x28)?; + .remove_breakpoint(&module, ModulePC::new(0x28))?; func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) .await?; assert_eq!(counter.load(Ordering::Relaxed), 1); // Should not have incremented from above. @@ -846,7 +863,7 @@ async fn breakpoint_events() -> wasmtime::Result<()> { wasmtime::DebugEvent::Breakpoint => { let stack = store.debug_exit_frames().next().unwrap(); let (_, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); - assert_eq!(pc, 0x24); + assert_eq!(pc.raw(), 0x24); } }, { @@ -854,7 +871,7 @@ async fn breakpoint_events() -> wasmtime::Result<()> { wasmtime::DebugEvent::Breakpoint => { let stack = store.debug_exit_frames().next().unwrap(); let (_, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); - assert_eq!(pc, 0x26); + assert_eq!(pc.raw(), 0x26); } }, { @@ -862,7 +879,7 @@ async fn breakpoint_events() -> wasmtime::Result<()> { wasmtime::DebugEvent::Breakpoint => { let stack = store.debug_exit_frames().next().unwrap(); let (_, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); - assert_eq!(pc, 0x28); + assert_eq!(pc.raw(), 0x28); } }, { @@ -870,7 +887,7 @@ async fn breakpoint_events() -> wasmtime::Result<()> { wasmtime::DebugEvent::Breakpoint => { let stack = store.debug_exit_frames().next().unwrap(); let (_, pc) = stack.wasm_function_index_and_pc(&mut store).unwrap().unwrap(); - assert_eq!(pc, 0x29); + assert_eq!(pc.raw(), 0x29); } } ); @@ -886,7 +903,7 @@ async fn breakpoint_events() -> wasmtime::Result<()> { store .edit_breakpoints() .unwrap() - .add_breakpoint(&module, 0x28) + .add_breakpoint(&module, ModulePC::new(0x28)) .unwrap(); // Now disable single-stepping. The single breakpoint set above @@ -949,7 +966,7 @@ async fn breakpoints_in_inlined_code() -> wasmtime::Result<()> { store .edit_breakpoints() .unwrap() - .add_breakpoint(&module, 0x2d)?; // `i32.add` in `$f`. + .add_breakpoint(&module, ModulePC::new(0x2d))?; // `i32.add` in `$f`. let instance = Instance::new_async(&mut store, &module, &[]).await?; let func_main = instance.get_func(&mut store, "main").unwrap(); @@ -1469,7 +1486,7 @@ async fn breakpoint_slips_to_first_opcode() -> wasmtime::Result<()> { assert_eq!(func.as_u32(), 0); // The breakpoint should fire at the first opcode // (0x24), not at the function body start (0x23). - assert_eq!(pc, 0x24); + assert_eq!(pc.raw(), 0x24); } } ); @@ -1480,7 +1497,7 @@ async fn breakpoint_slips_to_first_opcode() -> wasmtime::Result<()> { store .edit_breakpoints() .unwrap() - .add_breakpoint(&module, 0x23)?; + .add_breakpoint(&module, ModulePC::new(0x23))?; let instance = Instance::new_async(&mut store, &module, &[]).await?; let func = instance.get_func(&mut store, "main").unwrap(); @@ -1493,13 +1510,13 @@ async fn breakpoint_slips_to_first_opcode() -> wasmtime::Result<()> { // The actual breakpoint stored should be at the slipped PC. let breakpoints = store.breakpoints().unwrap().collect::>(); assert_eq!(breakpoints.len(), 1); - assert_eq!(breakpoints[0].pc, 0x24); + assert_eq!(breakpoints[0].pc, ModulePC::new(0x24)); // Removing with the originally requested PC should work. store .edit_breakpoints() .unwrap() - .remove_breakpoint(&module, 0x23)?; + .remove_breakpoint(&module, ModulePC::new(0x23))?; func.call_async(&mut store, &[Val::I32(1), Val::I32(2)], &mut results) .await?; // Counter should not have incremented now that we removed the @@ -1508,3 +1525,103 @@ async fn breakpoint_slips_to_first_opcode() -> wasmtime::Result<()> { Ok(()) } + +#[tokio::test] +#[cfg_attr(miri, ignore)] +async fn component_module_relative_breakpoint_pcs() -> wasmtime::Result<()> { + use wasmtime::component::{Component, Linker}; + + let _ = env_logger::try_init(); + + let m1_body = r#"(func (export "f1") (param i32 i32) (result i32) + local.get 0 + local.get 1 + i32.add)"#; + let m2_body = r#"(func (export "f2") (param i32 i32) (result i32) + local.get 0 + local.get 1 + i32.mul)"#; + + let _m1_wasm = wat::parse_str(&format!("(module {m1_body})"))?; + let _m2_wasm = wat::parse_str(&format!("(module {m2_body})"))?; + + let component_wat = format!( + r#"(component + (core module $m1 {m1_body}) + (core instance $i1 (instantiate $m1)) + (core module $m2 {m2_body}) + (core instance $i2 (instantiate $m2)) + (func (export "f1") (param "a" s32) (param "b" s32) (result s32) + (canon lift (core func $i1 "f1"))) + (func (export "f2") (param "a" s32) (param "b" s32) (result s32) + (canon lift (core func $i2 "f2"))))"#, + ); + + let mut config = Config::default(); + config.guest_debug(true); + let engine = Engine::new(&config)?; + + let component = Component::new(&engine, &component_wat)?; + let linker: Linker<()> = Linker::new(&engine); + let mut store = Store::new(&engine, ()); + + let instance = linker.instantiate_async(&mut store, &component).await?; + + let modules = store.debug_all_modules(); + assert_eq!(modules.len(), 2); + + // The i32.add / i32.mul instruction is at module-relative offset + // 0x26 in both modules. + let breakpoint_pc = ModulePC::new(0x26); + + // Record breakpoint PCs seen in each event. + let observed_pcs = Arc::new(Mutex::new(Vec::<(u32, u32)>::new())); + let observed_pcs_clone = observed_pcs.clone(); + + #[derive(Clone)] + struct D(Arc>>); + impl DebugHandler for D { + type Data = (); + fn handle( + &self, + mut store: StoreContextMut<'_, ()>, + _event: DebugEvent<'_>, + ) -> impl std::future::Future + Send { + let frame = store.debug_exit_frames().next().unwrap(); + let (func, pc) = frame + .wasm_function_index_and_pc(&mut store) + .unwrap() + .unwrap(); + self.0.lock().unwrap().push((func.as_u32(), pc.raw())); + async {} + } + } + store.set_debug_handler(D(observed_pcs_clone)); + + // Set breakpoints at the same module-relative PC (0x26) in both + // modules. + store + .edit_breakpoints() + .unwrap() + .add_breakpoint(&modules[0], breakpoint_pc)?; + store + .edit_breakpoints() + .unwrap() + .add_breakpoint(&modules[1], breakpoint_pc)?; + + let f1 = instance.get_typed_func::<(i32, i32), (i32,)>(&mut store, "f1")?; + let (result,) = f1.call_async(&mut store, (3, 5)).await?; + assert_eq!(result, 8); + + let f2 = instance.get_typed_func::<(i32, i32), (i32,)>(&mut store, "f2")?; + let (result,) = f2.call_async(&mut store, (3, 5)).await?; + assert_eq!(result, 15); + + // Both breakpoint PCs should be 0x26 (module-relative). + let pcs = observed_pcs.lock().unwrap(); + assert_eq!(pcs.len(), 2); + assert_eq!(pcs[0], (0, 0x26)); + assert_eq!(pcs[1], (0, 0x26)); + + Ok(()) +} diff --git a/tests/all/pulley.rs b/tests/all/pulley.rs index a35e4d6fbf40..8845044111bc 100644 --- a/tests/all/pulley.rs +++ b/tests/all/pulley.rs @@ -146,7 +146,7 @@ fn pulley_provenance_test() -> Result<()> { .unwrap() .unwrap(); assert_eq!(caller_func.as_u32(), 3); - assert_eq!(pc, 418); + assert_eq!(pc.raw(), 418); let parent_frame = caller_frame.parent(&mut caller).unwrap(); assert!(parent_frame.is_none()); diff --git a/tests/disas/debug-exceptions.wat b/tests/disas/debug-exceptions.wat index 440b63fdae59..8343daafcf2f 100644 --- a/tests/disas/debug-exceptions.wat +++ b/tests/disas/debug-exceptions.wat @@ -35,28 +35,28 @@ ;; mov x0, x2 ;; stur x2, [sp, #0x10] ;; nop -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 53, slot at FP-0xc0, locals , stack -;; ╰─╼ breakpoint patch: wasm PC 53, patch bytes [38, 1, 0, 148] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x35, slot at FP-0xc0, locals , stack +;; ╰─╼ breakpoint patch: wasm PC 0x35, patch bytes [38, 1, 0, 148] ;; ldur x0, [sp, #0x10] ;; nop -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 55, slot at FP-0xc0, locals , stack -;; ╰─╼ breakpoint patch: wasm PC 55, patch bytes [36, 1, 0, 148] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x37, slot at FP-0xc0, locals , stack +;; ╰─╼ breakpoint patch: wasm PC 0x37, patch bytes [36, 1, 0, 148] ;; ldur x0, [sp, #0x10] ;; nop -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 61, slot at FP-0xc0, locals , stack -;; ╰─╼ breakpoint patch: wasm PC 61, patch bytes [34, 1, 0, 148] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x3d, slot at FP-0xc0, locals , stack +;; ╰─╼ breakpoint patch: wasm PC 0x3d, patch bytes [34, 1, 0, 148] ;; mov w19, #0x2a ;; stur w19, [sp, #8] ;; nop -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 63, slot at FP-0xc0, locals , stack I32 @ slot+0x8 -;; ╰─╼ breakpoint patch: wasm PC 63, patch bytes [31, 1, 0, 148] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x3f, slot at FP-0xc0, locals , stack I32 @ slot+0x8 +;; ╰─╼ breakpoint patch: wasm PC 0x3f, patch bytes [31, 1, 0, 148] ;; nop -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 64, slot at FP-0xc0, locals , stack -;; ╰─╼ breakpoint patch: wasm PC 64, patch bytes [30, 1, 0, 148] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x40, slot at FP-0xc0, locals , stack +;; ╰─╼ breakpoint patch: wasm PC 0x40, patch bytes [30, 1, 0, 148] ;; stur w19, [sp, #8] ;; nop -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 66, slot at FP-0xc0, locals , stack I32 @ slot+0x8 -;; ╰─╼ breakpoint patch: wasm PC 66, patch bytes [28, 1, 0, 148] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x42, slot at FP-0xc0, locals , stack I32 @ slot+0x8 +;; ╰─╼ breakpoint patch: wasm PC 0x42, patch bytes [28, 1, 0, 148] ;; ldur x2, [sp, #0x10] ;; bl #0x448 ;; 84: mov x20, x2 @@ -89,7 +89,7 @@ ;; bl #0x3dc ;; ec: ldur x2, [sp, #0x10] ;; bl #0x414 -;; ╰─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 66, slot at FP-0xc0, locals , stack I32 @ slot+0x8 +;; ╰─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x42, slot at FP-0xc0, locals , stack I32 @ slot+0x8 ;; f4: .byte 0x1f, 0xc1, 0x00, 0x00 ;; mov x2, x0 ;; mov w3, w2 @@ -110,21 +110,21 @@ ;; stur w0, [sp, #8] ;; ldur x0, [sp, #0x10] ;; nop -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 72, slot at FP-0xc0, locals , stack I32 @ slot+0x8 -;; ╰─╼ breakpoint patch: wasm PC 72, patch bytes [234, 0, 0, 148] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x48, slot at FP-0xc0, locals , stack I32 @ slot+0x8 +;; ╰─╼ breakpoint patch: wasm PC 0x48, patch bytes [234, 0, 0, 148] ;; ldur x1, [sp, #0x10] ;; ldr x0, [x1, #0x30] ;; ldr x2, [x1, #0x40] ;; ldur x3, [sp, #0x10] ;; blr x0 -;; ╰─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 74, slot at FP-0xc0, locals , stack I32 @ slot+0x8 +;; ╰─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x4a, slot at FP-0xc0, locals , stack I32 @ slot+0x8 ;; 158: ldur x0, [sp, #0x10] ;; nop -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 74, slot at FP-0xc0, locals , stack I32 @ slot+0x8 -;; ╰─╼ breakpoint patch: wasm PC 74, patch bytes [227, 0, 0, 148] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x4a, slot at FP-0xc0, locals , stack I32 @ slot+0x8 +;; ╰─╼ breakpoint patch: wasm PC 0x4a, patch bytes [227, 0, 0, 148] ;; nop -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 75, slot at FP-0xc0, locals , stack -;; ╰─╼ breakpoint patch: wasm PC 75, patch bytes [226, 0, 0, 148] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x4b, slot at FP-0xc0, locals , stack +;; ╰─╼ breakpoint patch: wasm PC 0x4b, patch bytes [226, 0, 0, 148] ;; add sp, sp, #0x30 ;; ldp d8, d9, [sp], #0x10 ;; ldp d10, d11, [sp], #0x10 @@ -142,7 +142,7 @@ ;; 19c: bl #0x3dc ;; 1a0: ldur x2, [sp, #0x10] ;; 1a4: bl #0x414 -;; ╰─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 52, slot at FP-0xc0, locals , stack +;; ╰─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x34, slot at FP-0xc0, locals , stack ;; 1a8: .byte 0x1f, 0xc1, 0x00, 0x00 ;; 1ac: .byte 0x1f, 0xc1, 0x00, 0x00 ;; 1b0: .byte 0x1f, 0xc1, 0x00, 0x00 diff --git a/tests/disas/debug.wat b/tests/disas/debug.wat index e40e07208cd9..2a8663ea0aa8 100644 --- a/tests/disas/debug.wat +++ b/tests/disas/debug.wat @@ -22,21 +22,21 @@ ;; jb 0x62 ;; 29: movq %rdi, (%rsp) ;; nopl (%rax, %rax) -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 36, slot at FP-0x30, locals I32 @ slot+0x8, I32 @ slot+0xc, stack -;; ╰─╼ breakpoint patch: wasm PC 36, patch bytes [232, 184, 1, 0, 0] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x24, slot at FP-0x30, locals I32 @ slot+0x8, I32 @ slot+0xc, stack +;; ╰─╼ breakpoint patch: wasm PC 0x24, patch bytes [232, 184, 1, 0, 0] ;; movl %edx, 0x10(%rsp) ;; nopl (%rax, %rax) -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 38, slot at FP-0x30, locals I32 @ slot+0x8, I32 @ slot+0xc, stack I32 @ slot+0x10 -;; ╰─╼ breakpoint patch: wasm PC 38, patch bytes [232, 175, 1, 0, 0] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x26, slot at FP-0x30, locals I32 @ slot+0x8, I32 @ slot+0xc, stack I32 @ slot+0x10 +;; ╰─╼ breakpoint patch: wasm PC 0x26, patch bytes [232, 175, 1, 0, 0] ;; movl %ecx, 0x14(%rsp) ;; nopl (%rax, %rax) -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 40, slot at FP-0x30, locals I32 @ slot+0x8, I32 @ slot+0xc, stack I32 @ slot+0x10, I32 @ slot+0x14 -;; ╰─╼ breakpoint patch: wasm PC 40, patch bytes [232, 166, 1, 0, 0] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x28, slot at FP-0x30, locals I32 @ slot+0x8, I32 @ slot+0xc, stack I32 @ slot+0x10, I32 @ slot+0x14 +;; ╰─╼ breakpoint patch: wasm PC 0x28, patch bytes [232, 166, 1, 0, 0] ;; leal (%rdx, %rcx), %eax ;; movl %eax, 0x10(%rsp) ;; nopl (%rax, %rax) -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 41, slot at FP-0x30, locals I32 @ slot+0x8, I32 @ slot+0xc, stack I32 @ slot+0x10 -;; ╰─╼ breakpoint patch: wasm PC 41, patch bytes [232, 154, 1, 0, 0] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x29, slot at FP-0x30, locals I32 @ slot+0x8, I32 @ slot+0xc, stack I32 @ slot+0x10 +;; ╰─╼ breakpoint patch: wasm PC 0x29, patch bytes [232, 154, 1, 0, 0] ;; movl %eax, 0x10(%rsp) ;; movq 0x20(%rsp), %r12 ;; addq $0x30, %rsp @@ -48,7 +48,7 @@ ;; 67: callq 0x18c ;; 6c: movq %r12, %rdi ;; 6f: callq 0x1bd -;; ╰─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 35, slot at FP-0x30, locals I32 @ slot+0x8, I32 @ slot+0xc, stack +;; ╰─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x23, slot at FP-0x30, locals I32 @ slot+0x8, I32 @ slot+0xc, stack ;; 74: ud2 ;; ;; wasm[0]::array_to_wasm_trampoline[0]: diff --git a/tests/disas/issue-12808.wat b/tests/disas/issue-12808.wat index 07282269ec5b..04e3e417a3ee 100644 --- a/tests/disas/issue-12808.wat +++ b/tests/disas/issue-12808.wat @@ -22,24 +22,24 @@ ;; jb 0xb4 ;; 21: movq %rdi, (%rsp) ;; nopl (%rax, %rax) -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 31, slot at FP-0x30, locals , stack -;; ╰─╼ breakpoint patch: wasm PC 31, patch bytes [232, 4, 2, 0, 0] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x1f, slot at FP-0x30, locals , stack +;; ╰─╼ breakpoint patch: wasm PC 0x1f, patch bytes [232, 4, 2, 0, 0] ;; movl $0, 8(%rsp) ;; nopl (%rax, %rax) -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 33, slot at FP-0x30, locals , stack I32 @ slot+0x8 -;; ╰─╼ breakpoint patch: wasm PC 33, patch bytes [232, 247, 1, 0, 0] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x21, slot at FP-0x30, locals , stack I32 @ slot+0x8 +;; ╰─╼ breakpoint patch: wasm PC 0x21, patch bytes [232, 247, 1, 0, 0] ;; movl $0, 0xc(%rsp) ;; nopl (%rax, %rax) -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 35, slot at FP-0x30, locals , stack I32 @ slot+0x8, I32 @ slot+0xc -;; ╰─╼ breakpoint patch: wasm PC 35, patch bytes [232, 234, 1, 0, 0] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x23, slot at FP-0x30, locals , stack I32 @ slot+0x8, I32 @ slot+0xc +;; ╰─╼ breakpoint patch: wasm PC 0x23, patch bytes [232, 234, 1, 0, 0] ;; movl $0, 0x10(%rsp) ;; nopl (%rax, %rax) -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 37, slot at FP-0x30, locals , stack I32 @ slot+0x8, I32 @ slot+0xc, I32 @ slot+0x10 -;; ╰─╼ breakpoint patch: wasm PC 37, patch bytes [232, 221, 1, 0, 0] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x25, slot at FP-0x30, locals , stack I32 @ slot+0x8, I32 @ slot+0xc, I32 @ slot+0x10 +;; ╰─╼ breakpoint patch: wasm PC 0x25, patch bytes [232, 221, 1, 0, 0] ;; movl $0, 0x14(%rsp) ;; nopl (%rax, %rax) -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 39, slot at FP-0x30, locals , stack I32 @ slot+0x8, I32 @ slot+0xc, I32 @ slot+0x10, I32 @ slot+0x14 -;; ╰─╼ breakpoint patch: wasm PC 39, patch bytes [232, 208, 1, 0, 0] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x27, slot at FP-0x30, locals , stack I32 @ slot+0x8, I32 @ slot+0xc, I32 @ slot+0x10, I32 @ slot+0x14 +;; ╰─╼ breakpoint patch: wasm PC 0x27, patch bytes [232, 208, 1, 0, 0] ;; xorl %eax, %eax ;; testb %al, %al ;; jne 0x9d @@ -50,8 +50,8 @@ ;; movl %eax, 0xc(%rsp) ;; movq %rax, %rcx ;; nopl (%rax, %rax) -;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 43, slot at FP-0x30, locals , stack I32 @ slot+0x8, I32 @ slot+0xc -;; ╰─╼ breakpoint patch: wasm PC 43, patch bytes [232, 173, 1, 0, 0] +;; ├─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x2b, slot at FP-0x30, locals , stack I32 @ slot+0x8, I32 @ slot+0xc +;; ╰─╼ breakpoint patch: wasm PC 0x2b, patch bytes [232, 173, 1, 0, 0] ;; xorl %eax, %eax ;; movl $0, 8(%rsp) ;; movl %ecx, 0xc(%rsp) @@ -65,12 +65,12 @@ ;; a5: callq 0x1d0 ;; aa: movq %rbx, %rdi ;; ad: callq 0x201 -;; ╰─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 39, slot at FP-0x30, locals , stack I32 @ slot+0x8, I32 @ slot+0xc +;; ╰─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x27, slot at FP-0x30, locals , stack I32 @ slot+0x8, I32 @ slot+0xc ;; b2: ud2 ;; b4: movq %rdi, %rbx ;; b7: xorl %esi, %esi ;; b9: callq 0x1d0 ;; be: movq %rbx, %rdi ;; c1: callq 0x201 -;; ╰─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 30, slot at FP-0x30, locals , stack +;; ╰─╼ debug frame state (after previous inst): func key DefinedWasmFunction(StaticModuleIndex(0), DefinedFuncIndex(0)), wasm PC 0x1e, slot at FP-0x30, locals , stack ;; c6: ud2