Skip to content

Commit

Permalink
feat: Almost maybe working gas instrumentation
Browse files Browse the repository at this point in the history
  • Loading branch information
magik6k committed Apr 19, 2022
1 parent 880cd2e commit 3cab1fa
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 24 deletions.
4 changes: 2 additions & 2 deletions fvm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
12 changes: 10 additions & 2 deletions fvm/src/call_manager/default.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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()
{
Expand All @@ -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
Expand Down
53 changes: 33 additions & 20 deletions fvm/src/machine/engine.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::ops::Deref;
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};
Expand Down Expand Up @@ -62,6 +62,8 @@ impl Default for Engine {
}
}

use wasmtime::{Global, GlobalType, Mutability, Val, ValType};

struct EngineInner {
engine: wasmtime::Engine,
module_cache: Mutex<HashMap<Cid, Module>>,
Expand Down Expand Up @@ -98,7 +100,7 @@ struct Cache<K> {
instances: HashMap<Cid, wasmtime::InstancePre<InvocationData<K>>>,
}

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
Expand Down Expand Up @@ -142,15 +144,24 @@ impl Engine {
}

fn load_raw(&self, raw_wasm: &[u8]) -> anyhow::Result<Module> {
// 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)
}

Expand Down Expand Up @@ -187,40 +198,42 @@ impl Engine {
pub fn get_instance<K: Kernel>(
&self,
store: &mut wasmtime::Store<InvocationData<K>>,
gas_global: Global,
k: &Cid,
) -> anyhow::Result<Option<wasmtime::Instance>> {
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,
instances: HashMap::new(),
}
}),
};
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<K: Kernel>(&self, kernel: K) -> wasmtime::Store<InvocationData<K>> {
wasmtime::Store::new(&self.0.engine, InvocationData::new(kernel))
pub fn new_store<K: Kernel>(&self, kernel: K) -> (wasmtime::Store<InvocationData<K>>, 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)
}
}

0 comments on commit 3cab1fa

Please sign in to comment.