Skip to content

Commit

Permalink
Add metering middleware.
Browse files Browse the repository at this point in the history
  • Loading branch information
losfair authored and nlewycky committed Nov 20, 2020
1 parent e7dd725 commit 6537720
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 0 deletions.
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ wasmer-wasi = { version = "1.0.0-alpha5", path = "lib/wasi", optional = true }
wasmer-wast = { version = "1.0.0-alpha5", path = "tests/lib/wast", optional = true }
wasmer-cache = { version = "1.0.0-alpha5", path = "lib/cache", optional = true }
wasmer-types = { version = "1.0.0-alpha5", path = "lib/wasmer-types" }
wasmer-middlewares = { version = "1.0.0-alpha.5", path = "lib/middlewares", optional = true }
cfg-if = "1.0"

[workspace]
Expand Down Expand Up @@ -79,6 +80,7 @@ default = [
"cache",
"wasi",
# "emscripten",
"middlewares",
]
engine = []
jit = [
Expand Down Expand Up @@ -117,6 +119,7 @@ llvm = [
"wasmer-compiler-llvm",
"compiler",
]
middlewares = ["wasmer-middlewares"]

# Testing features
test-singlepass = [
Expand Down
19 changes: 19 additions & 0 deletions lib/middlewares/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "wasmer-middlewares"
version = "1.0.0-alpha5"
authors = ["Wasmer Engineering Team <[email protected]>"]
description = "A collection of various useful middlewares"
license = "(Apache-2.0 WITH LLVM-exception) or MIT"
categories = ["wasm"]
keywords = ["webassembly", "wasm"]
repository = "https://github.com/wasmerio/wasmer"
readme = "README.md"
edition = "2018"

[dependencies]
wasmer = { path = "../api", version = "1.0.0-alpha5" }
wasmer-types = { path = "../wasmer-types", version = "1.0.0-alpha5" }
wasmer-vm = { path = "../vm", version = "1.0.0-alpha5" }

[badges]
maintenance = { status = "actively-developed" }
5 changes: 5 additions & 0 deletions lib/middlewares/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Wasmer Middlewares

The `wasmer-middlewares` crate is a collection of various useful middlewares:

- `metering`: A middleware for tracking how many operators are executed in total and putting a limit on the total number of operators executed.
3 changes: 3 additions & 0 deletions lib/middlewares/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod metering;

pub use metering::Metering;
76 changes: 76 additions & 0 deletions lib/middlewares/src/metering.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//! `metering` is a middleware for tracking how many operators are executed in total
//! and putting a limit on the total number of operators executed.
use std::fmt;
use std::sync::Mutex;
use wasmer::wasmparser::{BinaryReader, Operator, Result as WpResult};
use wasmer::{
FunctionMiddleware, GlobalInit, GlobalType, LocalFunctionIndex, ModuleMiddleware, Mutability,
Type,
};
use wasmer_types::GlobalIndex;
use wasmer_vm::ModuleInfo;

/// The module-level metering middleware.
///
/// # Panic
///
/// An instance of `Metering` should not be shared among different modules, since it tracks
/// module-specific information like the global index to store metering state. Attempts to use
/// a `Metering` instance from multiple modules will result in a panic.
pub struct Metering<F: Fn(&Operator) -> u64 + Send + Sync> {
/// Initial limit of points.
initial_limit: u64,

/// Function that maps each operator to a cost in "points".
cost_function: F,

/// The global index in the current module for remaining points.
remaining_points_index: Mutex<Option<GlobalIndex>>,
}

impl<F: Fn(&Operator) -> u64 + Send + Sync> Metering<F> {
/// Creates a `Metering` middleware.
pub fn new(initial_limit: u64, cost_function: F) -> Self {
Self {
initial_limit,
cost_function,
remaining_points_index: Mutex::new(None),
}
}
}

impl<F: Fn(&Operator) -> u64 + Send + Sync> fmt::Debug for Metering<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Metering")
.field("initial_limit", &self.initial_limit)
.field("cost_function", &"<function>")
.field("remaining_points_index", &self.remaining_points_index)
.finish()
}
}

impl<F: Fn(&Operator) -> u64 + Send + Sync> ModuleMiddleware for Metering<F> {
/// Generates a `FunctionMiddleware` for a given function.
fn generate_function_middleware(&self, _: LocalFunctionIndex) -> Box<dyn FunctionMiddleware> {
unimplemented!();
}

/// Transforms a `ModuleInfo` struct in-place. This is called before application on functions begins.
fn transform_module_info(&self, module_info: &mut ModuleInfo) {
let mut remaining_points_index = self.remaining_points_index.lock().unwrap();
if remaining_points_index.is_some() {
panic!("Metering::transform_module_info: Attempting to use a `Metering` middleware from multiple modules.");
}

// Append a global for remaining points and initialize it.
*remaining_points_index = Some(
module_info
.globals
.push(GlobalType::new(Type::I64, Mutability::Var)),
);
module_info
.global_initializers
.push(GlobalInit::I64Const(self.initial_limit as i64));
}
}

0 comments on commit 6537720

Please sign in to comment.