diff options
author | Björn Gustavsson <[email protected]> | 2018-04-19 11:24:38 +0200 |
---|---|---|
committer | Björn Gustavsson <[email protected]> | 2018-04-19 12:49:51 +0200 |
commit | b5d657f176a440176eb073f749baadb7c45e19e4 (patch) | |
tree | f93e2edd9a0ee0b473c30064d51cffcf12e05875 /erts | |
parent | 0bcba3f1c9afc9a4164bf78641844a293768a7ec (diff) | |
download | otp-b5d657f176a440176eb073f749baadb7c45e19e4.tar.gz otp-b5d657f176a440176eb073f749baadb7c45e19e4.tar.bz2 otp-b5d657f176a440176eb073f749baadb7c45e19e4.zip |
Make stacktraces consistent when backtrace_depth is 0
It is allowed to set the backtrace depth to 0, but when
an exception is catched the stacktrace will still contain
one element:
1> erlang:system_flag(backtrace_depth, 0).
8
2> catch error(badarg).
{'EXIT',{badarg,[{shell,apply_fun,3,
[{file,"shell.erl"},{line,908}]}]}}
However, when an exception is raised using `erlang:raise/3`, there
will be no elements in the stacktrace:
3> catch erlang:raise(error, badarg, [{fake,name,[arg],[]}]).
{'EXIT',{badarg,[]}}
Since the `error_handler` module uses `erlang:raise/3` to
raise an exception when an undefined function is called,
there will not be any stacktrace when calling an undefined
function:
4> catch undef_module:undef_name(some_argument).
{'EXIT',{undef,[]}}
Fix this inconsistency by changing `erlang:raise/3` so that
it always includes one element in the stacktrace:
3> catch erlang:raise(error, badarg, [{fake,name,[arg],[]}]).
{'EXIT',{badarg,[{fake,name,[arg],[]}]}}
4> catch undef_module:undef_name(some_argument).
{'EXIT',{undef,[{undef_module,undef_name,[some_argument],[]}]}}
Diffstat (limited to 'erts')
-rw-r--r-- | erts/emulator/beam/bif.c | 7 | ||||
-rw-r--r-- | erts/emulator/test/exception_SUITE.erl | 56 |
2 files changed, 61 insertions, 2 deletions
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 017faffa48..79244b8544 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -1150,6 +1150,13 @@ BIF_RETTYPE raise_3(BIF_ALIST_3) /* Create stacktrace and store */ if (erts_backtrace_depth < depth) { depth = erts_backtrace_depth; + if (depth == 0) { + /* + * For consistency with stacktraces generated + * automatically, always include one element. + */ + depth = 1; + } must_copy = 1; } if (must_copy) { diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl index da0292f385..60d14ce841 100644 --- a/erts/emulator/test/exception_SUITE.erl +++ b/erts/emulator/test/exception_SUITE.erl @@ -23,7 +23,8 @@ -export([all/0, suite/0, 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]). + exception_with_heap_frag/1, backtrace_depth/1, + line_numbers/1]). -export([bad_guy/2]). -export([crash/1]). @@ -42,7 +43,7 @@ suite() -> all() -> [badmatch, pending_errors, nil_arith, top_of_stacktrace, stacktrace, nested_stacktrace, raise, gunilla, per, - exception_with_heap_frag, line_numbers]. + exception_with_heap_frag, backtrace_depth, line_numbers]. -define(try_match(E), catch ?MODULE:bar(), @@ -572,6 +573,57 @@ do_exception_with_heap_frag(Bin, [Sz|Sizes]) -> do_exception_with_heap_frag(Bin, Sizes); do_exception_with_heap_frag(_, []) -> ok. +backtrace_depth(Config) when is_list(Config) -> + _ = [do_backtrace_depth(D) || D <- lists:seq(0, 8)], + ok. + +do_backtrace_depth(D) -> + Old = erlang:system_flag(backtrace_depth, D), + try + Expected = max(1, D), + do_backtrace_depth_1(Expected) + after + _ = erlang:system_flag(backtrace_depth, Old) + end. + +do_backtrace_depth_1(D) -> + Exit = fun() -> + error(reason) + end, + HandCrafted = fun() -> + {'EXIT',{_,Stk0}} = (catch error(get_stacktrace)), + %% Fool the compiler to force a hand-crafted + %% stacktrace. + Stk = [hd(Stk0)|tl(Stk0)], + erlang:raise(error, reason, Stk) + end, + PassedOn = fun() -> + try error(get_stacktrace) + catch error:_:Stk -> + %% Just pass on the given stacktrace. + erlang:raise(error, reason, Stk) + end + end, + do_backtrace_depth_2(D, Exit), + do_backtrace_depth_2(D, HandCrafted), + do_backtrace_depth_2(D, PassedOn), + ok. + +do_backtrace_depth_2(D, Exc) -> + try + Exc() + catch + error:reason:Stk -> + if + length(Stk) =/= D -> + io:format("Expected depth: ~p\n", [D]), + io:format("~p\n", [Stk]), + error(bad_depth); + true -> + ok + end + end. + line_numbers(Config) when is_list(Config) -> {'EXIT',{{case_clause,bad_tag}, [{?MODULE,line1,2, |