422: Improved Emscripten debugging + optipng r=syrusakbary a=MarkMcCaskey

Improved Emscripten debugging + optipng

Co-authored-by: Mark McCaskey <mark@wasmer.io>
Co-authored-by: Syrus <me@syrusakbary.com>
This commit is contained in:
bors[bot] 2019-05-06 00:25:53 +00:00
commit 89b92e3305
14 changed files with 156 additions and 48 deletions

View File

@ -5,6 +5,7 @@ All PRs to the Wasmer repository must add to this file.
Blocks of changes will separated by version increments.
## **[Unreleased]**
- [#422](https://github.com/wasmerio/wasmer/pull/422) Improved Emscripten functions to run optipng and pngquant compiled to wasm
- [#409](https://github.com/wasmerio/wasmer/pull/409) Improved Emscripten functions to run JavascriptCore compiled to wasm
- [#399](https://github.com/wasmerio/wasmer/pull/399) Add example of using a plugin extended from WASI
- [#397](https://github.com/wasmerio/wasmer/pull/397) Fix WASI fs abstraction to work on Windows

View File

@ -43,7 +43,8 @@ rustc_version = "0.2.3"
[features]
default = ["fast-tests", "wasi"]
debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"]
debug = ["wasmer-runtime-core/debug"]
extra-debug = ["wasmer-clif-backend/debug", "wasmer-runtime-core/debug"]
# This feature will allow cargo test to run much faster
fast-tests = []
"backend:llvm" = ["wasmer-llvm-backend"]

View File

@ -84,5 +84,8 @@ production-release:
debug-release:
cargo build --release --features "debug"
extra-debug-release:
cargo build --release --features "extra-debug"
publish-release:
ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -delete ${VERSION} ./artifacts/

9
examples/README.md Normal file
View File

@ -0,0 +1,9 @@
# WebAssembly Examples
In this directory you can find a set of different examples that can run on the Wasmer WebAssembly runtime:
* Cowsay (WASI ABI) [[source-code](https://github.com/wapm-packages/cowsay)] [[wapm-package](https://wapm.io/package/cowsay)]
* Nginx (Emscripten ABI) [[source-code](https://github.com/wapm-packages/nginx)] [[wapm-package](https://wapm.io/package/nginx)]
* Lua (Emscripten ABI) [[source-code](https://github.com/wapm-packages/lua)] [[wapm-package](https://wapm.io/package/lua)]
* PHP (Emscripten ABI) [[source-code](https://github.com/wapm-packages/php)] [[wapm-package](https://wapm.io/package/php)]
* SQLite (Emscripten ABI) [[source-code](https://github.com/wapm-packages/sqlite)] [[wapm-package](https://wapm.io/package/sqlite)]

View File

@ -55,7 +55,7 @@ pub fn _dladdr(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 {
0
}
pub fn _pthread_attr_init(_ctx: &mut Ctx, _a: i32) -> i32 {
debug!("emscripten::_pthread_attr_init");
debug!("emscripten::_pthread_attr_init({})", _a);
0
}
pub fn _pthread_attr_destroy(_ctx: &mut Ctx, _a: i32) -> i32 {
@ -68,7 +68,10 @@ pub fn _pthread_attr_getstack(
_stacksize: i32,
_other: i32,
) -> i32 {
debug!("emscripten::_pthread_attr_getstack");
debug!(
"emscripten::_pthread_attr_getstack({}, {}, {})",
_stackaddr, _stacksize, _other
);
// TODO: Translate from Emscripten
// HEAP32[stackaddr >> 2] = STACK_BASE;
// HEAP32[stacksize >> 2] = TOTAL_STACK;
@ -87,7 +90,7 @@ pub fn _pthread_getspecific(_ctx: &mut Ctx, _a: i32) -> i32 {
0
}
pub fn _pthread_getattr_np(_ctx: &mut Ctx, _thread: i32, _attr: i32) -> i32 {
debug!("emscripten::_pthread_getattr_np");
debug!("emscripten::_pthread_getattr_np({}, {})", _thread, _attr);
0
}
pub fn _pthread_setspecific(_ctx: &mut Ctx, _a: i32, _b: i32) -> i32 {
@ -732,6 +735,10 @@ pub fn invoke_vj(ctx: &mut Ctx, index: i32, a1: i32, a2: i32) {
panic!("dyn_call_vj is set to None");
}
}
pub fn invoke_vjji(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32, a4: i32, a5: i32) {
debug!("emscripten::invoke_vjji");
invoke_no_return!(ctx, dyn_call_vjji, index, a1, a2, a3, a4, a5)
}
pub fn invoke_vij(ctx: &mut Ctx, index: i32, a1: i32, a2: i32, a3: i32) {
debug!("emscripten::invoke_vij");
if let Some(dyn_call_vij) = &get_emscripten_data(ctx).dyn_call_vij {

View File

@ -60,21 +60,51 @@ pub fn ___build_environment(ctx: &mut Ctx, environ: c_int) {
const MAX_ENV_VALUES: u32 = 64;
const TOTAL_ENV_SIZE: u32 = 1024;
let environment = emscripten_memory_pointer!(ctx.memory(0), environ) as *mut c_int;
unsafe {
let (mut pool_offset, env_ptr, mut pool_ptr) = unsafe {
let (pool_offset, _pool_slice): (u32, &mut [u8]) =
allocate_on_stack(ctx, TOTAL_ENV_SIZE as u32);
let (env_offset, _env_slice): (u32, &mut [u8]) =
allocate_on_stack(ctx, (MAX_ENV_VALUES * 4) as u32);
let env_ptr = emscripten_memory_pointer!(ctx.memory(0), env_offset) as *mut c_int;
let mut _pool_ptr = emscripten_memory_pointer!(ctx.memory(0), pool_offset) as *mut c_int;
let pool_ptr = emscripten_memory_pointer!(ctx.memory(0), pool_offset) as *mut u8;
*env_ptr = pool_offset as i32;
*environment = env_offset as i32;
// *env_ptr = 0;
(pool_offset, env_ptr, pool_ptr)
};
// unsafe {
// *env_ptr = 0;
// };
// *env_ptr = 0;
let default_vars = vec![
["USER", "web_user"],
["LOGNAME", "web_user"],
["PATH", "/"],
["PWD", "/"],
["HOME", "/home/web_user"],
["LANG", "C.UTF-8"],
["_", "thisProgram"],
];
let mut strings = vec![];
let mut total_size = 0;
for [key, val] in &default_vars {
let line = key.to_string() + "=" + val;
total_size += line.len();
strings.push(line);
}
if total_size as u32 > TOTAL_ENV_SIZE {
panic!("Environment size exceeded TOTAL_ENV_SIZE!");
}
unsafe {
for (i, s) in strings.iter().enumerate() {
for (j, c) in s.chars().enumerate() {
debug_assert!(c < u8::max_value() as char);
*pool_ptr.add(j) = c as u8;
}
*env_ptr.add(i * 4) = pool_offset as i32;
pool_offset += s.len() as u32 + 1;
pool_ptr = pool_ptr.add(s.len() + 1);
}
*env_ptr.add(strings.len() * 4) = 0;
}
}
pub fn ___assert_fail(_ctx: &mut Ctx, _a: c_int, _b: c_int, _c: c_int, _d: c_int) {

View File

@ -7,7 +7,7 @@ use wasmer_runtime_core::vm::Ctx;
/// setjmp
pub fn __setjmp(ctx: &mut Ctx, _env_addr: u32) -> c_int {
debug!("emscripten::__setjmp (setjmp)");
abort_with_message(ctx, "missing function: _longjmp");
abort_with_message(ctx, "missing function: _setjmp");
unreachable!()
// unsafe {
// // Rather than using the env as the holder of the jump buffer pointer,

View File

@ -127,6 +127,7 @@ pub struct EmscriptenData<'a> {
pub dyn_call_viijiii: Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32, i32)>>,
pub dyn_call_viijj: Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32)>>,
pub dyn_call_vj: Option<Func<'a, (i32, i32, i32)>>,
pub dyn_call_vjji: Option<Func<'a, (i32, i32, i32, i32, i32, i32)>>,
pub dyn_call_vij: Option<Func<'a, (i32, i32, i32, i32)>>,
pub dyn_call_viji: Option<Func<'a, (i32, i32, i32, i32, i32)>>,
pub dyn_call_vijiii: Option<Func<'a, (i32, i32, i32, i32, i32, i32, i32)>>,
@ -146,11 +147,7 @@ impl<'a> EmscriptenData<'a> {
pub fn new(instance: &'a mut Instance) -> EmscriptenData<'a> {
let malloc = instance.func("_malloc").unwrap();
let free = instance.func("_free").unwrap();
let memalign = if let Ok(func) = instance.func("_memalign") {
Some(func)
} else {
None
};
let memalign = instance.func("_memalign").ok();
let memset = instance.func("_memset").unwrap();
let stack_alloc = instance.func("stackAlloc").unwrap();
@ -198,6 +195,7 @@ impl<'a> EmscriptenData<'a> {
let dyn_call_viijiii = instance.func("dynCall_viijiii").ok();
let dyn_call_viijj = instance.func("dynCall_viijj").ok();
let dyn_call_vj = instance.func("dynCall_vj").ok();
let dyn_call_vjji = instance.func("dynCall_vjji").ok();
let dyn_call_vij = instance.func("dynCall_vij").ok();
let dyn_call_viji = instance.func("dynCall_viji").ok();
let dyn_call_vijiii = instance.func("dynCall_vijiii").ok();
@ -261,6 +259,7 @@ impl<'a> EmscriptenData<'a> {
dyn_call_viijiii,
dyn_call_viijj,
dyn_call_vj,
dyn_call_vjji,
dyn_call_vij,
dyn_call_viji,
dyn_call_vijiii,
@ -282,6 +281,7 @@ pub fn run_emscripten_instance(
instance: &mut Instance,
path: &str,
args: Vec<&str>,
entrypoint: Option<String>,
) -> CallResult<()> {
let mut data = EmscriptenData::new(instance);
let data_ptr = &mut data as *mut _ as *mut c_void;
@ -299,45 +299,54 @@ pub fn run_emscripten_instance(
// println!("running emscripten instance");
let main_func = instance.dyn_func("_main")?;
let num_params = main_func.signature().params().len();
let _result = match num_params {
2 => {
let (argc, argv) = store_module_arguments(instance.context_mut(), path, args);
instance.call("_main", &[Value::I32(argc as i32), Value::I32(argv as i32)])?;
}
0 => {
instance.call("_main", &[])?;
}
_ => panic!(
"The emscripten main function has received an incorrect number of params {}",
num_params
),
};
if let Some(ep) = entrypoint {
debug!("Running entry point: {}", &ep);
let ep_fn = instance.dyn_func(&ep)?;
let arg = unsafe { allocate_cstr_on_stack(instance.context_mut(), args[0]).0 };
//let (argc, argv) = store_module_arguments(instance.context_mut(), args);
instance.call(&ep, &[Value::I32(arg as i32)])?;
} else {
let main_func = instance.dyn_func("_main")?;
let num_params = main_func.signature().params().len();
let _result = match num_params {
2 => {
let mut new_args = vec![path];
new_args.extend(args);
let (argc, argv) = store_module_arguments(instance.context_mut(), new_args);
instance.call("_main", &[Value::I32(argc as i32), Value::I32(argv as i32)])?;
}
0 => {
instance.call("_main", &[])?;
}
_ => panic!(
"The emscripten main function has received an incorrect number of params {}",
num_params
),
};
}
// TODO atexit for emscripten
// println!("{:?}", data);
Ok(())
}
fn store_module_arguments(ctx: &mut Ctx, path: &str, args: Vec<&str>) -> (u32, u32) {
fn store_module_arguments(ctx: &mut Ctx, args: Vec<&str>) -> (u32, u32) {
let argc = args.len() + 1;
let mut args_slice = vec![0; argc];
args_slice[0] = unsafe { allocate_cstr_on_stack(ctx, path).0 };
for (slot, arg) in args_slice[1..argc].iter_mut().zip(args.iter()) {
for (slot, arg) in args_slice[0..argc].iter_mut().zip(args.iter()) {
*slot = unsafe { allocate_cstr_on_stack(ctx, &arg).0 };
}
let (argv_offset, argv_slice): (_, &mut [u32]) =
unsafe { allocate_on_stack(ctx, ((argc + 1) * 4) as u32) };
unsafe { allocate_on_stack(ctx, ((argc) * 4) as u32) };
assert!(!argv_slice.is_empty());
for (slot, arg) in argv_slice[0..argc].iter_mut().zip(args_slice.iter()) {
*slot = *arg
}
argv_slice[argc] = 0;
(argc as u32, argv_offset)
(argc as u32 - 1, argv_offset)
}
pub fn emscripten_set_up_memory(memory: &Memory, globals: &EmscriptenGlobalsData) {
@ -597,6 +606,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
"___syscall272" => func!(crate::syscalls::___syscall272),
"___syscall295" => func!(crate::syscalls::___syscall295),
"___syscall300" => func!(crate::syscalls::___syscall300),
"___syscall320" => func!(crate::syscalls::___syscall320),
"___syscall324" => func!(crate::syscalls::___syscall324),
"___syscall330" => func!(crate::syscalls::___syscall330),
"___syscall334" => func!(crate::syscalls::___syscall334),
@ -718,6 +728,7 @@ pub fn generate_emscripten_env(globals: &mut EmscriptenGlobals) -> ImportObject
"invoke_v" => func!(crate::emscripten_target::invoke_v),
"invoke_vi" => func!(crate::emscripten_target::invoke_vi),
"invoke_vj" => func!(crate::emscripten_target::invoke_vj),
"invoke_vjji" => func!(crate::emscripten_target::invoke_vjji),
"invoke_vii" => func!(crate::emscripten_target::invoke_vii),
"invoke_viii" => func!(crate::emscripten_target::invoke_viii),
"invoke_viiii" => func!(crate::emscripten_target::invoke_viiii),

View File

@ -21,8 +21,11 @@ pub fn _emscripten_memcpy_big(ctx: &mut Ctx, dest: u32, src: u32, len: u32) -> u
/// emscripten: _emscripten_get_heap_size
pub fn _emscripten_get_heap_size(ctx: &mut Ctx) -> u32 {
debug!("emscripten::_emscripten_get_heap_size",);
ctx.memory(0).size().bytes().0 as u32
debug!("emscripten::_emscripten_get_heap_size");
let result = ctx.memory(0).size().bytes().0 as u32;
debug!("=> {}", result);
result
}
// From emscripten implementation

View File

@ -82,7 +82,7 @@ pub fn _raise(_ctx: &mut Ctx, _one: i32) -> i32 {
}
pub fn _sem_init(_ctx: &mut Ctx, _one: i32, _two: i32, _three: i32) -> i32 {
debug!("emscripten::_sem_init");
debug!("emscripten::_sem_init: {}, {}, {}", _one, _two, _three);
0
}

View File

@ -31,12 +31,13 @@ use libc::{
off_t,
// open,
read,
rename,
// sockaddr_in,
// readv,
rmdir,
// writev,
stat,
write,
// sockaddr_in,
};
use wasmer_runtime_core::vm::Ctx;
@ -118,9 +119,23 @@ pub fn ___syscall20(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
unsafe { getpid() }
}
pub fn ___syscall38(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
debug!("emscripten::___syscall38");
-1
// rename
pub fn ___syscall38(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> i32 {
debug!("emscripten::___syscall38 (rename)");
let old_path_addr: u32 = varargs.get(ctx);
let new_path_addr: u32 = varargs.get(ctx);
let old_path = emscripten_memory_pointer!(ctx.memory(0), old_path_addr) as *const i8;
let new_path = emscripten_memory_pointer!(ctx.memory(0), new_path_addr) as *const i8;
let result = unsafe { rename(old_path, new_path) };
unsafe {
debug!(
"=> old_path: {}, new_path: {}, result: {}",
std::ffi::CStr::from_ptr(old_path).to_str().unwrap(),
std::ffi::CStr::from_ptr(new_path).to_str().unwrap(),
result
);
}
result
}
// rmdir
@ -246,12 +261,22 @@ pub fn ___syscall192(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_in
if fd == -1 {
let ptr = env::call_memalign(ctx, 16384, len);
if ptr == 0 {
return -1;
// ENOMEM
return -12;
}
let real_ptr = emscripten_memory_pointer!(ctx.memory(0), ptr) as *const u8;
env::call_memset(ctx, ptr, 0, len);
ptr as _
for i in 0..(len as usize) {
unsafe {
assert_eq!(*real_ptr.add(i), 0);
}
}
debug!("=> ptr: {}", ptr);
return ptr as i32;
} else {
-1
unimplemented!("temp during dev");
// return ENODEV
return -19;
}
}
@ -461,6 +486,12 @@ pub fn ___syscall300(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
-1
}
// utimensat
pub fn ___syscall320(ctx: &mut Ctx, _which: c_int, mut _varargs: VarArgs) -> c_int {
debug!("emscripten::___syscall320 (utimensat), {}", _which);
0
}
pub fn ___syscall334(_ctx: &mut Ctx, _one: i32, _two: i32) -> i32 {
debug!("emscripten::___syscall334");
-1

View File

@ -251,8 +251,9 @@ pub fn ___syscall33(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int
let path = emscripten_memory_pointer!(ctx.memory(0), path_ptr) as *const i8;
let result = unsafe { access(path, amode) };
debug!(
"=> path: {}, result: {}",
"=> path: {}, amode: {}, result: {}",
unsafe { std::ffi::CStr::from_ptr(path).to_str().unwrap() },
amode,
result
);
result
@ -354,8 +355,13 @@ pub fn ___syscall54(ctx: &mut Ctx, _which: c_int, mut varargs: VarArgs) -> c_int
debug!("emscripten::___syscall54 (ioctl) {}", _which);
let fd: i32 = varargs.get(ctx);
let request: u32 = varargs.get(ctx);
debug!("fd: {}, op: {}", fd, request);
debug!("=> fd: {}, op: {}", fd, request);
// Got the equivalents here: https://code.woboq.org/linux/linux/include/uapi/asm-generic/ioctls.h.html
// let argp: u32 = varargs.get(ctx);
// let argp_ptr = emscripten_memory_pointer!(ctx.memory(0), argp) as *mut c_void;
// let ret = unsafe { ioctl(fd, request as _, argp_ptr) };
// debug!("=> {}", ret);
// ret
match request as _ {
21537 => {
// FIONBIO

View File

@ -53,6 +53,7 @@ macro_rules! assert_emscripten_output {
&mut instance,
$name,
$args,
None,
).expect("run_emscripten_instance finishes");
let output = capturer.end().unwrap().0;

View File

@ -86,6 +86,10 @@ struct Run {
#[structopt(long = "em-symbol-map", parse(from_os_str), group = "emscripten")]
em_symbol_map: Option<PathBuf>,
/// Begin execution at the specified symbol
#[structopt(long = "em-entrypoint", group = "emscripten")]
em_entrypoint: Option<String>,
/// WASI pre-opened directory
#[structopt(long = "dir", multiple = true, group = "wasi")]
pre_opened_directories: Vec<String>,
@ -320,6 +324,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
options.path.to_str().unwrap()
},
options.args.iter().map(|arg| arg.as_str()).collect(),
options.em_entrypoint.clone(),
)
.map_err(|e| format!("{:?}", e))?;
} else {