aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/test/process_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/test/process_SUITE.erl')
-rw-r--r--erts/emulator/test/process_SUITE.erl738
1 files changed, 555 insertions, 183 deletions
diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index 72f3e8fe85..dae8990f56 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -1,18 +1,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2016. 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/.
+%% 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
%%
-%% 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.
+%% 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%
%%
@@ -25,7 +26,7 @@
%% process_info/1,2
%% register/2 (partially)
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(heap_binary_size, 64).
@@ -40,26 +41,35 @@
process_info_2_list/1, process_info_lock_reschedule/1,
process_info_lock_reschedule2/1,
process_info_lock_reschedule3/1,
+ process_info_garbage_collection/1,
bump_reductions/1, low_prio/1, binary_owner/1, yield/1, yield2/1,
process_status_exiting/1,
otp_4725/1, bad_register/1, garbage_collect/1, otp_6237/1,
process_info_messages/1, process_flag_badarg/1, process_flag_heap_size/1,
- spawn_opt_heap_size/1,
+ spawn_opt_heap_size/1, spawn_opt_max_heap_size/1,
processes_large_tab/1, processes_default_tab/1, processes_small_tab/1,
processes_this_tab/1, processes_apply_trap/1,
processes_last_call_trap/1, processes_gc_trap/1,
processes_term_proc_list/1,
otp_7738_waiting/1, otp_7738_suspended/1,
otp_7738_resume/1,
- garb_other_running/1]).
--export([prio_server/2, prio_client/2]).
+ garb_other_running/1,
+ no_priority_inversion/1,
+ no_priority_inversion2/1,
+ system_task_blast/1,
+ system_task_on_suspended/1,
+ gc_request_when_gc_disabled/1,
+ gc_request_blast_when_gc_disabled/1]).
+-export([prio_server/2, prio_client/2, init/1, handle_event/2]).
-export([init_per_testcase/2, end_per_testcase/2]).
-export([hangaround/2, processes_bif_test/0, do_processes/1,
processes_term_proc_list_test/1]).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 9}}].
all() ->
[spawn_with_binaries, t_exit_1, {group, t_exit_2},
@@ -68,12 +78,16 @@ all() ->
process_info_other_dist_msg, process_info_2_list,
process_info_lock_reschedule,
process_info_lock_reschedule2,
- process_info_lock_reschedule3, process_status_exiting,
+ process_info_lock_reschedule3,
+ process_info_garbage_collection,
+ process_status_exiting,
bump_reductions, low_prio, yield, yield2, otp_4725,
bad_register, garbage_collect, process_info_messages,
process_flag_badarg, process_flag_heap_size,
- spawn_opt_heap_size, otp_6237, {group, processes_bif},
- {group, otp_7738}, garb_other_running].
+ spawn_opt_heap_size, spawn_opt_max_heap_size, otp_6237,
+ {group, processes_bif},
+ {group, otp_7738}, garb_other_running,
+ {group, system_task}].
groups() ->
[{t_exit_2, [],
@@ -87,12 +101,26 @@ groups() ->
processes_gc_trap, processes_term_proc_list]},
{otp_7738, [],
[otp_7738_waiting, otp_7738_suspended,
- otp_7738_resume]}].
+ otp_7738_resume]},
+ {system_task, [],
+ [no_priority_inversion, no_priority_inversion2,
+ system_task_blast, system_task_on_suspended,
+ gc_request_when_gc_disabled, gc_request_blast_when_gc_disabled]}].
init_per_suite(Config) ->
- Config.
+ A0 = case application:start(sasl) of
+ ok -> [sasl];
+ _ -> []
+ end,
+ A = case application:start(os_mon) of
+ ok -> [os_mon|A0];
+ _ -> A0
+ end,
+ [{started_apps, A}|Config].
end_per_suite(Config) ->
+ As = proplists:get_value(started_apps, Config),
+ lists:foreach(fun (A) -> application:stop(A) end, As),
catch erts_debug:set_internal_state(available_internal_state, false),
Config.
@@ -102,14 +130,11 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
-
init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog=?t:timetrap(?t:minutes(10)),
- [{watchdog, Dog},{testcase, Func}|Config].
+ [{testcase, Func}|Config].
end_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog=?config(watchdog, Config),
- ?t:timetrap_cancel(Dog).
+ ok.
fun_spawn(Fun) ->
spawn_link(erlang, apply, [Fun, []]).
@@ -122,11 +147,7 @@ spawn_with_binaries(Config) when is_list(Config) ->
TwoMeg = lists:duplicate(1024, L),
Fun = fun() -> spawn(?MODULE, binary_owner, [list_to_binary(TwoMeg)]),
receive after 1 -> ok end end,
- Iter = case test_server:purify_is_running() of
- true -> 10;
- false -> 150
- end,
- test_server:do_times(Iter, Fun),
+ test_server:do_times(150, Fun),
ok.
binary_owner(Bin) when is_binary(Bin) ->
@@ -134,11 +155,10 @@ binary_owner(Bin) when is_binary(Bin) ->
%% Tests exit/1 with a big message.
t_exit_1(Config) when is_list(Config) ->
+ ct:timetrap({seconds, 20}),
start_spawner(),
- Dog = test_server:timetrap(test_server:seconds(20)),
process_flag(trap_exit, true),
test_server:do_times(10, fun t_exit_1/0),
- test_server:timetrap_cancel(Dog),
stop_spawner(),
ok.
@@ -152,11 +172,10 @@ t_exit_1() ->
%% Tests exit/2 with a lot of data in the exit message.
t_exit_2_other(Config) when is_list(Config) ->
+ ct:timetrap({seconds, 20}),
start_spawner(),
- Dog = test_server:timetrap(test_server:seconds(20)),
process_flag(trap_exit, true),
test_server:do_times(10, fun t_exit_2_other/0),
- test_server:timetrap_cancel(Dog),
stop_spawner(),
ok.
@@ -170,34 +189,32 @@ t_exit_2_other() ->
%% Tests that exit(Pid, normal) does not kill another process.;
t_exit_2_other_normal(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(20)),
+ ct:timetrap({seconds, 20}),
process_flag(trap_exit, true),
Pid = fun_spawn(fun() -> receive x -> ok end end),
exit(Pid, normal),
receive
{'EXIT', Pid, Reason} ->
- test_server:fail({process_died, Reason})
+ ct:fail({process_died, Reason})
after 1000 ->
ok
end,
case process_info(Pid) of
undefined ->
- test_server:fail(process_died_on_normal);
+ ct:fail(process_died_on_normal);
List when is_list(List) ->
ok
end,
exit(Pid, kill),
- test_server:timetrap_cancel(Dog),
ok.
%% Tests that we can trap an exit message sent with exit/2 from
%% the same process.
self_exit(Config) when is_list(Config) ->
+ ct:timetrap({seconds, 10}),
start_spawner(),
- Dog = test_server:timetrap(test_server:seconds(10)),
process_flag(trap_exit, true),
test_server:do_times(200, fun self_exit/0),
- test_server:timetrap_cancel(Dog),
stop_spawner(),
ok.
@@ -216,7 +233,7 @@ normal_suicide_exit(Config) when is_list(Config) ->
Pid = fun_spawn(fun() -> exit(self(), normal) end),
receive
{'EXIT', Pid, normal} -> ok;
- Other -> test_server:fail({bad_message, Other})
+ Other -> ct:fail({bad_message, Other})
end.
%% Tests exit(self(), Term) is equivalent to exit(Term) for a process
@@ -227,7 +244,7 @@ abnormal_suicide_exit(Config) when is_list(Config) ->
Pid = fun_spawn(fun() -> exit(self(), Garbage) end),
receive
{'EXIT', Pid, Garbage} -> ok;
- Other -> test_server:fail({bad_message, Other})
+ Other -> ct:fail({bad_message, Other})
end.
%% Tests that exit(self(), die) cannot be catched.
@@ -236,21 +253,20 @@ t_exit_2_catch(Config) when is_list(Config) ->
Pid = fun_spawn(fun() -> catch exit(self(), die) end),
receive
{'EXIT', Pid, normal} ->
- test_server:fail(catch_worked);
+ ct:fail(catch_worked);
{'EXIT', Pid, die} ->
ok;
Other ->
- test_server:fail({bad_message, Other})
+ ct:fail({bad_message, Other})
end.
%% Tests trapping of an 'EXIT' message generated by a bad argument to
%% the abs/1 bif. The 'EXIT' message will intentionally be very big.
trap_exit_badarg(Config) when is_list(Config) ->
+ ct:timetrap({seconds, 10}),
start_spawner(),
- Dog = test_server:timetrap(test_server:seconds(10)),
process_flag(trap_exit, true),
test_server:do_times(10, fun trap_exit_badarg/0),
- test_server:timetrap_cancel(Dog),
stop_spawner(),
ok.
@@ -264,7 +280,7 @@ trap_exit_badarg() ->
ok;
Other ->
ok = io:format("Bad EXIT message: ~P", [Other, 30]),
- test_server:fail(bad_exit_message)
+ ct:fail(bad_exit_message)
end.
bad_guy(Arg) ->
@@ -296,10 +312,9 @@ big_binary(N, Acc) ->
%% Test receiving an EXIT message when spawning a BIF with bad arguments.
trap_exit_badarg_in_bif(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(10)),
+ ct:timetrap({seconds, 10}),
process_flag(trap_exit, true),
test_server:do_times(10, fun trap_exit_badarg_bif/0),
- test_server:timetrap_cancel(Dog),
ok.
trap_exit_badarg_bif() ->
@@ -308,7 +323,7 @@ trap_exit_badarg_bif() ->
{'EXIT', Pid, {badarg, _}} ->
ok;
Other ->
- test_server:fail({unexpected, Other})
+ ct:fail({unexpected, Other})
end.
%% The following sequences of events have crasched Beam.
@@ -321,15 +336,13 @@ trap_exit_badarg_bif() ->
%% 3) The process will crash the next time it executes 'receive'.
exit_and_timeout(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(20)),
+ ct:timetrap({seconds, 20}),
process_flag(trap_exit, true),
Parent = self(),
Low = fun_spawn(fun() -> eat_low(Parent) end),
High = fun_spawn(fun() -> eat_high(Low) end),
eat_wait_for(Low, High),
-
- test_server:timetrap_cancel(Dog),
ok.
@@ -340,7 +353,7 @@ eat_wait_for(Low, High) ->
{'EXIT', High, normal} ->
eat_wait_for(Low, High);
Other ->
- test_server:fail({bad_message, Other})
+ ct:fail({bad_message, Other})
end.
eat_low(_Parent) ->
@@ -359,29 +372,26 @@ eat_high(Low) ->
process_flag(priority, high),
receive after 1000 -> ok end,
exit(Low, {you, are, dead}),
- {_, Sec, _} = now(),
- loop(Sec, Sec).
+ loop(erlang:monotonic_time() + erlang:convert_time_unit(5,seconds,native)).
%% Busy loop for 5 seconds.
-loop(OrigSec, CurrentSec) when CurrentSec < OrigSec+5 ->
- {_, NewSec, _} = now(),
- loop(OrigSec, NewSec);
-loop(_, _) ->
- ok.
+loop(StopTime) ->
+ case StopTime >= erlang:monotonic_time() of
+ true -> ok;
+ false -> loop(StopTime)
+ end.
%% Tries to send two different exit messages to a process.
%% (The second one should be ignored.)
exit_twice(Config) when is_list(Config) ->
- Dog = test_server:timetrap(test_server:seconds(20)),
+ ct:timetrap({seconds, 20}),
process_flag(trap_exit, true),
Low = fun_spawn(fun etwice_low/0),
High = fun_spawn(fun() -> etwice_high(Low) end),
etwice_wait_for(Low, High),
-
- test_server:timetrap_cancel(Dog),
ok.
etwice_wait_for(Low, High) ->
@@ -389,11 +399,11 @@ etwice_wait_for(Low, High) ->
{'EXIT', Low, first} ->
ok;
{'EXIT', Low, Other} ->
- test_server:fail({wrong_exit_reason, Other});
+ ct:fail({wrong_exit_reason, Other});
{'EXIT', High, normal} ->
etwice_wait_for(Low, High);
Other ->
- test_server:fail({bad_message, Other})
+ ct:fail({bad_message, Other})
end.
etwice_low() ->
@@ -412,6 +422,8 @@ t_process_info(Config) when is_list(Config) ->
{status, running} = process_info(self(), status),
{min_heap_size, 233} = process_info(self(), min_heap_size),
{min_bin_vheap_size,46422} = process_info(self(), min_bin_vheap_size),
+ {max_heap_size, #{ size := 0, kill := true, error_logger := true}} =
+ process_info(self(), max_heap_size),
{current_function,{?MODULE,t_process_info,1}} =
process_info(self(), current_function),
{current_function,{?MODULE,t_process_info,1}} =
@@ -551,6 +563,8 @@ process_info_other_msg(Config) when is_list(Config) ->
{min_heap_size, 233} = process_info(Pid, min_heap_size),
{min_bin_vheap_size, 46422} = process_info(Pid, min_bin_vheap_size),
+ {max_heap_size, #{ size := 0, kill := true, error_logger := true}} =
+ process_info(self(), max_heap_size),
Pid ! stop,
ok.
@@ -646,10 +660,6 @@ chk_pi_order([],[]) ->
chk_pi_order([{Arg, _}| Values], [Arg|Args]) ->
chk_pi_order(Values, Args).
-process_info_2_list(doc) ->
- [];
-process_info_2_list(suite) ->
- [];
process_info_2_list(Config) when is_list(Config) ->
Proc = spawn(fun () -> receive after infinity -> ok end end),
register(process_SUITE_process_info_2_list1, self()),
@@ -682,10 +692,6 @@ process_info_2_list(Config) when is_list(Config) ->
lists:foreach(fun ({backtrace, _}) -> ok end, V3),
ok.
-process_info_lock_reschedule(doc) ->
- [];
-process_info_lock_reschedule(suite) ->
- [];
process_info_lock_reschedule(Config) when is_list(Config) ->
%% We need a process that is running and an item that requires
%% process_info to take the main process lock.
@@ -718,7 +724,7 @@ process_info_lock_reschedule(Config) when is_list(Config) ->
exit(Target2, bang),
OkStatus;
{status, BadStatus} ->
- ?t:fail(BadStatus)
+ ct:fail(BadStatus)
end.
pi_loop(_Name, _Pid, 0) ->
@@ -727,10 +733,6 @@ pi_loop(Name, Pid, N) ->
{registered_name, Name} = process_info(Pid, registered_name),
pi_loop(Name, Pid, N-1).
-process_info_lock_reschedule2(doc) ->
- [];
-process_info_lock_reschedule2(suite) ->
- [];
process_info_lock_reschedule2(Config) when is_list(Config) ->
Parent = self(),
Fun = fun () ->
@@ -786,10 +788,6 @@ do_pi_msg_len(PT, AT) ->
lists:map(fun (_) -> ok end, [a,b,c,d]),
{message_queue_len, _} = process_info(element(2,PT), element(2,AT)).
-process_info_lock_reschedule3(doc) ->
- [];
-process_info_lock_reschedule3(suite) ->
- [];
process_info_lock_reschedule3(Config) when is_list(Config) ->
%% We need a process that is running and an item that requires
%% process_info to take the main process lock.
@@ -820,7 +818,7 @@ process_info_lock_reschedule3(Config) when is_list(Config) ->
exit(Target2, bang),
OkStatus;
{status, BadStatus} ->
- ?t:fail(BadStatus)
+ ct:fail(BadStatus)
end.
process_status_exiting(Config) when is_list(Config) ->
@@ -912,6 +910,73 @@ start_spawner() ->
stop_spawner() ->
ok.
+%% Tests erlang:process_info(Pid, garbage_collection_info)
+process_info_garbage_collection(_Config) ->
+ Parent = self(),
+ Pid = spawn_link(
+ fun() ->
+ %% We set mqd to off_heap and send an tuple
+ %% to process in order to force mbuf_size
+ %% to be used
+ process_flag(message_queue_data, off_heap),
+ receive go -> ok end,
+ (fun F(0) ->
+ Parent ! deep,
+ receive {ok,_} -> ok end,
+ [];
+ F(N) ->
+ timer:sleep(1),
+ [lists:seq(1,100) | F(N-1)]
+ end)(1000),
+ Parent ! shallow,
+ receive done -> ok end
+ end),
+ [{garbage_collection_info, Before},{total_heap_size, THSBefore}] =
+ erlang:process_info(Pid, [garbage_collection_info, total_heap_size]),
+ Pid ! go, receive deep -> ok end,
+ [{_, Deep},{_,THSDeep}] =
+ erlang:process_info(Pid, [garbage_collection_info, total_heap_size]),
+ Pid ! {ok, make_ref()}, receive shallow -> ok end,
+ [{_, After},{_, THSAfter}] =
+ erlang:process_info(Pid, [garbage_collection_info, total_heap_size]),
+ Pid ! done,
+
+ %% Do some general checks to see if everything seems to be roughly correct
+ ct:log("Before: ~p",[Before]),
+ ct:log("Deep: ~p",[Deep]),
+ ct:log("After: ~p",[After]),
+ ct:log("Before THS: ~p",[THSBefore]),
+ ct:log("Deep THS: ~p",[THSDeep]),
+ ct:log("After THS: ~p",[THSAfter]),
+
+ %% Check stack_size
+ true = gv(stack_size, Before) < gv(stack_size, Deep),
+ true = gv(stack_size, After) < gv(stack_size, Deep),
+
+ %% Check used heap size
+ true = gv(heap_size, Before) + gv(old_heap_size, Before)
+ < gv(heap_size, Deep) + gv(old_heap_size, Deep),
+ true = gv(heap_size, Before) + gv(old_heap_size, Before)
+ < gv(heap_size, After) + gv(old_heap_size, After),
+
+ %% Check that total_heap_size == heap_block_size + old_heap_block_size + mbuf_size
+ THSBefore = gv(heap_block_size, Before)
+ + gv(old_heap_block_size, Before)
+ + gv(mbuf_size, Before),
+
+ THSDeep = gv(heap_block_size, Deep)
+ + gv(old_heap_block_size, Deep)
+ + gv(mbuf_size, Deep),
+
+ THSAfter = gv(heap_block_size, After)
+ + gv(old_heap_block_size, After)
+ + gv(mbuf_size, After),
+
+ ok.
+
+gv(Key,List) ->
+ proplists:get_value(Key,List).
+
%% Tests erlang:bump_reductions/1.
bump_reductions(Config) when is_list(Config) ->
erlang:garbage_collect(),
@@ -922,10 +987,10 @@ bump_reductions(Config) when is_list(Config) ->
case R2-R1 of
Diff when Diff < 100 ->
ok = io:format("R1 = ~w, R2 = ~w", [R1, R2]),
- test_server:fail({small_diff, Diff});
+ ct:fail({small_diff, Diff});
Diff when Diff > 110 ->
ok = io:format("R1 = ~w, R2 = ~w", [R1, R2]),
- test_server:fail({big_diff, Diff});
+ ct:fail({big_diff, Diff});
Diff ->
io:format("~p\n", [Diff]),
ok
@@ -964,7 +1029,7 @@ low_prio_test(Config) when is_list(Config) ->
process_flag(trap_exit, true),
S = spawn_link(?MODULE, prio_server, [0, 0]),
PCs = spawn_prio_clients(S, erlang:system_info(schedulers_online)),
- timer:sleep(2000),
+ ct:sleep({seconds,3}),
lists:foreach(fun (P) -> exit(P, kill) end, PCs),
S ! exit,
receive {'EXIT', S, {A, B}} -> check_prio(A, B) end,
@@ -1012,8 +1077,7 @@ make_unaligned_sub_binary(Bin0) ->
<<0:3,Bin:Sz/binary,31:5>> = id(Bin1),
Bin.
-yield(doc) ->
- "Tests erlang:yield/1.";
+%% Tests erlang:yield/1
yield(Config) when is_list(Config) ->
case catch erlang:system_info(modified_timing_level) of
Level when is_integer(Level) ->
@@ -1054,7 +1118,7 @@ yield_test() ->
{Diff, _} ->
ok = io:format("R1 = ~w, R2 = ~w, Schedcnt = ~w",
[R1, R2, Schedcnt]),
- test_server:fail({measurement_error, Diff, Schedcnt})
+ ct:fail({measurement_error, Diff, Schedcnt})
end.
call_yield() ->
@@ -1091,8 +1155,6 @@ schedcnt(stop, {Ref, Pid}) when is_reference(Ref), is_pid(Pid) ->
Cnt
end.
-yield2(doc) -> [];
-yield2(suite) -> [];
yield2(Config) when is_list(Config) ->
Me = self(),
Go = make_ref(),
@@ -1143,7 +1205,7 @@ yield2(Config) when is_list(Config) ->
io:format("Reductions = ~p~n", [Reductions]),
ok;
{RedDiff, Reductions} ->
- ?t:fail({unexpected_reduction_count, Reductions})
+ ct:fail({unexpected_reduction_count, Reductions})
end,
none = next_tmsg(P),
@@ -1184,8 +1246,6 @@ fail_register(Name, Process) ->
{'EXIT',{badarg,_}} = (catch Name ! anything_goes),
ok.
-garbage_collect(doc) -> [];
-garbage_collect(suite) -> [];
garbage_collect(Config) when is_list(Config) ->
Prio = process_flag(priority, high),
true = erlang:garbage_collect(),
@@ -1224,10 +1284,7 @@ garbage_collect(Config) when is_list(Config) ->
process_flag(priority, Prio),
ok.
-process_info_messages(doc) ->
- ["This used to cause the nofrag emulator to dump core"];
-process_info_messages(suite) ->
- [];
+%% This used to cause the nofrag emulator to dump core
process_info_messages(Config) when is_list(Config) ->
process_info_messages_test(),
ok.
@@ -1285,10 +1342,6 @@ process_info_messages_test() ->
chk_badarg(Fun) ->
try Fun(), exit(no_badarg) catch error:badarg -> ok end.
-process_flag_badarg(doc) ->
- [];
-process_flag_badarg(suite) ->
- [];
process_flag_badarg(Config) when is_list(Config) ->
chk_badarg(fun () -> process_flag(gurka, banan) end),
chk_badarg(fun () -> process_flag(trap_exit, gurka) end),
@@ -1296,6 +1349,28 @@ process_flag_badarg(Config) when is_list(Config) ->
chk_badarg(fun () -> process_flag(min_heap_size, gurka) end),
chk_badarg(fun () -> process_flag(min_bin_vheap_size, gurka) end),
chk_badarg(fun () -> process_flag(min_bin_vheap_size, -1) end),
+
+ chk_badarg(fun () -> process_flag(max_heap_size, gurka) end),
+ chk_badarg(fun () -> process_flag(max_heap_size, -1) end),
+ chk_badarg(fun () ->
+ {_,Min} = process_info(self(), min_heap_size),
+ process_flag(max_heap_size, Min - 1)
+ end),
+ chk_badarg(fun () ->
+ {_,Min} = process_info(self(), min_heap_size),
+ process_flag(max_heap_size, #{size => Min - 1})
+ end),
+ chk_badarg(fun () -> process_flag(max_heap_size, #{}) end),
+ chk_badarg(fun () -> process_flag(max_heap_size, #{ kill => true }) end),
+ chk_badarg(fun () -> process_flag(max_heap_size, #{ size => 233,
+ kill => gurka }) end),
+ chk_badarg(fun () -> process_flag(max_heap_size, #{ size => 233,
+ error_logger => gurka }) end),
+ chk_badarg(fun () -> process_flag(max_heap_size, #{ size => 233,
+ kill => true,
+ error_logger => gurka }) end),
+ chk_badarg(fun () -> process_flag(max_heap_size, #{ size => 1 bsl 64 }) end),
+
chk_badarg(fun () -> process_flag(priority, 4711) end),
chk_badarg(fun () -> process_flag(save_calls, hmmm) end),
P= spawn_link(fun () -> receive die -> ok end end),
@@ -1306,8 +1381,6 @@ process_flag_badarg(Config) when is_list(Config) ->
-include_lib("stdlib/include/ms_transform.hrl").
-otp_6237(doc) -> [];
-otp_6237(suite) -> [];
otp_6237(Config) when is_list(Config) ->
Slctrs = lists:map(fun (_) ->
spawn_link(fun () ->
@@ -1374,11 +1447,10 @@ otp_6237_select_loop() ->
conses_per_red,
debug_level}).
-processes_large_tab(doc) ->
- [];
-processes_large_tab(suite) ->
- [];
processes_large_tab(Config) when is_list(Config) ->
+ sys_mem_cond_run(2048, fun () -> processes_large_tab_test(Config) end).
+
+processes_large_tab_test(Config) ->
enable_internal_state(),
MaxDbgLvl = 20,
MinProcTabSize = 2*(1 bsl 15),
@@ -1402,7 +1474,7 @@ processes_large_tab(Config) when is_list(Config) ->
#ptab_list_bif_info{debug_level = Lvl} when Lvl > MaxDbgLvl ->
20;
#ptab_list_bif_info{debug_level = Lvl} when Lvl < 0 ->
- ?t:fail({debug_level, Lvl});
+ ct:fail({debug_level, Lvl});
#ptab_list_bif_info{debug_level = Lvl} ->
Lvl
end,
@@ -1420,25 +1492,20 @@ processes_large_tab(Config) when is_list(Config) ->
[processes_bif_info]) of
#ptab_list_bif_info{tab_chunks = Chunks} when is_integer(Chunks),
Chunks > 1 -> ok;
- PBInfo -> ?t:fail(PBInfo)
+ PBInfo -> ct:fail(PBInfo)
end,
stop_node(LargeNode),
chk_processes_bif_test_res(Res).
-processes_default_tab(doc) ->
- [];
-processes_default_tab(suite) ->
- [];
processes_default_tab(Config) when is_list(Config) ->
+ sys_mem_cond_run(1024, fun () -> processes_default_tab_test(Config) end).
+
+processes_default_tab_test(Config) ->
{ok, DefaultNode} = start_node(Config, ""),
Res = rpc:call(DefaultNode, ?MODULE, processes_bif_test, []),
stop_node(DefaultNode),
chk_processes_bif_test_res(Res).
-processes_small_tab(doc) ->
- [];
-processes_small_tab(suite) ->
- [];
processes_small_tab(Config) when is_list(Config) ->
{ok, SmallNode} = start_node(Config, "+P 1024"),
Res = rpc:call(SmallNode, ?MODULE, processes_bif_test, []),
@@ -1447,16 +1514,20 @@ processes_small_tab(Config) when is_list(Config) ->
true = PBInfo#ptab_list_bif_info.tab_chunks < 10,
chk_processes_bif_test_res(Res).
-processes_this_tab(doc) ->
- [];
-processes_this_tab(suite) ->
- [];
processes_this_tab(Config) when is_list(Config) ->
- chk_processes_bif_test_res(processes_bif_test()).
+ Mem = case {erlang:system_info(build_type),
+ erlang:system_info(allocator)} of
+ {lcnt, {_, _Vsn, [sys_alloc], _Opts}} ->
+ %% When running +Mea min + lcnt we may need more memory
+ 1024 * 4;
+ _ ->
+ 1024
+ end,
+ sys_mem_cond_run(Mem, fun () -> chk_processes_bif_test_res(processes_bif_test()) end).
chk_processes_bif_test_res(ok) -> ok;
chk_processes_bif_test_res({comment, _} = Comment) -> Comment;
-chk_processes_bif_test_res(Failure) -> ?t:fail(Failure).
+chk_processes_bif_test_res(Failure) -> ct:fail(Failure).
print_processes_bif_info(#ptab_list_bif_info{min_start_reds = MinStartReds,
tab_chunks = TabChunks,
@@ -1467,7 +1538,7 @@ print_processes_bif_info(#ptab_list_bif_info{min_start_reds = MinStartReds,
term_procs_max_reds = TPMaxReds,
conses_per_red = ConsesPerRed,
debug_level = DbgLvl}) ->
- ?t:format("processes/0 bif info on node ~p:~n"
+ io:format("processes/0 bif info on node ~p:~n"
"Min start reductions = ~p~n"
"Process table chunks = ~p~n"
"Process table chunks size = ~p~n"
@@ -1508,7 +1579,7 @@ processes_unexpected_result(CorrectProcs, Procs) ->
status,
priority],
MissingProcs = CorrectProcs -- Procs,
- ?t:format("Missing processes: ~p",
+ io:format("Missing processes: ~p",
[lists:map(fun (Pid) ->
[{pid, Pid}
| case process_info(Pid, ProcInfo) of
@@ -1518,7 +1589,7 @@ processes_unexpected_result(CorrectProcs, Procs) ->
end,
MissingProcs)]),
SuperfluousProcs = Procs -- CorrectProcs,
- ?t:format("Superfluous processes: ~p",
+ io:format("Superfluous processes: ~p",
[lists:map(fun (Pid) ->
[{pid, Pid}
| case process_info(Pid, ProcInfo) of
@@ -1527,7 +1598,7 @@ processes_unexpected_result(CorrectProcs, Procs) ->
end]
end,
SuperfluousProcs)]),
- ?t:fail(unexpected_result).
+ ct:fail(unexpected_result).
hangaround(Cleaner, Type) ->
%% Type is only used to distinguish different processes from
@@ -1632,7 +1703,7 @@ do_processes_bif_test(WantReds, DieTest, Processes) ->
DoIt = make_ref(),
GetGoing = make_ref(),
{NoTestProcs, TestProcs} = spawn_initial_hangarounds(Cleaner),
- ?t:format("Testing with ~p processes~n", [NoTestProcs]),
+ io:format("Testing with ~p processes~n", [NoTestProcs]),
SpawnHangAround = fun () ->
spawn(?MODULE, hangaround, [Cleaner, new_hangaround])
end,
@@ -1674,7 +1745,7 @@ do_processes_bif_test(WantReds, DieTest, Processes) ->
Procs = lists:sort(Procs0),
CorrectProcs = lists:sort(CorrectProcs0),
LengthCorrectProcs = length(CorrectProcs),
- ?t:format("~p = length(CorrectProcs)~n", [LengthCorrectProcs]),
+ io:format("~p = length(CorrectProcs)~n", [LengthCorrectProcs]),
true = LengthCorrectProcs > NoTestProcs,
case CorrectProcs =:= Procs of
true ->
@@ -1695,12 +1766,12 @@ do_processes_bif_test(WantReds, DieTest, Processes) ->
do_processes_bif_die_test(false, _Processes) ->
- ?t:format("Skipping test killing process executing processes/0~n",[]),
+ io:format("Skipping test killing process executing processes/0~n",[]),
ok;
do_processes_bif_die_test(true, Processes) ->
do_processes_bif_die_test(5, Processes);
do_processes_bif_die_test(N, Processes) ->
- ?t:format("Doing test killing process executing processes/0~n",[]),
+ io:format("Doing test killing process executing processes/0~n",[]),
try
Tester = self(),
Oooh_Nooooooo = make_ref(),
@@ -1750,8 +1821,8 @@ do_processes_bif_die_test(N, Processes) ->
ok
catch
throw:{kill_in_trap, R} when N > 0 ->
- ?t:format("Failed to kill in trap: ~p~n", [R]),
- ?t:format("Trying again~n", []),
+ io:format("Failed to kill in trap: ~p~n", [R]),
+ io:format("Trying again~n", []),
do_processes_bif_die_test(N-1, Processes)
end.
@@ -1781,7 +1852,7 @@ wait_until_system_recover(Tmr) ->
receive
{timeout, Tmr, _} ->
Comment = "WARNING: Test processes still hanging around!",
- ?t:format("~s~n", [Comment]),
+ io:format("~s~n", [Comment]),
put(processes_bif_testcase_comment, Comment),
lists:foreach(
fun (P) when P == self() ->
@@ -1789,7 +1860,7 @@ wait_until_system_recover(Tmr) ->
(P) ->
case process_info(P, initial_call) of
{initial_call,{?MODULE, _, _} = MFA} ->
- ?t:format("~p ~p~n", [P, MFA]);
+ io:format("~p ~p~n", [P, MFA]);
{initial_call,{_, _, _}} ->
ok;
undefined ->
@@ -1805,10 +1876,6 @@ wait_until_system_recover(Tmr) ->
receive {timeout, Tmr, _} -> ok after 0 -> ok end,
ok.
-processes_last_call_trap(doc) ->
- [];
-processes_last_call_trap(suite) ->
- [];
processes_last_call_trap(Config) when is_list(Config) ->
enable_internal_state(),
Processes = fun () -> processes() end,
@@ -1831,10 +1898,6 @@ processes_last_call_trap(Config) when is_list(Config) ->
my_processes() ->
processes().
-processes_apply_trap(doc) ->
- [];
-processes_apply_trap(suite) ->
- [];
processes_apply_trap(Config) when is_list(Config) ->
enable_internal_state(),
PBInfo = erts_debug:get_internal_state(processes_bif_info),
@@ -1849,10 +1912,6 @@ processes_apply_trap(Config) when is_list(Config) ->
apply(erlang, processes, [])
end, lists:seq(1,100)).
-processes_gc_trap(doc) ->
- [];
-processes_gc_trap(suite) ->
- [];
processes_gc_trap(Config) when is_list(Config) ->
Tester = self(),
enable_internal_state(),
@@ -1891,10 +1950,6 @@ processes_gc_trap(Config) when is_list(Config) ->
exit(Suspendee, bang),
ok.
-process_flag_heap_size(doc) ->
- [];
-process_flag_heap_size(suite) ->
- [];
process_flag_heap_size(Config) when is_list(Config) ->
HSize = 2586, % must be gc fib+ number
VHSize = 318187, % must be gc fib+ number
@@ -1906,10 +1961,6 @@ process_flag_heap_size(Config) when is_list(Config) ->
VHSize = erlang:process_flag(min_bin_vheap_size, OldVHmin),
ok.
-spawn_opt_heap_size(doc) ->
- [];
-spawn_opt_heap_size(suite) ->
- [];
spawn_opt_heap_size(Config) when is_list(Config) ->
HSize = 987, % must be gc fib+ number
VHSize = 46422, % must be gc fib+ number
@@ -1920,10 +1971,110 @@ spawn_opt_heap_size(Config) when is_list(Config) ->
Pid ! stop,
ok.
-processes_term_proc_list(doc) ->
- [];
-processes_term_proc_list(suite) ->
- [];
+spawn_opt_max_heap_size(_Config) ->
+
+ error_logger:add_report_handler(?MODULE, self()),
+
+ %% Test that numerical limit works
+ max_heap_size_test(1024, 1024, true, true),
+
+ %% Test that map limit works
+ max_heap_size_test(#{ size => 1024 }, 1024, true, true),
+
+ %% Test that no kill is sent
+ max_heap_size_test(#{ size => 1024, kill => false }, 1024, false, true),
+
+ %% Test that no error_logger report is sent
+ max_heap_size_test(#{ size => 1024, error_logger => false }, 1024, true, false),
+
+ %% Test that system_flag works
+ erlang:system_flag(max_heap_size, #{ size => 0, kill => false,
+ error_logger => true}),
+ max_heap_size_test(#{ size => 1024 }, 1024, false, true),
+ max_heap_size_test(#{ size => 1024, kill => true }, 1024, true, true),
+
+ erlang:system_flag(max_heap_size, #{ size => 0, kill => true,
+ error_logger => false}),
+ max_heap_size_test(#{ size => 1024 }, 1024, true, false),
+ max_heap_size_test(#{ size => 1024, error_logger => true }, 1024, true, true),
+
+ erlang:system_flag(max_heap_size, #{ size => 1 bsl 20, kill => true,
+ error_logger => true}),
+ max_heap_size_test(#{ }, 1 bsl 20, true, true),
+
+ erlang:system_flag(max_heap_size, #{ size => 0, kill => true,
+ error_logger => true}),
+
+ %% Test that ordinary case works as expected again
+ max_heap_size_test(1024, 1024, true, true),
+
+ ok.
+
+max_heap_size_test(Option, Size, Kill, ErrorLogger)
+ when map_size(Option) == 0 ->
+ max_heap_size_test([], Size, Kill, ErrorLogger);
+max_heap_size_test(Option, Size, Kill, ErrorLogger)
+ when is_map(Option); is_integer(Option) ->
+ max_heap_size_test([{max_heap_size, Option}], Size, Kill, ErrorLogger);
+max_heap_size_test(Option, Size, Kill, ErrorLogger) ->
+ OomFun = fun F() -> timer:sleep(5),[lists:seq(1,1000)|F()] end,
+ Pid = spawn_opt(OomFun, Option),
+ {max_heap_size, MHSz} = erlang:process_info(Pid, max_heap_size),
+ ct:log("Default: ~p~nOption: ~p~nProc: ~p~n",
+ [erlang:system_info(max_heap_size), Option, MHSz]),
+
+ #{ size := Size} = MHSz,
+
+ Ref = erlang:monitor(process, Pid),
+ if Kill ->
+ receive
+ {'DOWN', Ref, process, Pid, killed} ->
+ ok
+ end;
+ true ->
+ ok
+ end,
+ if ErrorLogger ->
+ receive
+ {error, _, {emulator, _, [Pid|_]}} ->
+ ok
+ end;
+ true ->
+ ok
+ end,
+ if not Kill ->
+ exit(Pid, die),
+ receive
+ {'DOWN', Ref, process, Pid, die} ->
+ ok
+ end,
+ flush();
+ true ->
+ ok
+ end,
+ receive
+ M ->
+ ct:fail({unexpected_message, M})
+ after 10 ->
+ ok
+ end.
+
+flush() ->
+ receive
+ _M ->
+ flush()
+ after 1000 ->
+ ok
+ end.
+
+%% error_logger report handler proxy
+init(Pid) ->
+ {ok, Pid}.
+
+handle_event(Event, Pid) ->
+ Pid ! Event,
+ {ok, Pid}.
+
processes_term_proc_list(Config) when is_list(Config) ->
Tester = self(),
as_expected = processes_term_proc_list_test(false),
@@ -2073,28 +2224,19 @@ processes_term_proc_list_test(MustChk) ->
as_expected.
-otp_7738_waiting(doc) ->
- [];
-otp_7738_waiting(suite) ->
- [];
otp_7738_waiting(Config) when is_list(Config) ->
otp_7738_test(waiting).
-otp_7738_suspended(doc) ->
- [];
-otp_7738_suspended(suite) ->
- [];
otp_7738_suspended(Config) when is_list(Config) ->
otp_7738_test(suspended).
-otp_7738_resume(doc) ->
- [];
-otp_7738_resume(suite) ->
- [];
otp_7738_resume(Config) when is_list(Config) ->
otp_7738_test(resume).
otp_7738_test(Type) ->
+ sys_mem_cond_run(3072, fun () -> do_otp_7738_test(Type) end).
+
+do_otp_7738_test(Type) ->
T = self(),
S = spawn_link(fun () ->
receive
@@ -2156,8 +2298,8 @@ otp_7738_test(Type) ->
ok
after 2000 ->
I = process_info(R, [status, message_queue_len]),
- ?t:format("~p~n", [I]),
- ?t:fail(no_progress)
+ io:format("~p~n", [I]),
+ ct:fail(no_progress)
end,
ok.
@@ -2196,6 +2338,208 @@ garb_other_running(Config) when is_list(Config) ->
receive {'DOWN', Mon, process, Pid, normal} -> ok end,
ok.
+no_priority_inversion(Config) when is_list(Config) ->
+ Prio = process_flag(priority, max),
+ HTLs = lists:map(fun (_) ->
+ spawn_opt(fun () ->
+ tok_loop()
+ end,
+ [{priority, high}, monitor, link])
+ end,
+ lists:seq(1, 2*erlang:system_info(schedulers))),
+ receive after 500 -> ok end,
+ LTL = spawn_opt(fun () ->
+ tok_loop()
+ end,
+ [{priority, low}, monitor, link]),
+ false = erlang:check_process_code(element(1, LTL), nonexisting_module),
+ true = erlang:garbage_collect(element(1, LTL)),
+ lists:foreach(fun ({P, _}) ->
+ unlink(P),
+ exit(P, kill)
+ end, [LTL | HTLs]),
+ lists:foreach(fun ({P, M}) ->
+ receive
+ {'DOWN', M, process, P, killed} ->
+ ok
+ end
+ end, [LTL | HTLs]),
+ process_flag(priority, Prio),
+ ok.
+
+no_priority_inversion2(Config) when is_list(Config) ->
+ Prio = process_flag(priority, max),
+ MTLs = lists:map(fun (_) ->
+ spawn_opt(fun () ->
+ tok_loop()
+ end,
+ [{priority, max}, monitor, link])
+ end,
+ lists:seq(1, 2*erlang:system_info(schedulers))),
+ receive after 500 -> ok end,
+ {PL, ML} = spawn_opt(fun () ->
+ tok_loop()
+ end,
+ [{priority, low}, monitor, link]),
+ RL = request_gc(PL, low),
+ RN = request_gc(PL, normal),
+ RH = request_gc(PL, high),
+ receive
+ {garbage_collect, _, _} ->
+ ct:fail(unexpected_gc)
+ after 1000 ->
+ ok
+ end,
+ RM = request_gc(PL, max),
+ receive
+ {garbage_collect, RM, true} ->
+ ok
+ end,
+ lists:foreach(fun ({P, _}) ->
+ unlink(P),
+ exit(P, kill)
+ end, MTLs),
+ lists:foreach(fun ({P, M}) ->
+ receive
+ {'DOWN', M, process, P, killed} ->
+ ok
+ end
+ end, MTLs),
+ receive
+ {garbage_collect, RH, true} ->
+ ok
+ end,
+ receive
+ {garbage_collect, RN, true} ->
+ ok
+ end,
+ receive
+ {garbage_collect, RL, true} ->
+ ok
+ end,
+ unlink(PL),
+ exit(PL, kill),
+ receive
+ {'DOWN', ML, process, PL, killed} ->
+ ok
+ end,
+ process_flag(priority, Prio),
+ ok.
+
+request_gc(Pid, Prio) ->
+ Ref = make_ref(),
+ erts_internal:request_system_task(Pid, Prio, {garbage_collect, Ref}),
+ Ref.
+
+system_task_blast(Config) when is_list(Config) ->
+ Me = self(),
+ GCReq = fun () ->
+ RL = gc_req(Me, 100),
+ lists:foreach(fun (R) ->
+ receive
+ {garbage_collect, R, true} ->
+ ok
+ end
+ end, RL),
+ exit(it_worked)
+ end,
+ HTLs = lists:map(fun (_) -> spawn_monitor(GCReq) end, lists:seq(1, 1000)),
+ lists:foreach(fun ({P, M}) ->
+ receive
+ {'DOWN', M, process, P, it_worked} ->
+ ok
+ end
+ end, HTLs),
+ ok.
+
+gc_req(_Pid, 0) ->
+ [];
+gc_req(Pid, N) ->
+ R0 = request_gc(Pid, low),
+ R1 = request_gc(Pid, normal),
+ R2 = request_gc(Pid, high),
+ R3 = request_gc(Pid, max),
+ [R0, R1, R2, R3 | gc_req(Pid, N-1)].
+
+system_task_on_suspended(Config) when is_list(Config) ->
+ {P, M} = spawn_monitor(fun () ->
+ tok_loop()
+ end),
+ true = erlang:suspend_process(P),
+ {status, suspended} = process_info(P, status),
+ true = erlang:garbage_collect(P),
+ {status, suspended} = process_info(P, status),
+ true = erlang:resume_process(P),
+ false = ({status, suspended} == process_info(P, status)),
+ exit(P, kill),
+ receive
+ {'DOWN', M, process, P, killed} ->
+ ok
+ end.
+
+gc_request_when_gc_disabled(Config) when is_list(Config) ->
+ Master = self(),
+ AIS = erts_debug:set_internal_state(available_internal_state, true),
+ {P, M} = spawn_opt(fun () ->
+ true = erts_debug:set_internal_state(gc_state,
+ false),
+ Master ! {self(), gc_state, false},
+ receive after 1000 -> ok end,
+ Master ! {self(), gc_state, true},
+ false = erts_debug:set_internal_state(gc_state,
+ true),
+ receive after 100 -> ok end
+ end, [monitor, link]),
+ receive {P, gc_state, false} -> ok end,
+ ReqId = make_ref(),
+ async = garbage_collect(P, [{async, ReqId}]),
+ receive
+ {garbage_collect, ReqId, Result} ->
+ ct:fail({unexpected_gc, Result});
+ {P, gc_state, true} ->
+ ok
+ end,
+ receive {garbage_collect, ReqId, true} -> ok end,
+ erts_debug:set_internal_state(available_internal_state, AIS),
+ receive {'DOWN', M, process, P, _Reason} -> ok end,
+ ok.
+
+gc_request_blast_when_gc_disabled(Config) when is_list(Config) ->
+ Master = self(),
+ AIS = erts_debug:set_internal_state(available_internal_state, true),
+ {P, M} = spawn_opt(fun () ->
+ true = erts_debug:set_internal_state(gc_state,
+ false),
+ Master ! {self(), gc_state, false},
+ receive after 1000 -> ok end,
+ false = erts_debug:set_internal_state(gc_state,
+ true),
+ receive after 100 -> ok end
+ end, [monitor, link]),
+ receive {P, gc_state, false} -> ok end,
+ PMs = lists:map(fun (N) ->
+ Prio = case N rem 4 of
+ 0 -> max;
+ 1 -> high;
+ 2 -> normal;
+ 3 -> low
+ end,
+ spawn_opt(fun () ->
+ erlang:garbage_collect(P)
+ end, [monitor, link, {priority, Prio}])
+ end, lists:seq(1, 10000)),
+ lists:foreach(fun ({Proc, Mon}) ->
+ receive
+ {'DOWN', Mon, process, Proc, normal} ->
+ ok
+ end
+ end,
+ PMs),
+ erts_debug:set_internal_state(available_internal_state, AIS),
+ receive {'DOWN', M, process, P, _Reason} -> ok end,
+ ok.
+
+
%% Internal functions
wait_until(Fun) ->
@@ -2219,23 +2563,51 @@ start_node(Config) ->
start_node(Config, Args) when is_list(Config) ->
Pa = filename:dirname(code:which(?MODULE)),
- {A, B, C} = now(),
Name = list_to_atom(atom_to_list(?MODULE)
++ "-"
- ++ atom_to_list(?config(testcase, Config))
- ++ "-"
- ++ integer_to_list(A)
+ ++ atom_to_list(proplists:get_value(testcase, Config))
++ "-"
- ++ integer_to_list(B)
+ ++ integer_to_list(erlang:system_time(seconds))
++ "-"
- ++ integer_to_list(C)),
- ?t:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
+ ++ integer_to_list(erlang:unique_integer([positive]))),
+ test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
stop_node(Node) ->
- ?t:stop_node(Node).
+ test_server:stop_node(Node).
enable_internal_state() ->
case catch erts_debug:get_internal_state(available_internal_state) of
true -> true;
_ -> erts_debug:set_internal_state(available_internal_state, true)
end.
+
+sys_mem_cond_run(OrigReqSizeMB, TestFun) when is_integer(OrigReqSizeMB) ->
+ %% Debug normally needs more memory, so double the requirement
+ Debug = erlang:system_info(debug_compiled),
+ ReqSizeMB = if Debug -> OrigReqSizeMB * 2; true -> OrigReqSizeMB end,
+ case total_memory() of
+ TotMem when is_integer(TotMem), TotMem >= ReqSizeMB ->
+ TestFun();
+ TotMem when is_integer(TotMem) ->
+ {skipped, "Not enough memory ("++integer_to_list(TotMem)++" MB)"};
+ undefined ->
+ {skipped, "Could not retrieve memory information"}
+ end.
+
+
+total_memory() ->
+ %% Totat memory in MB.
+ try
+ MemoryData = memsup:get_system_memory_data(),
+ case lists:keysearch(total_memory, 1, MemoryData) of
+ {value, {total_memory, TM}} ->
+ TM div (1024*1024);
+ false ->
+ {value, {system_total_memory, STM}} =
+ lists:keysearch(system_total_memory, 1, MemoryData),
+ STM div (1024*1024)
+ end
+ catch
+ _ : _ ->
+ undefined
+ end.