diff --git a/lib/compiler/src/function.rs b/lib/compiler/src/function.rs index 17ac6cfd9..eb60db83f 100644 --- a/lib/compiler/src/function.rs +++ b/lib/compiler/src/function.rs @@ -14,15 +14,13 @@ use serde::{Deserialize, Serialize}; use wasm_common::entity::PrimaryMap; use wasm_common::LocalFuncIndex; -type FunctionBody = Vec; - /// The frame info for a Compiled function. /// /// This structure is only used for reconstructing /// the frame information after a `Trap`. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)] pub struct CompiledFunctionFrameInfo { - /// The traps (in the body) + /// The traps (in the function body) pub traps: Vec, /// The address map. diff --git a/lib/jit/src/instantiate.rs b/lib/jit/src/instantiate.rs index 15021c352..c4873b7d9 100644 --- a/lib/jit/src/instantiate.rs +++ b/lib/jit/src/instantiate.rs @@ -8,9 +8,10 @@ use crate::error::{DeserializeError, SerializeError}; use crate::error::{InstantiationError, LinkError}; use crate::link::link_module; use crate::resolver::{resolve_imports, Resolver}; -use crate::trap::register as register_frame_info; +use crate::serialize::CacheRawCompiledModule; use crate::trap::GlobalFrameInfoRegistration; use crate::trap::RuntimeError; +use crate::trap::{register as register_frame_info, ExtraFunctionInfo}; use serde::{Deserialize, Serialize}; use std::any::Any; use std::sync::{Arc, Mutex}; @@ -20,7 +21,9 @@ use wasm_common::{ LocalTableIndex, MemoryIndex, SignatureIndex, TableIndex, }; use wasmer_compiler::ModuleEnvironment; -use wasmer_compiler::{Compilation, CompileError, FunctionAddressMap, TrapInformation}; +use wasmer_compiler::{ + Compilation, CompileError, CompiledFunctionFrameInfo, FunctionAddressMap, TrapInformation, +}; use wasmer_runtime::{ InstanceHandle, LinearMemory, Module, SignatureRegistry, Table, VMFunctionBody, VMGlobalDefinition, VMSharedSignatureIndex, @@ -28,17 +31,6 @@ use wasmer_runtime::{ use wasmer_runtime::{MemoryPlan, TablePlan}; -/// Structure to cache the content ot the compilation -#[derive(Serialize, Deserialize)] -struct CacheRawCompiledModule { - compilation: Arc, - module: Arc, - data_initializers: Arc>, - // Plans for that module - memory_plans: PrimaryMap, - table_plans: PrimaryMap, -} - /// This is similar to `CompiledModule`, but references the data initializers /// from the wasm buffer rather than holding its own copy. struct RawCompiledModule { @@ -337,7 +329,20 @@ impl CompiledModule { if info.is_some() { return; } - *info = Some(register_frame_info(&self)); + + let extra_functions = self + .traps() + .values() + .zip(self.address_transform().values()) + .map(|(traps, instrs)| { + ExtraFunctionInfo::Processed(CompiledFunctionFrameInfo { + traps: traps.to_vec(), + address_map: (*instrs).clone(), + }) + }) + .collect::>(); + + *info = Some(register_frame_info(&self, extra_functions)); } /// Returns the a map for all traps in this module. diff --git a/lib/jit/src/lib.rs b/lib/jit/src/lib.rs index 99d87ebd4..bdf8fd6b6 100644 --- a/lib/jit/src/lib.rs +++ b/lib/jit/src/lib.rs @@ -32,6 +32,7 @@ mod function_table; mod instantiate; mod link; mod resolver; +mod serialize; mod trap; mod tunables; diff --git a/lib/jit/src/serialize.rs b/lib/jit/src/serialize.rs new file mode 100644 index 000000000..d543cd042 --- /dev/null +++ b/lib/jit/src/serialize.rs @@ -0,0 +1,39 @@ +use crate::instantiate::OwnedDataInitializer; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; +use wasmer_compiler::Compilation; +use wasmer_runtime::Module; + +use wasm_common::entity::PrimaryMap; +use wasm_common::{MemoryIndex, TableIndex}; +use wasmer_runtime::{MemoryPlan, TablePlan}; + +// #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +// pub struct CompiledFunction { +// /// The function body. +// #[serde(with = "serde_bytes")] +// pub body: Vec, + +// /// The relocations (in the body) +// pub relocations: Vec, + +// /// The jump tables offsets (in the body). +// pub jt_offsets: JumpTableOffsets, + +// /// The unwind information. +// pub unwind_info: CompiledFunctionUnwindInfo, + +// /// The frame information. +// pub frame_info: CompiledFunctionFrameInfo, +// } + +/// Structure to cache the content ot the compilation +#[derive(Serialize, Deserialize)] +pub struct CacheRawCompiledModule { + pub compilation: Arc, + pub module: Arc, + pub data_initializers: Arc>, + // Plans for that module + pub memory_plans: PrimaryMap, + pub table_plans: PrimaryMap, +} diff --git a/lib/jit/src/trap/error.rs b/lib/jit/src/trap/error.rs index dad54e50b..1168dbfc5 100644 --- a/lib/jit/src/trap/error.rs +++ b/lib/jit/src/trap/error.rs @@ -73,7 +73,24 @@ impl RuntimeError { code: TrapCode, backtrace: Backtrace, ) -> Self { - let desc = code.message(); + let desc = match code { + TrapCode::StackOverflow => "call stack exhausted", + TrapCode::HeapSetterOutOfBounds => "memory out of bounds: data segment does not fit", + TrapCode::HeapAccessOutOfBounds => "out of bounds memory access", + TrapCode::TableSetterOutOfBounds => { + "table out of bounds: elements segment does not fit" + } + TrapCode::TableAccessOutOfBounds => "undefined element: out of bounds table access", + TrapCode::OutOfBounds => "out of bounds", + TrapCode::IndirectCallToNull => "uninitialized element", + TrapCode::BadSignature => "indirect call type mismatch", + TrapCode::IntegerOverflow => "integer overflow", + TrapCode::IntegerDivisionByZero => "integer divide by zero", + TrapCode::BadConversionToInteger => "invalid conversion to integer", + TrapCode::UnreachableCodeReached => "unreachable", + TrapCode::Interrupt => "interrupt", + TrapCode::User(_) => unreachable!(), + }; let msg = format!("{}", desc); Self::new_with_trace(info, trap_pc, msg, backtrace) } diff --git a/lib/jit/src/trap/frame_info.rs b/lib/jit/src/trap/frame_info.rs index e69f15870..a4e2f3b9f 100644 --- a/lib/jit/src/trap/frame_info.rs +++ b/lib/jit/src/trap/frame_info.rs @@ -52,7 +52,7 @@ pub struct GlobalFrameInfoRegistration { } /// The function debug info, but unprocessed -#[derive(Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize)] pub struct ExtraFunctionInfoUnprocessed { #[serde(with = "serde_bytes")] bytes: Vec, @@ -85,11 +85,25 @@ impl Into for ExtraFunctionInfoUnprocessed { /// of compiling at the same time that emiting the JIT. /// In that case, we don't need to deserialize/process anything /// as the data is already in memory. +#[derive(Clone)] pub enum ExtraFunctionInfo { + /// the processed function Processed(CompiledFunctionFrameInfo), + /// the unprocessed Unprocessed(ExtraFunctionInfoUnprocessed), } +impl ExtraFunctionInfo { + /// Returns true if the extra function info is not yet + /// processed + pub fn is_unprocessed(&self) -> bool { + match self { + Self::Unprocessed(_) => true, + _ => false, + } + } +} + struct ModuleFrameInfo { start: usize, functions: BTreeMap, @@ -102,6 +116,19 @@ impl ModuleFrameInfo { &self.extra_functions.get(local_index).unwrap() } + fn process_function_debug_info(&mut self, local_index: LocalFuncIndex) { + let mut func = self.extra_functions.get_mut(local_index).unwrap(); + let processed: CompiledFunctionFrameInfo = match func { + ExtraFunctionInfo::Processed(_) => { + // This should be a no-op on processed info + return; + } + ExtraFunctionInfo::Unprocessed(unprocessed) => { + bincode::deserialize(&unprocessed.bytes).expect("Can't deserialize the info") + } + }; + *func = ExtraFunctionInfo::Processed(processed) + } fn instr_map(&self, local_index: LocalFuncIndex) -> &FunctionAddressMap { match self.function_debug_info(local_index) { ExtraFunctionInfo::Processed(di) => &di.address_map, @@ -138,8 +165,7 @@ impl GlobalFrameInfo { /// Returns an object if this `pc` is known to some previously registered /// module, or returns `None` if no information can be found. pub fn lookup_frame_info(&self, pc: usize) -> Option { - let module = self.module_info(pc)?; - let func = module.function_info(pc)?; + let (module, func) = self.maybe_process_frame(pc)?; // Use our relative position from the start of the function to find the // machine instruction that corresponds to `pc`, which then allows us to @@ -194,8 +220,7 @@ impl GlobalFrameInfo { /// Fetches trap information about a program counter in a backtrace. pub fn lookup_trap_info(&self, pc: usize) -> Option<&TrapInformation> { - let module = self.module_info(pc)?; - let func = module.function_info(pc)?; + let (module, func) = self.maybe_process_frame(pc)?; let traps = module.traps(func.local_index); let idx = traps .binary_search_by_key(&((pc - func.start) as u32), |info| info.code_offset) @@ -203,6 +228,21 @@ impl GlobalFrameInfo { Some(&traps[idx]) } + /// Get an process a Frame in case is not yet processed + fn maybe_process_frame(&self, pc: usize) -> Option<(&ModuleFrameInfo, &FunctionInfo)> { + let module = self.module_info(pc)?; + let func = module.function_info(pc)?; + let extra_func_info = module.function_debug_info(func.local_index); + if extra_func_info.is_unprocessed() { + let mut mutable_info = FRAME_INFO.write().unwrap(); + let mut mutable_module = mutable_info.module_info_mut(pc)?; + mutable_module.process_function_debug_info(func.local_index); + let module = self.module_info(pc)?; + return Some((module, func)); + } + Some((module, func)) + } + /// Gets a module given a pc fn module_info(&self, pc: usize) -> Option<&ModuleFrameInfo> { let (end, module_info) = self.ranges.range(pc..).next()?; @@ -236,7 +276,10 @@ impl Drop for GlobalFrameInfoRegistration { /// compiled functions within `module`. If the `module` has no functions /// then `None` will be returned. Otherwise the returned object, when /// dropped, will be used to unregister all name information from this map. -pub fn register(module: &CompiledModule) -> Option { +pub fn register( + module: &CompiledModule, + extra_functions: PrimaryMap, +) -> Option { let mut min = usize::max_value(); let mut max = 0; let mut functions = BTreeMap::new(); @@ -268,18 +311,6 @@ pub fn register(module: &CompiledModule) -> Option assert!(*prev_end < min); } - let extra_functions = module - .traps() - .values() - .zip(module.address_transform().values()) - .map(|(traps, instrs)| { - ExtraFunctionInfo::Processed(CompiledFunctionFrameInfo { - traps: traps.to_vec(), - address_map: (*instrs).clone(), - }) - }) - .collect::>(); - // ... then insert our range and assert nothing was there previously let prev = info.ranges.insert( max, diff --git a/lib/jit/src/trap/mod.rs b/lib/jit/src/trap/mod.rs index 1a37b64af..cf6897ba9 100644 --- a/lib/jit/src/trap/mod.rs +++ b/lib/jit/src/trap/mod.rs @@ -1,4 +1,7 @@ mod error; mod frame_info; pub use error::RuntimeError; -pub use frame_info::{register, FrameInfo, GlobalFrameInfoRegistration, FRAME_INFO}; +pub use frame_info::{ + register, ExtraFunctionInfo, ExtraFunctionInfoUnprocessed, FrameInfo, + GlobalFrameInfoRegistration, FRAME_INFO, +};