Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 5 additions & 5 deletions src/libAtomVM/bif.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
20 changes: 12 additions & 8 deletions src/libAtomVM/context.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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");

{
Expand All @@ -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;
Expand Down Expand Up @@ -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));
Expand Down
4 changes: 4 additions & 0 deletions src/libAtomVM/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ struct Context
term group_leader;

term exit_reason;

term exception_class;
term exception_reason;
term exception_stacktrace;
};

#ifndef TYPEDEF_CONTEXT
Expand Down
7 changes: 7 additions & 0 deletions src/libAtomVM/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
22 changes: 7 additions & 15 deletions src/libAtomVM/nifs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)) {
};
Expand Down Expand Up @@ -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[])
Expand All @@ -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[])
Expand Down
10 changes: 5 additions & 5 deletions src/libAtomVM/nifs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
71 changes: 38 additions & 33 deletions src/libAtomVM/opcodesswitch.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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) \
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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);
Expand Down
33 changes: 17 additions & 16 deletions src/libAtomVM/stacktrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down
Loading
Loading