%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1997-2018. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% -module(exception_SUITE). -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, change_exception_class/1, exception_with_heap_frag/1, backtrace_depth/1, line_numbers/1]). -export([bad_guy/2]). -export([crash/1]). -include_lib("common_test/include/ct.hrl"). -import(lists, [foreach/2]). %% The range analysis of the HiPE compiler results in a system limit error %% during compilation instead of at runtime, so do not perform this analysis. -compile([{hipe, [no_icode_range]}]). %% Module-level type optimization propagates the constants used when testing %% increment1/1 and increment2/1, which makes it test something completely %% different, so we're turning it off. -compile(no_module_opt). suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap, {minutes, 1}}]. all() -> [badmatch, pending_errors, nil_arith, top_of_stacktrace, stacktrace, nested_stacktrace, raise, gunilla, per, change_exception_class, exception_with_heap_frag, backtrace_depth, line_numbers]. -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) -> ?try_match(a), ?try_match(42), ?try_match({a, b, c}), ?try_match([]), ?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) -> pending(e_badmatch, {badmatch, b}), pending(x, function_clause), pending(e_case, {case_clause, xxx}), pending(e_if, if_clause), pending(e_badarith, badarith), pending(e_undef, undef), pending(e_timeoutval, timeout_value), pending(e_badarg, badarg), 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 -> ct: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 -> ct:fail({unexpected_message, Other}) after 10000 -> ct:fail(timeout) end, process_flag(trap_exit, false). pending({badarg,[{erlang,Bif,BifArgs,Loc1}, {?MODULE,Func,Arity,Loc2}|_]}, Func, Args, _Code) when is_atom(Bif), is_list(BifArgs), length(Args) =:= Arity, is_list(Loc1), is_list(Loc2) -> ok; pending({undef,[{non_existing_module,foo,[],Loc}|_]}, _, _, _) when is_list(Loc) -> ok; pending({function_clause,[{?MODULE,Func,Args,Loc}|_]}, Func, Args, _Code) when is_list(Loc) -> ok; pending({Code,[{?MODULE,Func,Arity,Loc}|_]}, Func, Args, Code) when length(Args) =:= Arity, is_list(Loc) -> ok; pending(Reason, _Function, _Args, _Code) -> ct: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) -> ba_plus_minus_times([], []), ba_plus_minus_times([], 0), ba_plus_minus_times([], 42), ba_plus_minus_times([], 38724978123478923784), ba_plus_minus_times([], 38.72), ba_plus_minus_times(0, []), ba_plus_minus_times(334, []), ba_plus_minus_times(387249797813478923784, []), ba_plus_minus_times(344.22, []), ba_div_rem([], []), ba_div_rem([], 0), ba_div_rem([], 1), ba_div_rem([], 42), ba_div_rem([], 38724978123478923784), ba_div_rem(344.22, []), ba_div_rem(0, []), ba_div_rem(1, []), ba_div_rem(334, []), ba_div_rem(387249797813478923784, []), ba_div_rem(344.22, []), ba_div_rem(344.22, 0.0), ba_div_rem(1, 0.0), ba_div_rem(392873498733971, 0.0), ba_bop([], []), ba_bop(0, []), ba_bop(42, []), ba_bop(-42342742987343, []), ba_bop(238.342, []), ba_bop([], 0), ba_bop([], -243), ba_bop([], 243), ba_bop([], 2438724982478933), ba_bop([], 3987.37), ba_bnot([]), ba_bnot(23.33), ba_shift([], []), ba_shift([], 0), ba_shift([], 4), ba_shift([], -4), ba_shift([], 2343333333333), ba_shift([], -333333333), ba_shift([], 234.00), ba_shift(23, []), ba_shift(0, []), ba_shift(-3433443433433323, []), ba_shift(433443433433323, []), 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). %% 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(), {_,Mref} = spawn_monitor(fun() -> exit({Tag,erlang:get_stacktrace()}) end), {Tag,[]} = receive {'DOWN',Mref,_,_,Info} -> Info end, V = [make_ref()|self()], {value2,{caught1,badarg,[{erlang,abs,[V],_}|_]=St1}} = stacktrace_1({'abs',V}, error, {value,V}), St1 = erase(stacktrace1), St1 = erase(stacktrace2), St1 = erlang:get_stacktrace(), {caught2,{error,badarith},[{erlang,'+',[0,a],_},{?MODULE,my_add,2,_}|_]=St2} = stacktrace_1({'div',{1,0}}, error, {'add',{0,a}}), [{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} = stacktrace_1({value,V}, error, {value,V}), St3 = erase(stacktrace1), St3 = erase(stacktrace2), St3 = erlang:get_stacktrace(), {caught2,{throw,V},[{?MODULE,foo,1,_}|_]=St4} = stacktrace_1({value,V}, error, {throw,V}), [{?MODULE,stacktrace_1,3,_}|_] = erase(stacktrace1), St4 = erase(stacktrace2), St4 = erlang:get_stacktrace(), try stacktrace_2() catch error:{badmatch,_} -> [{?MODULE,stacktrace_2,0,_}, {?MODULE,stacktrace,1,_}|_] = erlang:get_stacktrace(), ok end. 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. stacktrace_2() -> ok = erlang:process_info(self(), current_function), ok. nested_stacktrace(Conf) when is_list(Conf) -> V = [{make_ref()}|[self()]], value1 = nested_stacktrace_1({{value,{V,x1}},void,{V,x1}}, {void,void,void}), {caught1, [{erlang,'+',[V,x1],_},{?MODULE,my_add,2,_}|_], value2, [{erlang,'+',[V,x1],_},{?MODULE,my_add,2,_}|_]} = nested_stacktrace_1({{'add',{V,x1}},error,badarith}, {{value,{V,x2}},void,{V,x2}}), {caught1, [{erlang,'+',[V,x1],_},{?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) -> erase(raise), A = try try foo({'div',{1,0}}) catch error:badarith:A0 -> put(raise, A0 = erlang:get_stacktrace()), erlang:raise(error, badarith, A0) end catch error:badarith:A1 -> A1 = erlang:get_stacktrace(), A1 = get(raise) end, A = erlang:get_stacktrace(), A = get(raise), [{erlang,'div',[1, 0], _},{?MODULE,my_div,2,_}|_] = A, %% N = 8, % Must be even N = erlang:system_flag(backtrace_depth, N), B = odd_even(N, []), try even(N) catch error:function_clause -> ok end, B = erlang:get_stacktrace(), %% C0 = odd_even(N+1, []), C = lists:sublist(C0, N), try odd(N+1) catch error:function_clause -> ok end, C = erlang:get_stacktrace(), try erlang:raise(error, function_clause, C0) catch error:function_clause -> ok end, 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,[{file,"odd_even.erl"},{line,3}]}; true -> {?MODULE,odd,1,[{file,"odd_even.erl"},{line,6}]} end|R]); odd_even(1, R) -> [{?MODULE,odd,[1],[{file,"odd_even.erl"},{line,5}]}|R]. 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_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_element(A, B) -> element(A, B). my_abs(X) -> abs(X). gunilla(Config) when is_list(Config) -> {throw,kalle} = gunilla_1(), [] = 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. change_exception_class(_Config) -> try change_exception_class_1(fun() -> throw(arne) end) catch error:arne -> ok; Class:arne -> ct:fail({wrong_exception_class,Class}) end. change_exception_class_1(F) -> try change_exception_class_2(F) after %% The exception would be caught and rethrown using %% an i_raise instruction. Before the correction %% of the raw_raise instruction, the change of class %% would not stick. io:put_chars("Exception automatically rethrown here\n") end. change_exception_class_2(F) -> try F() catch throw:Reason:Stack -> %% Translated to a raw_raise instruction. %% The change of exception class would not stick %% if the i_raise instruction was later executed. erlang:raise(error, Reason, Stack) end. %% %% 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>>, 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>>, 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>>, 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>>, 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. 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, [{file,"fake_file.erl"},{line,3}]}, {?MODULE,line_numbers,1,_}|_]}} = (catch line1(bad_tag, 0)), {'EXIT',{badarith, [{?MODULE,line1,2, [{file,"fake_file.erl"},{line,5}]}, {?MODULE,line_numbers,1,_}|_]}} = (catch line1(a, not_an_integer)), {'EXIT',{{badmatch,{ok,1}}, [{?MODULE,line1,2, [{file,"fake_file.erl"},{line,7}]}, {?MODULE,line_numbers,1,_}|_]}} = (catch line1(a, 0)), {'EXIT',{crash, [{?MODULE,crash,1, [{file,"fake_file.erl"},{line,14}]}, {?MODULE,line_numbers,1,_}|_]}} = (catch line1(a, 41)), ModFile = ?MODULE_STRING++".erl", [{?MODULE,maybe_crash,1,[{file,"call.erl"},{line,28}]}, {?MODULE,call1,0,[{file,"call.erl"},{line,14}]}, {?MODULE,close_calls,1,[{file,"call.erl"},{line,5}]}, {?MODULE,line_numbers,1,[{file,ModFile},{line,_}]}|_] = close_calls(call1), [{?MODULE,maybe_crash,1,[{file,"call.erl"},{line,28}]}, {?MODULE,call2,0,[{file,"call.erl"},{line,18}]}, {?MODULE,close_calls,1,[{file,"call.erl"},{line,6}]}, {?MODULE,line_numbers,1,[{file,ModFile},{line,_}]}|_] = close_calls(call2), [{?MODULE,maybe_crash,1,[{file,"call.erl"},{line,28}]}, {?MODULE,call3,0,[{file,"call.erl"},{line,22}]}, {?MODULE,close_calls,1,[{file,"call.erl"},{line,7}]}, {?MODULE,line_numbers,1,[{file,ModFile},{line,_}]}|_] = close_calls(call3), no_crash = close_calls(other), <<0,0>> = build_binary1(16), {'EXIT',{badarg, [{?MODULE,build_binary1,1, [{file,"bit_syntax.erl"},{line,72503}]}, {?MODULE,line_numbers,1, [{file,ModFile},{line,_}]}|_]}} = (catch build_binary1(bad_size)), <<7,1,2,3>> = build_binary2(8, <<1,2,3>>), {'EXIT',{badarg, [{?MODULE,build_binary2,2, [{file,"bit_syntax.erl"},{line,72507}]}, {?MODULE,line_numbers,1, [{file,ModFile},{line,_}]}|_]}} = (catch build_binary2(bad_size, <<>>)), {'EXIT',{badarg, [{erlang,bit_size,[bad_binary],[]}, {?MODULE,build_binary2,2, [{file,"bit_syntax.erl"},{line,72507}]}, {?MODULE,line_numbers,1, [{file,ModFile},{line,_}]}|_]}} = (catch build_binary2(8, bad_binary)), <<"abc",357:16>> = build_binary3(<<"abc">>), {'EXIT',{badarg,[{?MODULE,build_binary3,1, [{file,"bit_syntax.erl"},{line,72511}]}, {?MODULE,line_numbers,1, [{file,ModFile},{line,_}]}|_]}} = (catch build_binary3(no_binary)), {'EXIT',{function_clause, [{?MODULE,do_call_abs,[y,y], [{file,"gc_bif.erl"},{line,18}]}, {?MODULE,line_numbers,1,_}|_]}} = (catch do_call_abs(y, y)), {'EXIT',{badarg, [{erlang,abs,[[]],[]}, {?MODULE,do_call_abs,2, [{file,"gc_bif.erl"},{line,19}]}, {?MODULE,line_numbers,1,_}|_]}} = (catch do_call_abs(x, [])), {'EXIT',{{badmatch,"42"}, [{MODULE,applied_bif_1,1,[{file,"applied_bif.erl"},{line,5}]}, {?MODULE,line_numbers,1,_}|_]}} = (catch applied_bif_1(42)), {'EXIT',{{badmatch,{current_location, {?MODULE,applied_bif_2,0, [{file,"applied_bif.erl"},{line,9}]}}}, [{MODULE,applied_bif_2,0,[{file,"applied_bif.erl"},{line,10}]}, {?MODULE,line_numbers,1,_}|_]}} = (catch applied_bif_2()), {'EXIT',{badarith, [{?MODULE,increment1,1,[{file,"increment.erl"},{line,45}]}, {?MODULE,line_numbers,1,_}|_]}} = (catch increment1(x)), {'EXIT',{badarith, [{?MODULE,increment2,1,[{file,"increment.erl"},{line,48}]}, {?MODULE,line_numbers,1,_}|_]}} = (catch increment2(x)), ok. id(I) -> I. -file("odd_even.erl", 1). %Line 1 even(N) when is_integer(N), N > 1, (N rem 2) == 0 -> odd(N-1)++[N]. %Line 3 odd(N) when is_integer(N), N > 1, (N rem 2) == 1 -> even(N-1)++[N]. %Line 6 %% %% If the compiler removes redundant line instructions (any %% line instruction with the same location as the previous), %% and the loader also removes line instructions before %% tail-recursive calls to external functions, then the %% badmatch exception in line 7 below will be reported as %% occurring in line 6. %% %% That means that any removal of redundant line instructions %% must all be done in the compiler OR in the loader. %% -file("fake_file.erl", 1). %Line 1 line1(Tag, X) -> %Line 2 case Tag of %Line 3 a -> Y = X + 1, %Line 5 Res = id({ok,Y}), %Line 6 ?MODULE:crash({ok,42} = Res); %Line 7 b -> x = id(x), %Line 9 ok %Line 10 end. %Line 11 crash(_) -> %Line 13 erlang:error(crash). %Line 14 -file("call.erl", 1). %Line 1 close_calls(Where) -> %Line 2 put(where_to_crash, Where), %Line 3 try call1(), %Line 5 call2(), %Line 6 call3(), %Line 7 no_crash %Line 8 catch error:crash -> erlang:get_stacktrace() %Line 10 end. %Line 11 call1() -> %Line 13 maybe_crash(call1), %Line 14 ok. %Line 15 call2() -> %Line 17 maybe_crash(call2), %Line 18 ok. %Line 19 call3() -> %Line 21 maybe_crash(call3), %Line 22 ok. %Line 23 maybe_crash(Name) -> %Line 25 case get(where_to_crash) of %Line 26 Name -> erlang:error(crash); %Line 28 _ -> ok %Line 30 end. -file("bit_syntax.erl", 72500). %Line 72500 build_binary1(Size) -> %Line 72501 id(42), %Line 72502 <<0:Size>>. %Line 72503 build_binary2(Size, Bin) -> %Line 72505 id(0), %Line 72506 <<7:Size,Bin/binary>>. %Line 72507 build_binary3(Bin) -> %Line 72509 id(0), %Line 72510 <>. %Line 72511 -file("gc_bif.erl", 17). do_call_abs(x, Arg) -> %Line 18 abs(Arg). %Line 19 %% Make sure a BIF that is applied does not leave the p->cp %% set (and thus generating an extra entry on the stack). -file("applied_bif.erl", 1). %% Explicit apply. applied_bif_1(I) -> %Line 3 L = apply(erlang, integer_to_list, [I]), %Line 4 fail = L, %Line 5 ok. %Line 6 %% Implicit apply. applied_bif_2() -> %Line 8 R = process_info(self(), current_location), %Line 9 fail = R, %Line 10 ok. %Line 11 %% The increment instruction used to decrement the instruction %% pointer, which would cause the line number in a stack trace to %% be the previous line number. -file("increment.erl", 42). increment1(Arg) -> %Line 43 Res = id(Arg), %Line 44 Res + 1. %Line 45 increment2(Arg) -> %Line 46 _ = id(Arg), %Line 47 Arg + 1. %Line 48