mirror of
https://github.com/bytecodealliance/wasm-micro-runtime.git
synced 2026-04-18 18:18:44 +00:00
Merge e6e3b325a8 into 389d2060df
This commit is contained in:
commit
c947c08c66
|
|
@ -87,6 +87,11 @@ wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst,
|
||||||
|
|
||||||
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||||
exec_env->instructions_to_execute = -1;
|
exec_env->instructions_to_execute = -1;
|
||||||
|
exec_env->metering_suspended = false;
|
||||||
|
exec_env->metering_suspend_frame = NULL;
|
||||||
|
exec_env->metering_suspend_function = NULL;
|
||||||
|
exec_env->metering_suspend_argc = 0;
|
||||||
|
exec_env->metering_suspend_argv = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return exec_env;
|
return exec_env;
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,21 @@ typedef struct WASMExecEnv {
|
||||||
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||||
/* instructions to execute */
|
/* instructions to execute */
|
||||||
int instructions_to_execute;
|
int instructions_to_execute;
|
||||||
|
|
||||||
|
/* true when classic interpreter suspended by instruction metering */
|
||||||
|
bool metering_suspended;
|
||||||
|
|
||||||
|
/* top frame to resume from when metering_suspended is true */
|
||||||
|
struct WASMInterpFrame *metering_suspend_frame;
|
||||||
|
|
||||||
|
/* function associated with metering suspended frame */
|
||||||
|
struct WASMFunctionInstance *metering_suspend_function;
|
||||||
|
|
||||||
|
/* argc captured for metering suspended function */
|
||||||
|
uint32 metering_suspend_argc;
|
||||||
|
|
||||||
|
/* argv captured for metering suspended function */
|
||||||
|
uint32 *metering_suspend_argv;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if WASM_ENABLE_FAST_JIT != 0
|
#if WASM_ENABLE_FAST_JIT != 0
|
||||||
|
|
|
||||||
|
|
@ -2473,6 +2473,30 @@ wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env,
|
||||||
{
|
{
|
||||||
exec_env->instructions_to_execute = instructions_to_execute;
|
exec_env->instructions_to_execute = instructions_to_execute;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
wasm_runtime_resume_wasm(WASMExecEnv *exec_env)
|
||||||
|
{
|
||||||
|
WASMFunctionInstanceCommon *function;
|
||||||
|
|
||||||
|
if (!wasm_runtime_exec_env_check(exec_env)) {
|
||||||
|
LOG_ERROR("Invalid exec env stack info.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!exec_env->metering_suspended || !exec_env->metering_suspend_function
|
||||||
|
|| !exec_env->metering_suspend_argv) {
|
||||||
|
wasm_runtime_set_exception(exec_env->module_inst,
|
||||||
|
"no metering resume is pending");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function =
|
||||||
|
(WASMFunctionInstanceCommon *)exec_env->metering_suspend_function;
|
||||||
|
return wasm_runtime_call_wasm(exec_env, function,
|
||||||
|
exec_env->metering_suspend_argc,
|
||||||
|
exec_env->metering_suspend_argv);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
WASMFuncType *
|
WASMFuncType *
|
||||||
|
|
|
||||||
|
|
@ -880,6 +880,10 @@ wasm_runtime_set_native_stack_boundary(WASMExecEnv *exec_env,
|
||||||
WASM_RUNTIME_API_EXTERN void
|
WASM_RUNTIME_API_EXTERN void
|
||||||
wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env,
|
wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env,
|
||||||
int instructions_to_execute);
|
int instructions_to_execute);
|
||||||
|
|
||||||
|
/* See wasm_export.h for description */
|
||||||
|
WASM_RUNTIME_API_EXTERN bool
|
||||||
|
wasm_runtime_resume_wasm(WASMExecEnv *exec_env);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0
|
#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0
|
||||||
|
|
|
||||||
|
|
@ -1928,6 +1928,26 @@ WASM_RUNTIME_API_EXTERN void
|
||||||
wasm_runtime_set_instruction_count_limit(wasm_exec_env_t exec_env,
|
wasm_runtime_set_instruction_count_limit(wasm_exec_env_t exec_env,
|
||||||
int instruction_count);
|
int instruction_count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resume wasm execution after an instruction metering trap.
|
||||||
|
*
|
||||||
|
* When instruction metering is enabled and execution stops with
|
||||||
|
* `instruction limit exceeded`, the runtime may preserve interpreter frame
|
||||||
|
* state in the exec env. This API resumes from that preserved state without
|
||||||
|
* requiring the host to call a specific exported function again.
|
||||||
|
*
|
||||||
|
* The caller should set a new instruction budget with
|
||||||
|
* `wasm_runtime_set_instruction_count_limit(...)` before resuming.
|
||||||
|
*
|
||||||
|
* @param exec_env the execution environment
|
||||||
|
*
|
||||||
|
* @return true if resumed execution succeeds, false otherwise and exception
|
||||||
|
* will be thrown, the caller can call wasm_runtime_get_exception to get
|
||||||
|
* exception info.
|
||||||
|
*/
|
||||||
|
WASM_RUNTIME_API_EXTERN bool
|
||||||
|
wasm_runtime_resume_wasm(wasm_exec_env_t exec_env);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dump runtime memory consumption, including:
|
* Dump runtime memory consumption, including:
|
||||||
* Exec env memory consumption
|
* Exec env memory consumption
|
||||||
|
|
|
||||||
|
|
@ -1558,6 +1558,37 @@ get_global_addr(uint8 *global_data, WASMGlobalInstance *global)
|
||||||
#define CHECK_INSTRUCTION_LIMIT() (void)0
|
#define CHECK_INSTRUCTION_LIMIT() (void)0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||||
|
static inline bool
|
||||||
|
is_instruction_metering_exception(WASMModuleInstance *module_inst)
|
||||||
|
{
|
||||||
|
const char *exception = wasm_get_exception(module_inst);
|
||||||
|
return exception && strstr(exception, "instruction limit exceeded");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
clear_metering_suspend_state(WASMExecEnv *exec_env)
|
||||||
|
{
|
||||||
|
exec_env->metering_suspended = false;
|
||||||
|
exec_env->metering_suspend_frame = NULL;
|
||||||
|
exec_env->metering_suspend_function = NULL;
|
||||||
|
exec_env->metering_suspend_argc = 0;
|
||||||
|
exec_env->metering_suspend_argv = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline WASMRuntimeFrame *
|
||||||
|
find_metering_resume_call_boundary(WASMRuntimeFrame *suspended_frame)
|
||||||
|
{
|
||||||
|
WASMRuntimeFrame *frame = suspended_frame;
|
||||||
|
|
||||||
|
while (frame && frame->prev_frame && frame->prev_frame->function) {
|
||||||
|
frame = frame->prev_frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
return frame ? frame->prev_frame : NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void
|
static void
|
||||||
wasm_interp_call_func_bytecode(WASMModuleInstance *module,
|
wasm_interp_call_func_bytecode(WASMModuleInstance *module,
|
||||||
WASMExecEnv *exec_env,
|
WASMExecEnv *exec_env,
|
||||||
|
|
@ -1671,6 +1702,16 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
|
||||||
#undef HANDLE_OPCODE
|
#undef HANDLE_OPCODE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||||
|
if (prev_frame && prev_frame->function == cur_func && prev_frame->ip) {
|
||||||
|
RECOVER_CONTEXT(prev_frame);
|
||||||
|
#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0
|
||||||
|
is_return_call = false;
|
||||||
|
#endif
|
||||||
|
goto resume_func;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if WASM_ENABLE_LABELS_AS_VALUES == 0
|
#if WASM_ENABLE_LABELS_AS_VALUES == 0
|
||||||
while (frame_ip < frame_ip_end) {
|
while (frame_ip < frame_ip_end) {
|
||||||
opcode = *frame_ip++;
|
opcode = *frame_ip++;
|
||||||
|
|
@ -6857,6 +6898,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
|
||||||
|
|
||||||
wasm_exec_env_set_cur_frame(exec_env, frame);
|
wasm_exec_env_set_cur_frame(exec_env, frame);
|
||||||
}
|
}
|
||||||
|
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||||
|
resume_func:
|
||||||
|
#endif
|
||||||
#if WASM_ENABLE_THREAD_MGR != 0
|
#if WASM_ENABLE_THREAD_MGR != 0
|
||||||
CHECK_SUSPEND_FLAGS();
|
CHECK_SUSPEND_FLAGS();
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -7413,6 +7457,10 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
|
||||||
wasm_runtime_get_running_mode((WASMModuleInstanceCommon *)module_inst);
|
wasm_runtime_get_running_mode((WASMModuleInstanceCommon *)module_inst);
|
||||||
/* Allocate sufficient cells for all kinds of return values. */
|
/* Allocate sufficient cells for all kinds of return values. */
|
||||||
bool alloc_frame = true;
|
bool alloc_frame = true;
|
||||||
|
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||||
|
bool resume_metering = false;
|
||||||
|
WASMRuntimeFrame *suspended_frame = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (argc < function->param_cell_num) {
|
if (argc < function->param_cell_num) {
|
||||||
char buf[128];
|
char buf[128];
|
||||||
|
|
@ -7456,7 +7504,34 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alloc_frame) {
|
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||||
|
if (running_mode == Mode_Interp && exec_env->metering_suspended) {
|
||||||
|
suspended_frame = exec_env->metering_suspend_frame;
|
||||||
|
if (!suspended_frame || suspended_frame->function != function) {
|
||||||
|
wasm_set_exception(module_inst,
|
||||||
|
"cannot call different function while metering "
|
||||||
|
"resume is pending");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
frame = find_metering_resume_call_boundary(suspended_frame);
|
||||||
|
if (!frame) {
|
||||||
|
wasm_set_exception(module_inst,
|
||||||
|
"invalid metering resume frame state");
|
||||||
|
clear_metering_suspend_state(exec_env);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resume_metering = true;
|
||||||
|
prev_frame = frame->prev_frame;
|
||||||
|
wasm_exec_env_set_cur_frame(exec_env, suspended_frame);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (alloc_frame
|
||||||
|
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||||
|
&& !resume_metering
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
unsigned all_cell_num =
|
unsigned all_cell_num =
|
||||||
function->ret_cell_num > 2 ? function->ret_cell_num : 2;
|
function->ret_cell_num > 2 ? function->ret_cell_num : 2;
|
||||||
unsigned frame_size;
|
unsigned frame_size;
|
||||||
|
|
@ -7513,7 +7588,13 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
|
||||||
else {
|
else {
|
||||||
if (running_mode == Mode_Interp) {
|
if (running_mode == Mode_Interp) {
|
||||||
wasm_interp_call_func_bytecode(module_inst, exec_env, function,
|
wasm_interp_call_func_bytecode(module_inst, exec_env, function,
|
||||||
frame);
|
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||||
|
resume_metering ? suspended_frame
|
||||||
|
: frame
|
||||||
|
#else
|
||||||
|
frame
|
||||||
|
#endif
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#if WASM_ENABLE_FAST_JIT != 0
|
#if WASM_ENABLE_FAST_JIT != 0
|
||||||
else if (running_mode == Mode_Fast_JIT) {
|
else if (running_mode == Mode_Fast_JIT) {
|
||||||
|
|
@ -7554,6 +7635,27 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||||
|
if ((running_mode == Mode_Interp)
|
||||||
|
&& is_instruction_metering_exception(module_inst)) {
|
||||||
|
exec_env->metering_suspended = true;
|
||||||
|
exec_env->metering_suspend_frame =
|
||||||
|
wasm_exec_env_get_cur_frame(exec_env);
|
||||||
|
if (exec_env->metering_suspend_frame) {
|
||||||
|
exec_env->metering_suspend_function =
|
||||||
|
exec_env->metering_suspend_frame->function;
|
||||||
|
exec_env->metering_suspend_argc = argc;
|
||||||
|
exec_env->metering_suspend_argv = argv;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
exec_env->metering_suspend_function = NULL;
|
||||||
|
exec_env->metering_suspend_argc = 0;
|
||||||
|
exec_env->metering_suspend_argv = NULL;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Output the return value to the caller */
|
/* Output the return value to the caller */
|
||||||
if (!wasm_copy_exception(module_inst, NULL)) {
|
if (!wasm_copy_exception(module_inst, NULL)) {
|
||||||
if (alloc_frame) {
|
if (alloc_frame) {
|
||||||
|
|
@ -7575,4 +7677,8 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
|
||||||
wasm_exec_env_set_cur_frame(exec_env, prev_frame);
|
wasm_exec_env_set_cur_frame(exec_env, prev_frame);
|
||||||
FREE_FRAME(exec_env, frame);
|
FREE_FRAME(exec_env, frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||||
|
clear_metering_suspend_state(exec_env);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,8 +90,21 @@ typedef float64 CellType_F64;
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||||
|
#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0
|
||||||
|
#define ROLLBACK_IP_AFTER_METERING_CHECK() \
|
||||||
|
do { \
|
||||||
|
frame_ip -= sizeof(void *); \
|
||||||
|
} while (0)
|
||||||
|
#else
|
||||||
|
#define ROLLBACK_IP_AFTER_METERING_CHECK() \
|
||||||
|
do { \
|
||||||
|
frame_ip -= sizeof(int32); \
|
||||||
|
} while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
#define CHECK_INSTRUCTION_LIMIT() \
|
#define CHECK_INSTRUCTION_LIMIT() \
|
||||||
if (instructions_left == 0) { \
|
if (instructions_left == 0) { \
|
||||||
|
ROLLBACK_IP_AFTER_METERING_CHECK(); \
|
||||||
wasm_set_exception(module, "instruction limit exceeded"); \
|
wasm_set_exception(module, "instruction limit exceeded"); \
|
||||||
goto got_exception; \
|
goto got_exception; \
|
||||||
} \
|
} \
|
||||||
|
|
@ -102,6 +115,37 @@ typedef float64 CellType_F64;
|
||||||
#define CHECK_INSTRUCTION_LIMIT() (void)0
|
#define CHECK_INSTRUCTION_LIMIT() (void)0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||||
|
static inline bool
|
||||||
|
is_instruction_metering_exception(WASMModuleInstance *module_inst)
|
||||||
|
{
|
||||||
|
const char *exception = wasm_get_exception(module_inst);
|
||||||
|
return exception && strstr(exception, "instruction limit exceeded");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
clear_metering_suspend_state(WASMExecEnv *exec_env)
|
||||||
|
{
|
||||||
|
exec_env->metering_suspended = false;
|
||||||
|
exec_env->metering_suspend_frame = NULL;
|
||||||
|
exec_env->metering_suspend_function = NULL;
|
||||||
|
exec_env->metering_suspend_argc = 0;
|
||||||
|
exec_env->metering_suspend_argv = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline WASMRuntimeFrame *
|
||||||
|
find_metering_resume_call_boundary(WASMRuntimeFrame *suspended_frame)
|
||||||
|
{
|
||||||
|
WASMRuntimeFrame *frame = suspended_frame;
|
||||||
|
|
||||||
|
while (frame && frame->prev_frame && frame->prev_frame->function) {
|
||||||
|
frame = frame->prev_frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
return frame ? frame->prev_frame : NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static inline uint32
|
static inline uint32
|
||||||
rotl32(uint32 n, uint32 c)
|
rotl32(uint32 n, uint32 c)
|
||||||
{
|
{
|
||||||
|
|
@ -1594,6 +1638,16 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||||
|
if (prev_frame && prev_frame->function == cur_func && prev_frame->ip) {
|
||||||
|
RECOVER_CONTEXT(prev_frame);
|
||||||
|
#if WASM_ENABLE_TAIL_CALL != 0 || WASM_ENABLE_GC != 0
|
||||||
|
is_return_call = false;
|
||||||
|
#endif
|
||||||
|
goto resume_func;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if WASM_ENABLE_LABELS_AS_VALUES == 0
|
#if WASM_ENABLE_LABELS_AS_VALUES == 0
|
||||||
while (frame_ip < frame_ip_end) {
|
while (frame_ip < frame_ip_end) {
|
||||||
opcode = *frame_ip++;
|
opcode = *frame_ip++;
|
||||||
|
|
@ -7788,6 +7842,14 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
|
||||||
HANDLE_OP_END();
|
HANDLE_OP_END();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||||
|
resume_func:
|
||||||
|
#if WASM_ENABLE_THREAD_MGR != 0
|
||||||
|
CHECK_SUSPEND_FLAGS();
|
||||||
|
#endif
|
||||||
|
HANDLE_OP_END();
|
||||||
|
#endif
|
||||||
|
|
||||||
return_func:
|
return_func:
|
||||||
{
|
{
|
||||||
FREE_FRAME(exec_env, frame);
|
FREE_FRAME(exec_env, frame);
|
||||||
|
|
@ -7888,13 +7950,17 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
|
||||||
WASMFunctionInstance *function, uint32 argc,
|
WASMFunctionInstance *function, uint32 argc,
|
||||||
uint32 argv[])
|
uint32 argv[])
|
||||||
{
|
{
|
||||||
WASMRuntimeFrame *prev_frame = wasm_exec_env_get_cur_frame(exec_env);
|
WASMRuntimeFrame *frame = NULL, *prev_frame, *outs_area;
|
||||||
WASMInterpFrame *frame, *outs_area;
|
|
||||||
|
|
||||||
/* Allocate sufficient cells for all kinds of return values. */
|
/* Allocate sufficient cells for all kinds of return values. */
|
||||||
unsigned all_cell_num =
|
unsigned all_cell_num =
|
||||||
function->ret_cell_num > 2 ? function->ret_cell_num : 2,
|
function->ret_cell_num > 2 ? function->ret_cell_num : 2,
|
||||||
i;
|
i;
|
||||||
|
bool alloc_frame = true;
|
||||||
|
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||||
|
bool resume_metering = false;
|
||||||
|
WASMRuntimeFrame *suspended_frame = NULL;
|
||||||
|
#endif
|
||||||
/* This frame won't be used by JITed code, so only allocate interp
|
/* This frame won't be used by JITed code, so only allocate interp
|
||||||
frame here. */
|
frame here. */
|
||||||
unsigned frame_size;
|
unsigned frame_size;
|
||||||
|
|
@ -7927,33 +7993,66 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!(frame =
|
prev_frame = wasm_exec_env_get_cur_frame(exec_env);
|
||||||
ALLOC_FRAME(exec_env, frame_size, (WASMInterpFrame *)prev_frame)))
|
|
||||||
return;
|
|
||||||
|
|
||||||
outs_area = wasm_exec_env_wasm_stack_top(exec_env);
|
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||||
frame->function = NULL;
|
if (exec_env->metering_suspended) {
|
||||||
frame->ip = NULL;
|
suspended_frame = exec_env->metering_suspend_frame;
|
||||||
/* There is no local variable. */
|
if (!suspended_frame || suspended_frame->function != function) {
|
||||||
frame->lp = frame->operand + 0;
|
wasm_set_exception(module_inst,
|
||||||
#if WASM_ENABLE_GC != 0
|
"cannot call different function while metering "
|
||||||
frame->frame_ref =
|
"resume is pending");
|
||||||
(uint8 *)(frame->lp
|
return;
|
||||||
+ (function->ret_cell_num > 2 ? function->ret_cell_num : 2));
|
}
|
||||||
#endif
|
frame = find_metering_resume_call_boundary(suspended_frame);
|
||||||
frame->ret_offset = 0;
|
if (!frame) {
|
||||||
|
wasm_set_exception(module_inst,
|
||||||
|
"invalid metering resume frame state");
|
||||||
|
clear_metering_suspend_state(exec_env);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ((uint8 *)(outs_area->operand + function->const_cell_num + argc)
|
resume_metering = true;
|
||||||
> exec_env->wasm_stack.top_boundary) {
|
prev_frame = frame->prev_frame;
|
||||||
wasm_set_exception((WASMModuleInstance *)exec_env->module_inst,
|
wasm_exec_env_set_cur_frame(exec_env, suspended_frame);
|
||||||
"wasm operand stack overflow");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (argc > 0)
|
if (alloc_frame
|
||||||
word_copy(outs_area->operand + function->const_cell_num, argv, argc);
|
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||||
|
&& !resume_metering
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
|
if (!(frame = ALLOC_FRAME(exec_env, frame_size,
|
||||||
|
(WASMInterpFrame *)prev_frame)))
|
||||||
|
return;
|
||||||
|
|
||||||
wasm_exec_env_set_cur_frame(exec_env, frame);
|
outs_area = wasm_exec_env_wasm_stack_top(exec_env);
|
||||||
|
frame->function = NULL;
|
||||||
|
frame->ip = NULL;
|
||||||
|
/* There is no local variable. */
|
||||||
|
frame->lp = frame->operand + 0;
|
||||||
|
#if WASM_ENABLE_GC != 0
|
||||||
|
frame->frame_ref =
|
||||||
|
(uint8 *)(frame->lp
|
||||||
|
+ (function->ret_cell_num > 2 ? function->ret_cell_num
|
||||||
|
: 2));
|
||||||
|
#endif
|
||||||
|
frame->ret_offset = 0;
|
||||||
|
|
||||||
|
if ((uint8 *)(outs_area->operand + function->const_cell_num + argc)
|
||||||
|
> exec_env->wasm_stack.top_boundary) {
|
||||||
|
wasm_set_exception((WASMModuleInstance *)exec_env->module_inst,
|
||||||
|
"wasm operand stack overflow");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc > 0)
|
||||||
|
word_copy(outs_area->operand + function->const_cell_num, argv,
|
||||||
|
argc);
|
||||||
|
|
||||||
|
wasm_exec_env_set_cur_frame(exec_env, frame);
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(os_writegsbase)
|
#if defined(os_writegsbase)
|
||||||
{
|
{
|
||||||
|
|
@ -7980,9 +8079,35 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
wasm_interp_call_func_bytecode(module_inst, exec_env, function, frame);
|
wasm_interp_call_func_bytecode(module_inst, exec_env, function,
|
||||||
|
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||||
|
resume_metering ? suspended_frame : frame
|
||||||
|
#else
|
||||||
|
frame
|
||||||
|
#endif
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||||
|
if (is_instruction_metering_exception(module_inst)) {
|
||||||
|
exec_env->metering_suspended = true;
|
||||||
|
exec_env->metering_suspend_frame =
|
||||||
|
wasm_exec_env_get_cur_frame(exec_env);
|
||||||
|
if (exec_env->metering_suspend_frame) {
|
||||||
|
exec_env->metering_suspend_function =
|
||||||
|
exec_env->metering_suspend_frame->function;
|
||||||
|
exec_env->metering_suspend_argc = argc;
|
||||||
|
exec_env->metering_suspend_argv = argv;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
exec_env->metering_suspend_function = NULL;
|
||||||
|
exec_env->metering_suspend_argc = 0;
|
||||||
|
exec_env->metering_suspend_argv = NULL;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Output the return value to the caller */
|
/* Output the return value to the caller */
|
||||||
if (!wasm_copy_exception(module_inst, NULL)) {
|
if (!wasm_copy_exception(module_inst, NULL)) {
|
||||||
for (i = 0; i < function->ret_cell_num; i++)
|
for (i = 0; i < function->ret_cell_num; i++)
|
||||||
|
|
@ -7996,8 +8121,14 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm_exec_env_set_cur_frame(exec_env, prev_frame);
|
if (alloc_frame) {
|
||||||
FREE_FRAME(exec_env, frame);
|
wasm_exec_env_set_cur_frame(exec_env, prev_frame);
|
||||||
|
FREE_FRAME(exec_env, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if WASM_ENABLE_INSTRUCTION_METERING != 0
|
||||||
|
clear_metering_suspend_state(exec_env);
|
||||||
|
#endif
|
||||||
#if WASM_ENABLE_OPCODE_COUNTER != 0
|
#if WASM_ENABLE_OPCODE_COUNTER != 0
|
||||||
wasm_interp_dump_op_count();
|
wasm_interp_dump_op_count();
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -620,6 +620,12 @@ SIMDE (SIMD Everywhere) implements SIMD operations in fast interpreter mode.
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> This limits the number of instructions a wasm module instance can run. Call `wasm_runtime_set_instruction_count_limit(...)` before `wasm_runtime_call_*(...)` to enforce the cap.
|
> This limits the number of instructions a wasm module instance can run. Call `wasm_runtime_set_instruction_count_limit(...)` before `wasm_runtime_call_*(...)` to enforce the cap.
|
||||||
|
>
|
||||||
|
> In classic and fast interpreter modes, when instruction budget is exhausted,
|
||||||
|
> the runtime raises `instruction limit exceeded`. If the host sets a new budget
|
||||||
|
> and calls the same function again (optionally after `wasm_runtime_clear_exception(...)`),
|
||||||
|
> execution resumes from preserved interpreter state instead of restarting from
|
||||||
|
> function entry.
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> This is only supported in classic interpreter mode.
|
> This is only supported in classic interpreter mode.
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,7 @@ add_subdirectory(tid-allocator)
|
||||||
add_subdirectory(unsupported-features)
|
add_subdirectory(unsupported-features)
|
||||||
add_subdirectory(exception-handling)
|
add_subdirectory(exception-handling)
|
||||||
add_subdirectory(running-modes)
|
add_subdirectory(running-modes)
|
||||||
|
add_subdirectory(instruction-metering)
|
||||||
|
|
||||||
if(FULL_TEST)
|
if(FULL_TEST)
|
||||||
message(STATUS "FULL_TEST=ON: include llm-enhanced-test")
|
message(STATUS "FULL_TEST=ON: include llm-enhanced-test")
|
||||||
|
|
|
||||||
54
tests/unit/instruction-metering/CMakeLists.txt
Normal file
54
tests/unit/instruction-metering/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
cmake_minimum_required(VERSION 3.14)
|
||||||
|
|
||||||
|
project(test-instruction-metering)
|
||||||
|
|
||||||
|
add_definitions(-DRUN_ON_LINUX)
|
||||||
|
|
||||||
|
set(WAMR_BUILD_AOT 0)
|
||||||
|
set(WAMR_BUILD_FAST_INTERP 1)
|
||||||
|
set(WAMR_BUILD_INTERP 1)
|
||||||
|
set(WAMR_BUILD_JIT 0)
|
||||||
|
set(WAMR_BUILD_LIBC_WASI 0)
|
||||||
|
set(WAMR_BUILD_APP_FRAMEWORK 0)
|
||||||
|
set(WAMR_BUILD_INSTRUCTION_METERING 1)
|
||||||
|
|
||||||
|
if(NOT DEFINED WASI_SDK_DIR)
|
||||||
|
set(WASI_SDK_DIR "/opt/wasi-sdk")
|
||||||
|
endif()
|
||||||
|
set(WASISDK_TOOLCHAIN "${WASI_SDK_DIR}/share/cmake/wasi-sdk.cmake")
|
||||||
|
|
||||||
|
include(ExternalProject)
|
||||||
|
include(GoogleTest)
|
||||||
|
if (TARGET gtest)
|
||||||
|
get_target_property(GTEST_INCLUDE_DIRS gtest INTERFACE_INCLUDE_DIRECTORIES)
|
||||||
|
if (GTEST_INCLUDE_DIRS)
|
||||||
|
include_directories(${GTEST_INCLUDE_DIRS})
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
ExternalProject_Add(
|
||||||
|
instruction_metering_wasm_apps
|
||||||
|
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps
|
||||||
|
BUILD_ALWAYS YES
|
||||||
|
CONFIGURE_COMMAND ${CMAKE_COMMAND} -S ${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps -B build
|
||||||
|
-DWASI_SDK_PREFIX=${WASI_SDK_DIR}
|
||||||
|
-DCMAKE_TOOLCHAIN_FILE=${WASISDK_TOOLCHAIN}
|
||||||
|
BUILD_COMMAND ${CMAKE_COMMAND} --build build
|
||||||
|
INSTALL_COMMAND ${CMAKE_COMMAND} --install build --prefix ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
include(../unit_common.cmake)
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
file(GLOB_RECURSE source_all ${CMAKE_CURRENT_SOURCE_DIR}/*.cc)
|
||||||
|
|
||||||
|
set(unit_test_sources
|
||||||
|
${source_all}
|
||||||
|
${WAMR_RUNTIME_LIB_SOURCE}
|
||||||
|
${UNCOMMON_SHARED_SOURCE}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(instruction_metering_test ${unit_test_sources})
|
||||||
|
target_link_libraries(instruction_metering_test gtest_main gtest)
|
||||||
|
add_dependencies(instruction_metering_test instruction_metering_wasm_apps)
|
||||||
|
|
||||||
|
gtest_discover_tests(instruction_metering_test)
|
||||||
|
|
@ -0,0 +1,224 @@
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "test_helper.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "bh_read_file.h"
|
||||||
|
#include "wasm_export.h"
|
||||||
|
|
||||||
|
class instruction_metering_resume_test_suite : public testing::Test
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
WAMRRuntimeRAII<512 * 1024> runtime;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::vector<uint8_t>
|
||||||
|
load_wasm_file(const std::string &path)
|
||||||
|
{
|
||||||
|
unsigned size = 0;
|
||||||
|
uint8_t *buf = (uint8_t *)bh_read_file_to_buffer(path.c_str(), &size);
|
||||||
|
EXPECT_NE(buf, nullptr);
|
||||||
|
std::vector<uint8_t> out(buf, buf + size);
|
||||||
|
wasm_runtime_free(buf);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(instruction_metering_resume_test_suite,
|
||||||
|
resume_after_instruction_limit_continues_execution)
|
||||||
|
{
|
||||||
|
std::string wasm_path = get_test_binary_dir() + "/resume_counter.wasm";
|
||||||
|
auto wasm = load_wasm_file(wasm_path);
|
||||||
|
|
||||||
|
char error_buf[128] = { 0 };
|
||||||
|
wasm_module_t module = wasm_runtime_load(wasm.data(), (uint32_t)wasm.size(),
|
||||||
|
error_buf, sizeof(error_buf));
|
||||||
|
ASSERT_NE(module, nullptr) << error_buf;
|
||||||
|
|
||||||
|
wasm_module_inst_t inst = wasm_runtime_instantiate(
|
||||||
|
module, 16 * 1024, 16 * 1024, error_buf, sizeof(error_buf));
|
||||||
|
ASSERT_NE(inst, nullptr) << error_buf;
|
||||||
|
|
||||||
|
wasm_exec_env_t exec_env = wasm_runtime_create_exec_env(inst, 16 * 1024);
|
||||||
|
ASSERT_NE(exec_env, nullptr);
|
||||||
|
|
||||||
|
wasm_function_inst_t fn = wasm_runtime_lookup_function(inst, "countdown");
|
||||||
|
ASSERT_NE(fn, nullptr);
|
||||||
|
|
||||||
|
uint32_t argv[1] = { 5000 };
|
||||||
|
|
||||||
|
wasm_runtime_set_instruction_count_limit(exec_env, 2);
|
||||||
|
bool ok = wasm_runtime_call_wasm(exec_env, fn, 1, argv);
|
||||||
|
EXPECT_FALSE(ok);
|
||||||
|
ASSERT_NE(wasm_runtime_get_exception(inst), nullptr);
|
||||||
|
EXPECT_NE(std::string(wasm_runtime_get_exception(inst))
|
||||||
|
.find("instruction limit exceeded"),
|
||||||
|
std::string::npos);
|
||||||
|
|
||||||
|
wasm_runtime_clear_exception(inst);
|
||||||
|
wasm_runtime_set_instruction_count_limit(exec_env, 200000);
|
||||||
|
ok = wasm_runtime_call_wasm(exec_env, fn, 1, argv);
|
||||||
|
|
||||||
|
EXPECT_TRUE(ok);
|
||||||
|
EXPECT_EQ(argv[0], 0u);
|
||||||
|
|
||||||
|
wasm_runtime_destroy_exec_env(exec_env);
|
||||||
|
wasm_runtime_deinstantiate(inst);
|
||||||
|
wasm_runtime_unload(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(instruction_metering_resume_test_suite,
|
||||||
|
reject_different_function_while_resume_pending)
|
||||||
|
{
|
||||||
|
std::string wasm_path = get_test_binary_dir() + "/resume_counter.wasm";
|
||||||
|
auto wasm = load_wasm_file(wasm_path);
|
||||||
|
|
||||||
|
char error_buf[128] = { 0 };
|
||||||
|
wasm_module_t module = wasm_runtime_load(wasm.data(), (uint32_t)wasm.size(),
|
||||||
|
error_buf, sizeof(error_buf));
|
||||||
|
ASSERT_NE(module, nullptr) << error_buf;
|
||||||
|
|
||||||
|
wasm_module_inst_t inst = wasm_runtime_instantiate(
|
||||||
|
module, 16 * 1024, 16 * 1024, error_buf, sizeof(error_buf));
|
||||||
|
ASSERT_NE(inst, nullptr) << error_buf;
|
||||||
|
|
||||||
|
wasm_exec_env_t exec_env = wasm_runtime_create_exec_env(inst, 16 * 1024);
|
||||||
|
ASSERT_NE(exec_env, nullptr);
|
||||||
|
|
||||||
|
wasm_function_inst_t countdown =
|
||||||
|
wasm_runtime_lookup_function(inst, "countdown");
|
||||||
|
wasm_function_inst_t noop = wasm_runtime_lookup_function(inst, "noop");
|
||||||
|
ASSERT_NE(countdown, nullptr);
|
||||||
|
ASSERT_NE(noop, nullptr);
|
||||||
|
|
||||||
|
uint32_t argv_countdown[1] = { 1000 };
|
||||||
|
wasm_runtime_set_instruction_count_limit(exec_env, 10);
|
||||||
|
bool ok =
|
||||||
|
wasm_runtime_call_wasm(exec_env, countdown, 1, argv_countdown);
|
||||||
|
EXPECT_FALSE(ok);
|
||||||
|
ASSERT_NE(wasm_runtime_get_exception(inst), nullptr);
|
||||||
|
EXPECT_NE(std::string(wasm_runtime_get_exception(inst))
|
||||||
|
.find("instruction limit exceeded"),
|
||||||
|
std::string::npos);
|
||||||
|
|
||||||
|
wasm_runtime_clear_exception(inst);
|
||||||
|
uint32_t argv_noop[1] = { 0 };
|
||||||
|
ok = wasm_runtime_call_wasm(exec_env, noop, 0, argv_noop);
|
||||||
|
|
||||||
|
EXPECT_FALSE(ok);
|
||||||
|
ASSERT_NE(wasm_runtime_get_exception(inst), nullptr);
|
||||||
|
EXPECT_NE(
|
||||||
|
std::string(wasm_runtime_get_exception(inst))
|
||||||
|
.find("cannot call different function while metering resume is "
|
||||||
|
"pending"),
|
||||||
|
std::string::npos);
|
||||||
|
|
||||||
|
wasm_runtime_destroy_exec_env(exec_env);
|
||||||
|
wasm_runtime_deinstantiate(inst);
|
||||||
|
wasm_runtime_unload(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(instruction_metering_resume_test_suite,
|
||||||
|
resume_nested_call_from_same_export_continues_execution)
|
||||||
|
{
|
||||||
|
std::string wasm_path = get_test_binary_dir() + "/resume_nested.wasm";
|
||||||
|
auto wasm = load_wasm_file(wasm_path);
|
||||||
|
|
||||||
|
char error_buf[128] = { 0 };
|
||||||
|
wasm_module_t module = wasm_runtime_load(wasm.data(), (uint32_t)wasm.size(),
|
||||||
|
error_buf, sizeof(error_buf));
|
||||||
|
ASSERT_NE(module, nullptr) << error_buf;
|
||||||
|
|
||||||
|
wasm_module_inst_t inst = wasm_runtime_instantiate(
|
||||||
|
module, 16 * 1024, 16 * 1024, error_buf, sizeof(error_buf));
|
||||||
|
ASSERT_NE(inst, nullptr) << error_buf;
|
||||||
|
|
||||||
|
wasm_exec_env_t exec_env = wasm_runtime_create_exec_env(inst, 16 * 1024);
|
||||||
|
ASSERT_NE(exec_env, nullptr);
|
||||||
|
|
||||||
|
wasm_function_inst_t install = wasm_runtime_lookup_function(inst, "install");
|
||||||
|
ASSERT_NE(install, nullptr);
|
||||||
|
|
||||||
|
uint32_t argv[1] = { 5000 };
|
||||||
|
|
||||||
|
wasm_runtime_set_instruction_count_limit(exec_env, 2);
|
||||||
|
bool ok = wasm_runtime_call_wasm(exec_env, install, 1, argv);
|
||||||
|
EXPECT_FALSE(ok);
|
||||||
|
ASSERT_NE(wasm_runtime_get_exception(inst), nullptr);
|
||||||
|
EXPECT_NE(std::string(wasm_runtime_get_exception(inst))
|
||||||
|
.find("instruction limit exceeded"),
|
||||||
|
std::string::npos);
|
||||||
|
|
||||||
|
wasm_runtime_clear_exception(inst);
|
||||||
|
wasm_runtime_set_instruction_count_limit(exec_env, 200000);
|
||||||
|
ok = wasm_runtime_call_wasm(exec_env, install, 1, argv);
|
||||||
|
if (!ok) {
|
||||||
|
const char *ex = wasm_runtime_get_exception(inst);
|
||||||
|
if (ex) {
|
||||||
|
fprintf(stderr, "nested resume failure exception: %s\n", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_TRUE(ok);
|
||||||
|
EXPECT_EQ(argv[0], 0u);
|
||||||
|
|
||||||
|
wasm_runtime_destroy_exec_env(exec_env);
|
||||||
|
wasm_runtime_deinstantiate(inst);
|
||||||
|
wasm_runtime_unload(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(instruction_metering_resume_test_suite,
|
||||||
|
resume_api_continues_nested_execution_without_recalling_export)
|
||||||
|
{
|
||||||
|
std::string wasm_path = get_test_binary_dir() + "/resume_nested.wasm";
|
||||||
|
auto wasm = load_wasm_file(wasm_path);
|
||||||
|
|
||||||
|
char error_buf[128] = { 0 };
|
||||||
|
wasm_module_t module = wasm_runtime_load(wasm.data(), (uint32_t)wasm.size(),
|
||||||
|
error_buf, sizeof(error_buf));
|
||||||
|
ASSERT_NE(module, nullptr) << error_buf;
|
||||||
|
|
||||||
|
wasm_module_inst_t inst = wasm_runtime_instantiate(
|
||||||
|
module, 16 * 1024, 16 * 1024, error_buf, sizeof(error_buf));
|
||||||
|
ASSERT_NE(inst, nullptr) << error_buf;
|
||||||
|
|
||||||
|
wasm_exec_env_t exec_env = wasm_runtime_create_exec_env(inst, 16 * 1024);
|
||||||
|
ASSERT_NE(exec_env, nullptr);
|
||||||
|
|
||||||
|
wasm_function_inst_t install = wasm_runtime_lookup_function(inst, "install");
|
||||||
|
ASSERT_NE(install, nullptr);
|
||||||
|
|
||||||
|
uint32_t argv[1] = { 5000 };
|
||||||
|
|
||||||
|
wasm_runtime_set_instruction_count_limit(exec_env, 20);
|
||||||
|
bool ok = wasm_runtime_call_wasm(exec_env, install, 1, argv);
|
||||||
|
EXPECT_FALSE(ok);
|
||||||
|
ASSERT_NE(wasm_runtime_get_exception(inst), nullptr);
|
||||||
|
EXPECT_NE(std::string(wasm_runtime_get_exception(inst))
|
||||||
|
.find("instruction limit exceeded"),
|
||||||
|
std::string::npos);
|
||||||
|
|
||||||
|
wasm_runtime_clear_exception(inst);
|
||||||
|
wasm_runtime_set_instruction_count_limit(exec_env, 200000);
|
||||||
|
ok = wasm_runtime_resume_wasm(exec_env);
|
||||||
|
if (!ok) {
|
||||||
|
const char *ex = wasm_runtime_get_exception(inst);
|
||||||
|
if (ex) {
|
||||||
|
fprintf(stderr, "resume api failure exception: %s\n", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_TRUE(ok);
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
uint32_t verify_argv[1] = { 5000 };
|
||||||
|
ok = wasm_runtime_call_wasm(exec_env, install, 1, verify_argv);
|
||||||
|
EXPECT_TRUE(ok);
|
||||||
|
if (ok) {
|
||||||
|
EXPECT_EQ(verify_argv[0], 0u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wasm_runtime_destroy_exec_env(exec_env);
|
||||||
|
wasm_runtime_deinstantiate(inst);
|
||||||
|
wasm_runtime_unload(module);
|
||||||
|
}
|
||||||
18
tests/unit/instruction-metering/wasm-apps/CMakeLists.txt
Normal file
18
tests/unit/instruction-metering/wasm-apps/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
project(instruction_metering_wasm_apps)
|
||||||
|
|
||||||
|
add_executable(resume_counter resume_counter.c)
|
||||||
|
set_target_properties(resume_counter PROPERTIES SUFFIX .wasm)
|
||||||
|
target_link_options(resume_counter PRIVATE
|
||||||
|
"-Wl,--no-entry"
|
||||||
|
"-Wl,--export=countdown"
|
||||||
|
"-Wl,--export=noop")
|
||||||
|
install(TARGETS resume_counter DESTINATION .)
|
||||||
|
|
||||||
|
add_executable(resume_nested resume_nested.c)
|
||||||
|
set_target_properties(resume_nested PROPERTIES SUFFIX .wasm)
|
||||||
|
target_link_options(resume_nested PRIVATE
|
||||||
|
"-Wl,--no-entry"
|
||||||
|
"-Wl,--export=install"
|
||||||
|
"-Wl,--export=noop")
|
||||||
|
install(TARGETS resume_nested DESTINATION .)
|
||||||
17
tests/unit/instruction-metering/wasm-apps/resume_counter.c
Normal file
17
tests/unit/instruction-metering/wasm-apps/resume_counter.c
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
countdown(int32_t n)
|
||||||
|
{
|
||||||
|
while (n > 0) {
|
||||||
|
n = n - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
noop(void)
|
||||||
|
{
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
23
tests/unit/instruction-metering/wasm-apps/resume_nested.c
Normal file
23
tests/unit/instruction-metering/wasm-apps/resume_nested.c
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
static int32_t
|
||||||
|
helper_loop(int32_t n)
|
||||||
|
{
|
||||||
|
while (n > 0) {
|
||||||
|
n = n - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
install(int32_t n)
|
||||||
|
{
|
||||||
|
return helper_loop(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
noop(void)
|
||||||
|
{
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user