mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-22 13:11:32 +00:00
add logging to plugin example, improve swap, and add some consts
This commit is contained in:
Binary file not shown.
@ -40,4 +40,6 @@ In this example, we instantiate a system with an extended (WASI)[wasi] ABI, allo
|
|||||||
|
|
||||||
Because the Rust WASI doesn't support the crate type of `cdylib`, we have to include a main function which we don't use. This is being discussed [here](https://github.com/WebAssembly/WASI/issues/24).
|
Because the Rust WASI doesn't support the crate type of `cdylib`, we have to include a main function which we don't use. This is being discussed [here](https://github.com/WebAssembly/WASI/issues/24).
|
||||||
|
|
||||||
|
We call the main function to initialize WASI's libpreopen internal datastructures and have the module call back into the host to set swap out the modules implementation of stdout. The host then provides a wrapper around stdout, allowing the guest's writes to stdout to be formatted in a host-appropriate manner.
|
||||||
|
|
||||||
[wasi]: https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/
|
[wasi]: https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
fn it_works() -> i32;
|
fn it_works() -> i32;
|
||||||
|
fn initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@ -9,4 +10,6 @@ pub fn plugin_entrypoint(n: i32) -> i32 {
|
|||||||
result + n
|
result + n
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() {}
|
pub fn main() {
|
||||||
|
unsafe { initialize() };
|
||||||
|
}
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
use wasmer_runtime::{func, imports, instantiate};
|
use wasmer_runtime::{func, imports, instantiate};
|
||||||
use wasmer_runtime_core::vm::Ctx;
|
use wasmer_runtime_core::vm::Ctx;
|
||||||
use wasmer_wasi::generate_import_object;
|
use wasmer_wasi::{
|
||||||
|
generate_import_object,
|
||||||
|
state::{self, WasiFile},
|
||||||
|
types,
|
||||||
|
};
|
||||||
|
|
||||||
static PLUGIN_LOCATION: &'static str = "examples/plugin-for-example.wasm";
|
static PLUGIN_LOCATION: &'static str = "examples/plugin-for-example.wasm";
|
||||||
|
|
||||||
@ -9,6 +13,107 @@ fn it_works(_ctx: &mut Ctx) -> i32 {
|
|||||||
5
|
5
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct LoggingWrapper {
|
||||||
|
pub wasm_module_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
// std io trait boiler plate so we can implement WasiFile
|
||||||
|
// LoggingWrapper is a write-only type so we just want to immediately
|
||||||
|
// fail when reading or Seeking
|
||||||
|
impl std::io::Read for LoggingWrapper {
|
||||||
|
fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
|
||||||
|
Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::Other,
|
||||||
|
"can not read from logging wrapper",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
fn read_to_end(&mut self, _buf: &mut Vec<u8>) -> std::io::Result<usize> {
|
||||||
|
Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::Other,
|
||||||
|
"can not read from logging wrapper",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
fn read_to_string(&mut self, _buf: &mut String) -> std::io::Result<usize> {
|
||||||
|
Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::Other,
|
||||||
|
"can not read from logging wrapper",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
fn read_exact(&mut self, _buf: &mut [u8]) -> std::io::Result<()> {
|
||||||
|
Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::Other,
|
||||||
|
"can not read from logging wrapper",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::io::Seek for LoggingWrapper {
|
||||||
|
fn seek(&mut self, _pos: std::io::SeekFrom) -> std::io::Result<u64> {
|
||||||
|
Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::Other,
|
||||||
|
"can not seek logging wrapper",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::io::Write for LoggingWrapper {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
|
let stdout = std::io::stdout();
|
||||||
|
let mut out = stdout.lock();
|
||||||
|
out.write(b"[")?;
|
||||||
|
out.write(self.wasm_module_name.as_bytes())?;
|
||||||
|
out.write(b"]: ")?;
|
||||||
|
out.write(buf)
|
||||||
|
}
|
||||||
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
|
std::io::stdout().flush()
|
||||||
|
}
|
||||||
|
fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
|
||||||
|
let stdout = std::io::stdout();
|
||||||
|
let mut out = stdout.lock();
|
||||||
|
out.write(b"[")?;
|
||||||
|
out.write(self.wasm_module_name.as_bytes())?;
|
||||||
|
out.write(b"]: ")?;
|
||||||
|
out.write_all(buf)
|
||||||
|
}
|
||||||
|
fn write_fmt(&mut self, fmt: std::fmt::Arguments) -> std::io::Result<()> {
|
||||||
|
let stdout = std::io::stdout();
|
||||||
|
let mut out = stdout.lock();
|
||||||
|
out.write(b"[")?;
|
||||||
|
out.write(self.wasm_module_name.as_bytes())?;
|
||||||
|
out.write(b"]: ")?;
|
||||||
|
out.write_fmt(fmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the WasiFile methods aren't relevant for a write-only Stdout-like implementation
|
||||||
|
impl WasiFile for LoggingWrapper {
|
||||||
|
fn last_accessed(&self) -> u64 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
fn last_modified(&self) -> u64 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
fn created_time(&self) -> u64 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
fn size(&self) -> u64 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called by the program when it wants to set itself up
|
||||||
|
fn initialize(ctx: &mut Ctx) {
|
||||||
|
let state = state::get_wasi_state(ctx);
|
||||||
|
let wasi_file_inner = LoggingWrapper {
|
||||||
|
wasm_module_name: "example module name".to_string(),
|
||||||
|
};
|
||||||
|
// swap stdout with our new wasifile
|
||||||
|
let _old_stdout = state
|
||||||
|
.fs
|
||||||
|
.swap_file(types::__WASI_STDOUT_FILENO, Box::new(wasi_file_inner))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Load the plugin data
|
// Load the plugin data
|
||||||
let wasm_bytes = std::fs::read(PLUGIN_LOCATION).expect(&format!(
|
let wasm_bytes = std::fs::read(PLUGIN_LOCATION).expect(&format!(
|
||||||
@ -22,6 +127,7 @@ fn main() {
|
|||||||
let custom_imports = imports! {
|
let custom_imports = imports! {
|
||||||
"env" => {
|
"env" => {
|
||||||
"it_works" => func!(it_works),
|
"it_works" => func!(it_works),
|
||||||
|
"initialize" => func!(initialize),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
// The WASI imports object contains all required import functions for a WASI module to run.
|
// The WASI imports object contains all required import functions for a WASI module to run.
|
||||||
@ -30,6 +136,8 @@ fn main() {
|
|||||||
let instance =
|
let instance =
|
||||||
instantiate(&wasm_bytes[..], &base_imports).expect("failed to instantiate wasm module");
|
instantiate(&wasm_bytes[..], &base_imports).expect("failed to instantiate wasm module");
|
||||||
|
|
||||||
|
let main = instance.func::<(), ()>("_start").unwrap();
|
||||||
|
main.call().expect("Could not initialize");
|
||||||
// get a reference to the function "plugin_entrypoint" which takes an i32 and returns an i32
|
// get a reference to the function "plugin_entrypoint" which takes an i32 and returns an i32
|
||||||
let entry_point = instance.func::<(i32), i32>("plugin_entrypoint").unwrap();
|
let entry_point = instance.func::<(i32), i32>("plugin_entrypoint").unwrap();
|
||||||
// call the "entry_point" function in WebAssembly with the number "2" as the i32 argument
|
// call the "entry_point" function in WebAssembly with the number "2" as the i32 argument
|
||||||
|
@ -19,9 +19,14 @@ use std::{
|
|||||||
};
|
};
|
||||||
use wasmer_runtime_core::{debug, vm::Ctx};
|
use wasmer_runtime_core::{debug, vm::Ctx};
|
||||||
|
|
||||||
|
/// the fd value of the virtual root
|
||||||
|
pub const VIRTUAL_ROOT_FD: __wasi_fd_t = 4;
|
||||||
|
/// all the rights enabled
|
||||||
|
pub const ALL_RIGHTS: __wasi_rights_t = 0x1FFFFFFF;
|
||||||
|
|
||||||
/// Get WasiState from a Ctx
|
/// Get WasiState from a Ctx
|
||||||
pub unsafe fn get_wasi_state(ctx: &mut Ctx) -> &mut WasiState {
|
pub fn get_wasi_state(ctx: &mut Ctx) -> &mut WasiState {
|
||||||
&mut *(ctx.data as *mut WasiState)
|
unsafe { &mut *(ctx.data as *mut WasiState) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A completely aribtrary "big enough" number used as the upper limit for
|
/// A completely aribtrary "big enough" number used as the upper limit for
|
||||||
@ -607,16 +612,35 @@ impl WasiFs {
|
|||||||
fd: __wasi_fd_t,
|
fd: __wasi_fd_t,
|
||||||
file: Box<dyn WasiFile>,
|
file: Box<dyn WasiFile>,
|
||||||
) -> Result<Option<Box<dyn WasiFile>>, WasiFsError> {
|
) -> Result<Option<Box<dyn WasiFile>>, WasiFsError> {
|
||||||
let base_fd = self.get_fd(fd).map_err(WasiFsError::from_wasi_err)?;
|
match fd {
|
||||||
let base_inode = base_fd.inode;
|
__WASI_STDIN_FILENO => {
|
||||||
|
let mut ret = file;
|
||||||
match &mut self.inodes[base_inode].kind {
|
std::mem::swap(&mut self.stdin, &mut ret);
|
||||||
Kind::File { ref mut handle, .. } => {
|
Ok(Some(ret))
|
||||||
let mut ret = Some(file);
|
}
|
||||||
std::mem::swap(handle, &mut ret);
|
__WASI_STDOUT_FILENO => {
|
||||||
Ok(ret)
|
let mut ret = file;
|
||||||
|
std::mem::swap(&mut self.stdout, &mut ret);
|
||||||
|
Ok(Some(ret))
|
||||||
|
}
|
||||||
|
__WASI_STDERR_FILENO => {
|
||||||
|
let mut ret = file;
|
||||||
|
std::mem::swap(&mut self.stderr, &mut ret);
|
||||||
|
Ok(Some(ret))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let base_fd = self.get_fd(fd).map_err(WasiFsError::from_wasi_err)?;
|
||||||
|
let base_inode = base_fd.inode;
|
||||||
|
|
||||||
|
match &mut self.inodes[base_inode].kind {
|
||||||
|
Kind::File { ref mut handle, .. } => {
|
||||||
|
let mut ret = Some(file);
|
||||||
|
std::mem::swap(handle, &mut ret);
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
_ => return Err(WasiFsError::NotAFile),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => Err(WasiFsError::NotAFile),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user