You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
94 lines
2.9 KiB
94 lines
2.9 KiB
// This test only works on Linux. It may be portable to MacOS as well,
|
|
// but the original author did not have a machine available to test it.
|
|
#![cfg(target_os = "linux")]
|
|
|
|
use anyhow::Result;
|
|
use wasmtime::*;
|
|
|
|
#[derive(Default)]
|
|
struct MemoryGrowFailureDetector {
|
|
current: usize,
|
|
desired: usize,
|
|
error: Option<String>,
|
|
}
|
|
|
|
impl ResourceLimiter for MemoryGrowFailureDetector {
|
|
fn memory_growing(&mut self, current: usize, desired: usize, _maximum: Option<usize>) -> bool {
|
|
self.current = current;
|
|
self.desired = desired;
|
|
true
|
|
}
|
|
|
|
fn memory_grow_failed(&mut self, err: &anyhow::Error) {
|
|
self.error = Some(err.to_string());
|
|
}
|
|
|
|
fn table_growing(&mut self, _current: u32, _desired: u32, _maximum: Option<u32>) -> bool {
|
|
true
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn custom_limiter_detect_os_oom_failure() -> Result<()> {
|
|
if std::env::var("WASMTIME_TEST_NO_HOG_MEMORY").is_ok() {
|
|
return Ok(());
|
|
}
|
|
|
|
// Default behavior of on-demand memory allocation so that a
|
|
// memory grow will hit Linux for a larger mmap.
|
|
let engine = Engine::default();
|
|
let linker = Linker::new(&engine);
|
|
let module = Module::new(&engine, r#"(module (memory (export "m") 0))"#).unwrap();
|
|
|
|
// Ask Linux to limit this process to 256MiB of memory
|
|
let process_max_memory: usize = 256 * 1024 * 1024;
|
|
unsafe {
|
|
// limit process to 256MiB memory
|
|
let rlimit = libc::rlimit {
|
|
rlim_cur: 0,
|
|
rlim_max: process_max_memory as u64,
|
|
};
|
|
let res = libc::setrlimit(libc::RLIMIT_DATA, &rlimit);
|
|
assert_eq!(res, 0, "setrlimit failed: {}", res);
|
|
};
|
|
|
|
let context = MemoryGrowFailureDetector::default();
|
|
|
|
let mut store = Store::new(&engine, context);
|
|
store.limiter(|s| s as &mut dyn ResourceLimiter);
|
|
let instance = linker.instantiate(&mut store, &module).unwrap();
|
|
let memory = instance.get_memory(&mut store, "m").unwrap();
|
|
|
|
// Small (640KiB) grow should succeed
|
|
memory.grow(&mut store, 10).unwrap();
|
|
assert!(store.data().error.is_none());
|
|
assert_eq!(store.data().current, 0);
|
|
assert_eq!(store.data().desired, 10 * 64 * 1024);
|
|
|
|
// Try to grow past the process's memory limit.
|
|
// This should fail.
|
|
let pages_exceeding_limit = process_max_memory / (64 * 1024);
|
|
let err_msg = memory
|
|
.grow(&mut store, pages_exceeding_limit as u64)
|
|
.unwrap_err()
|
|
.to_string();
|
|
assert!(
|
|
err_msg.starts_with("failed to grow memory"),
|
|
"unexpected error: {}",
|
|
err_msg
|
|
);
|
|
|
|
assert_eq!(store.data().current, 10 * 64 * 1024);
|
|
assert_eq!(
|
|
store.data().desired,
|
|
(pages_exceeding_limit + 10) * 64 * 1024
|
|
);
|
|
// The memory_grow_failed hook should show Linux gave OOM:
|
|
let err_msg = store.data().error.as_ref().unwrap();
|
|
assert!(
|
|
err_msg.starts_with("System call failed: Cannot allocate memory"),
|
|
"unexpected error: {}",
|
|
err_msg
|
|
);
|
|
Ok(())
|
|
}
|
|
|