From 3cab1fa485d3f081e12979be4dc373d3b3f9318e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 19 Apr 2022 16:54:59 +0200 Subject: [PATCH] feat: Almost maybe working gas instrumentation --- fvm/Cargo.toml | 4 +-- fvm/src/call_manager/default.rs | 12 ++++++-- fvm/src/machine/engine.rs | 53 ++++++++++++++++++++------------- 3 files changed, 45 insertions(+), 24 deletions(-) diff --git a/fvm/Cargo.toml b/fvm/Cargo.toml index 16c45f307e..fda9dfcf16 100644 --- a/fvm/Cargo.toml +++ b/fvm/Cargo.toml @@ -39,8 +39,8 @@ log = "0.4.14" byteorder = "1.4.3" anymap = "0.12.1" blake2b_simd = "1.0.0" -fvm-wasm-instrument = { version = "0.1.2", features = ["bulk"] } -#fvm-wasm-instrument = { path = "../../wasm-instrument", features = ["bulk"] } +#fvm-wasm-instrument = { version = "0.1.2", features = ["bulk"] } +fvm-wasm-instrument = { path = "../../wasm-instrument", features = ["bulk"] } [dependencies.wasmtime] version = "0.35.2" diff --git a/fvm/src/call_manager/default.rs b/fvm/src/call_manager/default.rs index 92239cd5fd..7840c8790b 100644 --- a/fvm/src/call_manager/default.rs +++ b/fvm/src/call_manager/default.rs @@ -1,3 +1,4 @@ +use std::panic::catch_unwind; use anyhow::Context as _; use derive_more::{Deref, DerefMut}; use fvm_ipld_encoding::{RawBytes, DAG_CBOR}; @@ -294,11 +295,11 @@ where }; // Make a store. - let mut store = engine.new_store(kernel); + let (mut store, gg) = engine.new_store(kernel); // Instantiate the module. let instance = match engine - .get_instance(&mut store, &state.code) + .get_instance(&mut store, gg, &state.code) .and_then(|i| i.context("actor code not found")) .or_fatal() { @@ -317,6 +318,13 @@ where // Invoke it. let return_block_id = invoke.call(&mut store, (param_id,))?; + use wasmtime::Val; + + /*match gg.get(&mut store) { + Val::I32(v) => println!("GAS REMAIN {}", v), + _ => println!("wtf9"), + };*/ + // Extract the return value, if there is one. let return_value: RawBytes = if return_block_id > NO_DATA_BLOCK_ID { let (code, ret) = store diff --git a/fvm/src/machine/engine.rs b/fvm/src/machine/engine.rs index 919e7fabcb..7028ba85aa 100644 --- a/fvm/src/machine/engine.rs +++ b/fvm/src/machine/engine.rs @@ -1,4 +1,3 @@ -use std::collections::hash_map::Entry; use std::collections::HashMap; use std::ops::Deref; use std::sync::{Arc, Mutex}; @@ -6,6 +5,7 @@ use std::sync::{Arc, Mutex}; use anyhow::anyhow; use cid::Cid; use fvm_ipld_blockstore::Blockstore; +use fvm_wasm_instrument::gas_metering::ConstantCostRules; use wasmtime::{Linker, Module, OptLevel}; use crate::syscalls::{bind_syscalls, InvocationData}; @@ -62,6 +62,8 @@ impl Default for Engine { } } +use wasmtime::{Global, GlobalType, Mutability, Val, ValType}; + struct EngineInner { engine: wasmtime::Engine, module_cache: Mutex>, @@ -98,7 +100,7 @@ struct Cache { instances: HashMap>>, } -const defaultStackLimit: u32 = 100000; // todo figure out a good number +const DEFAULT_STACK_LIMIT: u32 = 100000; // todo figure out a good number impl Engine { /// Instantiates and caches the Wasm modules for the bytecodes addressed by @@ -142,15 +144,24 @@ impl Engine { } fn load_raw(&self, raw_wasm: &[u8]) -> anyhow::Result { + // Note: when adding debug mode support (with recorded syscall replay) don't instrument to + // avoid breaking debug info + + use fvm_wasm_instrument::gas_metering::inject; use fvm_wasm_instrument::inject_stack_limiter; - use parity_wasm::deserialize_buffer; + use fvm_wasm_instrument::parity_wasm::deserialize_buffer; let m = deserialize_buffer(raw_wasm)?; - let m = inject_stack_limiter(m, defaultStackLimit).map_err(anyhow::Error::msg)?; + let m = inject_stack_limiter(m, DEFAULT_STACK_LIMIT).map_err(anyhow::Error::msg)?; + + let m = inject(m, &ConstantCostRules::default(), "env") + .map_err(|_| anyhow::Error::msg("injecting gas counter failed"))?; + let wasm = m.to_bytes()?; let module = Module::from_binary(&self.0.engine, wasm.as_slice())?; + Ok(module) } @@ -187,14 +198,17 @@ impl Engine { pub fn get_instance( &self, store: &mut wasmtime::Store>, + gas_global: Global, k: &Cid, ) -> anyhow::Result> { let mut instance_cache = self.0.instance_cache.lock().expect("cache poisoned"); let cache = match instance_cache.entry() { - anymap::Entry::Occupied(e) => e.into_mut(), + anymap::Entry::Occupied(e) => e.into_mut(), // todo what about gas here? anymap::Entry::Vacant(e) => e.insert({ let mut linker = Linker::new(&self.0.engine); + linker.allow_shadowing(true); + bind_syscalls(&mut linker)?; Cache { linker, @@ -202,25 +216,24 @@ impl Engine { } }), }; - let instance_pre = match cache.instances.entry(*k) { - Entry::Occupied(e) => e.into_mut(), - Entry::Vacant(e) => { - let module_cache = self.0.module_cache.lock().expect("module_cache poisoned"); - let module = match module_cache.get(k) { - Some(module) => module, - None => return Ok(None), - }; - // We can cache the "pre instance" because our linker only has host functions. - let pre = cache.linker.instantiate_pre(&mut *store, module)?; - e.insert(pre) - } + cache.linker.define("env", "gas", gas_global)?; + + let module_cache = self.0.module_cache.lock().expect("module_cache poisoned"); + let module = match module_cache.get(k) { + Some(module) => module, + None => return Ok(None), }; - let instance = instance_pre.instantiate(&mut *store)?; + let instance = cache.linker.instantiate(&mut *store, module)?; Ok(Some(instance)) } /// Construct a new wasmtime "store" from the given kernel. - pub fn new_store(&self, kernel: K) -> wasmtime::Store> { - wasmtime::Store::new(&self.0.engine, InvocationData::new(kernel)) + pub fn new_store(&self, kernel: K) -> (wasmtime::Store>, Global) { + let mut store = wasmtime::Store::new(&self.0.engine, InvocationData::new(kernel)); + + let ggtype = GlobalType::new(ValType::I32, Mutability::Var); + let gg = Global::new(&mut store, ggtype, Val::I32(2000000000)).unwrap(); // todo no unwrap + + (store, gg) } }