Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
68e4534
Iterate callstack API
Jan 17, 2025
d0c6da1
wamr bool type
Jan 27, 2025
1f4d3dd
clang-format
Jan 27, 2025
1b82ccc
meaning of the return bool type in the callback
Jan 27, 2025
813831d
keep devs notes out of public API
Jan 27, 2025
bf6b155
format
Jan 27, 2025
c8b8731
support standard frames as well
Jan 27, 2025
9ff8052
format
Jan 27, 2025
6bfc088
Calculate func_index instead of adding an extra field to wasm frame
Jan 28, 2025
b6daacb
ignore frames with no function
Jan 28, 2025
5bfbfd5
update typo in the comment
Jan 28, 2025
478b373
update signature
Jan 28, 2025
b9039f9
Merge branch 'main' into godjan/iterate_callstack
Jan 28, 2025
fb6c05e
add correct frame size for aot standard frames
Jan 28, 2025
f7204bd
standard frame is not supported when GC is enabled
Jan 28, 2025
267379c
Merge branch 'main' into godjan/iterate_callstack
Feb 5, 2025
32338bb
Copy read only API behind a flag instead of using user defined callback
Feb 24, 2025
cc3f0a0
Cleaning up
Feb 24, 2025
188eb1c
remove unnecessary includes
Feb 25, 2025
857e6b7
formatting
Feb 26, 2025
a5d8c0b
define if not defined
Feb 26, 2025
99cb6ec
formatting
Feb 26, 2025
fc3077b
address comments
Feb 27, 2025
bda012e
formatting
Feb 27, 2025
0b5084c
remove spare diff line
Feb 27, 2025
bc00b3e
address comments
Mar 3, 2025
ffcc157
clang format
Mar 3, 2025
32d0f55
spare line
Mar 3, 2025
6166788
spare lines
Mar 3, 2025
56bb7e7
last fixes
Mar 5, 2025
811f35b
identation
Mar 5, 2025
e488345
fix bug for return value when skip_n is passed
Mar 5, 2025
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
99 changes: 99 additions & 0 deletions core/iwasm/aot/aot_runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -4104,6 +4104,105 @@ 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)
{
/*
* Note for devs: please refrain from such modifications inside of
* aot_iterate_callstack_tiny_frame
* - 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
*/
uint8 *top_boundary = exec_env->wasm_stack.top_boundary;
uint8 *top = exec_env->wasm_stack.top;
uint8 *bottom = exec_env->wasm_stack.bottom;

bool is_top_index_in_range =
top_boundary >= top && top >= (bottom + sizeof(AOTTinyFrame));
if (!is_top_index_in_range) {
return;
}
bool is_top_aligned_with_bottom =
(unsigned long)(top - bottom) % sizeof(AOTTinyFrame) == 0;
if (!is_top_aligned_with_bottom) {
return;
}

AOTTinyFrame *frame = (AOTTinyFrame *)(top - sizeof(AOTTinyFrame));
WASMCApiFrame record_frame;
while (frame && (uint8_t *)frame >= bottom) {
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;
}
frame -= 1;
}
}

void
aot_iterate_callstack_standard_frame(WASMExecEnv *exec_env,
const wasm_frame_callback frame_handler,
void *user_data)
{
/*
* Note for devs: please refrain from such modifications inside of
* aot_iterate_callstack_standard_frame
* - 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
*/
WASMModuleInstance *module_inst =
(WASMModuleInstance *)wasm_exec_env_get_module_inst(exec_env);
AOTFrame *cur_frame = (AOTFrame *)wasm_exec_env_get_cur_frame(exec_env);
uint8 *top_boundary = exec_env->wasm_stack.top_boundary;
uint8 *bottom = exec_env->wasm_stack.bottom;

WASMCApiFrame record_frame;
while (cur_frame && (uint8_t *)cur_frame >= bottom
&& (uint8_t *)cur_frame + sizeof(AOTFrame) <= top_boundary) {
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;
}
cur_frame = cur_frame->prev_frame;
}
}

void
aot_iterate_callstack(WASMExecEnv *exec_env,
const wasm_frame_callback frame_handler, void *user_data)
{
/*
* Note for devs: please refrain from such modifications inside of
* aot_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
*/
if (!is_tiny_frame(exec_env)) {
aot_iterate_callstack_standard_frame(exec_env, frame_handler,
user_data);
}
else {
aot_iterate_callstack_tiny_frame(exec_env, frame_handler, user_data);
}
}

bool
aot_create_call_stack(struct WASMExecEnv *exec_env)
{
Expand Down
4 changes: 4 additions & 0 deletions core/iwasm/aot/aot_runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,10 @@ 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);

/**
* @brief Dump wasm call stack or get the size
*
Expand Down
33 changes: 33 additions & 0 deletions core/iwasm/common/wasm_runtime_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "bh_common.h"
#include "bh_assert.h"
#include "bh_log.h"
#include "wasm_export.h"
#include "wasm_native.h"
#include "wasm_runtime_common.h"
#include "wasm_memory.h"
Expand Down Expand Up @@ -1740,6 +1741,38 @@ 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)
{
/*
* Note for devs: please refrain from such modifications inside of
* wasm_iterate_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
* wasm_export.h
*/
#if WASM_ENABLE_DUMP_CALL_STACK
WASMModuleInstance *module_inst =
(WASMModuleInstance *)get_module_inst(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);
}
#endif

#if WASM_ENABLE_AOT != 0
if (module_inst->module_type == Wasm_Module_AoT) {
aot_iterate_callstack(exec_env, frame_callback, user_data);
}
#endif
#endif
}

bool
wasm_runtime_init_thread_env(void)
{
Expand Down
5 changes: 5 additions & 0 deletions core/iwasm/common/wasm_runtime_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,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);

/* See wasm_export.h for description */
WASM_RUNTIME_API_EXTERN WASMModuleInstanceCommon *
wasm_runtime_get_module_inst(WASMExecEnv *exec_env);
Expand Down
35 changes: 35 additions & 0 deletions core/iwasm/include/wasm_export.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ 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_frame_ptr_t;

/* WASM section */
typedef struct wasm_section_t {
struct wasm_section_t *next;
Expand Down Expand Up @@ -864,6 +867,38 @@ 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);

/**
* @brief Iterate over callstack frames and execute callback on it.
*
* Caution: This is not a thread-safe function. Ensure the exec_env
* is suspended before calling it from another thread.
*
* Usage: In the callback to read frames fields use APIs
* for wasm_frame_t from wasm_c_api.h
*
* Note: The function is async-signal-safe if called with verified arguments.
* Meaning it's safe to call it from a signal handler even on a signal
* interruption from another thread if next variables hold valid pointers
* - exec_env
* - exec_env->module_inst
* - 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
*/
WASM_RUNTIME_API_EXTERN void
wasm_iterate_callstack(const wasm_exec_env_t exec_env,
const wasm_frame_callback callback, void *user_data);

/**
* Get the singleton execution environment for the instance.
*
Expand Down
41 changes: 41 additions & 0 deletions core/iwasm/interpreter/wasm_runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "wasm_runtime.h"
#include "wasm.h"
#include "wasm_exec_env.h"
#include "wasm_loader.h"
#include "wasm_interp.h"
#include "bh_common.h"
Expand Down Expand Up @@ -4196,6 +4197,46 @@ wasm_get_module_inst_mem_consumption(const WASMModuleInstance *module_inst,
|| (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)
{
/*
* 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
*/
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;

WASMCApiFrame record_frame;
while (cur_frame && (uint8_t *)cur_frame >= bottom
&& (uint8_t *)cur_frame + sizeof(WASMInterpFrame) <= top_boundary) {
if (!cur_frame->function) {
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;
}
cur_frame = cur_frame->prev_frame;
}
}

bool
wasm_interp_create_call_stack(struct WASMExecEnv *exec_env)
{
Expand Down
6 changes: 6 additions & 0 deletions core/iwasm/interpreter/wasm_runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,12 @@ 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);

bool
wasm_interp_create_call_stack(struct WASMExecEnv *exec_env);

Expand Down
Loading