Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mcpp.toml
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ members = [
"tests/examples/vulkan-memory-allocator",
"tests/examples/wamr",
"tests/examples/wamr-features",
"tests/examples/wamr-runtime-features",
"tests/examples/quickjs-ng",
]

Expand Down
26 changes: 26 additions & 0 deletions pkgs/c/compat.wamr.lua
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@
-- compat.recastnavigation's DT_POLYREF64 out of the feature table does not
-- apply here.
--
-- `no-hw-bound-check`, `instruction-metering` and `thread-mgr` select runtime
-- build options rather than a guest library. They are defines of the same kind:
-- neither wasm_export.h nor wasm_c_api.h branches on any of them.
--
-- ─────────────────────────────────────────────────────────────────────────
-- CN MIRROR
--
Expand Down Expand Up @@ -140,6 +144,8 @@ package = {
"*/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include",
"*/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src",
"*/core/shared/platform/common/libc-util",
-- wasm_c_api.c includes thread_manager.h by bare name under thread-mgr
"*/core/iwasm/libraries/thread-mgr",
"mcpp_generated/include",
},

Expand All @@ -165,6 +171,14 @@ package = {
ldflags = { "-lpthread", "-lm" },
},

target_cfg = {
-- openkal has no sigaction: with thread-mgr, wasm_runtime_init cannot
-- install its wakeup handler and returns false
["cfg(all(kernel-abi = \"openkal\", c-abi = \"musl\"))"] = {
cflags = { "-DWASM_DISABLE_WAKEUP_BLOCKING_OP=1" },
},
},

generated_files = {
["mcpp_generated/include/mcpp_wamr_config.h"] =
[==[
Expand Down Expand Up @@ -239,6 +253,18 @@ package = {
targets = { ["wamr"] = { kind = "lib" } },

features = {
-- software bound checks only: no guard pages, sigaltstack or SIGSEGV handler
["no-hw-bound-check"] = {
defines = { "WASM_DISABLE_HW_BOUND_CHECK=1" },
},
["instruction-metering"] = {
defines = { "WASM_ENABLE_INSTRUCTION_METERING=1" },
},
-- wasm_runtime_terminate() from another thread stops a running guest
["thread-mgr"] = {
sources = { "*/core/iwasm/libraries/thread-mgr/*.c" },
defines = { "WASM_ENABLE_THREAD_MGR=1" },
},
-- Built-in libc wrappers exported to the guest under `env`.
["libc-builtin"] = {
sources = { "*/core/iwasm/libraries/libc-builtin/*.c" },
Expand Down
9 changes: 9 additions & 0 deletions tests/examples/wamr-runtime-features/mcpp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# WAMR execution-control features: software bound checks, instruction
# metering and the thread manager. The base member (tests/examples/wamr) does
# not request them.
[package]
name = "wamr-runtime-features-tests"
version = "0.1.0"

[target.'cfg(linux)'.dependencies.compat]
wamr = { version = "2.4.5", features = ["no-hw-bound-check", "instruction-metering", "thread-mgr"] }
116 changes: 116 additions & 0 deletions tests/examples/wamr-runtime-features/tests/runtime_features.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// compat.wamr with `no-hw-bound-check`, `instruction-metering` and
// `thread-mgr`: an infinite loop in the guest is stopped once by the
// instruction budget and once by wasm_runtime_terminate() from another thread.
#ifdef __linux__

#include <wasm_export.h>

#include <pthread.h>
#include <unistd.h>

#include <atomic>
#include <cstdio>
#include <cstdlib>
#include <cstring>

namespace {

// (module
// (func (export "spin") (loop (br 0)))
// (func (export "add") (param i32 i32) (result i32)
// local.get 0 local.get 1 i32.add))
const unsigned char kModule[] = {
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
0x01, 0x0a, 0x02, 0x60, 0x00, 0x00, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f,
0x03, 0x03, 0x02, 0x00, 0x01,
0x07, 0x0e, 0x02, 0x04, 's', 'p', 'i', 'n', 0x00, 0x00,
0x03, 'a', 'd', 'd', 0x00, 0x01,
0x0a, 0x11, 0x02, 0x07, 0x00, 0x03, 0x40, 0x0c, 0x00, 0x0b, 0x0b,
0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b,
};

wasm_module_inst_t g_inst;
wasm_exec_env_t g_env;
std::atomic<bool> g_returned{ false };

bool call(const char *name, uint32_t *argv, uint32_t argc)
{
return wasm_runtime_call_wasm(g_env, wasm_runtime_lookup_function(g_inst, name), argc, argv);
}

bool exception_has(const char *text)
{
const char *ex = wasm_runtime_get_exception(g_inst);
std::printf(" exception: %s\n", ex ? ex : "(none)");
return ex && std::strstr(ex, text);
}

bool usable_again()
{
wasm_runtime_clear_exception(g_inst);
uint32_t argv[2] = { 2, 3 };
return call("add", argv, 2) && argv[0] == 5;
}

// Repeats until the call has returned, so a terminate that lands before the
// guest started running is not lost.
void *terminate_until_returned(void *)
{
while (!g_returned) {
usleep(100 * 1000);
wasm_runtime_terminate(g_inst);
}
return nullptr;
}

} // namespace

int main()
{
alarm(60);

RuntimeInitArgs init;
std::memset(&init, 0, sizeof init);
init.mem_alloc_type = Alloc_With_System_Allocator;
if (!wasm_runtime_full_init(&init)) {
std::puts("wasm_runtime_full_init failed");
return 1;
}

char err[128] = { 0 };
unsigned char *image = static_cast<unsigned char *>(std::malloc(sizeof kModule));
std::memcpy(image, kModule, sizeof kModule);
wasm_module_t mod = wasm_runtime_load(image, sizeof kModule, err, sizeof err);
g_inst = mod ? wasm_runtime_instantiate(mod, 16 * 1024, 0, err, sizeof err) : nullptr;
g_env = g_inst ? wasm_runtime_create_exec_env(g_inst, 16 * 1024) : nullptr;
if (!g_env) {
std::printf("module setup failed: %s\n", err);
return 1;
}

uint32_t argv[2] = { 0, 0 };
bool ok = true;

wasm_runtime_set_instruction_count_limit(g_env, 1000000);
bool stopped = !call("spin", argv, 0) && exception_has("instruction limit");
std::printf("budget stops the loop %s\n", stopped ? "ok" : "FAIL");
ok = ok && stopped && usable_again();

wasm_runtime_set_instruction_count_limit(g_env, -1);
pthread_t th;
pthread_create(&th, nullptr, terminate_until_returned, nullptr);
bool terminated = !call("spin", argv, 0) && exception_has("terminated by user");
g_returned = true;
pthread_join(th, nullptr);
std::printf("terminate stops the loop %s\n", terminated ? "ok" : "FAIL");
ok = ok && terminated && usable_again();

std::printf("instance usable afterwards %s\n", ok ? "ok" : "FAIL");
return ok ? 0 : 1;
}

#else

int main() { return 0; }

#endif
Loading