Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Opt-out from fast instance reuse and foundation for other refactorings #8394

Merged
merged 15 commits into from
Apr 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions client/executor/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
derive_more = "0.99.2"
parity-wasm = "0.41.0"
pwasm-utils = "0.14.0"
codec = { package = "parity-scale-codec", version = "2.0.0" }
wasmi = "0.6.2"
sp-core = { version = "3.0.0", path = "../../../primitives/core" }
Expand Down
2 changes: 1 addition & 1 deletion client/executor/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@

pub mod error;
pub mod sandbox;
pub mod util;
pub mod wasm_runtime;
pub mod runtime_blob;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This file is part of Substrate.

// Copyright (C) 2020-2021 Parity Technologies (UK) Ltd.
// Copyright (C) 2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
Expand All @@ -16,53 +16,10 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! A set of utilities for resetting a wasm instance to its initial state.

use crate::error::{self, Error};
use super::RuntimeBlob;
use std::mem;
use parity_wasm::elements::{deserialize_buffer, DataSegment, Instruction, Module as RawModule};

/// A bunch of information collected from a WebAssembly module.
pub struct WasmModuleInfo {
raw_module: RawModule,
}

impl WasmModuleInfo {
/// Create `WasmModuleInfo` from the given wasm code.
///
/// Returns `None` if the wasm code cannot be deserialized.
pub fn new(wasm_code: &[u8]) -> Option<Self> {
let raw_module: RawModule = deserialize_buffer(wasm_code).ok()?;
Some(Self { raw_module })
}

/// Extract the data segments from the given wasm code.
///
/// Returns `Err` if the given wasm code cannot be deserialized.
fn data_segments(&self) -> Vec<DataSegment> {
self.raw_module
.data_section()
.map(|ds| ds.entries())
.unwrap_or(&[])
.to_vec()
}

/// The number of globals defined in locally in this module.
pub fn declared_globals_count(&self) -> u32 {
self.raw_module
.global_section()
.map(|gs| gs.entries().len() as u32)
.unwrap_or(0)
}

/// The number of imports of globals.
pub fn imported_globals_count(&self) -> u32 {
self.raw_module
.import_section()
.map(|is| is.globals() as u32)
.unwrap_or(0)
}
}
use parity_wasm::elements::Instruction;

/// This is a snapshot of data segments specialzied for a particular instantiation.
///
Expand All @@ -75,7 +32,7 @@ pub struct DataSegmentsSnapshot {

impl DataSegmentsSnapshot {
/// Create a snapshot from the data segments from the module.
pub fn take(module: &WasmModuleInfo) -> error::Result<Self> {
pub fn take(module: &RuntimeBlob) -> error::Result<Self> {
let data_segments = module
.data_segments()
.into_iter()
Expand Down Expand Up @@ -105,9 +62,7 @@ impl DataSegmentsSnapshot {
// if/when we gain those.
return Err(Error::ImportedGlobalsUnsupported);
}
insn => {
return Err(Error::InvalidInitializerExpression(format!("{:?}", insn)))
}
insn => return Err(Error::InvalidInitializerExpression(format!("{:?}", insn))),
};

Ok((offset, contents))
Expand Down
110 changes: 110 additions & 0 deletions client/executor/common/src/runtime_blob/globals_snapshot.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// This file is part of Substrate.

// Copyright (C) 2020-2021 Parity Technologies (UK) Ltd.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That file is technically moved, not created, so I left this.

// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use super::RuntimeBlob;

/// Saved value of particular exported global.
struct SavedValue<Global> {
/// The handle of this global which can be used to refer to this global.
handle: Global,
/// The global value that was observed during the snapshot creation.
value: sp_wasm_interface::Value,
}

/// An adapter for a wasm module instance that is focused on getting and setting globals.
pub trait InstanceGlobals {
/// A handle to a global which can be used to get or set a global variable. This is supposed to
/// be a lightweight handle, like an index or an Rc-like smart-pointer, which is cheap to clone.
type Global: Clone;
/// Get a handle to a global by it's export name.
///
/// The requested export is must exist in the exported list, and it should be a mutable global.
fn get_global(&self, export_name: &str) -> Self::Global;
/// Get the current value of the global.
fn get_global_value(&self, global: &Self::Global) -> sp_wasm_interface::Value;
/// Update the current value of the global.
///
/// The global behind the handle is guaranteed to be mutable and the value to be the same type
/// as the global.
fn set_global_value(&self, global: &Self::Global, value: sp_wasm_interface::Value);
}

/// A set of exposed mutable globals.
///
/// This is set of globals required to create a [`GlobalsSnapshot`] and that are collected from
/// a runtime blob that was instrumented by [`InstrumentModule::expose_mutable_globals`].
///
/// If the code wasn't instrumented then it would be empty and snapshot would do nothing.
pub struct ExposedMutableGlobalsSet(Vec<String>);

impl ExposedMutableGlobalsSet {
/// Collect the set from the given runtime blob. See the struct documentation for details.
pub fn collect(runtime_blob: &RuntimeBlob) -> Self {
let global_names = runtime_blob
.exported_internal_global_names()
.map(ToOwned::to_owned)
.collect();
Self(global_names)
}
}

/// A snapshot of a global variables values. This snapshot can be later used for restoring the
/// values to the preserved state.
///
/// Technically, a snapshot stores only values of mutable global variables. This is because
/// immutable global variables always have the same values.
///
/// We take it from an instance rather from a module because the start function could potentially
/// change any of the mutable global values.
pub struct GlobalsSnapshot<Global>(Vec<SavedValue<Global>>);

impl<Global> GlobalsSnapshot<Global> {
/// Take a snapshot of global variables for a given instance.
///
/// # Panics
///
/// This function panics if the instance doesn't correspond to the module from which the
/// [`ExposedMutableGlobalsSet`] was collected.
pub fn take<Instance>(mutable_globals: &ExposedMutableGlobalsSet, instance: &Instance) -> Self
where
Instance: InstanceGlobals<Global = Global>,
{
let global_names = &mutable_globals.0;
let mut saved_values = Vec::with_capacity(global_names.len());

for global_name in global_names {
let handle = instance.get_global(global_name);
let value = instance.get_global_value(&handle);
saved_values.push(SavedValue { handle, value });
}

Self(saved_values)
}

/// Apply the snapshot to the given instance.
///
/// This instance must be the same that was used for creation of this snapshot.
pub fn apply<Instance>(&self, instance: &Instance)
where
Instance: InstanceGlobals<Global = Global>,
{
for saved_value in &self.0 {
instance.set_global_value(&saved_value.handle, saved_value.value);
}
}
}
57 changes: 57 additions & 0 deletions client/executor/common/src/runtime_blob/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// This file is part of Substrate.

// Copyright (C) 2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! This module allows for inspection and instrumentation, i.e. modifying the module to alter it's
//! structure or behavior, of a wasm module.
//!
//! ## Instrumentation
//!
//! In ideal world, there would be no instrumentation. However, in the real world the execution
//! engines we use are somewhat limited in their APIs or abilities.
//!
//! To give you some examples:
//!
//! - wasmi allows reaching to non-exported mutable globals so that we could reset them.
//! Wasmtime doesn’t support that.
//!
//! We need to reset the globals because when we
//! execute the Substrate Runtime, we do not drop and create the instance anew, instead
//! we restore some selected parts of the state.
//!
//! - stack depth metering can be performed via instrumentation or deferred to the engine and say
//! be added directly in machine code. Implementing this in machine code is rather cumbersome so
//! instrumentation looks like a good solution.
//!
//! Stack depth metering is needed to make a wasm blob
//! execution deterministic, which in turn is needed by the Parachain Validation Function in Polkadot.
//!
//! ## Inspection
//!
//! Inspection of a wasm module may be needed to extract some useful information, such as to extract
//! data segment snapshot, which is helpful for quickly restoring the initial state of instances.
//! Inspection can be also useful to prove that a wasm module possesses some properties, such as,
//! is free of any floating point operations, which is a useful step towards making instances produced
//! from such a module deterministic.

mod data_segments_snapshot;
mod globals_snapshot;
mod runtime_blob;

pub use data_segments_snapshot::DataSegmentsSnapshot;
pub use globals_snapshot::{GlobalsSnapshot, ExposedMutableGlobalsSet, InstanceGlobals};
pub use runtime_blob::RuntimeBlob;
93 changes: 93 additions & 0 deletions client/executor/common/src/runtime_blob/runtime_blob.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// This file is part of Substrate.

// Copyright (C) 2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use parity_wasm::elements::{DataSegment, Module as RawModule, deserialize_buffer, serialize};

use crate::error::WasmError;

/// A bunch of information collected from a WebAssembly module.
#[derive(Clone)]
pub struct RuntimeBlob {
raw_module: RawModule,
}

impl RuntimeBlob {
/// Create `RuntimeBlob` from the given wasm code.
///
/// Returns `Err` if the wasm code cannot be deserialized.
pub fn new(wasm_code: &[u8]) -> Result<Self, WasmError> {
let raw_module: RawModule = deserialize_buffer(wasm_code)
.map_err(|e| WasmError::Other(format!("cannot deserialize module: {:?}", e)))?;
Ok(Self { raw_module })
}

/// Extract the data segments from the given wasm code.
pub(super) fn data_segments(&self) -> Vec<DataSegment> {
self.raw_module
.data_section()
.map(|ds| ds.entries())
.unwrap_or(&[])
.to_vec()
}

/// The number of globals defined in locally in this module.
pub fn declared_globals_count(&self) -> u32 {
self.raw_module
.global_section()
.map(|gs| gs.entries().len() as u32)
.unwrap_or(0)
}

/// The number of imports of globals.
pub fn imported_globals_count(&self) -> u32 {
self.raw_module
.import_section()
.map(|is| is.globals() as u32)
.unwrap_or(0)
}

/// Perform an instrumentation that makes sure that the mutable globals are exported.
pub fn expose_mutable_globals(&mut self) {
pwasm_utils::export_mutable_globals(&mut self.raw_module, "exported_internal_global");
}

/// Returns an iterator of all globals which were exported by [`expose_mutable_globals`].
pub(super) fn exported_internal_global_names<'module>(
&'module self,
) -> impl Iterator<Item = &'module str> {
let exports = self
.raw_module
.export_section()
.map(|es| es.entries())
.unwrap_or(&[]);
exports.iter().filter_map(|export| match export.internal() {
parity_wasm::elements::Internal::Global(_)
if export.field().starts_with("exported_internal_global") =>
{
Some(export.field())
}
_ => None,
})
}

/// Consumes this runtime blob and serializes it.
pub fn serialize(self) -> Vec<u8> {
serialize(self.raw_module)
.expect("serializing into a vec should succeed; qed")
}
}
20 changes: 14 additions & 6 deletions client/executor/src/wasm_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,14 +300,22 @@ pub fn create_wasm_runtime_with_code(
.map(|runtime| -> Arc<dyn WasmModule> { Arc::new(runtime) })
}
#[cfg(feature = "wasmtime")]
WasmExecutionMethod::Compiled =>
WasmExecutionMethod::Compiled => {
let blob = sc_executor_common::runtime_blob::RuntimeBlob::new(code)?;
sc_executor_wasmtime::create_runtime(
code,
heap_pages,
sc_executor_wasmtime::CodeSupplyMode::Verbatim { blob },
sc_executor_wasmtime::Config {
heap_pages: heap_pages as u32,
allow_missing_func_imports,
cache_path: cache_path.map(ToOwned::to_owned),
semantics: sc_executor_wasmtime::Semantics {
fast_instance_reuse: true,
stack_depth_metering: false,
},
},
host_functions,
allow_missing_func_imports,
cache_path,
).map(|runtime| -> Arc<dyn WasmModule> { Arc::new(runtime) }),
).map(|runtime| -> Arc<dyn WasmModule> { Arc::new(runtime) })
},
}
}

Expand Down
Loading