diff options
Diffstat (limited to 'erts/emulator/test')
-rw-r--r-- | erts/emulator/test/bif_SUITE.erl | 10 | ||||
-rw-r--r-- | erts/emulator/test/exception_SUITE.erl | 34 | ||||
-rw-r--r-- | erts/emulator/test/process_SUITE.erl | 84 | ||||
-rw-r--r-- | erts/emulator/test/system_info_SUITE.erl | 53 |
4 files changed, 161 insertions, 20 deletions
diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index 9e7bcd5255..9f6e1059b9 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -609,6 +609,16 @@ binary_to_existing_atom(Config) when is_list(Config) -> UnlikelyAtom = binary_to_atom(id(UnlikelyBin), latin1), UnlikelyAtom = binary_to_existing_atom(UnlikelyBin, latin1), + + %% ERL-944; a binary that was too large would overflow the latin1-to-utf8 + %% conversion buffer. + OverflowAtom = <<0:511/unit:8, + 196, 133, 196, 133, 196, 133, 196, 133, 196, 133, + 196, 133, 196, 133, 196, 133, 196, 133, 196, 133, + 196, 133, 196, 133, 196, 133, 196, 133, 196, 133, + 196, 133, 196, 133, 196, 133, 196, 133, 196, 133>>, + {'EXIT', _} = (catch binary_to_existing_atom(OverflowAtom, latin1)), + ok. diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl index aec66cb9a3..6e6f7d78ab 100644 --- a/erts/emulator/test/exception_SUITE.erl +++ b/erts/emulator/test/exception_SUITE.erl @@ -23,6 +23,7 @@ -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]). @@ -43,6 +44,7 @@ suite() -> 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), @@ -507,6 +509,38 @@ t1(_,X,_) -> 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 diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index 82844551c0..4ed120e7a2 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -1089,42 +1089,86 @@ process_info_status_handled_signal(Config) when is_list(Config) -> %% OTP-15709 %% Provoke a bug where process_info(reductions) returned wrong result %% because REDS_IN (def_arg_reg[5]) is read when the process in not running. +%% +%% And a bug where process_info(reductions) on a process which was releasing its +%% main lock during execution could result in negative reduction diffs. process_info_reductions(Config) when is_list(Config) -> - pi_reductions_tester(spawn_link(fun() -> pi_reductions_spinnloop() end)), - pi_reductions_tester(spawn_link(fun() -> pi_reductions_recvloop() end)), + {S1, S2} = case erlang:system_info(schedulers) of + 1 -> {1,1}; + _ -> {1,2} + end, + io:format("Run on schedulers ~p and ~p\n", [S1,S2]), + Boss = self(), + Doer = spawn_opt(fun () -> + pi_reductions_tester(true, 10, fun pi_reductions_spinnloop/0, S2), + pi_reductions_tester(true, 10, fun pi_reductions_recvloop/0, S2), + pi_reductions_tester(false, 100, fun pi_reductions_main_unlocker/0, S2), + Boss ! {self(), done} + end, + [link, {scheduler, S1}]), + + {Doer, done} = receive M -> M end, ok. -pi_reductions_tester(Pid) -> - {_, DiffList} = - lists:foldl(fun(_, {Prev, Acc}) -> - %% Add another item that force sending the request - %% as a signal, like 'current_function'. - PI = process_info(Pid, [reductions, current_function]), - [{reductions,Reds}, {current_function,_}] = PI, - Diff = Reds - Prev, - {Diff, true} = {Diff, (Diff >= 0)}, - {Diff, true} = {Diff, (Diff =< 1000*1000)}, - {Reds, [Diff | Acc]} - end, - {0, []}, - lists:seq(1,10)), +pi_reductions_tester(ForceSignal, MaxCalls, Fun, S2) -> + Pid = spawn_opt(Fun, [link, {scheduler,S2}]), + Extra = case ForceSignal of + true -> + %% Add another item that force sending the request + %% as a signal, like 'current_function'. + [current_function]; + false -> + [] + end, + LoopFun = fun Me(Calls, Prev, Acc0) -> + PI = process_info(Pid, [reductions | Extra]), + [{reductions,Reds} | _] = PI, + Diff = Reds - Prev, + %% Verify we get sane non-negative reduction diffs + {Diff, true} = {Diff, (Diff >= 0)}, + {Diff, true} = {Diff, (Diff =< 1000*1000)}, + Acc1 = [Diff | Acc0], + case Calls >= MaxCalls of + true -> Acc1; + false -> Me(Calls+1, Reds, Acc1) + end + end, + DiffList = LoopFun(0, 0, []), unlink(Pid), exit(Pid,kill), - io:format("Reduction diffs: ~p\n", [DiffList]), + io:format("Reduction diffs: ~p\n", [lists:reverse(DiffList)]), ok. pi_reductions_spinnloop() -> %% 6 args to make use of def_arg_reg[5] which is also used as REDS_IN - pi_reductions_spinnloop(1, atom, "hej", self(), make_ref(), 3.14). + pi_reductions_spinnloop(999*1000, atom, "hej", self(), make_ref(), 3.14). -pi_reductions_spinnloop(A,B,C,D,E,F) -> - pi_reductions_spinnloop(B,C,D,E,F,A). +pi_reductions_spinnloop(N,A,B,C,D,E) when N > 0 -> + pi_reductions_spinnloop(N-1,B,C,D,E,A); +pi_reductions_spinnloop(0,_,_,_,_,_) -> + %% Stop to limit max number of reductions consumed + pi_reductions_recvloop(). pi_reductions_recvloop() -> receive "a free lunch" -> false end. +pi_reductions_main_unlocker() -> + Other = spawn_link(fun() -> receive die -> ok end end), + pi_reductions_main_unlocker_loop(Other). + +pi_reductions_main_unlocker_loop(Other) -> + %% Assumption: register(OtherPid, Name) will unlock main lock of calling + %% process during execution. + register(pi_reductions_main_unlocker, Other), + unregister(pi_reductions_main_unlocker), + + %% Yield in order to increase probability of process_info sometimes probing + %% this process when it's not RUNNING. + erlang:yield(), + pi_reductions_main_unlocker_loop(Other). + %% Tests erlang:bump_reductions/1. bump_reductions(Config) when is_list(Config) -> diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl index 4e663fed7f..da2c0ef8ac 100644 --- a/erts/emulator/test/system_info_SUITE.erl +++ b/erts/emulator/test/system_info_SUITE.erl @@ -37,10 +37,13 @@ -export([process_count/1, system_version/1, misc_smoke_tests/1, heap_size/1, wordsize/1, memory/1, ets_limit/1, atom_limit/1, + procs_bug/1, ets_count/1, atom_count/1, system_logger/1]). -export([init/1, handle_event/2, handle_call/2]). +-export([init_per_testcase/2, end_per_testcase/2]). + suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap, {minutes, 2}}]. @@ -48,8 +51,20 @@ suite() -> all() -> [process_count, system_version, misc_smoke_tests, ets_count, heap_size, wordsize, memory, ets_limit, atom_limit, atom_count, + procs_bug, system_logger]. + +init_per_testcase(procs_bug, Config) -> + procs_bug(init_per_testcase, Config); +init_per_testcase(_, Config) -> + Config. + +end_per_testcase(procs_bug, Config) -> + procs_bug(end_per_testcase, Config); +end_per_testcase(_, _) -> + ok. + %%% %%% The test cases ------------------------------------------------------------- %%% @@ -649,3 +664,41 @@ handle_call(Msg, State) -> handle_event(Event, State) -> State ! {report_handler, Event}, {ok, State}. + + +%% OTP-15909: Provoke bug that would cause VM crash +%% if doing system_info(procs) when process have queued exit/down signals. +procs_bug(init_per_testcase, Config) -> + %% Use single scheduler and process prio to starve monitoring processes + %% from handling their received DOWN signals. + OldSchedOnline = erlang:system_flag(schedulers_online,1), + [{schedulers_online, OldSchedOnline} | Config]; +procs_bug(end_per_testcase, Config) -> + erlang:system_flag(schedulers_online, + proplists:get_value(schedulers_online, Config)), + ok. + +procs_bug(Config) when is_list(Config) -> + {Monee,_} = spawn_opt(fun () -> receive die -> ok end end, + [monitor,{priority,max}]), + Papa = self(), + Pids = [begin + P = spawn_opt(fun () -> + erlang:monitor(process, Monee), + Papa ! {self(),ready}, + receive "nada" -> no end + end, + [link, {priority,normal}]), + {P, ready} = receive M -> M end, + P + end + || _ <- lists:seq(1,10)], + process_flag(priority,high), + Monee ! die, + {'DOWN',_,process,Monee,normal} = receive M -> M end, + + %% This call did crash VM as Pids have pending DOWN signals. + erlang:system_info(procs), + process_flag(priority,normal), + [begin unlink(P), exit(P, kill) end || P <- Pids], + ok. |