From 012ba89fe3109e67bcf17d95fc7e82191dc192e4 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Wed, 5 Nov 2025 13:22:22 +0100 Subject: [PATCH 1/3] nifs.c: use RAISE() macro instead of writing registers RAISE()/RAISE_ERROR() is more maintainable than directly writing to registers. Signed-off-by: Davide Bettio --- src/libAtomVM/nifs.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index 7cff3a188..b8d435791 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -3060,9 +3060,7 @@ static term nif_erlang_system_flag(Context *ctx, int argc, term argv[]) int new_value = term_to_int(value); int nb_processors = smp_get_online_processors(); if (UNLIKELY(new_value < 1) || UNLIKELY(new_value > nb_processors)) { - argv[0] = ERROR_ATOM; - argv[1] = BADARG_ATOM; - return term_invalid_term(); + RAISE_ERROR(BADARG_ATOM); } while (!ATOMIC_COMPARE_EXCHANGE_WEAK_INT(&ctx->global->online_schedulers, &old_value, new_value)) { }; @@ -3607,11 +3605,7 @@ static term nif_erlang_throw(Context *ctx, int argc, term argv[]) { UNUSED(argc); - term t = argv[0]; - - ctx->x[0] = THROW_ATOM; - ctx->x[1] = t; - return term_invalid_term(); + RAISE(THROW_ATOM, argv[0]); } static term nif_erlang_raise(Context *ctx, int argc, term argv[]) @@ -3622,10 +3616,8 @@ static term nif_erlang_raise(Context *ctx, int argc, term argv[]) if (UNLIKELY(ex_class != ERROR_ATOM && ex_class != LOWERCASE_EXIT_ATOM && ex_class != THROW_ATOM)) { return BADARG_ATOM; } - ctx->x[0] = ex_class; - ctx->x[1] = argv[1]; - ctx->x[2] = term_nil(); - return term_invalid_term(); + ctx->exception_stacktrace = term_nil(); + RAISE(ex_class, argv[1]); } static term nif_ets_new(Context *ctx, int argc, term argv[]) From f744fd4933ea0f1022ea13f0c1554e150da03469 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Wed, 5 Nov 2025 13:38:13 +0100 Subject: [PATCH 2/3] NIFs: remove VALIDATE_VALUE / RAISE_ERROR macros Just use the ones defined in nifs.h. Signed-off-by: Davide Bettio --- src/platforms/generic_unix/lib/platform_nifs.c | 12 ------------ src/platforms/rp2/src/lib/platform_nifs.c | 7 ------- 2 files changed, 19 deletions(-) diff --git a/src/platforms/generic_unix/lib/platform_nifs.c b/src/platforms/generic_unix/lib/platform_nifs.c index 64f77e548..3d0d385dc 100644 --- a/src/platforms/generic_unix/lib/platform_nifs.c +++ b/src/platforms/generic_unix/lib/platform_nifs.c @@ -37,18 +37,6 @@ //#define ENABLE_TRACE #include "trace.h" -#define VALIDATE_VALUE(value, verify_function) \ - if (UNLIKELY(!verify_function((value)))) { \ - argv[0] = ERROR_ATOM; \ - argv[1] = BADARG_ATOM; \ - return term_invalid_term(); \ - } - -#define RAISE_ERROR(error_type_atom) \ - ctx->x[0] = ERROR_ATOM; \ - ctx->x[1] = (error_type_atom); \ - return term_invalid_term(); - #if ATOMVM_HAS_MBEDTLS // declared in otp_crypt diff --git a/src/platforms/rp2/src/lib/platform_nifs.c b/src/platforms/rp2/src/lib/platform_nifs.c index 5f2ea026b..34734078b 100644 --- a/src/platforms/rp2/src/lib/platform_nifs.c +++ b/src/platforms/rp2/src/lib/platform_nifs.c @@ -44,13 +44,6 @@ // #define ENABLE_TRACE #include "trace.h" -#define VALIDATE_VALUE(value, verify_function) \ - if (UNLIKELY(!verify_function((value)))) { \ - argv[0] = ERROR_ATOM; \ - argv[1] = BADARG_ATOM; \ - return term_invalid_term(); \ - } - static term nif_atomvm_platform(Context *ctx, int argc, term argv[]) { UNUSED(ctx); From e8830d4dc6865df4189ab6290cb5d99484895954 Mon Sep 17 00:00:00 2001 From: Davide Bettio Date: Wed, 5 Nov 2025 13:38:13 +0100 Subject: [PATCH 3/3] Instead of using x[0..2] registers, store exception on dedicated ctx fields Store exception information in context fields: - exception_class (was `x[0]` register) - exception_reason (was `x[1]` register) - exception_stacktrace (was `x[2]` register). This avoids overwriting function arguments when raising an error, allowing better diagnostic, also this should allow the fix of #1951 and proposes an alternative approach to #1804. Also, simplify stacktrace_build(): it requires just Context * parameter. Signed-off-by: Davide Bettio --- src/libAtomVM/bif.c | 10 ++--- src/libAtomVM/context.c | 20 ++++++---- src/libAtomVM/context.h | 4 ++ src/libAtomVM/memory.c | 7 ++++ src/libAtomVM/nifs.c | 6 +-- src/libAtomVM/nifs.h | 10 ++--- src/libAtomVM/opcodesswitch.h | 71 +++++++++++++++++++---------------- src/libAtomVM/stacktrace.c | 33 ++++++++-------- src/libAtomVM/stacktrace.h | 2 +- 9 files changed, 92 insertions(+), 71 deletions(-) diff --git a/src/libAtomVM/bif.c b/src/libAtomVM/bif.c index e6e933b4b..9dec902fe 100644 --- a/src/libAtomVM/bif.c +++ b/src/libAtomVM/bif.c @@ -43,15 +43,15 @@ #include "bifs_hash.h" #pragma GCC diagnostic pop -#define RAISE_ERROR(error_type_atom) \ - ctx->x[0] = ERROR_ATOM; \ - ctx->x[1] = (error_type_atom); \ +#define RAISE_ERROR(error_type_atom) \ + ctx->exception_class = ERROR_ATOM; \ + ctx->exception_reason = (error_type_atom); \ return term_invalid_term(); #define RAISE_ERROR_BIF(fail_label, error_type_atom) \ if (fail_label == 0) { \ - ctx->x[0] = ERROR_ATOM; \ - ctx->x[1] = (error_type_atom); \ + ctx->exception_class = ERROR_ATOM; \ + ctx->exception_reason = (error_type_atom); \ } \ return term_invalid_term(); diff --git a/src/libAtomVM/context.c b/src/libAtomVM/context.c index f682e8c34..17e76a667 100644 --- a/src/libAtomVM/context.c +++ b/src/libAtomVM/context.c @@ -123,6 +123,10 @@ Context *context_new(GlobalContext *glb) ctx->bs = term_invalid_term(); ctx->bs_offset = 0; + ctx->exception_class = term_nil(); + ctx->exception_reason = term_nil(); + ctx->exception_stacktrace = term_nil(); + ctx->exit_reason = NORMAL_ATOM; globalcontext_init_process(glb, ctx); @@ -1159,7 +1163,7 @@ COLD_FUNC void context_dump(Context *ctx) fprintf(stderr, "\n"); fprintf(stderr, "\nStacktrace:\n"); - term_display(stderr, stacktrace_build(ctx, &ctx->x[2], 3), ctx); + term_display(stderr, stacktrace_build(ctx), ctx); fprintf(stderr, "\n\n"); { @@ -1171,12 +1175,12 @@ COLD_FUNC void context_dump(Context *ctx) cp_mod->module_index, label, offset); } - fprintf(stderr, "x[0]: "); - term_display(stderr, ctx->x[0], ctx); - fprintf(stderr, "\nx[1]: "); - term_display(stderr, ctx->x[1], ctx); - fprintf(stderr, "\nx[2]: "); - term_display(stderr, ctx->x[2], ctx); + fprintf(stderr, "Exception:\n- Class: "); + term_display(stderr, ctx->exception_class, ctx); + fprintf(stderr, "\n- Reason: "); + term_display(stderr, ctx->exception_reason, ctx); + fprintf(stderr, "\n- Stacktrace: "); + term_display(stderr, ctx->exception_stacktrace, ctx); fprintf(stderr, "\n\nStack \n-----\n\n"); term *ct = ctx->e; @@ -1272,7 +1276,7 @@ COLD_FUNC void context_dump(Context *ctx) synclist_unlock(&glb->processes_table); // If crash is caused by out_of_memory, print more data about memory usage - if (ctx->x[0] == ERROR_ATOM && ctx->x[1] == OUT_OF_MEMORY_ATOM) { + if (ctx->exception_class == ERROR_ATOM && ctx->exception_reason == OUT_OF_MEMORY_ATOM) { fprintf(stderr, "\n\nContext memory info\n-------------------\n"); fprintf(stderr, "context_size = %zu\n", context_size(ctx)); fprintf(stderr, "context_avail_free_memory = %zu\n", context_avail_free_memory(ctx)); diff --git a/src/libAtomVM/context.h b/src/libAtomVM/context.h index bcdf04c3e..046b3ef6b 100644 --- a/src/libAtomVM/context.h +++ b/src/libAtomVM/context.h @@ -157,6 +157,10 @@ struct Context term group_leader; term exit_reason; + + term exception_class; + term exception_reason; + term exception_stacktrace; }; #ifndef TYPEDEF_CONTEXT diff --git a/src/libAtomVM/memory.c b/src/libAtomVM/memory.c index 25a6f83ee..273bbf8a3 100644 --- a/src/libAtomVM/memory.c +++ b/src/libAtomVM/memory.c @@ -298,6 +298,13 @@ static enum MemoryGCResult memory_gc(Context *ctx, size_t new_size, size_t num_r TRACE("- Running copy GC on exit reason\n"); ctx->exit_reason = memory_shallow_copy_term(old_root_fragment, ctx->exit_reason, &ctx->heap.heap_ptr, true); + TRACE("- Running copy GC on exception data\n"); + // ctx->exception_class is always an atom + ctx->exception_reason = memory_shallow_copy_term( + old_root_fragment, ctx->exception_reason, &ctx->heap.heap_ptr, true); + ctx->exception_stacktrace = memory_shallow_copy_term( + old_root_fragment, ctx->exception_stacktrace, &ctx->heap.heap_ptr, true); + TRACE("- Running copy GC on group leader\n"); ctx->group_leader = memory_shallow_copy_term(old_root_fragment, ctx->group_leader, &ctx->heap.heap_ptr, true); diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index b8d435791..5a1f1d3ed 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -68,9 +68,9 @@ #define FLOAT_BUF_SIZE 64 -#define RAISE(a, b) \ - ctx->x[0] = (a); \ - ctx->x[1] = (b); \ +#define RAISE(a, b) \ + ctx->exception_class = (a); \ + ctx->exception_reason = (b); \ return term_invalid_term(); #ifndef MAX diff --git a/src/libAtomVM/nifs.h b/src/libAtomVM/nifs.h index a569f42c1..43469e827 100644 --- a/src/libAtomVM/nifs.h +++ b/src/libAtomVM/nifs.h @@ -36,14 +36,14 @@ extern "C" { #define VALIDATE_VALUE(value, verify_function) \ if (UNLIKELY(!verify_function((value)))) { \ - argv[0] = ERROR_ATOM; \ - argv[1] = BADARG_ATOM; \ + ctx->exception_class = ERROR_ATOM; \ + ctx->exception_reason = BADARG_ATOM; \ return term_invalid_term(); \ } -#define RAISE_ERROR(error_type_atom) \ - ctx->x[0] = ERROR_ATOM; \ - ctx->x[1] = (error_type_atom); \ +#define RAISE_ERROR(error_type_atom) \ + ctx->exception_class = ERROR_ATOM; \ + ctx->exception_reason = (error_type_atom); \ return term_invalid_term(); const struct Nif *nifs_get(const char *mfa); diff --git a/src/libAtomVM/opcodesswitch.h b/src/libAtomVM/opcodesswitch.h index d1fdeacb4..aa7d25442 100644 --- a/src/libAtomVM/opcodesswitch.h +++ b/src/libAtomVM/opcodesswitch.h @@ -59,23 +59,24 @@ extern "C" { #ifdef IMPL_EXECUTE_LOOP #if AVM_NO_JIT -#define SET_ERROR(error_type_atom) \ - x_regs[0] = ERROR_ATOM; \ - x_regs[1] = error_type_atom; \ - x_regs[2] = stacktrace_create_raw(ctx, mod, pc - code, ERROR_ATOM); +#define SET_ERROR(error_type_atom) \ + ctx->exception_class = ERROR_ATOM; \ + ctx->exception_reason = error_type_atom; \ + ctx->exception_stacktrace = stacktrace_create_raw(ctx, mod, pc - code, ERROR_ATOM); #elif AVM_NO_EMU -#define SET_ERROR(error_type_atom) \ - x_regs[0] = ERROR_ATOM; \ - x_regs[1] = error_type_atom; \ - x_regs[2] = stacktrace_create_raw(ctx, mod, (const uint8_t *) native_pc - (const uint8_t *) mod->native_code, ERROR_ATOM); +#define SET_ERROR(error_type_atom) \ + ctx->exception_class = ERROR_ATOM; \ + ctx->exception_reason = error_type_atom; \ + ctx->exception_stacktrace = stacktrace_create_raw(ctx, mod, (const uint8_t *) native_pc - (const uint8_t *) mod->native_code, ERROR_ATOM); #else -#define SET_ERROR(error_type_atom) \ - x_regs[0] = ERROR_ATOM; \ - x_regs[1] = error_type_atom; \ - if (mod->native_code) { \ - x_regs[2] = stacktrace_create_raw(ctx, mod, (const uint8_t *) native_pc - (const uint8_t *) mod->native_code, ERROR_ATOM); \ - } else { \ - x_regs[2] = stacktrace_create_raw(ctx, mod, pc - code, ERROR_ATOM); \ +#define SET_ERROR(error_type_atom) \ + ctx->exception_class = ERROR_ATOM; \ + ctx->exception_reason = error_type_atom; \ + if (mod->native_code) { \ + ctx->exception_stacktrace = stacktrace_create_raw(ctx, mod, \ + (const uint8_t *) native_pc - (const uint8_t *) mod->native_code, ERROR_ATOM); \ + } else { \ + ctx->exception_stacktrace = stacktrace_create_raw(ctx, mod, pc - code, ERROR_ATOM); \ } #endif @@ -1312,7 +1313,7 @@ static void destroy_extended_registers(Context *ctx, unsigned int live) #endif #define HANDLE_ERROR() \ - x_regs[2] = stacktrace_create_raw(ctx, mod, pc - code, x_regs[0]); \ + ctx->exception_stacktrace = stacktrace_create_raw(ctx, mod, pc - code, ctx->exception_class); \ goto handle_error; #define VERIFY_IS_INTEGER(t, opcode_name, label) \ @@ -3973,6 +3974,9 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) #ifdef IMPL_EXECUTE_LOOP // clears the catch value on stack WRITE_REGISTER(dreg, term_nil()); + ctx->x[0] = ctx->exception_class; + ctx->x[1] = ctx->exception_reason; + ctx->x[2] = ctx->exception_stacktrace; #endif break; } @@ -4020,9 +4024,9 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) #ifdef IMPL_EXECUTE_LOOP TRACE("raise/2 stacktrace=0x%" TERM_X_FMT " exc_value=0x%" TERM_X_FMT "\n", stacktrace, exc_value); - x_regs[0] = stacktrace_exception_class(stacktrace); - x_regs[1] = exc_value; - x_regs[2] = stacktrace_create_raw(ctx, mod, saved_pc - code, x_regs[0]); + ctx->exception_class = stacktrace_exception_class(stacktrace); + ctx->exception_reason = exc_value; + ctx->exception_stacktrace = stacktrace_create_raw(ctx, mod, saved_pc - code, ctx->exception_class); goto handle_error; #endif @@ -4053,19 +4057,19 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) #ifdef IMPL_EXECUTE_LOOP WRITE_REGISTER(dreg, term_nil()); // C.f. https://www.erlang.org/doc/reference_manual/expressions.html#catch-and-throw - switch (term_to_atom_index(x_regs[0])) { + switch (term_to_atom_index(ctx->exception_class)) { case THROW_ATOM_INDEX: - x_regs[0] = x_regs[1]; + x_regs[0] = ctx->exception_reason; break; case ERROR_ATOM_INDEX: { - x_regs[2] = stacktrace_build(ctx, &x_regs[2], 3); + x_regs[2] = stacktrace_build(ctx); // MEMORY_CAN_SHRINK because catch_end is classified as gc in beam_ssa_codegen.erl if (UNLIKELY(memory_ensure_free_with_roots(ctx, TUPLE_SIZE(2) * 2, 2, x_regs + 1, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { RAISE_ERROR(OUT_OF_MEMORY_ATOM); } term reason_tuple = term_alloc_tuple(2, &ctx->heap); - term_put_tuple_element(reason_tuple, 0, x_regs[1]); + term_put_tuple_element(reason_tuple, 0, ctx->exception_reason); term_put_tuple_element(reason_tuple, 1, x_regs[2]); term exit_tuple = term_alloc_tuple(2, &ctx->heap); term_put_tuple_element(exit_tuple, 0, EXIT_ATOM); @@ -4081,7 +4085,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) } term exit_tuple = term_alloc_tuple(2, &ctx->heap); term_put_tuple_element(exit_tuple, 0, EXIT_ATOM); - term_put_tuple_element(exit_tuple, 1, x_regs[1]); + term_put_tuple_element(exit_tuple, 1, ctx->exception_reason); x_regs[0] = exit_tuple; break; @@ -6569,9 +6573,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) TRACE("build_stacktrace/0\n"); #ifdef IMPL_EXECUTE_LOOP - - x_regs[0] = stacktrace_build(ctx, &x_regs[0], 1); - + x_regs[0] = stacktrace_build(ctx); #endif break; } @@ -6590,6 +6592,9 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) ex_class != THROW_ATOM)) { x_regs[0] = BADARG_ATOM; } else { + ctx->exception_class = x_regs[0]; + ctx->exception_reason = x_regs[1]; + ctx->exception_stacktrace = x_regs[2]; goto handle_error; } #endif @@ -7567,15 +7572,15 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) #ifdef AVM_PRINT_PROCESS_CRASH_DUMPS // Do not print crash dump if reason is normal or shutdown. - if (x_regs[0] != LOWERCASE_EXIT_ATOM || (x_regs[1] != NORMAL_ATOM && x_regs[1] != SHUTDOWN_ATOM)) { + if (ctx->exception_class != LOWERCASE_EXIT_ATOM || (ctx->exception_reason != NORMAL_ATOM && ctx->exception_reason != SHUTDOWN_ATOM)) { context_dump(ctx); } #endif - if (x_regs[0] == LOWERCASE_EXIT_ATOM) { - ctx->exit_reason = x_regs[1]; + if (ctx->exception_class == LOWERCASE_EXIT_ATOM) { + ctx->exit_reason = ctx->exception_reason; } else { - bool throw = ctx->x[0] == THROW_ATOM; + bool throw = ctx->exception_class == THROW_ATOM; int exit_reason_tuple_size = (throw ? TUPLE_SIZE(2) : 0) + TUPLE_SIZE(2); if (memory_ensure_free_with_roots(ctx, exit_reason_tuple_size, 1, x_regs + 1, MEMORY_CAN_SHRINK) != MEMORY_GC_OK) { @@ -7585,10 +7590,10 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb) if (throw) { error_term = term_alloc_tuple(2, &ctx->heap); term_put_tuple_element(error_term, 0, NOCATCH_ATOM); - term_put_tuple_element(error_term, 1, x_regs[1]); + term_put_tuple_element(error_term, 1, ctx->exception_reason); } else { // error - error_term = x_regs[1]; + error_term = ctx->exception_reason; } term exit_reason_tuple = term_alloc_tuple(2, &ctx->heap); diff --git a/src/libAtomVM/stacktrace.c b/src/libAtomVM/stacktrace.c index 1f38e1ca8..ec7d372a8 100644 --- a/src/libAtomVM/stacktrace.c +++ b/src/libAtomVM/stacktrace.c @@ -33,11 +33,9 @@ term stacktrace_create_raw(Context *ctx, Module *mod, int current_offset, term e return exception_class; } -term stacktrace_build(Context *ctx, term *stack_info, uint32_t live) +term stacktrace_build(Context *ctx) { UNUSED(ctx); - UNUSED(stack_info); - UNUSED(live); return UNDEFINED_ATOM; } @@ -168,8 +166,8 @@ term stacktrace_create_raw(Context *ctx, Module *mod, int current_offset, term e // {num_frames, num_aux_terms, filename_lens, num_mods, [{module, offset}, ...]} size_t requested_size = TUPLE_SIZE(6) + num_frames * (2 + TUPLE_SIZE(2)); - // We need to preserve x0 and x1 that contain information on the current exception - if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, 2, ctx->x, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { + // TODO: we need to know which registers contain function call arguments and save them + if (UNLIKELY(memory_ensure_free_opt(ctx, requested_size, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { fprintf(stderr, "WARNING: Unable to allocate heap space for raw stacktrace\n"); return OUT_OF_MEMORY_ATOM; } @@ -257,38 +255,41 @@ static term find_path_created(const void *key, struct ModulePathPair *module_pat return term_invalid_term(); } -term stacktrace_build(Context *ctx, term *stack_info, uint32_t live) +term stacktrace_build(Context *ctx) { GlobalContext *glb = ctx->global; - if (*stack_info == OUT_OF_MEMORY_ATOM) { - return *stack_info; + term stack_info = ctx->exception_stacktrace; + + if (stack_info == OUT_OF_MEMORY_ATOM) { + return stack_info; } - if (!term_is_tuple(*stack_info)) { + if (!term_is_tuple(stack_info)) { return UNDEFINED_ATOM; } - int num_frames = term_to_int(term_get_tuple_element(*stack_info, 0)); - int num_aux_terms = term_to_int(term_get_tuple_element(*stack_info, 1)); - int filename_lens = term_to_int(term_get_tuple_element(*stack_info, 2)); - int num_mods = term_to_int(term_get_tuple_element(*stack_info, 3)); + int num_frames = term_to_int(term_get_tuple_element(stack_info, 0)); + int num_aux_terms = term_to_int(term_get_tuple_element(stack_info, 1)); + int filename_lens = term_to_int(term_get_tuple_element(stack_info, 2)); + int num_mods = term_to_int(term_get_tuple_element(stack_info, 3)); struct ModulePathPair *module_paths = malloc(num_mods * sizeof(struct ModulePathPair)); if (IS_NULL_PTR(module_paths)) { fprintf(stderr, "Unable to allocate space for module paths. Returning raw stacktrace.\n"); - return *stack_info; + return stack_info; } // // [{module, function, arity, [{file, string()}, {line, int}]}, ...] // size_t requested_size = (TUPLE_SIZE(4) + 2) * num_frames + num_aux_terms * (4 + 2 * TUPLE_SIZE(2)) + 2 * filename_lens; - if (UNLIKELY(memory_ensure_free_with_roots(ctx, requested_size, live, ctx->x, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_ensure_free_opt(ctx, requested_size, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { free(module_paths); return OUT_OF_MEMORY_ATOM; } + stack_info = ctx->exception_stacktrace; - term raw_stacktrace = term_get_tuple_element(*stack_info, 4); + term raw_stacktrace = term_get_tuple_element(stack_info, 4); term stacktrace = term_nil(); term el = raw_stacktrace; diff --git a/src/libAtomVM/stacktrace.h b/src/libAtomVM/stacktrace.h index d5d1228cf..9d036df20 100644 --- a/src/libAtomVM/stacktrace.h +++ b/src/libAtomVM/stacktrace.h @@ -37,7 +37,7 @@ term stacktrace_create_raw(Context *ctx, Module *mod, int current_offset, term e * @param live number of x registers to preserve, which should include stack_info * @return the built stack trace */ -term stacktrace_build(Context *ctx, term *stack_info, uint32_t live); +term stacktrace_build(Context *ctx); term stacktrace_exception_class(term stack_info); #ifdef __cplusplus