mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-24 06:01:33 +00:00
Move inline breakpoint outside of runtime backend
There was some code smell leaking inline breakpoint implementation into the runtime core backend instead of the compiler itself
This commit is contained in:
@ -104,83 +104,6 @@ pub struct InlineBreakpoint {
|
||||
pub ty: InlineBreakpointType,
|
||||
}
|
||||
|
||||
/// Inline breakpoint size for (x86-64, singlepass).
|
||||
pub const INLINE_BREAKPOINT_SIZE_X86_SINGLEPASS: usize = 7;
|
||||
|
||||
/// Inline breakpoint size for (aarch64, singlepass).
|
||||
pub const INLINE_BREAKPOINT_SIZE_AARCH64_SINGLEPASS: usize = 12;
|
||||
|
||||
/// Returns the inline breakpoint size corresponding to an (Architecture, Backend) pair.
|
||||
pub fn get_inline_breakpoint_size(arch: Architecture, backend: Backend) -> Option<usize> {
|
||||
match (arch, backend) {
|
||||
(Architecture::X64, Backend::Singlepass) => Some(INLINE_BREAKPOINT_SIZE_X86_SINGLEPASS),
|
||||
(Architecture::Aarch64, Backend::Singlepass) => {
|
||||
Some(INLINE_BREAKPOINT_SIZE_AARCH64_SINGLEPASS)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to read an inline breakpoint from the code.
|
||||
///
|
||||
/// Inline breakpoints are detected by special instruction sequences that never
|
||||
/// appear in valid code.
|
||||
pub fn read_inline_breakpoint(
|
||||
arch: Architecture,
|
||||
backend: Backend,
|
||||
code: &[u8],
|
||||
) -> Option<InlineBreakpoint> {
|
||||
match arch {
|
||||
Architecture::X64 => match backend {
|
||||
Backend::Singlepass => {
|
||||
if code.len() < INLINE_BREAKPOINT_SIZE_X86_SINGLEPASS {
|
||||
None
|
||||
} else if &code[..INLINE_BREAKPOINT_SIZE_X86_SINGLEPASS - 1]
|
||||
== &[
|
||||
0x0f, 0x0b, // ud2
|
||||
0x0f, 0xb9, // ud
|
||||
0xcd, 0xff, // int 0xff
|
||||
]
|
||||
{
|
||||
Some(InlineBreakpoint {
|
||||
size: INLINE_BREAKPOINT_SIZE_X86_SINGLEPASS,
|
||||
ty: match code[INLINE_BREAKPOINT_SIZE_X86_SINGLEPASS - 1] {
|
||||
0 => InlineBreakpointType::Middleware,
|
||||
_ => return None,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
Architecture::Aarch64 => match backend {
|
||||
Backend::Singlepass => {
|
||||
if code.len() < INLINE_BREAKPOINT_SIZE_AARCH64_SINGLEPASS {
|
||||
None
|
||||
} else if &code[..INLINE_BREAKPOINT_SIZE_AARCH64_SINGLEPASS - 4]
|
||||
== &[
|
||||
0, 0, 0, 0, // udf #0
|
||||
0xff, 0xff, 0x00, 0x00, // udf #65535
|
||||
]
|
||||
{
|
||||
Some(InlineBreakpoint {
|
||||
size: INLINE_BREAKPOINT_SIZE_AARCH64_SINGLEPASS,
|
||||
ty: match code[INLINE_BREAKPOINT_SIZE_AARCH64_SINGLEPASS - 4] {
|
||||
0 => InlineBreakpointType::Middleware,
|
||||
_ => return None,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod backend_test {
|
||||
use super::*;
|
||||
@ -316,6 +239,23 @@ pub trait RunnableModule: Send + Sync {
|
||||
fn get_local_function_offsets(&self) -> Option<Vec<usize>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns the inline breakpoint size corresponding to an Architecture (None in case is not implemented)
|
||||
fn get_inline_breakpoint_size(&self, _arch: Architecture) -> Option<usize> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Attempts to read an inline breakpoint from the code.
|
||||
///
|
||||
/// Inline breakpoints are detected by special instruction sequences that never
|
||||
/// appear in valid code.
|
||||
fn read_inline_breakpoint(
|
||||
&self,
|
||||
_arch: Architecture,
|
||||
_code: &[u8],
|
||||
) -> Option<InlineBreakpoint> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CacheGen: Send + Sync {
|
||||
|
@ -111,6 +111,7 @@ impl LocalBacking {
|
||||
.map(|index| {
|
||||
module
|
||||
.runnable_module
|
||||
.borrow()
|
||||
.get_func(&module.info, LocalFuncIndex::new(index))
|
||||
.unwrap()
|
||||
.as_ptr() as *const _
|
||||
@ -380,6 +381,7 @@ impl LocalBacking {
|
||||
LocalOrImport::Local(local_func_index) => (
|
||||
module
|
||||
.runnable_module
|
||||
.borrow()
|
||||
.get_func(&module.info, local_func_index)
|
||||
.unwrap()
|
||||
.as_ptr()
|
||||
@ -413,6 +415,7 @@ impl LocalBacking {
|
||||
LocalOrImport::Local(local_func_index) => (
|
||||
module
|
||||
.runnable_module
|
||||
.borrow()
|
||||
.get_func(&module.info, local_func_index)
|
||||
.unwrap()
|
||||
.as_ptr()
|
||||
|
@ -18,6 +18,7 @@ use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use wasmparser::{self, WasmDecoder};
|
||||
use wasmparser::{Operator, Type as WpType};
|
||||
|
||||
@ -249,7 +250,7 @@ impl<
|
||||
})?;
|
||||
Ok(ModuleInner {
|
||||
cache_gen,
|
||||
runnable_module: Box::new(exec_context),
|
||||
runnable_module: Rc::new(RefCell::new(Box::new(exec_context))),
|
||||
info: Arc::try_unwrap(info).unwrap().into_inner().unwrap(),
|
||||
})
|
||||
}
|
||||
|
@ -272,9 +272,7 @@ extern "C" fn signal_trap_handler(
|
||||
siginfo: *mut siginfo_t,
|
||||
ucontext: *mut c_void,
|
||||
) {
|
||||
use crate::backend::{
|
||||
get_inline_breakpoint_size, read_inline_breakpoint, Architecture, InlineBreakpointType,
|
||||
};
|
||||
use crate::backend::{Architecture, InlineBreakpointType};
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
static ARCH: Architecture = Architecture::X64;
|
||||
@ -291,17 +289,18 @@ extern "C" fn signal_trap_handler(
|
||||
CURRENT_CODE_VERSIONS.with(|versions| {
|
||||
let versions = versions.borrow();
|
||||
for v in versions.iter() {
|
||||
let magic_size = if let Some(x) = get_inline_breakpoint_size(ARCH, v.backend) {
|
||||
x
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
let runnable_module = v.runnable_module.borrow();
|
||||
let magic_size =
|
||||
if let Some(x) = runnable_module.get_inline_breakpoint_size(ARCH) {
|
||||
x
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
let ip = fault.ip.get();
|
||||
let end = v.base + v.msm.total_size;
|
||||
if ip >= v.base && ip < end && ip + magic_size <= end {
|
||||
if let Some(ib) = read_inline_breakpoint(
|
||||
if let Some(ib) = runnable_module.read_inline_breakpoint(
|
||||
ARCH,
|
||||
v.backend,
|
||||
std::slice::from_raw_parts(ip as *const u8, magic_size),
|
||||
) {
|
||||
match ib.ty {
|
||||
|
@ -103,6 +103,7 @@ impl Instance {
|
||||
LocalOrImport::Local(local_func_index) => instance
|
||||
.module
|
||||
.runnable_module
|
||||
.borrow()
|
||||
.get_func(&instance.module.info, local_func_index)
|
||||
.unwrap(),
|
||||
LocalOrImport::Import(import_func_index) => NonNull::new(
|
||||
@ -132,6 +133,7 @@ impl Instance {
|
||||
let wasm_trampoline = instance
|
||||
.module
|
||||
.runnable_module
|
||||
.borrow()
|
||||
.get_trampoline(&instance.module.info, sig_index)
|
||||
.expect("wasm trampoline");
|
||||
|
||||
@ -146,9 +148,11 @@ impl Instance {
|
||||
|
||||
/// Load an `Instance` using the given loader.
|
||||
pub fn load<T: Loader>(&self, loader: T) -> ::std::result::Result<T::Instance, T::Error> {
|
||||
loader.load(&*self.module.runnable_module, &self.module.info, unsafe {
|
||||
&*self.inner.vmctx
|
||||
})
|
||||
loader.load(
|
||||
&**self.module.runnable_module.borrow(),
|
||||
&self.module.info,
|
||||
unsafe { &*self.inner.vmctx },
|
||||
)
|
||||
}
|
||||
|
||||
/// Through generic magic and the awe-inspiring power of traits, we bring you...
|
||||
@ -215,6 +219,7 @@ impl Instance {
|
||||
let func_wasm_inner = self
|
||||
.module
|
||||
.runnable_module
|
||||
.borrow()
|
||||
.get_trampoline(&self.module.info, sig_index)
|
||||
.unwrap();
|
||||
|
||||
@ -222,6 +227,7 @@ impl Instance {
|
||||
LocalOrImport::Local(local_func_index) => (
|
||||
self.module
|
||||
.runnable_module
|
||||
.borrow()
|
||||
.get_func(&self.module.info, local_func_index)
|
||||
.unwrap(),
|
||||
None,
|
||||
@ -362,7 +368,7 @@ impl Instance {
|
||||
|
||||
call_func_with_index(
|
||||
&self.module.info,
|
||||
&*self.module.runnable_module,
|
||||
&**self.module.runnable_module.borrow(),
|
||||
&self.inner.import_backing,
|
||||
self.inner.vmctx,
|
||||
func_index,
|
||||
@ -461,6 +467,7 @@ impl InstanceInner {
|
||||
LocalOrImport::Local(local_func_index) => (
|
||||
module
|
||||
.runnable_module
|
||||
.borrow()
|
||||
.get_func(&module.info, local_func_index)
|
||||
.expect("broken invariant, func resolver not synced with module.exports")
|
||||
.cast()
|
||||
@ -790,7 +797,7 @@ impl<'a> DynFunc<'a> {
|
||||
|
||||
call_func_with_index(
|
||||
&self.module.info,
|
||||
&*self.module.runnable_module,
|
||||
&**self.module.runnable_module.borrow(),
|
||||
&self.instance_inner.import_backing,
|
||||
self.instance_inner.vmctx,
|
||||
self.func_index,
|
||||
@ -812,6 +819,7 @@ impl<'a> DynFunc<'a> {
|
||||
LocalOrImport::Local(local_func_index) => self
|
||||
.module
|
||||
.runnable_module
|
||||
.borrow()
|
||||
.get_func(&self.module.info, local_func_index)
|
||||
.unwrap()
|
||||
.as_ptr(),
|
||||
|
@ -19,11 +19,12 @@ use crate::backend::CacheGen;
|
||||
use indexmap::IndexMap;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
/// This is used to instantiate a new WebAssembly module.
|
||||
#[doc(hidden)]
|
||||
pub struct ModuleInner {
|
||||
pub runnable_module: Box<dyn RunnableModule>,
|
||||
pub runnable_module: Rc<RefCell<Box<dyn RunnableModule>>>,
|
||||
pub cache_gen: Box<dyn CacheGen>,
|
||||
|
||||
pub info: ModuleInfo,
|
||||
|
@ -2,9 +2,10 @@
|
||||
//! state could read or updated at runtime. Use cases include generating stack traces, switching
|
||||
//! generated code from one tier to another, or serializing state of a running instace.
|
||||
|
||||
use crate::backend::Backend;
|
||||
use crate::backend::{Backend, RunnableModule};
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::Bound::{Included, Unbounded};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
/// An index to a register
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||
@ -173,7 +174,7 @@ pub struct InstanceImage {
|
||||
}
|
||||
|
||||
/// A `CodeVersion` is a container for a unit of generated code for a module.
|
||||
#[derive(Debug, Clone)]
|
||||
// #[derive(Debug, Clone)]
|
||||
pub struct CodeVersion {
|
||||
/// Indicates if this code version is the baseline version.
|
||||
pub baseline: bool,
|
||||
@ -186,6 +187,9 @@ pub struct CodeVersion {
|
||||
|
||||
/// The backend used to compile this module.
|
||||
pub backend: Backend,
|
||||
|
||||
/// `RunnableModule` for this code version.
|
||||
pub runnable_module: Rc<RefCell<Box<dyn RunnableModule>>>,
|
||||
}
|
||||
|
||||
impl ModuleStateMap {
|
||||
|
@ -14,6 +14,7 @@ use crate::vm::Ctx;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
struct Defer<F: FnOnce()>(Option<F>);
|
||||
impl<F: FnOnce()> Drop for Defer<F> {
|
||||
@ -125,10 +126,18 @@ pub unsafe fn run_tiering<F: Fn(InteractiveShellContext) -> ShellExitOperation>(
|
||||
msm: baseline
|
||||
.module
|
||||
.runnable_module
|
||||
.borrow()
|
||||
.get_module_state_map()
|
||||
.unwrap(),
|
||||
base: baseline.module.runnable_module.get_code().unwrap().as_ptr() as usize,
|
||||
base: baseline
|
||||
.module
|
||||
.runnable_module
|
||||
.borrow()
|
||||
.get_code()
|
||||
.unwrap()
|
||||
.as_ptr() as usize,
|
||||
backend: baseline_backend,
|
||||
runnable_module: baseline.module.runnable_module.clone(),
|
||||
});
|
||||
let n_versions: Cell<usize> = Cell::new(1);
|
||||
|
||||
@ -182,15 +191,18 @@ pub unsafe fn run_tiering<F: Fn(InteractiveShellContext) -> ShellExitOperation>(
|
||||
msm: optimized
|
||||
.module
|
||||
.runnable_module
|
||||
.borrow()
|
||||
.get_module_state_map()
|
||||
.unwrap(),
|
||||
base: optimized
|
||||
.module
|
||||
.runnable_module
|
||||
.borrow()
|
||||
.get_code()
|
||||
.unwrap()
|
||||
.as_ptr() as usize,
|
||||
backend: backend_id,
|
||||
runnable_module: optimized.module.runnable_module.clone(),
|
||||
});
|
||||
n_versions.set(n_versions.get() + 1);
|
||||
|
||||
|
@ -515,7 +515,7 @@ macro_rules! impl_traits {
|
||||
// At this point, there is an error that needs to
|
||||
// be trapped.
|
||||
unsafe {
|
||||
(&*vmctx.module).runnable_module.do_early_trap(err)
|
||||
(&*vmctx.module).runnable_module.borrow().do_early_trap(err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -627,7 +627,7 @@ macro_rules! impl_traits {
|
||||
// At this point, there is an error that needs to
|
||||
// be trapped.
|
||||
unsafe {
|
||||
(&*vmctx.module).runnable_module.do_early_trap(err)
|
||||
(&*vmctx.module).runnable_module.borrow().do_early_trap(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -457,6 +457,7 @@ impl Ctx {
|
||||
|
||||
let sig_index = SigRegistry.lookup_sig_index(signature.clone());
|
||||
runnable
|
||||
.borrow()
|
||||
.get_trampoline(&module.info, sig_index)
|
||||
.expect("wasm trampoline")
|
||||
};
|
||||
@ -975,6 +976,7 @@ mod vm_ctx_tests {
|
||||
use crate::module::{ModuleInfo, ModuleInner, StringTable};
|
||||
use crate::structures::Map;
|
||||
use std::ffi::c_void;
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
struct TestData {
|
||||
x: u32,
|
||||
@ -1095,7 +1097,7 @@ mod vm_ctx_tests {
|
||||
}
|
||||
|
||||
ModuleInner {
|
||||
runnable_module: Box::new(Placeholder),
|
||||
runnable_module: Rc::new(RefCell::new(Box::new(Placeholder))),
|
||||
cache_gen: Box::new(Placeholder),
|
||||
info: ModuleInfo {
|
||||
memories: Map::new(),
|
||||
|
Reference in New Issue
Block a user