From ff25110840cdac21dc366e7aea00c6523377f529 Mon Sep 17 00:00:00 2001 From: Enrico Loparco Date: Thu, 11 Jan 2024 05:13:05 +0100 Subject: [PATCH] Return stack frames of crashed thread when using wasm-c-api (#2908) When using the wasm-c-api and there's a trap, `wasm_func_call()` returns a `wasm_trap_t *` object. No matter which thread crashes, the trap contains the stack frames of the main thread. With this PR, when there's an exception, the stack frames of the thread where the exception occurs are stored into the thread cluster. `wasm_func_call()` can then return those stack frames. --- core/iwasm/common/wasm_c_api.c | 43 +++++++++++++++---- core/iwasm/common/wasm_c_api_internal.h | 3 ++ .../libraries/thread-mgr/thread_manager.c | 28 ++++++++++++ .../libraries/thread-mgr/thread_manager.h | 7 +++ 4 files changed, 73 insertions(+), 8 deletions(-) diff --git a/core/iwasm/common/wasm_c_api.c b/core/iwasm/common/wasm_c_api.c index 41cfdf37..27b1e040 100644 --- a/core/iwasm/common/wasm_c_api.c +++ b/core/iwasm/common/wasm_c_api.c @@ -1921,10 +1921,26 @@ wasm_frame_func_offset(const wasm_frame_t *frame) return frame ? frame->func_offset : 0; } +void +wasm_frame_vec_clone_internal(Vector *src, Vector *out) +{ + bh_assert(src->num_elems != 0 && src->data); + + bh_vector_destroy(out); + if (!bh_vector_init(out, src->num_elems, sizeof(WASMCApiFrame), false)) { + bh_vector_destroy(out); + return; + } + + bh_memcpy_s(out->data, src->num_elems * sizeof(WASMCApiFrame), src->data, + src->num_elems * sizeof(WASMCApiFrame)); + out->num_elems = src->num_elems; +} + static wasm_trap_t * wasm_trap_new_internal(wasm_store_t *store, WASMModuleInstanceCommon *inst_comm_rt, - const char *error_info) + const char *error_info, Vector *cluster_frames) { wasm_trap_t *trap; #if WASM_ENABLE_DUMP_CALL_STACK != 0 @@ -1954,7 +1970,9 @@ wasm_trap_new_internal(wasm_store_t *store, /* fill in frames */ #if WASM_ENABLE_DUMP_CALL_STACK != 0 - trap->frames = ((WASMModuleInstance *)inst_comm_rt)->frames; + trap->frames = cluster_frames + ? cluster_frames + : ((WASMModuleInstance *)inst_comm_rt)->frames; if (trap->frames) { /* fill in instances */ @@ -2065,10 +2083,7 @@ wasm_trap_trace(const wasm_trap_t *trap, own wasm_frame_vec_t *out) } for (i = 0; i < trap->frames->num_elems; i++) { - wasm_frame_t *frame; - - frame = ((wasm_frame_t *)trap->frames->data) + i; - + wasm_frame_t *frame = ((wasm_frame_t *)trap->frames->data) + i; if (!(out->data[i] = wasm_frame_new(frame->instance, frame->module_offset, frame->func_index, frame->func_offset))) { @@ -3252,6 +3267,7 @@ wasm_func_call(const wasm_func_t *func, const wasm_val_vec_t *params, WASMFunctionInstanceCommon *func_comm_rt = NULL; WASMExecEnv *exec_env = NULL; size_t param_count, result_count, alloc_count; + Vector *cluster_frames = NULL; bh_assert(func && func->type); @@ -3364,9 +3380,20 @@ failed: if (argv != argv_buf) wasm_runtime_free(argv); - return wasm_trap_new_internal( +#if WASM_ENABLE_DUMP_CALL_STACK != 0 && WASM_ENABLE_THREAD_MGR != 0 + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + cluster_frames = &cluster->exception_frames; + wasm_cluster_traverse_lock(exec_env); +#endif + + wasm_trap_t *trap = wasm_trap_new_internal( func->store, func->inst_comm_rt, - wasm_runtime_get_exception(func->inst_comm_rt)); + wasm_runtime_get_exception(func->inst_comm_rt), cluster_frames); + +#if WASM_ENABLE_DUMP_CALL_STACK != 0 && WASM_ENABLE_THREAD_MGR != 0 + wasm_cluster_traverse_unlock(exec_env); +#endif + return trap; } size_t diff --git a/core/iwasm/common/wasm_c_api_internal.h b/core/iwasm/common/wasm_c_api_internal.h index b327011b..3967bb27 100644 --- a/core/iwasm/common/wasm_c_api_internal.h +++ b/core/iwasm/common/wasm_c_api_internal.h @@ -240,4 +240,7 @@ wasm_memory_new_internal(wasm_store_t *store, uint16 memory_idx_rt, wasm_table_t * wasm_table_new_internal(wasm_store_t *store, uint16 table_idx_rt, WASMModuleInstanceCommon *inst_comm_rt); + +void +wasm_frame_vec_clone_internal(Vector *src, Vector *out); #endif /* _WASM_C_API_INTERNAL_H */ diff --git a/core/iwasm/libraries/thread-mgr/thread_manager.c b/core/iwasm/libraries/thread-mgr/thread_manager.c index ce7ff82a..eafec668 100644 --- a/core/iwasm/libraries/thread-mgr/thread_manager.c +++ b/core/iwasm/libraries/thread-mgr/thread_manager.c @@ -4,6 +4,7 @@ */ #include "thread_manager.h" +#include "../common/wasm_c_api_internal.h" #if WASM_ENABLE_INTERP != 0 #include "../interpreter/wasm_runtime.h" @@ -370,6 +371,10 @@ wasm_cluster_destroy(WASMCluster *cluster) wasm_debug_instance_destroy(cluster); #endif +#if WASM_ENABLE_DUMP_CALL_STACK != 0 + bh_vector_destroy(&cluster->exception_frames); +#endif + wasm_runtime_free(cluster); } @@ -1321,6 +1326,29 @@ wasm_cluster_set_exception(WASMExecEnv *exec_env, const char *exception) data.exception = exception; os_mutex_lock(&cluster->lock); +#if WASM_ENABLE_DUMP_CALL_STACK != 0 + if (has_exception) { + /* Save the stack frames of the crashed thread into the cluster */ + WASMModuleInstance *module_inst = + (WASMModuleInstance *)get_module_inst(exec_env); + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode + && wasm_interp_create_call_stack(exec_env)) { + wasm_frame_vec_clone_internal(module_inst->frames, + &cluster->exception_frames); + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT + && aot_create_call_stack(exec_env)) { + wasm_frame_vec_clone_internal(module_inst->frames, + &cluster->exception_frames); + } +#endif + } +#endif /* WASM_ENABLE_DUMP_CALL_STACK != 0 */ cluster->has_exception = has_exception; traverse_list(&cluster->exec_env_list, set_exception_visitor, &data); os_mutex_unlock(&cluster->lock); diff --git a/core/iwasm/libraries/thread-mgr/thread_manager.h b/core/iwasm/libraries/thread-mgr/thread_manager.h index 588adac4..06d20883 100644 --- a/core/iwasm/libraries/thread-mgr/thread_manager.h +++ b/core/iwasm/libraries/thread-mgr/thread_manager.h @@ -51,6 +51,13 @@ struct WASMCluster { #if WASM_ENABLE_DEBUG_INTERP != 0 WASMDebugInstance *debug_inst; #endif + +#if WASM_ENABLE_DUMP_CALL_STACK != 0 + /* When an exception occurs in a thread, the stack frames of that thread are + * saved into the cluster + */ + Vector exception_frames; +#endif }; void