Skip to content

Commit 766f378

Browse files
authored
Merge pull request #4033 from g0djan/godjan/iterate_callstack
Copy callstack API
2 parents 48a87dc + e488345 commit 766f378

10 files changed

+301
-13
lines changed

CMakeLists.txt

+5
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ if (NOT DEFINED WAMR_BUILD_LIB_WASI_THREADS)
9999
set (WAMR_BUILD_LIB_WASI_THREADS 0)
100100
endif ()
101101

102+
if (NOT DEFINED WAMR_ENABLE_COPY_CALLSTACK)
103+
# Disable copy callstack by default
104+
set (WAMR_ENABLE_COPY_CALLSTACK 0)
105+
endif()
106+
102107
if (NOT DEFINED WAMR_BUILD_MINI_LOADER)
103108
# Disable wasm mini loader by default
104109
set (WAMR_BUILD_MINI_LOADER 0)

build-scripts/config_common.cmake

+8
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,14 @@ if (WAMR_BUILD_SHARED_HEAP EQUAL 1)
324324
message (" Shared heap enabled")
325325
endif()
326326

327+
if (WAMR_ENABLE_COPY_CALLSTACK EQUAL 1)
328+
add_definitions (-DWAMR_ENABLE_COPY_CALLSTACK=1)
329+
message(" Copy callstack enabled")
330+
else ()
331+
add_definitions (-DWAMR_ENABLE_COPY_CALLSTACK=0)
332+
message(" Copy callstack disabled")
333+
endif()
334+
327335
if (WAMR_BUILD_MEMORY64 EQUAL 1)
328336
# if native is 32-bit or cross-compiled to 32-bit
329337
if (NOT WAMR_BUILD_TARGET MATCHES ".*64.*")

core/config.h

+4
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@
193193
#error "Heap aux stack allocation must be enabled for WASI threads"
194194
#endif
195195

196+
#ifndef WAMR_ENABLE_COPY_CALLSTACK
197+
#define WAMR_ENABLE_COPY_CALLSTACK 0
198+
#endif
199+
196200
#ifndef WASM_ENABLE_BASE_LIB
197201
#define WASM_ENABLE_BASE_LIB 0
198202
#endif

core/iwasm/aot/aot_runtime.c

+130
Original file line numberDiff line numberDiff line change
@@ -4103,6 +4103,136 @@ aot_frame_update_profile_info(WASMExecEnv *exec_env, bool alloc_frame)
41034103
}
41044104
#endif /* end of WASM_ENABLE_AOT_STACK_FRAME != 0 */
41054105

4106+
#if WAMR_ENABLE_COPY_CALLSTACK != 0
4107+
uint32
4108+
aot_copy_callstack_tiny_frame(WASMExecEnv *exec_env, wasm_frame_t *buffer,
4109+
const uint32 length, const uint32 skip_n,
4110+
char *error_buf, uint32 error_buf_size)
4111+
{
4112+
/*
4113+
* Note for devs: please refrain from such modifications inside of
4114+
* aot_copy_callstack_tiny_frame
4115+
* - any allocations/freeing memory
4116+
* - dereferencing any pointers other than: exec_env, exec_env->module_inst,
4117+
* exec_env->module_inst->module, pointers between stack's bottom and
4118+
* top_boundary For more details check wasm_copy_callstack in
4119+
* wasm_export.h
4120+
*/
4121+
uint8 *top_boundary = exec_env->wasm_stack.top_boundary;
4122+
uint8 *top = exec_env->wasm_stack.top;
4123+
uint8 *bottom = exec_env->wasm_stack.bottom;
4124+
uint32 count = 0;
4125+
4126+
bool is_top_index_in_range =
4127+
top_boundary >= top && top >= (bottom + sizeof(AOTTinyFrame));
4128+
if (!is_top_index_in_range) {
4129+
char *err_msg =
4130+
"Top of the stack pointer is outside of the stack boundaries";
4131+
strncpy(error_buf, err_msg, error_buf_size);
4132+
return 0;
4133+
}
4134+
bool is_top_aligned_with_bottom =
4135+
(unsigned long)(top - bottom) % sizeof(AOTTinyFrame) == 0;
4136+
if (!is_top_aligned_with_bottom) {
4137+
char *err_msg = "Top of the stack is not aligned with the bottom";
4138+
strncpy(error_buf, err_msg, error_buf_size);
4139+
return 0;
4140+
}
4141+
4142+
AOTTinyFrame *frame = (AOTTinyFrame *)(top - sizeof(AOTTinyFrame));
4143+
WASMCApiFrame record_frame;
4144+
while (frame && (uint8_t *)frame >= bottom && count < (skip_n + length)) {
4145+
if (count < skip_n) {
4146+
++count;
4147+
frame -= 1;
4148+
continue;
4149+
}
4150+
record_frame.instance = exec_env->module_inst;
4151+
record_frame.module_offset = 0;
4152+
record_frame.func_index = frame->func_index;
4153+
record_frame.func_offset = frame->ip_offset;
4154+
buffer[count - skip_n] = record_frame;
4155+
frame -= 1;
4156+
++count;
4157+
}
4158+
return count >= skip_n ? count - skip_n : 0;
4159+
}
4160+
4161+
uint32
4162+
aot_copy_callstack_standard_frame(WASMExecEnv *exec_env, wasm_frame_t *buffer,
4163+
const uint32 length, const uint32 skip_n,
4164+
char *error_buf, uint32_t error_buf_size)
4165+
{
4166+
/*
4167+
* Note for devs: please refrain from such modifications inside of
4168+
* aot_iterate_callstack_standard_frame
4169+
* - any allocations/freeing memory
4170+
* - dereferencing any pointers other than: exec_env, exec_env->module_inst,
4171+
* exec_env->module_inst->module, pointers between stack's bottom and
4172+
* top_boundary For more details check wasm_iterate_callstack in
4173+
* wasm_export.h
4174+
*/
4175+
4176+
uint32 count = 0;
4177+
#if WASM_ENABLE_GC == 0
4178+
WASMModuleInstance *module_inst =
4179+
(WASMModuleInstance *)wasm_exec_env_get_module_inst(exec_env);
4180+
AOTFrame *cur_frame = (AOTFrame *)wasm_exec_env_get_cur_frame(exec_env);
4181+
uint8 *top_boundary = exec_env->wasm_stack.top_boundary;
4182+
uint8 *bottom = exec_env->wasm_stack.bottom;
4183+
uint32 frame_size = (uint32)offsetof(AOTFrame, lp);
4184+
4185+
WASMCApiFrame record_frame;
4186+
while (cur_frame && (uint8_t *)cur_frame >= bottom
4187+
&& (uint8_t *)cur_frame + frame_size <= top_boundary
4188+
&& count < (skip_n + length)) {
4189+
if (count < skip_n) {
4190+
++count;
4191+
cur_frame = cur_frame->prev_frame;
4192+
continue;
4193+
}
4194+
record_frame.instance = module_inst;
4195+
record_frame.module_offset = 0;
4196+
record_frame.func_index = (uint32)cur_frame->func_index;
4197+
record_frame.func_offset = (uint32)cur_frame->ip_offset;
4198+
buffer[count - skip_n] = record_frame;
4199+
cur_frame = cur_frame->prev_frame;
4200+
++count;
4201+
}
4202+
#else
4203+
/*
4204+
* TODO: add support for standard frames when GC is enabled
4205+
* now it poses a risk due to variable size of the frame
4206+
*/
4207+
#endif
4208+
return count >= skip_n ? count - skip_n : 0;
4209+
}
4210+
4211+
uint32
4212+
aot_copy_callstack(WASMExecEnv *exec_env, wasm_frame_t *buffer,
4213+
const uint32 length, const uint32 skip_n, char *error_buf,
4214+
uint32_t error_buf_size)
4215+
{
4216+
/*
4217+
* Note for devs: please refrain from such modifications inside of
4218+
* aot_iterate_callstack
4219+
* - any allocations/freeing memory
4220+
* - dereferencing any pointers other than: exec_env, exec_env->module_inst,
4221+
* exec_env->module_inst->module, pointers between stack's bottom and
4222+
* top_boundary For more details check wasm_iterate_callstack in
4223+
* wasm_export.h
4224+
*/
4225+
if (!is_tiny_frame(exec_env)) {
4226+
return aot_copy_callstack_standard_frame(
4227+
exec_env, buffer, length, skip_n, error_buf, error_buf_size);
4228+
}
4229+
else {
4230+
return aot_copy_callstack_tiny_frame(exec_env, buffer, length, skip_n,
4231+
error_buf, error_buf_size);
4232+
}
4233+
}
4234+
#endif // WAMR_ENABLE_COPY_CALLSTACK
4235+
41064236
#if WASM_ENABLE_DUMP_CALL_STACK != 0
41074237
bool
41084238
aot_create_call_stack(struct WASMExecEnv *exec_env)

core/iwasm/aot/aot_runtime.h

+7
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,13 @@ aot_frame_update_profile_info(WASMExecEnv *exec_env, bool alloc_frame);
777777
bool
778778
aot_create_call_stack(struct WASMExecEnv *exec_env);
779779

780+
#if WAMR_ENABLE_COPY_CALLSTACK != 0
781+
uint32
782+
aot_copy_callstack(WASMExecEnv *exec_env, wasm_frame_t *buffer,
783+
const uint32 length, const uint32 skip_n, char *error_buf,
784+
uint32_t error_buf_size);
785+
#endif // WAMR_ENABLE_COPY_CALLSTACK
786+
780787
/**
781788
* @brief Dump wasm call stack or get the size
782789
*

core/iwasm/common/wasm_runtime_common.c

+39
Original file line numberDiff line numberDiff line change
@@ -1740,6 +1740,45 @@ wasm_runtime_destroy_exec_env(WASMExecEnv *exec_env)
17401740
wasm_exec_env_destroy(exec_env);
17411741
}
17421742

1743+
#if WAMR_ENABLE_COPY_CALLSTACK != 0
1744+
uint32
1745+
wasm_copy_callstack(const wasm_exec_env_t exec_env, wasm_frame_t *buffer,
1746+
const uint32 length, const uint32 skip_n, char *error_buf,
1747+
uint32_t error_buf_size)
1748+
{
1749+
/*
1750+
* Note for devs: please refrain from such modifications inside of
1751+
* wasm_copy_callstack to preserve async-signal-safety
1752+
* - any allocations/freeing memory
1753+
* - dereferencing any pointers other than: exec_env, exec_env->module_inst,
1754+
* exec_env->module_inst->module, pointers between stack's bottom and
1755+
* top_boundary For more details check wasm_copy_callstack in
1756+
* wasm_export.h
1757+
*/
1758+
#if WASM_ENABLE_DUMP_CALL_STACK
1759+
WASMModuleInstance *module_inst =
1760+
(WASMModuleInstance *)get_module_inst(exec_env);
1761+
1762+
#if WASM_ENABLE_INTERP != 0
1763+
if (module_inst->module_type == Wasm_Module_Bytecode) {
1764+
return wasm_interp_copy_callstack(exec_env, buffer, length, skip_n,
1765+
error_buf, error_buf_size);
1766+
}
1767+
#endif
1768+
1769+
#if WASM_ENABLE_AOT != 0
1770+
if (module_inst->module_type == Wasm_Module_AoT) {
1771+
return aot_copy_callstack(exec_env, buffer, length, skip_n, error_buf,
1772+
error_buf_size);
1773+
}
1774+
#endif
1775+
#endif
1776+
char *err_msg = "No copy_callstack API was actually executed";
1777+
strncpy(error_buf, err_msg, error_buf_size);
1778+
return 0;
1779+
}
1780+
#endif // WAMR_ENABLE_COPY_CALLSTACK
1781+
17431782
bool
17441783
wasm_runtime_init_thread_env(void)
17451784
{

core/iwasm/common/wasm_runtime_common.h

+7-13
Original file line numberDiff line numberDiff line change
@@ -464,19 +464,6 @@ typedef struct WASMRegisteredModule {
464464
typedef package_type_t PackageType;
465465
typedef wasm_section_t WASMSection, AOTSection;
466466

467-
typedef struct wasm_frame_t {
468-
/* wasm_instance_t */
469-
void *instance;
470-
uint32 module_offset;
471-
uint32 func_index;
472-
uint32 func_offset;
473-
const char *func_name_wp;
474-
475-
uint32 *sp;
476-
uint8 *frame_ref;
477-
uint32 *lp;
478-
} WASMCApiFrame;
479-
480467
#if WASM_ENABLE_JIT != 0
481468
typedef struct LLVMJITOptions {
482469
uint32 opt_level;
@@ -652,6 +639,13 @@ wasm_runtime_create_exec_env(WASMModuleInstanceCommon *module_inst,
652639
WASM_RUNTIME_API_EXTERN void
653640
wasm_runtime_destroy_exec_env(WASMExecEnv *exec_env);
654641

642+
#if WAMR_ENABLE_COPY_CALLSTACK != 0
643+
WASM_RUNTIME_API_EXTERN uint32_t
644+
wasm_copy_callstack(const wasm_exec_env_t exec_env, wasm_frame_t *buffer,
645+
const uint32 length, const uint32 skip_n, char *error_buf,
646+
uint32 error_buf_size);
647+
#endif // WAMR_ENABLE_COPY_CALLSTACK
648+
655649
/* See wasm_export.h for description */
656650
WASM_RUNTIME_API_EXTERN WASMModuleInstanceCommon *
657651
wasm_runtime_get_module_inst(WASMExecEnv *exec_env);

core/iwasm/include/wasm_export.h

+44
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,21 @@ typedef WASMFunctionInstanceCommon *wasm_function_inst_t;
126126
struct WASMMemoryInstance;
127127
typedef struct WASMMemoryInstance *wasm_memory_inst_t;
128128

129+
typedef struct wasm_frame_t {
130+
/* wasm_instance_t */
131+
void *instance;
132+
uint32_t module_offset;
133+
uint32_t func_index;
134+
uint32_t func_offset;
135+
const char *func_name_wp;
136+
137+
uint32_t *sp;
138+
uint8_t *frame_ref;
139+
uint32_t *lp;
140+
} WASMCApiFrame;
141+
142+
typedef WASMCApiFrame wasm_frame_t;
143+
129144
/* WASM section */
130145
typedef struct wasm_section_t {
131146
struct wasm_section_t *next;
@@ -864,6 +879,35 @@ wasm_runtime_create_exec_env(wasm_module_inst_t module_inst,
864879
WASM_RUNTIME_API_EXTERN void
865880
wasm_runtime_destroy_exec_env(wasm_exec_env_t exec_env);
866881

882+
/**
883+
* @brief Copy callstack frames.
884+
*
885+
* Caution: This is not a thread-safe function. Ensure the exec_env
886+
* is suspended before calling it from another thread.
887+
*
888+
* Usage: In the callback to read frames fields use APIs
889+
* for wasm_frame_t from wasm_c_api.h
890+
*
891+
* Note: The function is async-signal-safe if called with verified arguments.
892+
* Meaning it's safe to call it from a signal handler even on a signal
893+
* interruption from another thread if next variables hold valid pointers
894+
* - exec_env
895+
* - exec_env->module_inst
896+
* - exec_env->module_inst->module
897+
*
898+
* @param exec_env the execution environment that containes frames
899+
* @param buffer the buffer of size equal length * sizeof(wasm_frame_t) to copy
900+
* frames to
901+
* @param length the number of frames to copy
902+
* @param skip_n the number of frames to skip from the top of the stack
903+
*
904+
* @return number of copied frames
905+
*/
906+
WASM_RUNTIME_API_EXTERN uint32_t
907+
wasm_copy_callstack(const wasm_exec_env_t exec_env, wasm_frame_t *buffer,
908+
const uint32_t length, const uint32_t skip_n,
909+
char *error_buf, uint32_t error_buf_size);
910+
867911
/**
868912
* Get the singleton execution environment for the instance.
869913
*

core/iwasm/interpreter/wasm_runtime.c

+49
Original file line numberDiff line numberDiff line change
@@ -4195,6 +4195,55 @@ wasm_get_module_inst_mem_consumption(const WASMModuleInstance *module_inst,
41954195
#endif /* end of (WASM_ENABLE_MEMORY_PROFILING != 0) \
41964196
|| (WASM_ENABLE_MEMORY_TRACING != 0) */
41974197

4198+
#if WAMR_ENABLE_COPY_CALLSTACK != 0
4199+
uint32
4200+
wasm_interp_copy_callstack(WASMExecEnv *exec_env, wasm_frame_t *buffer,
4201+
uint32 length, uint32 skip_n, char *error_buf,
4202+
uint32_t error_buf_size)
4203+
{
4204+
/*
4205+
* Note for devs: please refrain from such modifications inside of
4206+
* wasm_interp_copy_callstack
4207+
* - any allocations/freeing memory
4208+
* - dereferencing any pointers other than: exec_env, exec_env->module_inst,
4209+
* exec_env->module_inst->module, pointers between stack's bottom and
4210+
* top_boundary For more details check wasm_copy_callstack in
4211+
* wasm_export.h
4212+
*/
4213+
WASMModuleInstance *module_inst =
4214+
(WASMModuleInstance *)wasm_exec_env_get_module_inst(exec_env);
4215+
WASMInterpFrame *cur_frame = wasm_exec_env_get_cur_frame(exec_env);
4216+
uint8 *top_boundary = exec_env->wasm_stack.top_boundary;
4217+
uint8 *bottom = exec_env->wasm_stack.bottom;
4218+
uint32 count = 0;
4219+
4220+
WASMCApiFrame record_frame;
4221+
while (cur_frame && (uint8_t *)cur_frame >= bottom
4222+
&& (uint8_t *)cur_frame + sizeof(WASMInterpFrame) <= top_boundary
4223+
&& count < (skip_n + length)) {
4224+
if (!cur_frame->function) {
4225+
cur_frame = cur_frame->prev_frame;
4226+
continue;
4227+
}
4228+
if (count < skip_n) {
4229+
++count;
4230+
cur_frame = cur_frame->prev_frame;
4231+
continue;
4232+
}
4233+
record_frame.instance = module_inst;
4234+
record_frame.module_offset = 0;
4235+
// It's safe to dereference module_inst->e because "e" is asigned only
4236+
// once in wasm_instantiate
4237+
record_frame.func_index =
4238+
(uint32)(cur_frame->function - module_inst->e->functions);
4239+
buffer[count - skip_n] = record_frame;
4240+
cur_frame = cur_frame->prev_frame;
4241+
++count;
4242+
}
4243+
return count >= skip_n ? count - skip_n : 0;
4244+
}
4245+
#endif // WAMR_ENABLE_COPY_CALLSTACK
4246+
41984247
#if WASM_ENABLE_DUMP_CALL_STACK != 0
41994248
bool
42004249
wasm_interp_create_call_stack(struct WASMExecEnv *exec_env)

core/iwasm/interpreter/wasm_runtime.h

+8
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,14 @@ wasm_get_table_inst(const WASMModuleInstance *module_inst, uint32 tbl_idx)
730730
}
731731

732732
#if WASM_ENABLE_DUMP_CALL_STACK != 0
733+
734+
#if WAMR_ENABLE_COPY_CALLSTACK != 0
735+
uint32
736+
wasm_interp_copy_callstack(WASMExecEnv *exec_env, wasm_frame_t *buffer,
737+
uint32 length, uint32 skip_n, char *error_buf,
738+
uint32_t error_buf_size);
739+
#endif // WAMR_ENABLE_COPY_CALLSTACK
740+
733741
bool
734742
wasm_interp_create_call_stack(struct WASMExecEnv *exec_env);
735743

0 commit comments

Comments
 (0)