aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/test/exception_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/test/exception_SUITE.erl')
-rw-r--r--erts/emulator/test/exception_SUITE.erl497
1 files changed, 497 insertions, 0 deletions
diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl
new file mode 100644
index 0000000000..f1e6e004ad
--- /dev/null
+++ b/erts/emulator/test/exception_SUITE.erl
@@ -0,0 +1,497 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(exception_SUITE).
+
+-export([all/1, badmatch/1, pending_errors/1, nil_arith/1,
+ stacktrace/1, nested_stacktrace/1, raise/1, gunilla/1, per/1,
+ exception_with_heap_frag/1]).
+
+-export([bad_guy/2]).
+
+-include("test_server.hrl").
+-import(lists, [foreach/2]).
+
+all(suite) ->
+ [badmatch, pending_errors, nil_arith,
+ stacktrace, nested_stacktrace, raise, gunilla, per,
+ exception_with_heap_frag].
+
+-define(try_match(E),
+ catch ?MODULE:bar(),
+ {'EXIT', {{badmatch, nomatch}, _}} = (catch E = id(nomatch))).
+
+%% Test that deliberately bad matches are reported correctly.
+
+badmatch(Config) when is_list(Config) ->
+ ?line ?try_match(a),
+ ?line ?try_match(42),
+ ?line ?try_match({a, b, c}),
+ ?line ?try_match([]),
+ ?line ?try_match(1.0),
+ ok.
+
+%% Test various exceptions, in the presence of a previous error suppressed
+%% in a guard.
+pending_errors(Config) when is_list(Config) ->
+ ?line pending(e_badmatch, {badmatch, b}),
+ ?line pending(x, function_clause),
+ ?line pending(e_case, {case_clause, xxx}),
+ ?line pending(e_if, if_clause),
+ ?line pending(e_badarith, badarith),
+ ?line pending(e_undef, undef),
+ ?line pending(e_timeoutval, timeout_value),
+ ?line pending(e_badarg, badarg),
+ ?line pending(e_badarg_spawn, badarg),
+ ok.
+
+bad_guy(pe_badarith, Other) when Other+1 == 0 -> % badarith (suppressed)
+ ok;
+bad_guy(pe_badarg, Other) when length(Other) > 0 -> % badarg (suppressed)
+ ok;
+bad_guy(_, e_case) ->
+ case id(xxx) of
+ ok -> ok
+ end; % case_clause
+bad_guy(_, e_if) ->
+ if
+ a == b -> ok
+ end; % if_clause
+bad_guy(_, e_badarith) ->
+ 1+b; % badarith
+bad_guy(_, e_undef) ->
+ non_existing_module:foo(); % undef
+bad_guy(_, e_timeoutval) ->
+ receive
+ after arne -> % timeout_value
+ ok
+ end;
+bad_guy(_, e_badarg) ->
+ node(xxx); % badarg
+bad_guy(_, e_badarg_spawn) ->
+ spawn({}, {}, {}); % badarg
+bad_guy(_, e_badmatch) ->
+ a = id(b). % badmatch
+
+pending(Arg, Expected) ->
+ pending(pe_badarith, Arg, Expected),
+ pending(pe_badarg, Arg, Expected).
+
+pending(First, Second, Expected) ->
+ pending_catched(First, Second, Expected),
+ pending_exit_message([First, Second], Expected).
+
+pending_catched(First, Second, Expected) ->
+ ok = io:format("Catching bad_guy(~p, ~p)", [First, Second]),
+ case catch bad_guy(First, Second) of
+ {'EXIT', Reason} ->
+ pending(Reason, bad_guy, [First, Second], Expected);
+ Other ->
+ test_server:fail({not_exit, Other})
+ end.
+
+pending_exit_message(Args, Expected) ->
+ ok = io:format("Trapping EXITs from spawn_link(~p, ~p, ~p)",
+ [?MODULE, bad_guy, Args]),
+ process_flag(trap_exit, true),
+ Pid = spawn_link(?MODULE, bad_guy, Args),
+ receive
+ {'EXIT', Pid, Reason} ->
+ pending(Reason, bad_guy, Args, Expected);
+ Other ->
+ test_server:fail({unexpected_message, Other})
+ after 10000 ->
+ test_server:fail(timeout)
+ end,
+ process_flag(trap_exit, false).
+
+pending({badarg, [{erlang,Bif,BifArgs},{?MODULE,Func,Arity}|_]}, Func, Args, _Code)
+ when is_atom(Bif), is_list(BifArgs), length(Args) == Arity ->
+ ok;
+pending({undef,[{non_existing_module,foo,[]}|_]}, _, _, _) ->
+ ok;
+pending({function_clause,[{?MODULE,Func,Args}|_]}, Func, Args, _Code) ->
+ ok;
+pending({Code,[{?MODULE,Func,Arity}|_]}, Func, Args, Code) when length(Args) == Arity ->
+ ok;
+pending(Reason, _Function, _Args, _Code) ->
+ test_server:fail({bad_exit_reason,Reason}).
+
+%% Test that doing arithmetics on [] gives a badarith EXIT and not a crash.
+
+nil_arith(Config) when is_list(Config) ->
+ ?line ba_plus_minus_times([], []),
+
+ ?line ba_plus_minus_times([], 0),
+ ?line ba_plus_minus_times([], 42),
+ ?line ba_plus_minus_times([], 38724978123478923784),
+ ?line ba_plus_minus_times([], 38.72),
+
+ ?line ba_plus_minus_times(0, []),
+ ?line ba_plus_minus_times(334, []),
+ ?line ba_plus_minus_times(387249797813478923784, []),
+ ?line ba_plus_minus_times(344.22, []),
+
+ ?line ba_div_rem([], []),
+
+ ?line ba_div_rem([], 0),
+ ?line ba_div_rem([], 1),
+ ?line ba_div_rem([], 42),
+ ?line ba_div_rem([], 38724978123478923784),
+ ?line ba_div_rem(344.22, []),
+
+ ?line ba_div_rem(0, []),
+ ?line ba_div_rem(1, []),
+ ?line ba_div_rem(334, []),
+ ?line ba_div_rem(387249797813478923784, []),
+ ?line ba_div_rem(344.22, []),
+
+ ?line ba_div_rem(344.22, 0.0),
+ ?line ba_div_rem(1, 0.0),
+ ?line ba_div_rem(392873498733971, 0.0),
+
+ ?line ba_bop([], []),
+ ?line ba_bop(0, []),
+ ?line ba_bop(42, []),
+ ?line ba_bop(-42342742987343, []),
+ ?line ba_bop(238.342, []),
+ ?line ba_bop([], 0),
+ ?line ba_bop([], -243),
+ ?line ba_bop([], 243),
+ ?line ba_bop([], 2438724982478933),
+ ?line ba_bop([], 3987.37),
+
+ ?line ba_bnot([]),
+ ?line ba_bnot(23.33),
+
+ ?line ba_shift([], []),
+ ?line ba_shift([], 0),
+ ?line ba_shift([], 4),
+ ?line ba_shift([], -4),
+ ?line ba_shift([], 2343333333333),
+ ?line ba_shift([], -333333333),
+ ?line ba_shift([], 234.00),
+ ?line ba_shift(23, []),
+ ?line ba_shift(0, []),
+ ?line ba_shift(-3433443433433323, []),
+ ?line ba_shift(433443433433323, []),
+ ?line ba_shift(343.93, []),
+ ok.
+
+ba_plus_minus_times(A, B) ->
+ io:format("~p + ~p", [A, B]),
+ {'EXIT', {badarith, _}} = (catch A + B),
+ io:format("~p - ~p", [A, B]),
+ {'EXIT', {badarith, _}} = (catch A - B),
+ io:format("~p * ~p", [A, B]),
+ {'EXIT', {badarith, _}} = (catch A * B).
+
+ba_div_rem(A, B) ->
+ io:format("~p / ~p", [A, B]),
+ {'EXIT', {badarith, _}} = (catch A / B),
+ io:format("~p div ~p", [A, B]),
+ {'EXIT', {badarith, _}} = (catch A div B),
+ io:format("~p rem ~p", [A, B]),
+ {'EXIT', {badarith, _}} = (catch A rem B).
+
+ba_bop(A, B) ->
+ io:format("~p band ~p", [A, B]),
+ {'EXIT', {badarith, _}} = (catch A band B),
+ io:format("~p bor ~p", [A, B]),
+ {'EXIT', {badarith, _}} = (catch A bor B),
+ io:format("~p bxor ~p", [A, B]),
+ {'EXIT', {badarith, _}} = (catch A bxor B).
+
+ba_shift(A, B) ->
+ io:format("~p bsl ~p", [A, B]),
+ {'EXIT', {badarith, _}} = (catch A bsl B),
+ io:format("~p bsr ~p", [A, B]),
+ {'EXIT', {badarith, _}} = (catch A bsr B).
+
+ba_bnot(A) ->
+ io:format("bnot ~p", [A]),
+ {'EXIT', {badarith, _}} = (catch bnot A).
+
+
+
+stacktrace(Conf) when is_list(Conf) ->
+ Tag = make_ref(),
+ ?line {_,Mref} = spawn_monitor(fun() -> exit({Tag,erlang:get_stacktrace()}) end),
+ ?line {Tag,[]} = receive {'DOWN',Mref,_,_,Info} -> Info end,
+ V = [make_ref()|self()],
+ ?line {value2,{caught1,badarg,[{erlang,abs,[V]}|_]=St1}} =
+ stacktrace_1({'abs',V}, error, {value,V}),
+ ?line St1 = erase(stacktrace1),
+ ?line St1 = erase(stacktrace2),
+ ?line St1 = erlang:get_stacktrace(),
+ ?line {caught2,{error,badarith},[{?MODULE,my_add,2}|_]=St2} =
+ stacktrace_1({'div',{1,0}}, error, {'add',{0,a}}),
+ ?line [{?MODULE,my_div,2}|_] = erase(stacktrace1),
+ ?line St2 = erase(stacktrace2),
+ ?line St2 = erlang:get_stacktrace(),
+ ?line {caught2,{error,{try_clause,V}},[{?MODULE,stacktrace_1,3}|_]=St3} =
+ stacktrace_1({value,V}, error, {value,V}),
+ ?line St3 = erase(stacktrace1),
+ ?line St3 = erase(stacktrace2),
+ ?line St3 = erlang:get_stacktrace(),
+ ?line {caught2,{throw,V},[{?MODULE,foo,1}|_]=St4} =
+ stacktrace_1({value,V}, error, {throw,V}),
+ ?line [{?MODULE,stacktrace_1,3}|_] = erase(stacktrace1),
+ ?line St4 = erase(stacktrace2),
+ ?line St4 = erlang:get_stacktrace(),
+ ok.
+
+stacktrace_1(X, C1, Y) ->
+ erase(stacktrace1),
+ erase(stacktrace2),
+ try try foo(X) of
+ C1 -> value1
+ catch
+ C1:D1 -> {caught1,D1,erlang:get_stacktrace()}
+ after
+ put(stacktrace1, erlang:get_stacktrace()),
+ foo(Y)
+ end of
+ V2 -> {value2,V2}
+ catch
+ C2:D2 -> {caught2,{C2,D2},erlang:get_stacktrace()}
+ after
+ put(stacktrace2, erlang:get_stacktrace())
+ end.
+
+
+
+nested_stacktrace(Conf) when is_list(Conf) ->
+ V = [{make_ref()}|[self()]],
+ ?line value1 =
+ nested_stacktrace_1({{value,{V,x1}},void,{V,x1}},
+ {void,void,void}),
+ ?line {caught1,
+ [{?MODULE,my_add,2}|_],
+ value2,
+ [{?MODULE,my_add,2}|_]} =
+ nested_stacktrace_1({{'add',{V,x1}},error,badarith},
+ {{value,{V,x2}},void,{V,x2}}),
+ ?line {caught1,
+ [{?MODULE,my_add,2}|_],
+ {caught2,[{erlang,abs,[V]}|_]},
+ [{erlang,abs,[V]}|_]} =
+ nested_stacktrace_1({{'add',{V,x1}},error,badarith},
+ {{'abs',V},error,badarg}),
+ ok.
+
+nested_stacktrace_1({X1,C1,V1}, {X2,C2,V2}) ->
+ try foo(X1) of
+ V1 -> value1
+ catch
+ C1:V1 ->
+ S1 = erlang:get_stacktrace(),
+ T2 =
+ try foo(X2) of
+ V2 -> value2
+ catch
+ C2:V2 -> {caught2,erlang:get_stacktrace()}
+ end,
+ {caught1,S1,T2,erlang:get_stacktrace()}
+ end.
+
+
+
+raise(Conf) when is_list(Conf) ->
+ ?line erase(raise),
+ ?line A =
+ try
+ ?line try foo({'div',{1,0}})
+ catch
+ error:badarith ->
+ put(raise, A0 = erlang:get_stacktrace()),
+ ?line erlang:raise(error, badarith, A0)
+ end
+ catch
+ error:badarith ->
+ ?line A1 = erlang:get_stacktrace(),
+ ?line A1 = get(raise)
+ end,
+ ?line A = erlang:get_stacktrace(),
+ ?line A = get(raise),
+ ?line [{?MODULE,my_div,2}|_] = A,
+ %%
+ N = 8, % Must be even
+ ?line N = erlang:system_flag(backtrace_depth, N),
+ ?line try even(N)
+ catch error:function_clause -> ok
+ end,
+ ?line B = odd_even(N, []),
+ ?line B = erlang:get_stacktrace(),
+ %%
+ ?line C0 = odd_even(N+1, []),
+ ?line C = lists:sublist(C0, N),
+ ?line try odd(N+1)
+ catch error:function_clause -> ok
+ end,
+ ?line C = erlang:get_stacktrace(),
+ ?line try erlang:raise(error, function_clause, C0)
+ catch error:function_clause -> ok
+ end,
+ ?line C = erlang:get_stacktrace(),
+ ok.
+
+odd_even(N, R) when is_integer(N), N > 1 ->
+ odd_even(N-1,
+ [if (N rem 2) == 0 ->
+ {?MODULE,even,1};
+ true ->
+ {?MODULE,odd,1}
+ end|R]);
+odd_even(1, R) ->
+ [{?MODULE,odd,[1]}|R].
+
+even(N) when is_integer(N), N > 1, (N rem 2) == 0 ->
+ odd(N-1)++[N].
+
+odd(N) when is_integer(N), N > 1, (N rem 2) == 1 ->
+ even(N-1)++[N].
+
+
+foo({value,Value}) -> Value;
+foo({'div',{A,B}}) ->
+ my_div(A, B);
+foo({'add',{A,B}}) ->
+ my_add(A, B);
+foo({'abs',X}) ->
+ my_abs(X);
+foo({error,Error}) ->
+ erlang:error(Error);
+foo({throw,Throw}) ->
+ erlang:throw(Throw);
+foo({exit,Exit}) ->
+ erlang:exit(Exit);
+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_abs(X) -> abs(X).
+
+gunilla(Config) when is_list(Config) ->
+ ?line {throw,kalle} = gunilla_1(),
+ ?line [] = erlang:get_stacktrace(),
+ ok.
+
+gunilla_1() ->
+ try try arne()
+ after
+ pelle
+ end
+ catch
+ C:R ->
+ {C,R}
+ end.
+
+arne() ->
+ %% Empty stack trace used to cause change the error class to 'error'.
+ erlang:raise(throw, kalle, []).
+
+per(Config) when is_list(Config) ->
+ try
+ t1(0,pad,0),
+ t2(0,pad,0)
+ catch
+ error:badarith ->
+ ok
+ end.
+
+t1(_,X,_) ->
+ (1 bsl X) + 1.
+
+t2(_,X,_) ->
+ (X bsl 1) + 1.
+
+%%
+%% Make sure that even if a BIF builds an heap fragment, then causes an exception,
+%% the stacktrace term will still be OK (specifically, that it does not contain
+%% stale pointers to the arguments).
+%%
+exception_with_heap_frag(Config) when is_list(Config) ->
+ Sizes = lists:seq(0, 512),
+
+ %% Floats are only validated when the heap fragment has been allocated.
+ BadFloat = <<131,99,53,46,48,$X,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,101,45,48,49,0,0,0,0,0>>,
+ ?line do_exception_with_heap_frag(BadFloat, Sizes),
+
+ %% {Binary,BadFloat}: When the error in float is discovered, a refc-binary
+ %% has been allocated and the list of refc-binaries goes through the
+ %% heap fragment.
+ BinAndFloat =
+ <<131,104,2,109,0,0,1,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,
+ 21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,
+ 46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,
+ 71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
+ 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,
+ 116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,
+ 135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,
+ 154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,
+ 173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
+ 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,
+ 211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,
+ 230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,
+ 249,250,251,252,253,254,255,99,51,46,49,52,$B,$l,$u,$r,$f,48,48,48,48,48,48,
+ 48,48,49,50,52,51,52,101,43,48,48,0,0,0,0,0>>,
+ ?line do_exception_with_heap_frag(BinAndFloat, Sizes),
+
+ %% {Fun,BadFloat}
+ FunAndFloat =
+ <<131,104,2,112,0,0,0,66,0,238,239,135,138,137,216,89,57,22,111,52,126,16,84,
+ 71,8,0,0,0,0,0,0,0,0,100,0,1,116,97,0,98,5,175,169,123,103,100,0,13,110,111,
+ 110,111,100,101,64,110,111,104,111,115,116,0,0,0,41,0,0,0,0,0,99,50,46,55,48,
+ $Y,57,57,57,57,57,57,57,57,57,57,57,57,57,54,52,52,55,101,43,48,48,0,0,0,0,0>>,
+ ?line do_exception_with_heap_frag(FunAndFloat, Sizes),
+
+ %% [ExternalPid|BadFloat]
+ ExtPidAndFloat =
+ <<131,108,0,0,0,1,103,100,0,13,107,97,108,108,101,64,115,116,114,105,100,101,
+ 114,0,0,0,36,0,0,0,0,2,99,48,46,$@,48,48,48,48,48,48,48,48,48,48,48,48,48,48,
+ 48,48,48,48,48,101,43,48,48,0,0,0,0,0>>,
+ ?line do_exception_with_heap_frag(ExtPidAndFloat, Sizes),
+
+ ok.
+
+do_exception_with_heap_frag(Bin, [Sz|Sizes]) ->
+ Filler = erlang:make_tuple(Sz, a),
+ spawn(fun() ->
+ try
+ binary_to_term(Bin)
+ catch
+ _:_ ->
+ %% term_to_binary/1 is an easy way to traverse the
+ %% entire stacktrace term to make sure that every part
+ %% of it is OK.
+ term_to_binary(erlang:get_stacktrace())
+ end,
+ id(Filler)
+ end),
+ do_exception_with_heap_frag(Bin, Sizes);
+do_exception_with_heap_frag(_, []) -> ok.
+
+id(I) -> I.