Skip to content

Commit 7d3b2a8

Browse files
authored
Make memory profiling show native stack usage (bytecodealliance#1917)
1 parent 0435acd commit 7d3b2a8

File tree

15 files changed

+182
-9
lines changed

15 files changed

+182
-9
lines changed

core/iwasm/aot/aot_runtime.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ bh_static_assert(offsetof(WASMExecEnv, aux_stack_boundary)
3030
bh_static_assert(offsetof(WASMExecEnv, aux_stack_bottom)
3131
== 7 * sizeof(uintptr_t));
3232
bh_static_assert(offsetof(WASMExecEnv, native_symbol) == 8 * sizeof(uintptr_t));
33+
bh_static_assert(offsetof(WASMExecEnv, native_stack_top_min)
34+
== 9 * sizeof(uintptr_t));
3335

3436
bh_static_assert(offsetof(AOTModuleInstance, memories) == 1 * sizeof(uint64));
3537
bh_static_assert(offsetof(AOTModuleInstance, func_ptrs) == 5 * sizeof(uint64));
@@ -1257,6 +1259,7 @@ invoke_native_with_hw_bound_check(WASMExecEnv *exec_env, void *func_ptr,
12571259
/* Check native stack overflow firstly to ensure we have enough
12581260
native stack to run the following codes before actually calling
12591261
the aot function in invokeNative function. */
1262+
RECORD_STACK_USAGE(exec_env, (uint8 *)&module_inst);
12601263
if ((uint8 *)&module_inst < exec_env->native_stack_boundary
12611264
+ page_size * (guard_page_count + 1)) {
12621265
aot_set_exception_with_id(module_inst, EXCE_NATIVE_STACK_OVERFLOW);
@@ -1856,6 +1859,7 @@ aot_call_indirect(WASMExecEnv *exec_env, uint32 tbl_idx, uint32 table_elem_idx,
18561859
exec_env->native_stack_boundary must have been set, we don't set
18571860
it again */
18581861

1862+
RECORD_STACK_USAGE(exec_env, (uint8 *)&module_inst);
18591863
if ((uint8 *)&module_inst < exec_env->native_stack_boundary) {
18601864
aot_set_exception_with_id(module_inst, EXCE_NATIVE_STACK_OVERFLOW);
18611865
goto fail;

core/iwasm/common/wasm_exec_env.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ wasm_exec_env_set_thread_info(WASMExecEnv *exec_env)
211211
exec_env->handle = os_self_thread();
212212
exec_env->native_stack_boundary =
213213
stack_boundary ? stack_boundary + WASM_STACK_GUARD_SIZE : NULL;
214+
exec_env->native_stack_top_min = (void *)UINTPTR_MAX;
214215
}
215216

216217
#if WASM_ENABLE_THREAD_MGR != 0

core/iwasm/common/wasm_exec_env.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ typedef struct WASMExecEnv {
8484
void **native_symbol;
8585
#endif
8686

87+
/*
88+
* The lowest stack pointer value observed.
89+
* Assumption: native stack grows to the lower address.
90+
*/
91+
uint8 *native_stack_top_min;
92+
8793
#if WASM_ENABLE_FAST_JIT != 0
8894
/**
8995
* Cache for
@@ -165,6 +171,17 @@ typedef struct WASMExecEnv {
165171
} wasm_stack;
166172
} WASMExecEnv;
167173

174+
#if WASM_ENABLE_MEMORY_PROFILING != 0
175+
#define RECORD_STACK_USAGE(e, p) \
176+
do { \
177+
if ((e)->native_stack_top_min > (p)) { \
178+
(e)->native_stack_top_min = (p); \
179+
} \
180+
} while (0)
181+
#else
182+
#define RECORD_STACK_USAGE(e, p) (void)0
183+
#endif
184+
168185
WASMExecEnv *
169186
wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst,
170187
uint32 stack_size);

core/iwasm/common/wasm_runtime_common.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1399,6 +1399,22 @@ wasm_runtime_dump_mem_consumption(WASMExecEnv *exec_env)
13991399
else
14001400
os_printf("Total aux stack used: no enough info to profile\n");
14011401

1402+
/*
1403+
* Report the native stack usage estimation.
1404+
*
1405+
* Unlike the aux stack above, we report the amount unused
1406+
* because we don't know the stack "bottom".
1407+
*
1408+
* Note that this is just about what the runtime itself observed.
1409+
* It doesn't cover host func implementations, signal handlers, etc.
1410+
*/
1411+
if (exec_env->native_stack_top_min != (void *)UINTPTR_MAX)
1412+
os_printf("Native stack left: %zd\n",
1413+
exec_env->native_stack_top_min
1414+
- exec_env->native_stack_boundary);
1415+
else
1416+
os_printf("Native stack left: no enough info to profile\n");
1417+
14021418
os_printf("Total app heap used: %u\n", app_heap_peak_size);
14031419
}
14041420
#endif /* end of (WASM_ENABLE_MEMORY_PROFILING != 0) \

core/iwasm/compilation/aot_compiler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ check_type_compatible(uint8 src_type, uint8 dst_type)
259259
#define I32_SIX LLVM_CONST(i32_six)
260260
#define I32_SEVEN LLVM_CONST(i32_seven)
261261
#define I32_EIGHT LLVM_CONST(i32_eight)
262+
#define I32_NINE LLVM_CONST(i32_nine)
262263
#define I32_NEG_ONE LLVM_CONST(i32_neg_one)
263264
#define I64_NEG_ONE LLVM_CONST(i64_neg_one)
264265
#define I32_MIN LLVM_CONST(i32_min)

core/iwasm/compilation/aot_emit_function.c

Lines changed: 100 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,87 @@ call_aot_free_frame_func(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
366366
#endif /* end of (WASM_ENABLE_DUMP_CALL_STACK != 0) \
367367
|| (WASM_ENABLE_PERF_PROFILING != 0) */
368368

369+
static bool
370+
record_stack_usage(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
371+
uint32 callee_cell_num)
372+
{
373+
LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder);
374+
LLVMBasicBlockRef block_update;
375+
LLVMBasicBlockRef block_after_update;
376+
LLVMValueRef callee_local_size, new_sp, cmp;
377+
LLVMValueRef native_stack_top_min;
378+
LLVMTypeRef ptrdiff_type;
379+
if (comp_ctx->pointer_size == sizeof(uint64_t)) {
380+
ptrdiff_type = I64_TYPE;
381+
}
382+
else {
383+
ptrdiff_type = I32_TYPE;
384+
}
385+
386+
/*
387+
* new_sp = last_alloca - callee_local_size;
388+
* if (*native_stack_top_min_addr > new_sp) {
389+
* *native_stack_top_min_addr = new_sp;
390+
* }
391+
*/
392+
393+
if (!(callee_local_size = LLVMConstInt(
394+
ptrdiff_type, -(int64_t)callee_cell_num * 4, true))) {
395+
aot_set_last_error("llvm build const failed.");
396+
return false;
397+
}
398+
if (!(new_sp = LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE,
399+
func_ctx->last_alloca,
400+
&callee_local_size, 1, "new_sp"))) {
401+
aot_set_last_error("llvm build gep failed");
402+
return false;
403+
}
404+
if (!(native_stack_top_min = LLVMBuildLoad2(
405+
comp_ctx->builder, OPQ_PTR_TYPE,
406+
func_ctx->native_stack_top_min_addr, "native_stack_top_min"))) {
407+
aot_set_last_error("llvm build load failed");
408+
return false;
409+
}
410+
if (!(cmp = LLVMBuildICmp(comp_ctx->builder, LLVMIntULT, new_sp,
411+
native_stack_top_min, "cmp"))) {
412+
aot_set_last_error("llvm build icmp failed.");
413+
return false;
414+
}
415+
416+
if (!(block_update = LLVMAppendBasicBlockInContext(
417+
comp_ctx->context, func_ctx->func, "block_update"))) {
418+
aot_set_last_error("llvm add basic block failed.");
419+
return false;
420+
}
421+
if (!(block_after_update = LLVMAppendBasicBlockInContext(
422+
comp_ctx->context, func_ctx->func, "block_after_update"))) {
423+
aot_set_last_error("llvm add basic block failed.");
424+
return false;
425+
}
426+
LLVMMoveBasicBlockAfter(block_update, block_curr);
427+
LLVMMoveBasicBlockAfter(block_after_update, block_update);
428+
429+
if (!LLVMBuildCondBr(comp_ctx->builder, cmp, block_update,
430+
block_after_update)) {
431+
aot_set_last_error("llvm build cond br failed.");
432+
return false;
433+
}
434+
435+
LLVMPositionBuilderAtEnd(comp_ctx->builder, block_update);
436+
if (!LLVMBuildStore(comp_ctx->builder, new_sp,
437+
func_ctx->native_stack_top_min_addr)) {
438+
aot_set_last_error("llvm build store failed");
439+
return false;
440+
}
441+
if (!LLVMBuildBr(comp_ctx->builder, block_after_update)) {
442+
aot_set_last_error("llvm build br failed.");
443+
return false;
444+
}
445+
446+
LLVMPositionBuilderAtEnd(comp_ctx->builder, block_after_update);
447+
return true;
448+
}
449+
369450
static bool
370451
check_stack_boundary(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
371452
uint32 callee_cell_num)
@@ -409,6 +490,19 @@ check_stack_boundary(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
409490
return true;
410491
}
411492

493+
static bool
494+
check_stack(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
495+
uint32 callee_cell_num)
496+
{
497+
if (comp_ctx->enable_stack_estimation
498+
&& !record_stack_usage(comp_ctx, func_ctx, callee_cell_num))
499+
return false;
500+
if (comp_ctx->enable_stack_bound_check
501+
&& !check_stack_boundary(comp_ctx, func_ctx, callee_cell_num))
502+
return false;
503+
return true;
504+
}
505+
412506
/**
413507
* Check whether the app address and its buffer are inside the linear memory,
414508
* if no, throw exception
@@ -852,8 +946,7 @@ aot_compile_op_call(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
852946
callee_cell_num =
853947
aot_func->param_cell_num + aot_func->local_cell_num + 1;
854948

855-
if (comp_ctx->enable_stack_bound_check
856-
&& !check_stack_boundary(comp_ctx, func_ctx, callee_cell_num))
949+
if (!check_stack(comp_ctx, func_ctx, callee_cell_num))
857950
goto fail;
858951

859952
#if LLVM_VERSION_MAJOR >= 14
@@ -1467,12 +1560,11 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
14671560
/* Translate call non-import block */
14681561
LLVMPositionBuilderAtEnd(comp_ctx->builder, block_call_non_import);
14691562

1470-
if (comp_ctx->enable_stack_bound_check
1471-
&& !check_stack_boundary(comp_ctx, func_ctx,
1472-
param_cell_num + ext_cell_num
1473-
+ 1
1474-
/* Reserve some local variables */
1475-
+ 16))
1563+
if (!check_stack(comp_ctx, func_ctx,
1564+
param_cell_num + ext_cell_num
1565+
+ 1
1566+
/* Reserve some local variables */
1567+
+ 16))
14761568
goto fail;
14771569

14781570
/* Load function pointer */

core/iwasm/compilation/aot_llvm.c

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,21 @@ create_native_stack_bound(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
286286
return true;
287287
}
288288

289+
static bool
290+
create_native_stack_top_min(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
291+
{
292+
LLVMValueRef offset = I32_NINE;
293+
294+
if (!(func_ctx->native_stack_top_min_addr = LLVMBuildInBoundsGEP2(
295+
comp_ctx->builder, OPQ_PTR_TYPE, func_ctx->exec_env, &offset, 1,
296+
"native_stack_top_min_addr"))) {
297+
aot_set_last_error("llvm build in bounds gep failed");
298+
return false;
299+
}
300+
301+
return true;
302+
}
303+
289304
static bool
290305
create_aux_stack_info(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
291306
{
@@ -434,7 +449,8 @@ create_local_variables(AOTCompData *comp_data, AOTCompContext *comp_ctx,
434449
}
435450
}
436451

437-
if (comp_ctx->enable_stack_bound_check) {
452+
if (comp_ctx->enable_stack_bound_check
453+
|| comp_ctx->enable_stack_estimation) {
438454
if (aot_func_type->param_count + func->local_count > 0) {
439455
func_ctx->last_alloca = func_ctx->locals[aot_func_type->param_count
440456
+ func->local_count - 1];
@@ -963,6 +979,10 @@ aot_create_func_context(AOTCompData *comp_data, AOTCompContext *comp_ctx,
963979
&& !create_native_stack_bound(comp_ctx, func_ctx)) {
964980
goto fail;
965981
}
982+
if (comp_ctx->enable_stack_estimation
983+
&& !create_native_stack_top_min(comp_ctx, func_ctx)) {
984+
goto fail;
985+
}
966986

967987
/* Get auxiliary stack info */
968988
if (wasm_func->has_op_set_global_aux_stack
@@ -1622,6 +1642,9 @@ aot_create_comp_context(AOTCompData *comp_data, aot_comp_option_t option)
16221642
if (option->disable_llvm_lto)
16231643
comp_ctx->disable_llvm_lto = true;
16241644

1645+
if (option->enable_stack_estimation)
1646+
comp_ctx->enable_stack_estimation = true;
1647+
16251648
comp_ctx->opt_level = option->opt_level;
16261649
comp_ctx->size_level = option->size_level;
16271650

core/iwasm/compilation/aot_llvm.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ typedef struct AOTFuncContext {
163163
LLVMValueRef aot_inst;
164164
LLVMValueRef argv_buf;
165165
LLVMValueRef native_stack_bound;
166+
LLVMValueRef native_stack_top_min_addr;
166167
LLVMValueRef aux_stack_bound;
167168
LLVMValueRef aux_stack_bottom;
168169
LLVMValueRef native_symbol;
@@ -313,6 +314,9 @@ typedef struct AOTCompContext {
313314
/* Native stack bounday Check */
314315
bool enable_stack_bound_check;
315316

317+
/* Native stack usage estimation */
318+
bool enable_stack_estimation;
319+
316320
/* 128-bit SIMD */
317321
bool enable_simd;
318322

@@ -403,6 +407,7 @@ typedef struct AOTCompOption {
403407
bool enable_aux_stack_frame;
404408
bool disable_llvm_intrinsics;
405409
bool disable_llvm_lto;
410+
bool enable_stack_estimation;
406411
uint32 opt_level;
407412
uint32 size_level;
408413
uint32 output_format;

core/iwasm/include/aot_export.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ typedef struct AOTCompOption {
5555
bool enable_aux_stack_frame;
5656
bool disable_llvm_intrinsics;
5757
bool disable_llvm_lto;
58+
bool enable_stack_estimation;
5859
uint32_t opt_level;
5960
uint32_t size_level;
6061
uint32_t output_format;

core/iwasm/interpreter/wasm_interp_classic.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4150,6 +4150,7 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
41504150
}
41514151
argc = function->param_cell_num;
41524152

4153+
RECORD_STACK_USAGE(exec_env, (uint8 *)&prev_frame);
41534154
#if !(defined(OS_ENABLE_HW_BOUND_CHECK) \
41544155
&& WASM_DISABLE_STACK_HW_BOUND_CHECK == 0)
41554156
if ((uint8 *)&prev_frame < exec_env->native_stack_boundary) {

0 commit comments

Comments
 (0)