Skip to content

Commit d7581ac

Browse files
committed
Support passing stacktrace to (re)raise (#128)
Instead of ignoring the stacktrace passed to `:erlang.raise/3` it properly (?) handles it. Thans to that, the following code: ```elixir try do # This needs a fix BTW inspect(fn x -> x + 1 end) rescue e -> {e, __STACKTRACE__} end ``` that used to return: ```elixir {%ArgumentError{message: "argument error"}, [ {:elixir, :eval_external_handler, 3, [file: ~c"patches/estdlib/elixir.erl", line: 145]}, {:erl_eval, :avmo_do_apply, 4, [file: ~c"erl_eval", line: 754]}, {:erl_eval, :avmo_do_apply, 7, [file: ~c"erl_eval", line: 750]}, {:erl_eval, :avmo_try_clauses, 10, [file: ~c"erl_eval", line: 1052]}, {:erl_eval, :avmo_try_clauses, 10, [file: ~c"erl_eval", line: 1060]}, {:erl_eval, :avmo_try_clauses, 10, [file: ~c"erl_eval", line: 1077]}, {:elixir, :avmo_eval_forms, 4, [file: ~c"src/elixir.erl", line: 364]}, {:elixir, :avmo_eval_forms, 4, [file: ~c"src/elixir.erl", line: 374]}, {Module.ParallelChecker, :verify, 1, [file: ~c"lib/module/parallel_checker.ex", line: 112]}, {Module.ParallelChecker, :verify, 1, [file: ~c"lib/module/parallel_checker.ex", line: 111]}, {Code, :avmo_validated_eval_string, 3, [file: ~c"lib/code.ex", line: 572]}, {RunExpr, :run, 1, [file: ~c"tmp/modules/117000009/_build/code.ex", line: 17]}, {RunExpr, :start, 0, [file: ~c"tmp/modules/117000009/_build/code.ex", line: 6]} ]} ``` now returns: ```elixir {%ArgumentError{message: "argument error"}, [ {Inspect.Function, :uniq, 1, [file: ~c"lib/inspect.ex", line: 497]}, {Inspect.Function, :default_inspect, 2, [file: ~c"lib/inspect.ex", line: 479]}, {Inspect.Algebra, :to_doc, 2, [file: ~c"lib/inspect/algebra.ex", line: 396]}, {Kernel, :inspect, 2, [file: ~c"lib/kernel.ex", line: 2381]}, {:elixir_eval, :__FILE__, 1, [ file: ~c"/Users/matheksm/ewr/fission_lib/tmp/modules/117000009/_build/code.ex", line: 18 ]}, {:elixir, :eval_external_handler, 3, [file: ~c"patches/estdlib/elixir.erl", line: 98]}, {:elixir, :eval_external_handler, 3, [file: ~c"patches/estdlib/elixir.erl", line: 99]}, {:erl_eval, :avmo_do_apply, 4, [file: ~c"erl_eval", line: 754]}, {:erl_eval, :avmo_do_apply, 7, [file: ~c"erl_eval", line: 750]}, {:erl_eval, :avmo_try_clauses, 10, [file: ~c"erl_eval", line: 1052]}, {:erl_eval, :avmo_try_clauses, 10, [file: ~c"erl_eval", line: 1060]}, {:erl_eval, :avmo_try_clauses, 10, [file: ~c"erl_eval", line: 1077]}, {:elixir, :avmo_eval_forms, 4, [file: ~c"src/elixir.erl", line: 364]}, {:elixir, :avmo_eval_forms, 4, [file: ~c"src/elixir.erl", line: 374]}, {Module.ParallelChecker, :verify, 1, [file: ~c"lib/module/parallel_checker.ex", line: 112]}, {Module.ParallelChecker, :verify, 1, [file: ~c"lib/module/parallel_checker.ex", line: 111]}, {Code, :avmo_validated_eval_string, 3, [file: ~c"lib/code.ex", line: 572]}, {RunExpr, :run, 1, [file: ~c"tmp/modules/117000009/_build/code.ex", line: 17]} ]} ``` However, I'm not 100% sure this doesn't break something ¯\\_(ツ)\_/¯ I plan to upstream this shortly and hopefully get some feedback. cc @bblaszkow06 @FKubisSWM --- These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license). SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later Signed-off-by: Mateusz Front <[email protected]>
1 parent 3b6901b commit d7581ac

File tree

8 files changed

+64
-12
lines changed

8 files changed

+64
-12
lines changed

src/libAtomVM/bif.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,14 @@
4545
#define RAISE_ERROR(error_type_atom) \
4646
ctx->x[0] = ERROR_ATOM; \
4747
ctx->x[1] = (error_type_atom); \
48+
ctx->x[2] = term_nil(); \
4849
return term_invalid_term();
4950

5051
#define RAISE_ERROR_BIF(fail_label, error_type_atom) \
5152
if (fail_label == 0) { \
5253
ctx->x[0] = ERROR_ATOM; \
5354
ctx->x[1] = (error_type_atom); \
55+
ctx->x[2] = term_nil(); \
5456
} \
5557
return term_invalid_term();
5658

src/libAtomVM/nifs.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
#define RAISE(a, b) \
7070
ctx->x[0] = (a); \
7171
ctx->x[1] = (b); \
72+
ctx->x[2] = term_nil(); \
7273
return term_invalid_term();
7374

7475
#ifndef MAX
@@ -2983,9 +2984,7 @@ static term nif_erlang_system_flag(Context *ctx, int argc, term argv[])
29832984
int new_value = term_to_int(value);
29842985
int nb_processors = smp_get_online_processors();
29852986
if (UNLIKELY(new_value < 1) || UNLIKELY(new_value > nb_processors)) {
2986-
argv[0] = ERROR_ATOM;
2987-
argv[1] = BADARG_ATOM;
2988-
return term_invalid_term();
2987+
RAISE_ERROR(BADARG_ATOM);
29892988
}
29902989
while (!ATOMIC_COMPARE_EXCHANGE_WEAK_INT(&ctx->global->online_schedulers, &old_value, new_value)) {};
29912990
return term_from_int32(old_value);
@@ -3531,6 +3530,7 @@ static term nif_erlang_throw(Context *ctx, int argc, term argv[])
35313530

35323531
ctx->x[0] = THROW_ATOM;
35333532
ctx->x[1] = t;
3533+
ctx->x[2] = term_nil();
35343534
return term_invalid_term();
35353535
}
35363536

@@ -3544,7 +3544,7 @@ static term nif_erlang_raise(Context *ctx, int argc, term argv[])
35443544
}
35453545
ctx->x[0] = ex_class;
35463546
ctx->x[1] = argv[1];
3547-
ctx->x[2] = term_nil();
3547+
ctx->x[2] = argv[2];
35483548
return term_invalid_term();
35493549
}
35503550

src/libAtomVM/nifs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,14 @@ extern "C" {
3838
if (UNLIKELY(!verify_function((value)))) { \
3939
argv[0] = ERROR_ATOM; \
4040
argv[1] = BADARG_ATOM; \
41+
argv[2] = term_nil(); \
4142
return term_invalid_term(); \
4243
}
4344

4445
#define RAISE_ERROR(error_type_atom) \
4546
ctx->x[0] = ERROR_ATOM; \
4647
ctx->x[1] = (error_type_atom); \
48+
ctx->x[2] = term_nil(); \
4749
return term_invalid_term();
4850

4951
const struct Nif *nifs_get(const char *mfa);

src/libAtomVM/opcodesswitch.h

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,7 +1170,7 @@ static void destroy_extended_registers(Context *ctx, unsigned int live)
11701170
#define PROCESS_MAYBE_TRAP_RETURN_VALUE(return_value) \
11711171
if (term_is_invalid_term(return_value)) { \
11721172
if (UNLIKELY(!context_get_flags(ctx, Trap))) { \
1173-
HANDLE_ERROR(); \
1173+
HANDLE_ERROR_MAYBE_STACKTRACE(); \
11741174
} else { \
11751175
SCHEDULE_WAIT(mod, pc); \
11761176
} \
@@ -1180,7 +1180,7 @@ static void destroy_extended_registers(Context *ctx, unsigned int live)
11801180
if (term_is_invalid_term(return_value)) { \
11811181
if (UNLIKELY(!context_get_flags(ctx, Trap))) { \
11821182
pc = rest_pc; \
1183-
HANDLE_ERROR(); \
1183+
HANDLE_ERROR_MAYBE_STACKTRACE(); \
11841184
} else { \
11851185
SCHEDULE_WAIT(mod, pc); \
11861186
} \
@@ -1189,7 +1189,7 @@ static void destroy_extended_registers(Context *ctx, unsigned int live)
11891189
#define PROCESS_MAYBE_TRAP_RETURN_VALUE_LAST(return_value) \
11901190
if (term_is_invalid_term(return_value)) { \
11911191
if (UNLIKELY(!context_get_flags(ctx, Trap))) { \
1192-
HANDLE_ERROR(); \
1192+
HANDLE_ERROR_MAYBE_STACKTRACE(); \
11931193
} else { \
11941194
DO_RETURN(); \
11951195
SCHEDULE_WAIT(mod, pc); \
@@ -1216,6 +1216,10 @@ static void destroy_extended_registers(Context *ctx, unsigned int live)
12161216
x_regs[2] = stacktrace_create_raw(ctx, mod, pc - code, x_regs[0]); \
12171217
goto handle_error;
12181218

1219+
#define HANDLE_ERROR_MAYBE_STACKTRACE() \
1220+
x_regs[2] = x_regs[2] == term_nil() ? stacktrace_create_raw(ctx, mod, pc - code, x_regs[0]) : x_regs[2]; \
1221+
goto handle_error;
1222+
12191223
#define VERIFY_IS_INTEGER(t, opcode_name, label) \
12201224
if (UNLIKELY(!term_is_integer(t))) { \
12211225
TRACE(opcode_name ": " #t " is not an integer\n"); \
@@ -3741,9 +3745,13 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
37413745

37423746
#ifdef IMPL_EXECUTE_LOOP
37433747
TRACE("raise/2 stacktrace=0x%lx exc_value=0x%lx\n", stacktrace, exc_value);
3744-
x_regs[0] = stacktrace_exception_class(stacktrace);
3745-
x_regs[1] = exc_value;
3746-
x_regs[2] = stacktrace_create_raw(ctx, mod, saved_pc - code, x_regs[0]);
3748+
if (stacktrace_is_built(stacktrace)) {
3749+
x_regs[2] = stacktrace;
3750+
} else {
3751+
x_regs[0] = stacktrace_exception_class(stacktrace);
3752+
x_regs[1] = exc_value;
3753+
x_regs[2] = stacktrace_create_raw(ctx, mod, saved_pc - code, x_regs[0]);
3754+
}
37473755
goto handle_error;
37483756
#endif
37493757

@@ -3780,7 +3788,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
37803788
break;
37813789

37823790
case ERROR_ATOM_INDEX: {
3783-
x_regs[2] = stacktrace_build(ctx, &x_regs[2], 3);
3791+
x_regs[2] = stacktrace_ensure_built(ctx, &x_regs[2], 3);
37843792
// MEMORY_CAN_SHRINK because catch_end is classified as gc in beam_ssa_codegen.erl
37853793
if (UNLIKELY(memory_ensure_free_with_roots(ctx, TUPLE_SIZE(2) * 2, 2, x_regs + 1, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
37863794
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
@@ -6249,7 +6257,7 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
62496257

62506258
#ifdef IMPL_EXECUTE_LOOP
62516259

6252-
x_regs[0] = stacktrace_build(ctx, &x_regs[0], 1);
6260+
x_regs[0] = stacktrace_ensure_built(ctx, &x_regs[0], 1);
62536261

62546262
#endif
62556263
break;

src/libAtomVM/stacktrace.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,20 @@ term stacktrace_create_raw(Context *ctx, Module *mod, int current_offset, term e
3333
return exception_class;
3434
}
3535

36+
bool stacktrace_is_built(term stack_info_or_stacktrace)
37+
{
38+
UNUSED(stack_info_or_stacktrace);
39+
return true;
40+
}
41+
42+
term stacktrace_ensure_built(Context *ctx, term *stack_info_or_stacktrace, uint32_t live)
43+
{
44+
UNUSED(ctx);
45+
UNUSED(stack_info_or_stacktrace);
46+
UNUSED(live);
47+
return UNDEFINED_ATOM;
48+
}
49+
3650
term stacktrace_build(Context *ctx, term *stack_info, uint32_t live)
3751
{
3852
UNUSED(ctx);
@@ -257,6 +271,27 @@ static term find_path_created(const void *key, struct ModulePathPair *module_pat
257271
return term_invalid_term();
258272
}
259273

274+
bool stacktrace_is_built(term stack_info_or_stacktrace)
275+
{
276+
TERM_DEBUG_ASSERT(term_is_tuple(stack_info_or_stacktrace) || term_is_list(stack_info_or_stacktrace));
277+
if (term_is_tuple(stack_info_or_stacktrace)) {
278+
return false;
279+
} else if (term_is_list(stack_info_or_stacktrace)) {
280+
return true;
281+
} else {
282+
fprintf(stderr, "Error: invalid stack_info or stacktrace passed to stacktrace_is_built");
283+
return false;
284+
}
285+
}
286+
287+
term stacktrace_ensure_built(Context *ctx, term *stack_info_or_stacktrace, uint32_t live)
288+
{
289+
if (stacktrace_is_built(*stack_info_or_stacktrace)) {
290+
return *stack_info_or_stacktrace;
291+
}
292+
return stacktrace_build(ctx, stack_info_or_stacktrace, live);
293+
}
294+
260295
term stacktrace_build(Context *ctx, term *stack_info, uint32_t live)
261296
{
262297
GlobalContext *glb = ctx->global;

src/libAtomVM/stacktrace.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ extern "C" {
3030
#endif
3131

3232
term stacktrace_create_raw(Context *ctx, Module *mod, int current_offset, term exception_class);
33+
bool stacktrace_is_built(term stack_info_or_stacktrace);
34+
term stacktrace_ensure_built(Context *ctx, term *stack_info_or_stacktrace, uint32_t live);
3335
/**
3436
* @brief Build a stack trace
3537
* @param ctx context

src/platforms/generic_unix/lib/platform_nifs.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,14 @@
4040
if (UNLIKELY(!verify_function((value)))) { \
4141
argv[0] = ERROR_ATOM; \
4242
argv[1] = BADARG_ATOM; \
43+
ctx->x[2] = term_nil(); \
4344
return term_invalid_term(); \
4445
}
4546

4647
#define RAISE_ERROR(error_type_atom) \
4748
ctx->x[0] = ERROR_ATOM; \
4849
ctx->x[1] = (error_type_atom); \
50+
ctx->x[2] = term_nil(); \
4951
return term_invalid_term();
5052

5153
#if ATOMVM_HAS_MBEDTLS

src/platforms/rp2/src/lib/platform_nifs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
if (UNLIKELY(!verify_function((value)))) { \
4949
argv[0] = ERROR_ATOM; \
5050
argv[1] = BADARG_ATOM; \
51+
ctx->x[2] = term_nil(); \
5152
return term_invalid_term(); \
5253
}
5354

0 commit comments

Comments
 (0)