mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-08 05:38:19 +00:00
Merge branch 'master' into compilerconfig-owned
This commit is contained in:
@@ -7,6 +7,10 @@
|
||||
|
||||
## **[Unreleased]**
|
||||
|
||||
### Added
|
||||
|
||||
* [#1867](https://github.com/wasmerio/wasmer/pull/1867) Added `Metering::get_remaining_points` and `Metering::set_remaining_points`
|
||||
|
||||
### Changed
|
||||
|
||||
- [#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"
|
||||
path = "examples/hello_world.rs"
|
||||
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::{
|
||||
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::{
|
||||
ChainableNamedResolver, DeserializeError, Engine, Export, FrameInfo, LinkError, NamedResolver,
|
||||
NamedResolverChain, Resolver, RuntimeError, SerializeError,
|
||||
};
|
||||
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,
|
||||
};
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ use wasmer::wasmparser::{
|
||||
Operator, Result as WpResult, Type as WpType, TypeOrFuncType as WpTypeOrFuncType,
|
||||
};
|
||||
use wasmer::{
|
||||
FunctionMiddleware, GlobalInit, GlobalType, LocalFunctionIndex, MiddlewareReaderState,
|
||||
ModuleMiddleware, Mutability, Type,
|
||||
ExportIndex, FunctionMiddleware, GlobalInit, GlobalType, Instance, LocalFunctionIndex,
|
||||
MiddlewareReaderState, ModuleMiddleware, Mutability, Type, Value,
|
||||
};
|
||||
use wasmer_types::GlobalIndex;
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
@@ -86,14 +110,18 @@ impl<F: Fn(&Operator) -> u64 + Copy + Clone + Send + Sync + 'static> ModuleMiddl
|
||||
}
|
||||
|
||||
// Append a global for remaining points and initialize it.
|
||||
*remaining_points_index = Some(
|
||||
module_info
|
||||
let global_index = module_info
|
||||
.globals
|
||||
.push(GlobalType::new(Type::I64, Mutability::Var)),
|
||||
);
|
||||
.push(GlobalType::new(Type::I64, Mutability::Var));
|
||||
*remaining_points_index = Some(global_index.clone());
|
||||
module_info
|
||||
.global_initializers
|
||||
.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