mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-09 06:08:29 +00:00
Merge branch 'master' into compilerconfig-owned
This commit is contained in:
@@ -7,6 +7,10 @@
|
|||||||
|
|
||||||
## **[Unreleased]**
|
## **[Unreleased]**
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* [#1867](https://github.com/wasmerio/wasmer/pull/1867) Added `Metering::get_remaining_points` and `Metering::set_remaining_points`
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- [#1874](https://github.com/wasmerio/wasmer/pull/1874) Set `CompilerConfig` to be owned (following wasm-c-api)
|
- [#1874](https://github.com/wasmerio/wasmer/pull/1874) Set `CompilerConfig` to be owned (following wasm-c-api)
|
||||||
|
|||||||
@@ -255,3 +255,8 @@ required-features = ["cranelift"]
|
|||||||
name = "hello-world"
|
name = "hello-world"
|
||||||
path = "examples/hello_world.rs"
|
path = "examples/hello_world.rs"
|
||||||
required-features = ["cranelift"]
|
required-features = ["cranelift"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "metering"
|
||||||
|
path = "examples/metering.rs"
|
||||||
|
required-features = ["cranelift"]
|
||||||
|
|||||||
165
examples/metering.rs
Normal file
165
examples/metering.rs
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
//! Wasmer will let you easily run Wasm module in a Rust host.
|
||||||
|
//!
|
||||||
|
//! This example illustrates the basics of using Wasmer metering features:
|
||||||
|
//!
|
||||||
|
//! 1. How to enable metering in a module
|
||||||
|
//! 2. How to meter a specific function call
|
||||||
|
//! 3. How to make execution fails if cost exceeds a given limit
|
||||||
|
//!
|
||||||
|
//! You can run the example directly by executing in Wasmer root:
|
||||||
|
//!
|
||||||
|
//! ```shell
|
||||||
|
//! cargo run --example metering --release --features "cranelift"
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Ready?
|
||||||
|
|
||||||
|
use anyhow::bail;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use wasmer::wasmparser::Operator;
|
||||||
|
use wasmer::CompilerConfig;
|
||||||
|
use wasmer::{imports, wat2wasm, Instance, Module, Store};
|
||||||
|
use wasmer_compiler_cranelift::Cranelift;
|
||||||
|
use wasmer_engine_jit::JIT;
|
||||||
|
use wasmer_middlewares::Metering;
|
||||||
|
|
||||||
|
fn main() -> anyhow::Result<()> {
|
||||||
|
// Let's declare the Wasm module.
|
||||||
|
//
|
||||||
|
// We are using the text representation of the module here but you can also load `.wasm`
|
||||||
|
// files using the `include_bytes!` macro.
|
||||||
|
let wasm_bytes = wat2wasm(
|
||||||
|
br#"
|
||||||
|
(module
|
||||||
|
(type $add_t (func (param i32) (result i32)))
|
||||||
|
(func $add_one_f (type $add_t) (param $value i32) (result i32)
|
||||||
|
local.get $value
|
||||||
|
i32.const 1
|
||||||
|
i32.add)
|
||||||
|
(export "add_one" (func $add_one_f)))
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Let's define our cost function.
|
||||||
|
//
|
||||||
|
// This function will be called for each `Operator` encountered during
|
||||||
|
// the Wasm module execution. It should return the cost of the operator
|
||||||
|
// that it received as it first argument.
|
||||||
|
let cost_function = |operator: &Operator| -> u64 {
|
||||||
|
match operator {
|
||||||
|
Operator::LocalGet { .. } | Operator::I32Const { .. } => 1,
|
||||||
|
Operator::I32Add { .. } => 2,
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Now let's create our metering middleware.
|
||||||
|
//
|
||||||
|
// `Metering` needs to be configured with a limit (the gas limit) and
|
||||||
|
// a cost function.
|
||||||
|
//
|
||||||
|
// For each `Operator`, the metering middleware will call the cost
|
||||||
|
// function and subtract the cost from the gas.
|
||||||
|
let metering = Arc::new(Metering::new(10, cost_function));
|
||||||
|
let mut compiler_config = Cranelift::default();
|
||||||
|
compiler_config.push_middleware(metering.clone());
|
||||||
|
|
||||||
|
// Create a Store.
|
||||||
|
//
|
||||||
|
// We use our previously create compiler configuration
|
||||||
|
// with the JIT engine.
|
||||||
|
let store = Store::new(&JIT::new(&compiler_config).engine());
|
||||||
|
|
||||||
|
println!("Compiling module...");
|
||||||
|
// Let's compile the Wasm module.
|
||||||
|
let module = Module::new(&store, wasm_bytes)?;
|
||||||
|
|
||||||
|
// Create an empty import object.
|
||||||
|
let import_object = imports! {};
|
||||||
|
|
||||||
|
println!("Instantiating module...");
|
||||||
|
// Let's instantiate the Wasm module.
|
||||||
|
let instance = Instance::new(&module, &import_object)?;
|
||||||
|
|
||||||
|
// We now have an instance ready to be used.
|
||||||
|
//
|
||||||
|
// Our module exports a single `add_one` function. We want to
|
||||||
|
// measure the cost of executing this function.
|
||||||
|
let add_one = instance
|
||||||
|
.exports
|
||||||
|
.get_function("add_one")?
|
||||||
|
.native::<i32, i32>()?;
|
||||||
|
|
||||||
|
println!("Calling `add_one` function once...");
|
||||||
|
add_one.call(1)?;
|
||||||
|
|
||||||
|
// As you can see here, after the first call we have 6 remaining gas points.
|
||||||
|
//
|
||||||
|
// This is correct, here are the details of how it has been computed:
|
||||||
|
// * `local.get $value` is a `Operator::LocalGet` which costs 1 point;
|
||||||
|
// * `i32.const` is a `Operator::I32Const` which costs 1 point;
|
||||||
|
// * `i32.add` is a `Operator::I32Add` which costs 2 points.
|
||||||
|
let remaining_points_after_first_call = metering.get_remaining_points(&instance);
|
||||||
|
assert_eq!(remaining_points_after_first_call, 6);
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Remaining points after the first call: {:?}",
|
||||||
|
remaining_points_after_first_call
|
||||||
|
);
|
||||||
|
|
||||||
|
println!("Calling `add_one` function twice...");
|
||||||
|
add_one.call(1)?;
|
||||||
|
|
||||||
|
// We spent 4 more gas points with the second call.
|
||||||
|
// We have 2 remaining points.
|
||||||
|
let remaining_points_after_second_call = metering.get_remaining_points(&instance);
|
||||||
|
assert_eq!(remaining_points_after_second_call, 2);
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Remaining points after the second call: {:?}",
|
||||||
|
remaining_points_after_second_call
|
||||||
|
);
|
||||||
|
|
||||||
|
// Because calling our `add_one` function consumes 4 gas points,
|
||||||
|
// calling it a third time will fail: we already consume 8 gas
|
||||||
|
// points, there are only two remaining.
|
||||||
|
println!("Calling `add_one` function a third time...");
|
||||||
|
match add_one.call(1) {
|
||||||
|
Ok(result) => {
|
||||||
|
bail!(
|
||||||
|
"Expected failure while calling `add_one`, found: {}",
|
||||||
|
result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
println!("Calling `add_one` failed: not enough gas points remaining.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Becasue the previous call failed, it did not consume any gas point.
|
||||||
|
// We still have 2 remaining points.
|
||||||
|
let remaining_points_after_third_call = metering.get_remaining_points(&instance);
|
||||||
|
assert_eq!(remaining_points_after_third_call, 2);
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Remaining points after third call: {:?}",
|
||||||
|
remaining_points_after_third_call
|
||||||
|
);
|
||||||
|
|
||||||
|
// Now let's see how we can set a new limit...
|
||||||
|
println!("Set new remaining points points to 10");
|
||||||
|
let new_limit = 10;
|
||||||
|
metering.set_remaining_points(&instance, new_limit);
|
||||||
|
|
||||||
|
let remaining_points = metering.get_remaining_points(&instance);
|
||||||
|
assert_eq!(remaining_points, new_limit);
|
||||||
|
|
||||||
|
println!("Remaining points: {:?}", remaining_points);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_metering() -> anyhow::Result<()> {
|
||||||
|
main()
|
||||||
|
}
|
||||||
@@ -77,13 +77,15 @@ pub use target_lexicon::{Architecture, CallingConvention, OperatingSystem, Tripl
|
|||||||
pub use wasmer_compiler::{
|
pub use wasmer_compiler::{
|
||||||
wasmparser, CompilerConfig, FunctionMiddleware, MiddlewareReaderState, ModuleMiddleware,
|
wasmparser, CompilerConfig, FunctionMiddleware, MiddlewareReaderState, ModuleMiddleware,
|
||||||
};
|
};
|
||||||
pub use wasmer_compiler::{CpuFeature, Features, Target};
|
pub use wasmer_compiler::{
|
||||||
|
CompileError, CpuFeature, Features, ParseCpuFeatureError, Target, WasmError,
|
||||||
|
};
|
||||||
pub use wasmer_engine::{
|
pub use wasmer_engine::{
|
||||||
ChainableNamedResolver, DeserializeError, Engine, Export, FrameInfo, LinkError, NamedResolver,
|
ChainableNamedResolver, DeserializeError, Engine, Export, FrameInfo, LinkError, NamedResolver,
|
||||||
NamedResolverChain, Resolver, RuntimeError, SerializeError,
|
NamedResolverChain, Resolver, RuntimeError, SerializeError,
|
||||||
};
|
};
|
||||||
pub use wasmer_types::{
|
pub use wasmer_types::{
|
||||||
Atomically, Bytes, GlobalInit, LocalFunctionIndex, MemoryView, Pages, ValueType,
|
Atomically, Bytes, ExportIndex, GlobalInit, LocalFunctionIndex, MemoryView, Pages, ValueType,
|
||||||
WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE,
|
WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ use wasmer::wasmparser::{
|
|||||||
Operator, Result as WpResult, Type as WpType, TypeOrFuncType as WpTypeOrFuncType,
|
Operator, Result as WpResult, Type as WpType, TypeOrFuncType as WpTypeOrFuncType,
|
||||||
};
|
};
|
||||||
use wasmer::{
|
use wasmer::{
|
||||||
FunctionMiddleware, GlobalInit, GlobalType, LocalFunctionIndex, MiddlewareReaderState,
|
ExportIndex, FunctionMiddleware, GlobalInit, GlobalType, Instance, LocalFunctionIndex,
|
||||||
ModuleMiddleware, Mutability, Type,
|
MiddlewareReaderState, ModuleMiddleware, Mutability, Type, Value,
|
||||||
};
|
};
|
||||||
use wasmer_types::GlobalIndex;
|
use wasmer_types::GlobalIndex;
|
||||||
use wasmer_vm::ModuleInfo;
|
use wasmer_vm::ModuleInfo;
|
||||||
@@ -52,6 +52,30 @@ impl<F: Fn(&Operator) -> u64 + Copy + Clone + Send + Sync> Metering<F> {
|
|||||||
remaining_points_index: Mutex::new(None),
|
remaining_points_index: Mutex::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the remaining points in an Instance.
|
||||||
|
///
|
||||||
|
/// Important: the instance Module must been processed with the `Metering` middleware.
|
||||||
|
pub fn get_remaining_points(&self, instance: &Instance) -> u64 {
|
||||||
|
instance
|
||||||
|
.exports
|
||||||
|
.get_global("remaining_points")
|
||||||
|
.expect("Can't get `remaining_points` from Instance")
|
||||||
|
.get()
|
||||||
|
.unwrap_i64() as _
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the provided remaining points in an Instance.
|
||||||
|
///
|
||||||
|
/// Important: the instance Module must been processed with the `Metering` middleware.
|
||||||
|
pub fn set_remaining_points(&self, instance: &Instance, points: u64) {
|
||||||
|
instance
|
||||||
|
.exports
|
||||||
|
.get_global("remaining_points")
|
||||||
|
.expect("Can't get `remaining_points` from Instance")
|
||||||
|
.set(Value::I64(points as _))
|
||||||
|
.expect("Can't set `remaining_points` in Instance");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Fn(&Operator) -> u64 + Copy + Clone + Send + Sync> fmt::Debug for Metering<F> {
|
impl<F: Fn(&Operator) -> u64 + Copy + Clone + Send + Sync> fmt::Debug for Metering<F> {
|
||||||
@@ -86,14 +110,18 @@ impl<F: Fn(&Operator) -> u64 + Copy + Clone + Send + Sync + 'static> ModuleMiddl
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Append a global for remaining points and initialize it.
|
// Append a global for remaining points and initialize it.
|
||||||
*remaining_points_index = Some(
|
let global_index = module_info
|
||||||
module_info
|
.globals
|
||||||
.globals
|
.push(GlobalType::new(Type::I64, Mutability::Var));
|
||||||
.push(GlobalType::new(Type::I64, Mutability::Var)),
|
*remaining_points_index = Some(global_index.clone());
|
||||||
);
|
|
||||||
module_info
|
module_info
|
||||||
.global_initializers
|
.global_initializers
|
||||||
.push(GlobalInit::I64Const(self.initial_limit as i64));
|
.push(GlobalInit::I64Const(self.initial_limit as i64));
|
||||||
|
|
||||||
|
module_info.exports.insert(
|
||||||
|
"remaining_points".to_string(),
|
||||||
|
ExportIndex::Global(global_index),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user