diff --git a/CMakeLists.txt b/CMakeLists.txt index 6362d0e56..287559ff1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,6 +104,11 @@ if (NOT DEFINED WAMR_BUILD_LIB_WASI_THREADS) set (WAMR_BUILD_LIB_WASI_THREADS 0) endif () +if (NOT DEFINED WAMR_ENABLE_COPY_CALLSTACK) + # Disable copy callstack by default + set (WAMR_ENABLE_COPY_CALLSTACK 0) +endif() + if (NOT DEFINED WAMR_BUILD_MINI_LOADER) # Disable wasm mini loader by default set (WAMR_BUILD_MINI_LOADER 0) diff --git a/build-scripts/config_common.cmake b/build-scripts/config_common.cmake index 88abf7324..c7cfff842 100644 --- a/build-scripts/config_common.cmake +++ b/build-scripts/config_common.cmake @@ -319,6 +319,14 @@ if (WAMR_BUILD_SHARED_HEAP EQUAL 1) message (" Shared heap enabled") endif() +if (WAMR_ENABLE_COPY_CALLSTACK EQUAL 1) + add_definitions (-DWAMR_ENABLE_COPY_CALLSTACK=1) + message(" Copy callstack enabled") +else () + add_definitions (-DWAMR_ENABLE_COPY_CALLSTACK=0) + message(" Copy callstack disabled") +endif() + if (WAMR_BUILD_MEMORY64 EQUAL 1) # if native is 32-bit or cross-compiled to 32-bit if (NOT WAMR_BUILD_TARGET MATCHES ".*64.*") diff --git a/core/iwasm/aot/aot_runtime.c b/core/iwasm/aot/aot_runtime.c index f5a135be9..0cd53e4a5 100644 --- a/core/iwasm/aot/aot_runtime.c +++ b/core/iwasm/aot/aot_runtime.c @@ -4103,11 +4103,11 @@ aot_frame_update_profile_info(WASMExecEnv *exec_env, bool alloc_frame) } #endif /* end of WASM_ENABLE_AOT_STACK_FRAME != 0 */ -#if WASM_ENABLE_DUMP_CALL_STACK != 0 -void -aot_iterate_callstack_tiny_frame(WASMExecEnv *exec_env, - const wasm_frame_callback frame_handler, - void *user_data) +#if WAMR_ENABLE_COPY_CALLSTACK != 0 +uint32 +aot_copy_callstack_tiny_frame(WASMExecEnv *exec_env, wasm_frame_ptr_t buffer, + const uint32 length, + const uint32 skip_n) { /* * Note for devs: please refrain from such modifications inside of @@ -4122,35 +4122,43 @@ aot_iterate_callstack_tiny_frame(WASMExecEnv *exec_env, uint8 *top = exec_env->wasm_stack.top; uint8 *bottom = exec_env->wasm_stack.bottom; + uint32 count = 0; + bool is_top_index_in_range = top_boundary >= top && top >= (bottom + sizeof(AOTTinyFrame)); if (!is_top_index_in_range) { - return; + return count; } bool is_top_aligned_with_bottom = (unsigned long)(top - bottom) % sizeof(AOTTinyFrame) == 0; if (!is_top_aligned_with_bottom) { - return; + return count; } AOTTinyFrame *frame = (AOTTinyFrame *)(top - sizeof(AOTTinyFrame)); WASMCApiFrame record_frame; - while (frame && (uint8_t *)frame >= bottom) { + while (frame && (uint8_t *)frame >= bottom + && count < (skip_n + length)) { + if (count < skip_n) { + ++count; + frame -= 1; + continue; + } record_frame.instance = exec_env->module_inst; record_frame.module_offset = 0; record_frame.func_index = frame->func_index; record_frame.func_offset = frame->ip_offset; - if (!frame_handler(user_data, &record_frame)) { - break; - } + buffer[count - skip_n] = record_frame; frame -= 1; + ++count; } + return count; } -void -aot_iterate_callstack_standard_frame(WASMExecEnv *exec_env, - const wasm_frame_callback frame_handler, - void *user_data) +uint32 +aot_copy_callstack_standard_frame(WASMExecEnv *exec_env, wasm_frame_ptr_t buffer, + const uint32 length, + const uint32 skip_n) { /* * Note for devs: please refrain from such modifications inside of @@ -4169,17 +4177,24 @@ aot_iterate_callstack_standard_frame(WASMExecEnv *exec_env, uint8 *bottom = exec_env->wasm_stack.bottom; uint32 frame_size = (uint32)offsetof(AOTFrame, lp); + uint32 count = 0; + WASMCApiFrame record_frame; while (cur_frame && (uint8_t *)cur_frame >= bottom - && (uint8_t *)cur_frame + frame_size <= top_boundary) { + && (uint8_t *)cur_frame + frame_size <= top_boundary + && count < (skip_n + length)) { + if (count < skip_n) { + ++count; + cur_frame = cur_frame->prev_frame; + continue; + } record_frame.instance = module_inst; record_frame.module_offset = 0; record_frame.func_index = (uint32)cur_frame->func_index; record_frame.func_offset = (uint32)cur_frame->ip_offset; - if (!frame_handler(user_data, &record_frame)) { - break; - } + buffer[count - skip_n] = record_frame; cur_frame = cur_frame->prev_frame; + ++count; } #else /* @@ -4189,9 +4204,11 @@ aot_iterate_callstack_standard_frame(WASMExecEnv *exec_env, #endif } -void -aot_iterate_callstack(WASMExecEnv *exec_env, - const wasm_frame_callback frame_handler, void *user_data) + +uint32 +aot_copy_callstack(WASMExecEnv *exec_env, wasm_frame_ptr_t buffer, + const uint32 length, + const uint32 skip_n) { /* * Note for devs: please refrain from such modifications inside of @@ -4203,14 +4220,15 @@ aot_iterate_callstack(WASMExecEnv *exec_env, * wasm_export.h */ if (!is_tiny_frame(exec_env)) { - aot_iterate_callstack_standard_frame(exec_env, frame_handler, - user_data); + return aot_copy_callstack_standard_frame(exec_env, buffer, length, skip_n); } else { - aot_iterate_callstack_tiny_frame(exec_env, frame_handler, user_data); + return aot_copy_callstack_tiny_frame(exec_env, buffer, length, skip_n); } } +#endif // WAMR_ENABLE_COPY_CALLSTACK +#if WASM_ENABLE_DUMP_CALL_STACK != 0 bool aot_create_call_stack(struct WASMExecEnv *exec_env) { @@ -4391,7 +4409,7 @@ aot_dump_call_stack(WASMExecEnv *exec_env, bool print, char *buf, uint32 len) return total_len + 1; } -#endif /* end of WASM_ENABLE_DUMP_CALL_STACK != 0 */ +#endif /* end of WASM_ENABLE_DUMP_CALL_STACK != 0 && WASM_ENABLE_AOT_STACK_FRAME != 0 */ #if WASM_ENABLE_PERF_PROFILING != 0 void diff --git a/core/iwasm/aot/aot_runtime.h b/core/iwasm/aot/aot_runtime.h index 7a26be3ea..0cc66c4b6 100644 --- a/core/iwasm/aot/aot_runtime.h +++ b/core/iwasm/aot/aot_runtime.h @@ -10,6 +10,7 @@ #include "../common/wasm_runtime_common.h" #include "../interpreter/wasm_runtime.h" #include "../compilation/aot.h" +#include "platform_common.h" #if WASM_ENABLE_GC != 0 #include "gc_export.h" #endif @@ -777,9 +778,12 @@ aot_frame_update_profile_info(WASMExecEnv *exec_env, bool alloc_frame); bool aot_create_call_stack(struct WASMExecEnv *exec_env); -void -aot_iterate_callstack(WASMExecEnv *exec_env, - const wasm_frame_callback frame_handler, void *user_data); +#if WAMR_ENABLE_COPY_CALLSTACK != 0 +uint32 +aot_copy_callstack(WASMExecEnv *exec_env, wasm_frame_ptr_t buffer, + const uint32 length, + const uint32 skip_n); +#endif // WAMR_ENABLE_COPY_CALLSTACK /** * @brief Dump wasm call stack or get the size diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c index 7046c3b1a..8d04f3799 100644 --- a/core/iwasm/common/wasm_runtime_common.c +++ b/core/iwasm/common/wasm_runtime_common.c @@ -7,6 +7,7 @@ #include "bh_common.h" #include "bh_assert.h" #include "bh_log.h" +#include "platform_common.h" #include "wasm_export.h" #include "wasm_native.h" #include "wasm_runtime_common.h" @@ -1741,18 +1742,19 @@ wasm_runtime_destroy_exec_env(WASMExecEnv *exec_env) wasm_exec_env_destroy(exec_env); } -void -wasm_iterate_callstack(const wasm_exec_env_t exec_env, - const wasm_frame_callback frame_callback, - void *user_data) +#if WAMR_ENABLE_COPY_CALLSTACK != 0 +uint32 +wasm_copy_callstack(const wasm_exec_env_t exec_env, wasm_frame_ptr_t buffer, + const uint32 length, + const uint32 skip_n) { /* * Note for devs: please refrain from such modifications inside of - * wasm_iterate_callstack to preserve async-signal-safety + * wasm_copy_callstack to preserve async-signal-safety * - any allocations/freeing memory * - dereferencing any pointers other than: exec_env, exec_env->module_inst, * exec_env->module_inst->module, pointers between stack's bottom and - * top_boundary For more details check wasm_iterate_callstack in + * top_boundary For more details check wasm_copy_callstack in * wasm_export.h */ #if WASM_ENABLE_DUMP_CALL_STACK @@ -1761,17 +1763,18 @@ wasm_iterate_callstack(const wasm_exec_env_t exec_env, #if WASM_ENABLE_INTERP != 0 if (module_inst->module_type == Wasm_Module_Bytecode) { - wasm_interp_iterate_callstack(exec_env, frame_callback, user_data); + return wasm_interp_copy_callstack(exec_env, buffer, length, skip_n); } #endif #if WASM_ENABLE_AOT != 0 if (module_inst->module_type == Wasm_Module_AoT) { - aot_iterate_callstack(exec_env, frame_callback, user_data); + return aot_copy_callstack(exec_env, buffer, length, skip_n); } #endif #endif } +#endif // WAMR_ENABLE_COPY_CALLSTACK bool wasm_runtime_init_thread_env(void) diff --git a/core/iwasm/common/wasm_runtime_common.h b/core/iwasm/common/wasm_runtime_common.h index 1650615ef..19f11bd48 100644 --- a/core/iwasm/common/wasm_runtime_common.h +++ b/core/iwasm/common/wasm_runtime_common.h @@ -8,6 +8,7 @@ #include "bh_platform.h" #include "bh_common.h" +#include "platform_common.h" #include "wasm_exec_env.h" #include "wasm_native.h" #include "../include/wasm_export.h" @@ -464,19 +465,6 @@ typedef struct WASMRegisteredModule { typedef package_type_t PackageType; typedef wasm_section_t WASMSection, AOTSection; -typedef struct wasm_frame_t { - /* wasm_instance_t */ - void *instance; - uint32 module_offset; - uint32 func_index; - uint32 func_offset; - const char *func_name_wp; - - uint32 *sp; - uint8 *frame_ref; - uint32 *lp; -} WASMCApiFrame; - #if WASM_ENABLE_JIT != 0 typedef struct LLVMJITOptions { uint32 opt_level; @@ -652,10 +640,11 @@ wasm_runtime_create_exec_env(WASMModuleInstanceCommon *module_inst, WASM_RUNTIME_API_EXTERN void wasm_runtime_destroy_exec_env(WASMExecEnv *exec_env); -WASM_RUNTIME_API_EXTERN void -wasm_iterate_callstack(const wasm_exec_env_t exec_env, - const wasm_frame_callback frame_handler, - void *user_data); +#if WAMR_ENABLE_COPY_CALLSTACK != 0 +WASM_RUNTIME_API_EXTERN uint32_t +wasm_copy_callstack(const wasm_exec_env_t exec_env, wasm_frame_ptr_t buffer, + const uint32 length, const uint32 skip_n); +#endif // WAMR_ENABLE_COPY_CALLSTACK /* See wasm_export.h for description */ WASM_RUNTIME_API_EXTERN WASMModuleInstanceCommon * diff --git a/core/iwasm/include/wasm_export.h b/core/iwasm/include/wasm_export.h index aba7fd4ec..18a417cc9 100644 --- a/core/iwasm/include/wasm_export.h +++ b/core/iwasm/include/wasm_export.h @@ -126,8 +126,23 @@ typedef WASMFunctionInstanceCommon *wasm_function_inst_t; struct WASMMemoryInstance; typedef struct WASMMemoryInstance *wasm_memory_inst_t; -struct wasm_frame_t; + +typedef struct wasm_frame_t { + /* wasm_instance_t */ + void *instance; + uint32_t module_offset; + uint32_t func_index; + uint32_t func_offset; + const char *func_name_wp; + + uint32_t *sp; + uint8_t *frame_ref; + uint32_t *lp; +} WASMCApiFrame; + +// #if WAMR_ENABLE_COPY_CALLSTACK != 0 typedef struct wasm_frame_t *wasm_frame_ptr_t; +// #endif // WAMR_ENABLE_COPY_CALLSTACK /* WASM section */ typedef struct wasm_section_t { @@ -867,16 +882,10 @@ wasm_runtime_create_exec_env(wasm_module_inst_t module_inst, WASM_RUNTIME_API_EXTERN void wasm_runtime_destroy_exec_env(wasm_exec_env_t exec_env); -/** - * Callback to be called on wasm_frame_t*. - * It accepts void* as a context that can be used for closures. - * It returns bool so the iterating can stop when the callback returns false. - * E.g. callback that returns false after processing 100 frames - */ -typedef bool (*wasm_frame_callback)(void *, wasm_frame_ptr_t); +// #if WAMR_ENABLE_COPY_CALLSTACK != 0 /** - * @brief Iterate over callstack frames and execute callback on it. + * @brief Copy callstack frames. * * Caution: This is not a thread-safe function. Ensure the exec_env * is suspended before calling it from another thread. @@ -892,12 +901,16 @@ typedef bool (*wasm_frame_callback)(void *, wasm_frame_ptr_t); * - exec_env->module_inst->module * * @param exec_env the execution environment that containes frames - * @param callback the callback function provided by the user - * @param user_data context for callback provided by the user + * @param buffer the buffer of size equal length * sizeof(frame) to copy frames to + * @param length the number of frames to copy + * @param skip_n the number of frames to skip from the top of the stack + * + * @return number of copied frames */ -WASM_RUNTIME_API_EXTERN void -wasm_iterate_callstack(const wasm_exec_env_t exec_env, - const wasm_frame_callback callback, void *user_data); +WASM_RUNTIME_API_EXTERN uint32_t +wasm_copy_callstack(const wasm_exec_env_t exec_env, wasm_frame_ptr_t buffer, + const uint32_t length, const uint32_t skip_n); +// #endif /** * Get the singleton execution environment for the instance. diff --git a/core/iwasm/interpreter/wasm_runtime.c b/core/iwasm/interpreter/wasm_runtime.c index 9dd68cca0..c616ac44b 100644 --- a/core/iwasm/interpreter/wasm_runtime.c +++ b/core/iwasm/interpreter/wasm_runtime.c @@ -6,6 +6,7 @@ #include "wasm_runtime.h" #include "wasm.h" #include "wasm_exec_env.h" +#include "wasm_export.h" #include "wasm_loader.h" #include "wasm_interp.h" #include "bh_common.h" @@ -4196,46 +4197,57 @@ wasm_get_module_inst_mem_consumption(const WASMModuleInstance *module_inst, #endif /* end of (WASM_ENABLE_MEMORY_PROFILING != 0) \ || (WASM_ENABLE_MEMORY_TRACING != 0) */ -#if WASM_ENABLE_DUMP_CALL_STACK != 0 -void -wasm_interp_iterate_callstack(WASMExecEnv *exec_env, - const wasm_frame_callback frame_handler, - void *user_data) + +#if WAMR_ENABLE_COPY_CALLSTACK != 0 +uint32 +wasm_interp_copy_callstack(WASMExecEnv *exec_env, wasm_frame_ptr_t buffer, + uint32 length, uint32 skip_n) { /* - * Note for devs: please refrain from such modifications inside of - * wasm_interp_iterate_callstack - * - any allocations/freeing memory - * - dereferencing any pointers other than: exec_env, exec_env->module_inst, - * exec_env->module_inst->module, pointers between stack's bottom and - * top_boundary For more details check wasm_iterate_callstack in - * wasm_export.h - */ + * Note for devs: please refrain from such modifications inside of + * wasm_interp_copy_callstack + * - any allocations/freeing memory + * - dereferencing any pointers other than: exec_env, exec_env->module_inst, + * exec_env->module_inst->module, pointers between stack's bottom and + * top_boundary For more details check wasm_copy_callstack in + * wasm_export.h + */ WASMModuleInstance *module_inst = (WASMModuleInstance *)wasm_exec_env_get_module_inst(exec_env); WASMInterpFrame *cur_frame = wasm_exec_env_get_cur_frame(exec_env); uint8 *top_boundary = exec_env->wasm_stack.top_boundary; uint8 *bottom = exec_env->wasm_stack.bottom; + uint32 count = 0; + WASMCApiFrame record_frame; while (cur_frame && (uint8_t *)cur_frame >= bottom - && (uint8_t *)cur_frame + sizeof(WASMInterpFrame) <= top_boundary) { + && (uint8_t *)cur_frame + sizeof(WASMInterpFrame) <= top_boundary + && count < (skip_n + length)) { if (!cur_frame->function) { cur_frame = cur_frame->prev_frame; continue; } + if (count < skip_n) { + ++count; + cur_frame = cur_frame->prev_frame; + continue; + } record_frame.instance = module_inst; record_frame.module_offset = 0; // It's safe to dereference module_inst->e because "e" is asigned only // once in wasm_instantiate record_frame.func_index = (uint32)(cur_frame->function - module_inst->e->functions); - if (!frame_handler(user_data, &record_frame)) { - break; - } + buffer[count - skip_n] = record_frame; cur_frame = cur_frame->prev_frame; + ++count; } + return count; } +#endif // WAMR_ENABLE_COPY_CALLSTACK + +#if WASM_ENABLE_DUMP_CALL_STACK != 0 bool wasm_interp_create_call_stack(struct WASMExecEnv *exec_env) diff --git a/core/iwasm/interpreter/wasm_runtime.h b/core/iwasm/interpreter/wasm_runtime.h index 1b439bac1..84abf63b1 100644 --- a/core/iwasm/interpreter/wasm_runtime.h +++ b/core/iwasm/interpreter/wasm_runtime.h @@ -12,6 +12,7 @@ #include "bh_hashmap.h" #include "../common/wasm_runtime_common.h" #include "../common/wasm_exec_env.h" +#include "wasm_export.h" #ifdef __cplusplus extern "C" { @@ -731,10 +732,11 @@ wasm_get_table_inst(const WASMModuleInstance *module_inst, uint32 tbl_idx) #if WASM_ENABLE_DUMP_CALL_STACK != 0 -void -wasm_interp_iterate_callstack(WASMExecEnv *exec_env, - const wasm_frame_callback frame_handler, - void *user_data); +#if WAMR_ENABLE_COPY_CALLSTACK != 0 +uint32 +wasm_interp_copy_callstack(WASMExecEnv *exec_env, wasm_frame_ptr_t buffer, + uint32 length, uint32 skip_n); +#endif // WAMR_ENABLE_COPY_CALLSTACK bool wasm_interp_create_call_stack(struct WASMExecEnv *exec_env);