diff --git a/Cargo.lock b/Cargo.lock
index a33cb02f7f0d4..4d5d812895321 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -7469,7 +7469,6 @@ dependencies = [
"parity-wasm 0.42.2",
"parking_lot 0.11.1",
"paste 1.0.4",
- "regex",
"sc-executor-common",
"sc-executor-wasmi",
"sc-executor-wasmtime",
@@ -7501,8 +7500,10 @@ name = "sc-executor-common"
version = "0.9.0"
dependencies = [
"derive_more",
+ "mach",
"parity-scale-codec",
"pwasm-utils",
+ "regex",
"sp-allocator",
"sp-core",
"sp-maybe-compressed-blob",
@@ -7531,9 +7532,11 @@ name = "sc-executor-wasmtime"
version = "0.9.0"
dependencies = [
"assert_matches",
+ "bitflags",
"cfg-if 1.0.0",
"libc",
"log",
+ "mach",
"parity-scale-codec",
"parity-wasm 0.42.2",
"sc-executor-common",
diff --git a/client/executor/Cargo.toml b/client/executor/Cargo.toml
index 27e90ddcc85e6..7cb2e12fd3913 100644
--- a/client/executor/Cargo.toml
+++ b/client/executor/Cargo.toml
@@ -50,7 +50,6 @@ sc-tracing = { version = "3.0.0", path = "../tracing" }
tracing = "0.1.25"
tracing-subscriber = "0.2.18"
paste = "1.0"
-regex = "1"
[features]
default = [ "std" ]
diff --git a/client/executor/common/Cargo.toml b/client/executor/common/Cargo.toml
index cb238f3a96fb0..0a9b3ad365cbe 100644
--- a/client/executor/common/Cargo.toml
+++ b/client/executor/common/Cargo.toml
@@ -13,6 +13,12 @@ readme = "README.md"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
+[target.'cfg(target_os = "macos")'.dependencies]
+mach = "0.3"
+
+[target.'cfg(target_os = "linux")'.dependencies]
+regex = "1"
+
[dependencies]
derive_more = "0.99.2"
pwasm-utils = "0.18.0"
diff --git a/client/executor/common/src/lib.rs b/client/executor/common/src/lib.rs
index 25e06314aba39..f002514d6b35f 100644
--- a/client/executor/common/src/lib.rs
+++ b/client/executor/common/src/lib.rs
@@ -25,3 +25,4 @@ pub mod error;
pub mod sandbox;
pub mod wasm_runtime;
pub mod runtime_blob;
+pub mod test_utils;
diff --git a/client/executor/common/src/test_utils.rs b/client/executor/common/src/test_utils.rs
new file mode 100644
index 0000000000000..f0cc495a58eeb
--- /dev/null
+++ b/client/executor/common/src/test_utils.rs
@@ -0,0 +1,32 @@
+// 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 .
+
+//! Helper functions that are useful during testing. Those are mostly operating system
+//! dependend because they are used to inspect the current state of the system.
+
+#[cfg(target_os = "linux")]
+mod linux;
+
+#[cfg(target_os = "linux")]
+pub use linux::*;
+
+#[cfg(target_os = "macos")]
+mod macos;
+
+#[cfg(target_os = "macos")]
+pub use macos::*;
diff --git a/client/executor/src/integration_tests/linux/smaps.rs b/client/executor/common/src/test_utils/linux.rs
similarity index 77%
rename from client/executor/src/integration_tests/linux/smaps.rs
rename to client/executor/common/src/test_utils/linux.rs
index 8088a5a3ea952..d9d8746d87d7c 100644
--- a/client/executor/src/integration_tests/linux/smaps.rs
+++ b/client/executor/common/src/test_utils/linux.rs
@@ -16,11 +16,19 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-//! A tool for extracting information about the memory consumption of the current process from
-//! the procfs.
+//! Implementation of Linux specific tests and/or helper functions.
-use std::ops::Range;
-use std::collections::BTreeMap;
+use crate::wasm_runtime::WasmInstance;
+use std::{
+ ops::Range,
+ collections::BTreeMap,
+};
+
+/// Returns how much bytes of the instance's memory is currently resident (backed by phys mem)
+pub fn instance_resident_bytes(instance: &dyn WasmInstance) -> usize {
+ let base_addr = instance.linear_memory_range().unwrap().start;
+ Smaps::new().get_rss(base_addr).expect("failed to get rss")
+}
/// An interface to the /proc/self/smaps
///
@@ -30,6 +38,7 @@ use std::collections::BTreeMap;
pub struct Smaps(Vec<(Range, BTreeMap)>);
impl Smaps {
+ /// Create a in-memory representation of the calling processe's memory map.
pub fn new() -> Self {
let regex_start = regex::RegexBuilder::new("^([0-9a-f]+)-([0-9a-f]+)")
.multi_line(true)
@@ -68,6 +77,13 @@ impl Smaps {
Self(output)
}
+ /// Returns how much memory is currently resident in the memory mapping that is
+ /// associated with the specified address.
+ pub fn get_rss(&self, addr: usize) -> Option {
+ self.get_map(addr).get("Rss").cloned()
+ }
+
+ /// Get the mapping at the specified address.
fn get_map(&self, addr: usize) -> &BTreeMap {
&self.0
.iter()
@@ -75,8 +91,4 @@ impl Smaps {
.unwrap()
.1
}
-
- pub fn get_rss(&self, addr: usize) -> Option {
- self.get_map(addr).get("Rss").cloned()
- }
}
diff --git a/client/executor/common/src/test_utils/macos.rs b/client/executor/common/src/test_utils/macos.rs
new file mode 100644
index 0000000000000..13fa7fa3ee3ee
--- /dev/null
+++ b/client/executor/common/src/test_utils/macos.rs
@@ -0,0 +1,109 @@
+// This file is part of Substrate.
+
+// Copyright (C) 2017-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 .
+
+//! Implementation of macOS specific tests and/or helper functions.
+
+use crate::wasm_runtime::WasmInstance;
+use std::{convert::TryInto, mem::MaybeUninit, ops::Range, fmt};
+use mach::{
+ kern_return::KERN_SUCCESS,
+ traps::mach_task_self,
+ vm::mach_vm_region,
+ vm_page_size::vm_page_shift,
+ vm_region::{vm_region_extended_info, vm_region_info_t, VM_REGION_EXTENDED_INFO},
+};
+
+/// Size and metadata of a memory mapped region.
+pub struct Region {
+ /// The virtual memory range (addr..addr + size) of the region.
+ pub range: Range,
+ /// Metadata describing the memory mapping.
+ pub info: vm_region_extended_info,
+}
+
+impl fmt::Display for Region {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ writeln!(f, "{:016x?}: {:#?}", self.range, self.info)
+ }
+}
+
+impl Region {
+ /// The length of the covered area in bytes.
+ pub fn len(&self) -> u64 {
+ self.range.end - self.range.start
+ }
+}
+
+/// Returns how much bytes of the instance's memory is currently resident (backed by phys mem)
+pub fn instance_resident_bytes(instance: &dyn WasmInstance) -> usize {
+ let range = instance.linear_memory_range().unwrap();
+ let regions = get_regions((range.start as u64)..(range.end as u64)).unwrap();
+ assert_ne!(regions.len(), 0);
+ let resident_pages: u64 = regions.iter().map(|r| u64::from(r.info.pages_resident)).sum();
+ let resident_size = unsafe { resident_pages << vm_page_shift };
+ resident_size.try_into().unwrap()
+}
+
+/// Get all consecutive memory mappings that lie inside the specified range.
+///
+/// Returns an error if some parts of the range are unmapped.
+pub fn get_regions(range: Range) -> Result, String> {
+ let mut regions = Vec::new();
+ let mut addr = range.start;
+
+ loop {
+ let mut size = MaybeUninit::::uninit();
+ let mut info = MaybeUninit::::uninit();
+ let result = unsafe {
+ mach_vm_region(
+ mach_task_self(),
+ &mut addr,
+ size.as_mut_ptr(),
+ VM_REGION_EXTENDED_INFO,
+ (info.as_mut_ptr()) as vm_region_info_t,
+ &mut vm_region_extended_info::count(),
+ &mut 0,
+ )
+ };
+ assert_eq!(result, KERN_SUCCESS, "mach_vm_region returned an error");
+ if result != KERN_SUCCESS {
+ Err(format!("Failed to get region at address 0x{:016x} with error {}", addr, result))?;
+ }
+
+ let size = unsafe { size.assume_init() };
+ let info = unsafe { info.assume_init() };
+
+ // We only consider mappings that are fully enclosed by the supplied range
+ if addr < range.start || addr + size > range.end {
+ break;
+ }
+
+ regions.push(Region {
+ range: addr..(addr + size),
+ info,
+ });
+
+ // Check whether this is the last region.
+ addr += size;
+ if addr == range.end {
+ break;
+ }
+ }
+
+ Ok(regions)
+}
diff --git a/client/executor/common/src/wasm_runtime.rs b/client/executor/common/src/wasm_runtime.rs
index 12ff92a2c607f..b20b0ed1fe8a0 100644
--- a/client/executor/common/src/wasm_runtime.rs
+++ b/client/executor/common/src/wasm_runtime.rs
@@ -20,6 +20,7 @@
use crate::error::Error;
use sp_wasm_interface::Value;
+use std::ops::Range;
/// A method to be used to find the entrypoint when calling into the runtime
///
@@ -94,12 +95,14 @@ pub trait WasmInstance: Send {
/// This method is only suitable for getting immutable globals.
fn get_global_const(&self, name: &str) -> Result