From e8d45ae14c6c3bdfcbbc7964228b004ef4f11ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 24 May 2017 12:59:14 +0200 Subject: Add stacktrace entries to BIF calls from emulator The goal of this change is to improve debugging of emulator calls. For example, the following code rem(1, y) will error with atom `badarith` when y is 0 and the stacktrace has no entry for `erlang:rem/2`, making such cases very hard to debug. This patch makes it so the stacktrace includes `erlang:rem(1, 0)`. The following emulator BIFs have been changed: * band/2 * bnot/1 * bor/2 * bsl/2 * bsr/2 * bxor/2 * div/2 * element/2 * int_div/2 * rem/2 * sminus/2 * splus/2 * stimes/2 --- erts/emulator/beam/beam_emu.c | 66 ++++++++++++++++----------- erts/emulator/test/exception_SUITE.erl | 82 ++++++++++++++++++++++++++++------ lib/compiler/test/trycatch_SUITE.erl | 6 +-- 3 files changed, 112 insertions(+), 42 deletions(-) diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 79d751d13e..1997c74697 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -204,6 +204,21 @@ do { \ #define ISCATCHEND(instr) ((Eterm *) *(instr) == OpCode(catch_end_y)) +#define BIF_ERROR_ARITY_1(Op1, BIF) \ + if (Arg(0) != 0) goto jump_f; \ + reg[0] = Op1; \ + SWAPOUT; \ + I = handle_error(c_p, I, reg, &bif_export[BIF]->info.mfa); \ + goto post_error_handling + +#define BIF_ERROR_ARITY_2(Op1, Op2, BIF) \ + if (Arg(0) != 0) goto jump_f; \ + reg[0] = Op1; \ + reg[1] = Op2; \ + SWAPOUT; \ + I = handle_error(c_p, I, reg, &bif_export[BIF]->info.mfa); \ + goto post_error_handling + /* * Special Beam instructions. */ @@ -1485,7 +1500,7 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array) goto find_func_info; } -#define DO_OUTLINED_ARITH_2(name, Op1, Op2) \ +#define DO_OUTLINED_ARITH_2(name, Op1, Op2, BIF)\ do { \ Eterm result; \ Uint live = Arg(1); \ @@ -1499,7 +1514,7 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array) if (is_value(result)) { \ StoreBifResult(4, result); \ } \ - goto lb_Cl_error; \ + BIF_ERROR_ARITY_2(reg[live], reg[live+1], BIF);\ } while (0) { @@ -1529,7 +1544,7 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array) StoreBifResult(4, result); } } - DO_OUTLINED_ARITH_2(mixed_plus, PlusOp1, PlusOp2); + DO_OUTLINED_ARITH_2(mixed_plus, PlusOp1, PlusOp2, BIF_splus_2); } { @@ -1554,7 +1569,7 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array) StoreBifResult(4, result); } } - DO_OUTLINED_ARITH_2(mixed_minus, MinusOp1, MinusOp2); + DO_OUTLINED_ARITH_2(mixed_minus, MinusOp1, MinusOp2, BIF_sminus_2); } { @@ -1770,8 +1785,9 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array) StoreBifResult(3, result); } } + c_p->freason = BADARG; + BIF_ERROR_ARITY_2(element_index, element_tuple, BIF_element_2); } - /* Fall through */ OpCase(badarg_j): badarg: @@ -1798,7 +1814,8 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array) StoreBifResult(3, result); } } - goto badarg; + c_p->freason = BADARG; + BIF_ERROR_ARITY_2(make_small(Arg(2)), fast_element_tuple, BIF_element_2); } OpCase(catch_yf): @@ -2929,14 +2946,14 @@ do { \ { Eterm Op1, Op2; GetArg2(2, Op1, Op2); - DO_OUTLINED_ARITH_2(mixed_times, Op1, Op2); + DO_OUTLINED_ARITH_2(mixed_times, Op1, Op2, BIF_stimes_2); } OpCase(i_m_div_jIssd): { Eterm Op1, Op2; GetArg2(2, Op1, Op2); - DO_OUTLINED_ARITH_2(mixed_div, Op1, Op2); + DO_OUTLINED_ARITH_2(mixed_div, Op1, Op2, BIF_div_2); } OpCase(i_int_div_jIssd): @@ -2945,7 +2962,8 @@ do { \ GetArg2(2, Op1, Op2); if (Op2 == SMALL_ZERO) { - goto badarith; + c_p->freason = BADARITH; + BIF_ERROR_ARITY_2(Op1, Op2, BIF_intdiv_2); } else if (is_both_small(Op1, Op2)) { Sint ires = signed_val(Op1) / signed_val(Op2); if (MY_IS_SSMALL(ires)) { @@ -2953,7 +2971,7 @@ do { \ StoreBifResult(4, result); } } - DO_OUTLINED_ARITH_2(int_div, Op1, Op2); + DO_OUTLINED_ARITH_2(int_div, Op1, Op2, BIF_intdiv_2); } { @@ -2970,12 +2988,13 @@ do { \ do_rem: if (RemOp2 == SMALL_ZERO) { - goto badarith; + c_p->freason = BADARITH; + BIF_ERROR_ARITY_2(RemOp1, RemOp2, BIF_rem_2); } else if (is_both_small(RemOp1, RemOp2)) { Eterm result = make_small(signed_val(RemOp1) % signed_val(RemOp2)); StoreBifResult(4, result); } else { - DO_OUTLINED_ARITH_2(int_rem, RemOp1, RemOp2); + DO_OUTLINED_ARITH_2(int_rem, RemOp1, RemOp2, BIF_rem_2); } } @@ -2999,7 +3018,7 @@ do { \ Eterm result = BandOp1 & BandOp2; StoreBifResult(4, result); } - DO_OUTLINED_ARITH_2(band, BandOp1, BandOp2); + DO_OUTLINED_ARITH_2(band, BandOp1, BandOp2, BIF_band_2); } /* @@ -3032,7 +3051,7 @@ do { \ Eterm result = Op1 | Op2; StoreBifResult(4, result); } - DO_OUTLINED_ARITH_2(bor, Op1, Op2); + DO_OUTLINED_ARITH_2(bor, Op1, Op2, BIF_bor_2); } OpCase(i_bxor_jIssd): @@ -3050,7 +3069,7 @@ do { \ Eterm result = (Op1 ^ Op2) | make_small(0); StoreBifResult(4, result); } - DO_OUTLINED_ARITH_2(bxor, Op1, Op2); + DO_OUTLINED_ARITH_2(bxor, Op1, Op2, BIF_bxor_2); } { @@ -3081,8 +3100,9 @@ do { \ Op2 = make_small(bignum_header_is_neg(*big_val(Op2)) ? MAX_SMALL : MIN_SMALL); goto do_bsl; - } - goto badarith; + } + c_p->freason = BADARITH; + BIF_ERROR_ARITY_2(Op1, Op2, BIF_bsr_2); OpCase(i_bsl_jIssd): GetArg2(2, Op1, Op2); @@ -3182,10 +3202,8 @@ do { \ } /* Fall through if the left argument is not an integer. */ } - /* - * One or more non-integer arguments. - */ - goto badarith; + c_p->freason = BADARITH; + BIF_ERROR_ARITY_2(Op1, Op2, BIF_bsl_2); } OpCase(i_int_bnot_jsId): @@ -3203,16 +3221,12 @@ do { \ HEAVY_SWAPIN; ERTS_HOLE_CHECK(c_p); if (is_nil(bnot_val)) { - goto lb_Cl_error; + BIF_ERROR_ARITY_1(reg[live], BIF_bnot_1); } } StoreBifResult(3, bnot_val); } - badarith: - c_p->freason = BADARITH; - goto lb_Cl_error; - OpCase(i_apply): { BeamInstr *next; HEAVY_SWAPOUT; diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl index 76e3556bc4..8cba69582c 100644 --- a/erts/emulator/test/exception_SUITE.erl +++ b/erts/emulator/test/exception_SUITE.erl @@ -21,7 +21,7 @@ -module(exception_SUITE). -export([all/0, suite/0, - badmatch/1, pending_errors/1, nil_arith/1, + badmatch/1, pending_errors/1, nil_arith/1, top_of_stacktrace/1, stacktrace/1, nested_stacktrace/1, raise/1, gunilla/1, per/1, exception_with_heap_frag/1, line_numbers/1]). @@ -36,8 +36,8 @@ suite() -> {timetrap, {seconds, 10}}]. all() -> - [badmatch, pending_errors, nil_arith, stacktrace, - nested_stacktrace, raise, gunilla, per, + [badmatch, pending_errors, nil_arith, top_of_stacktrace, + stacktrace, nested_stacktrace, raise, gunilla, per, exception_with_heap_frag, line_numbers]. -define(try_match(E), @@ -241,7 +241,54 @@ ba_bnot(A) -> io:format("bnot ~p", [A]), {'EXIT', {badarith, _}} = (catch bnot A). +%% Test that BIFs are added to the top of the stacktrace. + +top_of_stacktrace(Conf) when is_list(Conf) -> + %% Arithmetic operators + {'EXIT', {badarith, [{erlang, '+', [1, ok], _} | _]}} = (catch my_add(1, ok)), + {'EXIT', {badarith, [{erlang, '-', [1, ok], _} | _]}} = (catch my_minus(1, ok)), + {'EXIT', {badarith, [{erlang, '*', [1, ok], _} | _]}} = (catch my_times(1, ok)), + {'EXIT', {badarith, [{erlang, 'div', [1, ok], _} | _]}} = (catch my_div(1, ok)), + {'EXIT', {badarith, [{erlang, 'div', [1, 0], _} | _]}} = (catch my_div(1, 0)), + {'EXIT', {badarith, [{erlang, 'rem', [1, ok], _} | _]}} = (catch my_rem(1, ok)), + {'EXIT', {badarith, [{erlang, 'rem', [1, 0], _} | _]}} = (catch my_rem(1, 0)), + + %% Bit operators + {'EXIT', {badarith, [{erlang, 'band', [1, ok], _} | _]}} = (catch my_band(1, ok)), + {'EXIT', {badarith, [{erlang, 'bor', [1, ok], _} | _]}} = (catch my_bor(1, ok)), + {'EXIT', {badarith, [{erlang, 'bsl', [1, ok], _} | _]}} = (catch my_bsl(1, ok)), + {'EXIT', {badarith, [{erlang, 'bsr', [1, ok], _} | _]}} = (catch my_bsr(1, ok)), + {'EXIT', {badarith, [{erlang, 'bxor', [1, ok], _} | _]}} = (catch my_bxor(1, ok)), + {'EXIT', {badarith, [{erlang, 'bnot', [ok], _} | _]}} = (catch my_bnot(ok)), + + %% Tuples + {'EXIT', {badarg, [{erlang, element, [1, ok], _} | _]}} = (catch my_element(1, ok)), + {'EXIT', {badarg, [{erlang, element, [ok, {}], _} | _]}} = (catch my_element(ok, {})), + {'EXIT', {badarg, [{erlang, element, [1, {}], _} | _]}} = (catch my_element(1, {})), + {'EXIT', {badarg, [{erlang, element, [1, {}], _} | _]}} = (catch element(1, erlang:make_tuple(0, ok))), + + %% System limits + Maxbig = maxbig(), + MinusMaxbig = -Maxbig, + {'EXIT', {system_limit, [{erlang, '+', [Maxbig, 1], _} | _]}} = (catch my_add(Maxbig, 1)), + {'EXIT', {system_limit, [{erlang, '+', [Maxbig, 1], _} | _]}} = (catch my_add(maxbig_gc(), 1)), + {'EXIT', {system_limit, [{erlang, '-', [MinusMaxbig, 1], _} | _]}} = (catch my_minus(-Maxbig, 1)), + {'EXIT', {system_limit, [{erlang, '-', [MinusMaxbig, 1], _} | _]}} = (catch my_minus(-maxbig_gc(), 1)), + {'EXIT', {system_limit, [{erlang, '*', [Maxbig, 2], _} | _]}} = (catch my_times(Maxbig, 2)), + {'EXIT', {system_limit, [{erlang, '*', [Maxbig, 2], _} | _]}} = (catch my_times(maxbig_gc(), 2)), + {'EXIT', {system_limit, [{erlang, 'bnot', [Maxbig], _} | _]}} = (catch my_bnot(Maxbig)), + {'EXIT', {system_limit, [{erlang, 'bnot', [Maxbig], _} | _]}} = (catch my_bnot(maxbig_gc())), + ok. + +maxbig() -> + %% We assume that the maximum arity is (1 bsl 19) - 1. + Ws = erlang:system_info(wordsize), + (((1 bsl ((16777184 * (Ws div 4))-1)) - 1) bsl 1) + 1. +maxbig_gc() -> + Maxbig = maxbig(), + erlang:garbage_collect(), + Maxbig. stacktrace(Conf) when is_list(Conf) -> Tag = make_ref(), @@ -253,9 +300,9 @@ stacktrace(Conf) when is_list(Conf) -> St1 = erase(stacktrace1), St1 = erase(stacktrace2), St1 = erlang:get_stacktrace(), - {caught2,{error,badarith},[{?MODULE,my_add,2,_}|_]=St2} = + {caught2,{error,badarith},[{erlang,'+',[0,a],_},{?MODULE,my_add,2,_}|_]=St2} = stacktrace_1({'div',{1,0}}, error, {'add',{0,a}}), - [{?MODULE,my_div,2,_}|_] = erase(stacktrace1), + [{erlang,'div',[1,0],_},{?MODULE,my_div,2,_}|_] = erase(stacktrace1), St2 = erase(stacktrace2), St2 = erlang:get_stacktrace(), {caught2,{error,{try_clause,V}},[{?MODULE,stacktrace_1,3,_}|_]=St3} = @@ -308,13 +355,13 @@ nested_stacktrace(Conf) when is_list(Conf) -> nested_stacktrace_1({{value,{V,x1}},void,{V,x1}}, {void,void,void}), {caught1, - [{?MODULE,my_add,2,_}|_], + [{erlang,'+',[V,x1],_},{?MODULE,my_add,2,_}|_], value2, - [{?MODULE,my_add,2,_}|_]} = + [{erlang,'+',[V,x1],_},{?MODULE,my_add,2,_}|_]} = nested_stacktrace_1({{'add',{V,x1}},error,badarith}, {{value,{V,x2}},void,{V,x2}}), {caught1, - [{?MODULE,my_add,2,_}|_], + [{erlang,'+',[V,x1],_},{?MODULE,my_add,2,_}|_], {caught2,[{erlang,abs,[V],_}|_]}, [{erlang,abs,[V],_}|_]} = nested_stacktrace_1({{'add',{V,x1}},error,badarith}, @@ -355,7 +402,7 @@ raise(Conf) when is_list(Conf) -> end, A = erlang:get_stacktrace(), A = get(raise), - [{?MODULE,my_div,2,_}|_] = A, + [{erlang,'div',[1, 0], _},{?MODULE,my_div,2,_}|_] = A, %% N = 8, % Must be even N = erlang:system_flag(backtrace_depth, N), @@ -404,11 +451,20 @@ foo({raise,{Class,Reason,Stacktrace}}) -> erlang:raise(Class, Reason, Stacktrace). %%foo(function_clause) -> % must not be defined! -my_div(A, B) -> - A div B. +my_add(A, B) -> A + B. +my_minus(A, B) -> A - B. +my_times(A, B) -> A * B. +my_div(A, B) -> A div B. +my_rem(A, B) -> A rem B. + +my_band(A, B) -> A band B. +my_bor(A, B) -> A bor B. +my_bsl(A, B) -> A bsl B. +my_bsr(A, B) -> A bsr B. +my_bxor(A, B) -> A bxor B. +my_bnot(A) -> bnot A. -my_add(A, B) -> - A + B. +my_element(A, B) -> element(A, B). my_abs(X) -> abs(X). diff --git a/lib/compiler/test/trycatch_SUITE.erl b/lib/compiler/test/trycatch_SUITE.erl index a591d6cc93..42dbf7d5f0 100644 --- a/lib/compiler/test/trycatch_SUITE.erl +++ b/lib/compiler/test/trycatch_SUITE.erl @@ -324,11 +324,11 @@ eclectic(Conf) when is_list(Conf) -> {{error,{exit,V},{'EXIT',V}},V} = eclectic_1({foo,{error,{exit,V}}}, error, {value,V}), {{value,{value,V},V}, - {'EXIT',{badarith,[{?MODULE,my_add,2,_}|_]}}} = + {'EXIT',{badarith,[{erlang,'+',[0,a],_},{?MODULE,my_add,2,_}|_]}}} = eclectic_1({foo,{value,{value,V}}}, undefined, {'add',{0,a}}), {{'EXIT',V},V} = eclectic_1({catch_foo,{exit,V}}, undefined, {throw,V}), - {{error,{'div',{1,0}},{'EXIT',{badarith,[{?MODULE,my_div,2,_}|_]}}}, + {{error,{'div',{1,0}},{'EXIT',{badarith,[{erlang,'div',[1,0],_},{?MODULE,my_div,2,_}|_]}}}, {'EXIT',V}} = eclectic_1({foo,{error,{'div',{1,0}}}}, error, {exit,V}), {{{error,V},{'EXIT',{V,[{?MODULE,foo,1,_}|_]}}}, @@ -345,7 +345,7 @@ eclectic(Conf) when is_list(Conf) -> eclectic_2({error,{value,V}}, throw, {error,V}), {{caught,{'EXIT',{badarg,[{erlang,abs,[V],_}|_]}}},V} = eclectic_2({value,{'abs',V}}, undefined, {value,V}), - {{caught,{'EXIT',{badarith,[{?MODULE,my_add,2,_}|_]}}},V} = + {{caught,{'EXIT',{badarith,[{erlang,'+',[0,a],_},{?MODULE,my_add,2,_}|_]}}},V} = eclectic_2({exit,{'add',{0,a}}}, exit, {value,V}), {{caught,{'EXIT',V}},undefined} = eclectic_2({value,{error,V}}, undefined, {exit,V}), -- cgit v1.2.3