diff --git a/CHANGELOG.md b/CHANGELOG.md index cde45b92e0d..2c716afb488 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,11 @@ Blocks of changes will separated by version increments. ## **[Unreleased]** -- [#835](https://github.com/wasmerio/wasmer/pull/836) Update Cranelift fork version to `0.44.0` +- [#836](https://github.com/wasmerio/wasmer/pull/836) Update Cranelift fork version to `0.44.0` - [#839](https://github.com/wasmerio/wasmer/pull/839) Change supported version to stable Rust 1.37+ - [#834](https://github.com/wasmerio/wasmer/pull/834) Fix panic when unwraping `wasmer` arguments +- [#835](https://github.com/wasmerio/wasmer/pull/835) Add parallel execution example (independent instances created from the same `ImportObject` and `Module` run with rayon) +- [#834](https://github.com/wasmerio/wasmer/pull/834) Fix panic when parsing numerical arguments for no-ABI targets run with the wasmer binary - [#833](https://github.com/wasmerio/wasmer/pull/833) Add doc example of using ImportObject's new `maybe_with_namespace` method - [#832](https://github.com/wasmerio/wasmer/pull/832) Delete unused runtime ABI - [#809](https://github.com/wasmerio/wasmer/pull/809) Fix bugs leading to panics in `LocalBacking`. diff --git a/Cargo.lock b/Cargo.lock index 4e2844e20e1..fcb1b9d24f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -734,6 +734,11 @@ dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "md5" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "memchr" version = "2.2.1" @@ -828,6 +833,24 @@ dependencies = [ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "parallel" +version = "0.1.0" +dependencies = [ + "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-runtime 0.7.0", + "wasmer-runtime-core 0.7.0", +] + +[[package]] +name = "parallel-guest" +version = "0.1.0" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "md5 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parking_lot" version = "0.9.0" @@ -1884,6 +1907,7 @@ dependencies = [ "checksum llvm-sys 80.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2110cd4daf9cd8e39dd3b933b1a2a2ac7315e91f7c92b3a20beab526c63b5978" "checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +"checksum md5 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e6bcd6433cff03a4bfc3d9834d504467db1f1cf6d0ea765d37d330249ed629d" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" "checksum memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff" "checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" diff --git a/Cargo.toml b/Cargo.toml index e9916b3ca5e..32757c6810f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,8 @@ members = [ "lib/emscripten-tests", "lib/middleware-common-tests", "examples/plugin-for-example", + "examples/parallel", + "examples/parallel-guest", ] [build-dependencies] diff --git a/examples/parallel-guest.wasm b/examples/parallel-guest.wasm new file mode 100755 index 00000000000..0f8d24dc7fd Binary files /dev/null and b/examples/parallel-guest.wasm differ diff --git a/examples/parallel-guest/Cargo.toml b/examples/parallel-guest/Cargo.toml new file mode 100644 index 00000000000..e233243b92d --- /dev/null +++ b/examples/parallel-guest/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "parallel-guest" +version = "0.1.0" +authors = ["The Wasmer Engineering Team "] +license = "MIT" +edition = "2018" +publish = false + +[dependencies] +md5 = "0.6" +lazy_static = "1" \ No newline at end of file diff --git a/examples/parallel-guest/src/main.rs b/examples/parallel-guest/src/main.rs new file mode 100644 index 00000000000..ead45932db9 --- /dev/null +++ b/examples/parallel-guest/src/main.rs @@ -0,0 +1,82 @@ +#[macro_use] +extern crate lazy_static; + +extern "C" { + fn get_hashed_password(ptr: u32, len: u32) -> u32; + fn print_char(c: u32); +} + +fn print_str(s: &str) { + for c in s.chars() { + unsafe { print_char(c as u32) }; + } + unsafe { print_char(b'\n' as u32) }; +} + +fn load_hashed_password() -> Option { + let mut buffer = String::with_capacity(32); + for _ in 0..32 { + buffer.push(0 as char); + } + let result = + unsafe { get_hashed_password(buffer.as_mut_ptr() as u32, buffer.capacity() as u32) }; + + if result == 0 { + Some(buffer) + } else { + None + } +} + +lazy_static! { + static ref HASHED_PASSWORD: String = load_hashed_password().unwrap(); +} + +static PASSWORD_CHARS: &'static [u8] = b"abcdefghijklmnopqrstuvwxyz0123456789"; + +// for simplicty we define a scheme for mapping numbers onto passwords +fn num_to_password(mut num: u64) -> String { + let mut extra_zero = num == 0; + let mut out = String::new(); + while num > 0 { + out.push(PASSWORD_CHARS[num as usize % PASSWORD_CHARS.len()] as char); + extra_zero = extra_zero || num == PASSWORD_CHARS.len() as u64; + num /= PASSWORD_CHARS.len() as u64; + } + + if extra_zero { + out.push(PASSWORD_CHARS[0] as char); + } + + out +} + +#[repr(C)] +struct RetStr { + ptr: u32, + len: u32, +} + +// returns a (pointer, len) to the password or null +#[no_mangle] +fn check_password(from: u64, to: u64) -> u64 { + for i in from..to { + let password = num_to_password(i); + let digest = md5::compute(&password); + + let hash_as_str = format!("{:x}", digest); + if hash_as_str == *HASHED_PASSWORD { + let ret = RetStr { + ptr: password.as_ptr() as usize as u32, + len: password.len() as u32, + }; + // leak the data so ending the function doesn't corrupt it, if we cared the host could free it after + std::mem::forget(password); + return unsafe { std::mem::transmute(ret) }; + } + } + + return 0; +} + +fn main() {} diff --git a/examples/parallel/Cargo.toml b/examples/parallel/Cargo.toml new file mode 100644 index 00000000000..2353cf232bc --- /dev/null +++ b/examples/parallel/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "parallel" +version = "0.1.0" +authors = ["The Wasmer Engineering Team "] +edition = "2018" +repository = "https://github.com/wasmerio/wasmer" +publish = false +license = "MIT" + +[dependencies] +rayon = "1.2" +time = "0.1" +wasmer-runtime = { path = "../../lib/runtime" } +wasmer-runtime-core = { path = "../../lib/runtime-core" } diff --git a/examples/parallel/README.md b/examples/parallel/README.md new file mode 100644 index 00000000000..fd4d1ad70fd --- /dev/null +++ b/examples/parallel/README.md @@ -0,0 +1,5 @@ +# Parallel Wasmer example + +This example shows executing independent code from multiple threads on an "embarassingly parallel" problem + +This is a toy example of cracking md5 hashes. This is not a benchmark. This example is not optimized, it will compare poorly to an implementation that is. diff --git a/examples/parallel/src/main.rs b/examples/parallel/src/main.rs new file mode 100644 index 00000000000..b68b0eaeba3 --- /dev/null +++ b/examples/parallel/src/main.rs @@ -0,0 +1,137 @@ +use rayon::prelude::*; +use wasmer_runtime::{compile_with, compiler_for_backend, func, imports, instantiate, Backend}; +use wasmer_runtime_core::{ + memory::ptr::{Array, WasmPtr}, + vm::Ctx, +}; + +static PLUGIN_LOCATION: &'static str = "../parallel-guest.wasm"; + +fn get_hashed_password(ctx: &mut Ctx, ptr: WasmPtr, len: u32) -> u32 { + // "hard" password - 7 characters + //let password = b"2ab96390c7dbe3439de74d0c9b0b1767"; + // "easy" password - 5 characters + let password = b"ab56b4d92b40713acc5af89985d4b786"; + let memory = ctx.memory(0); + if let Some(writer) = ptr.deref(memory, 0, len) { + for (i, byte) in password.iter().enumerate() { + writer[i].set(*byte) + } + + 0 + } else { + u32::max_value() + } +} + +#[repr(C)] +struct RetStr { + ptr: u32, + len: u32, +} + +fn print_char(_cxt: &mut Ctx, c: u32) { + print!("{}", c as u8 as char); +} + +fn main() { + let wasm_bytes = std::fs::read(PLUGIN_LOCATION).expect(&format!( + "Could not read in WASM plugin at {}", + PLUGIN_LOCATION + )); + + let imports = imports! { + "env" => { + "get_hashed_password" => func!(get_hashed_password), + "print_char" => func!(print_char), + }, + }; + let compiler = compiler_for_backend(Backend::default()).unwrap(); + let module = compile_with(&wasm_bytes[..], compiler.as_ref()).unwrap(); + + println!("Parallel"); + let start_ts = time::SteadyTime::now(); + for outer in 0..1000u64 { + let start = outer * 1000; + let end = start + 1000; + let out = (start..=end) + .into_par_iter() + .filter_map(|i| { + let instance = module + .clone() + .instantiate(&imports) + .expect("failed to instantiate wasm module"); + let check_password = instance.func::<(u64, u64), u64>("check_password").unwrap(); + let j = i * 10000; + let result = check_password.call(j, j + 10000).unwrap(); + print!("."); + use std::io::Write; + std::io::stdout().flush().unwrap(); + if result != 0 { + let res: RetStr = unsafe { std::mem::transmute(result) }; + + let ctx = instance.context(); + let memory = ctx.memory(0); + let wasm_ptr: WasmPtr = WasmPtr::new(res.ptr); + let password_str = wasm_ptr + .get_utf8_string(memory, res.len) + .unwrap() + .to_string(); + Some(password_str) + } else { + None + } + }) + .find_first(|_: &String| true); + if out.is_some() { + let end_ts = time::SteadyTime::now(); + let delta = end_ts - start_ts; + println!( + "Password cracked: \"{}\" in {}.{:03}", + out.unwrap(), + delta.num_seconds(), + (delta.num_milliseconds() % 1000), + ); + break; + } + } + + println!("Serial:"); + let start_ts = time::SteadyTime::now(); + let instance = + instantiate(&wasm_bytes[..], &imports).expect("failed to instantiate wasm module"); + + let check_password = instance.func::<(u64, u64), u64>("check_password").unwrap(); + + let mut out: Option = None; + for i in (0..=u64::max_value()).step_by(10000) { + let result = check_password.call(i, i + 10000).unwrap(); + print!("."); + use std::io::Write; + std::io::stdout().flush().unwrap(); + if result != 0 { + out = Some(unsafe { std::mem::transmute(result) }); + break; + } + } + println!(""); + + if let Some(res) = out { + let ctx = instance.context(); + let memory = ctx.memory(0); + let wasm_ptr: WasmPtr = WasmPtr::new(res.ptr); + + let password_str = wasm_ptr.get_utf8_string(memory, res.len).unwrap(); + + let end_ts = time::SteadyTime::now(); + let delta = end_ts - start_ts; + println!( + "Password cracked: \"{}\" in {}.{:03}", + password_str, + delta.num_seconds(), + (delta.num_milliseconds() % 1000), + ); + } else { + println!("Password not found!"); + } +}