aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/test/statistics_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/test/statistics_SUITE.erl')
-rw-r--r--erts/emulator/test/statistics_SUITE.erl341
1 files changed, 341 insertions, 0 deletions
diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl
new file mode 100644
index 0000000000..bc12821887
--- /dev/null
+++ b/erts/emulator/test/statistics_SUITE.erl
@@ -0,0 +1,341 @@
+%%
+%% %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(statistics_SUITE).
+
+%% Tests the statistics/1 bif.
+
+-export([all/1,
+ init_per_testcase/2,
+ fin_per_testcase/2,
+ wall_clock/1, wall_clock_zero_diff/1, wall_clock_update/1,
+ runtime/1, runtime_zero_diff/1, runtime_zero_update/1,
+ runtime_update/1, runtime_diff/1,
+ run_queue/1, run_queue_one/1,
+ reductions/1, reductions_big/1, garbage_collection/1, io/1,
+ badarg/1]).
+
+%% Internal exports.
+
+-export([hog/1]).
+
+-include("test_server.hrl").
+
+init_per_testcase(_, Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(300)),
+ [{watchdog, Dog}|Config].
+
+fin_per_testcase(_, Config) ->
+ Dog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+all(suite) -> [wall_clock, runtime, reductions, reductions_big, run_queue,
+ garbage_collection, io, badarg].
+
+
+%%% Testing statistics(wall_clock).
+
+wall_clock(suite) -> [wall_clock_zero_diff, wall_clock_update].
+
+
+wall_clock_zero_diff(doc) ->
+ "Tests that the 'Wall clock since last call' element of the result "
+ "is zero when statistics(runtime) is called twice in succession.";
+wall_clock_zero_diff(Config) when is_list(Config) ->
+ wall_clock_zero_diff1(16).
+
+wall_clock_zero_diff1(N) when N > 0 ->
+ ?line {Time, _} = statistics(wall_clock),
+ ?line case statistics(wall_clock) of
+ {Time, 0} -> ok;
+ _ -> wall_clock_zero_diff1(N-1)
+ end;
+wall_clock_zero_diff1(0) ->
+ ?line test_server:fail("Difference never zero.").
+
+wall_clock_update(doc) ->
+ "Test that the time differences returned by two calls to "
+ "statistics(wall_clock) are compatible, and are within a small number "
+ "of ms of the amount of real time we waited for.";
+wall_clock_update(Config) when is_list(Config) ->
+ wall_clock_update1(6).
+
+wall_clock_update1(N) when N > 0 ->
+ ?line {T1_wc_time, _} = statistics(wall_clock),
+ ?line receive after 1000 -> ok end,
+ ?line {T2_wc_time, Wc_Diff} = statistics(wall_clock),
+
+ ?line Wc_Diff = T2_wc_time - T1_wc_time,
+ ?line test_server:format("Wall clock diff = ~p; should be = 1000..1040~n",
+ [Wc_Diff]),
+ case ?t:is_debug() of
+ false ->
+ ?line true = Wc_Diff =< 1040;
+ true ->
+ ?line true = Wc_Diff =< 2000 %Be more tolerant in debug-compiled emulator.
+ end,
+ ?line true = Wc_Diff >= 1000,
+ wall_clock_update1(N-1);
+wall_clock_update1(0) ->
+ ok.
+
+
+%%% Test statistics(runtime).
+
+runtime(suite) -> [runtime_zero_diff, runtime_zero_update, runtime_update,
+ runtime_diff].
+
+runtime_zero_diff(doc) ->
+ "Tests that the difference between the times returned from two consectuitive "
+ "calls to statistics(runtime) is zero.";
+runtime_zero_diff(Config) when is_list(Config) ->
+ ?line runtime_zero_diff1(16).
+
+runtime_zero_diff1(N) when N > 0 ->
+ ?line {T1, _} = statistics(runtime),
+ ?line case statistics(runtime) of
+ {T1, 0} -> ok;
+ _ -> runtime_zero_diff1(N-1)
+ end;
+runtime_zero_diff1(0) ->
+ ?line test_server:fail("statistics(runtime) never returned zero difference").
+
+runtime_zero_update(doc) ->
+ "Test that the time differences returned by two calls to "
+ "statistics(runtime) several seconds apart is zero.";
+runtime_zero_update(Config) when is_list(Config) ->
+ case ?t:is_debug() of
+ false -> ?line runtime_zero_update1(6);
+ true -> {skip,"Unreliable in DEBUG build"}
+ end.
+
+runtime_zero_update1(N) when N > 0 ->
+ ?line {T1, _} = statistics(runtime),
+ ?line receive after 7000 -> ok end,
+ ?line case statistics(runtime) of
+ {T, Td} when Td =< 80 ->
+ test_server:format("ok, Runtime before: {~p, _} after: {~p, ~p}",
+ [T1, T, Td]),
+ ok;
+ {T, R} ->
+ test_server:format("nok, Runtime before: {~p, _} after: {~p, ~p}",
+ [T1, T, R]),
+ runtime_zero_update1(N-1)
+ end;
+runtime_zero_update1(0) ->
+ ?line test_server:fail("statistics(runtime) never returned zero difference").
+
+runtime_update(doc) ->
+ "Test that the statistics(runtime) returns a substanstially updated difference "
+ "after running a process that takes all CPU power of the Erlang process "
+ "for a second.";
+runtime_update(Config) when is_list(Config) ->
+ case ?t:is_cover() of
+ false ->
+ ?line process_flag(priority, high),
+ ?line test_server:m_out_of_n(1, 10, fun runtime_update/0);
+ true ->
+ {skip,"Cover-compiled"}
+ end.
+
+runtime_update() ->
+ ?line {T1,_} = statistics(runtime),
+ ?line spawn_link(fun cpu_heavy/0),
+ receive after 1000 -> ok end,
+ ?line {T2,Diff} = statistics(runtime),
+ ?line Delta = abs(Diff-1000),
+ ?line test_server:format("T1 = ~p, T2 = ~p, Diff = ~p, abs(Diff-1000) = ~p",
+ [T1,T2,Diff,Delta]),
+ ?line if
+ abs(Diff-1000) =:= Delta, Delta =< 100 ->
+ ok
+ end.
+
+cpu_heavy() ->
+ cpu_heavy().
+
+runtime_diff(doc) ->
+ "Test that the difference between two consecutive absolute runtimes is "
+ "equal to the last relative runtime. The loop runs a lot of times since "
+ "the bug which this test case tests for showed up only rarely.";
+runtime_diff(Config) when is_list(Config) ->
+ runtime_diff1(1000).
+
+runtime_diff1(N) when N > 0 ->
+ ?line {T1_wc_time, _} = statistics(runtime),
+ ?line do_much(),
+ ?line {T2_wc_time, Wc_Diff} = statistics(runtime),
+ ?line Wc_Diff = T2_wc_time - T1_wc_time,
+ runtime_diff1(N-1);
+runtime_diff1(0) ->
+ ok.
+
+%%% do_much(100000) takes about 760 ms on boromir.
+%%% do_much(1000) takes about 8 ms on boromir.
+
+do_much() ->
+ do_much(1000).
+
+do_much(0) ->
+ ok;
+do_much(N) ->
+ _ = 4784728478274827 * 72874284728472,
+ do_much(N-1).
+
+
+reductions(doc) ->
+ "Test that statistics(reductions) is callable, and that "
+ "Total_Reductions and Reductions_Since_Last_Call make sense. "
+ "(This to fail on pre-R3A version of JAM.";
+reductions(Config) when is_list(Config) ->
+ {Reductions, _} = statistics(reductions),
+
+ %% Each loop of reductions/2 takes 4 reductions + that the garbage built
+ %% outside the heap in the BIF calls will bump the reductions.
+ %% 300 * 4 is more than CONTEXT_REDS (1000). Thus, there will be one or
+ %% more context switches.
+
+ reductions(300, Reductions).
+
+reductions(N, Previous) when N > 0 ->
+ ?line {Reductions, Diff} = statistics(reductions),
+ ?line build_some_garbage(),
+ ?line if Reductions > 0 -> ok end,
+ ?line if Diff >= 0 -> ok end,
+ io:format("Previous = ~p, Reductions = ~p, Diff = ~p, DiffShouldBe = ~p",
+ [Previous, Reductions, Diff, Reductions-Previous]),
+ ?line if Reductions == Previous+Diff -> reductions(N-1, Reductions) end;
+reductions(0, _) ->
+ ok.
+
+build_some_garbage() ->
+ %% This will build garbage outside the process heap, which will cause
+ %% a garbage collection in the scheduler.
+ processes().
+
+reductions_big(doc) ->
+ "Test that the number of reductions can be returned as a big number.";
+reductions_big(Config) when is_list(Config) ->
+ ?line reductions_big_loop(),
+ ok.
+
+reductions_big_loop() ->
+ erlang:yield(),
+ case statistics(reductions) of
+ {Red, Diff} when Red >= 16#7ffFFFF ->
+ ok = io:format("Reductions = ~w, Diff = ~w", [Red, Diff]);
+ _ ->
+ reductions_big_loop()
+ end.
+
+
+%%% Tests of statistics(run_queue).
+
+run_queue(suite) -> [run_queue_one].
+
+run_queue_one(doc) ->
+ "Tests that statistics(run_queue) returns 1 if we start a "
+ "CPU-bound process.";
+run_queue_one(Config) when is_list(Config) ->
+ ?line MS = erlang:system_flag(multi_scheduling, block),
+ ?line run_queue_one_test(Config),
+ ?line erlang:system_flag(multi_scheduling, unblock),
+ case MS of
+ blocked ->
+ {comment,
+ "Multi-scheduling blocked during test. This test-case "
+ "was not written to work with multiple schedulers."};
+ _ -> ok
+ end.
+
+
+run_queue_one_test(Config) when is_list(Config) ->
+ ?line Hog = spawn_link(?MODULE, hog, [self()]),
+ ?line receive
+ hog_started ->
+ Hog ! go
+ end,
+ ?line receive after 100 -> ok end, % Give hog a head start.
+ ?line case statistics(run_queue) of
+ N when N >= 1 -> ok;
+ Other -> ?line ?t:fail({unexpected,Other})
+ end,
+ ok.
+
+%% CPU-bound process, going at low priority. It will always be ready
+%% to run.
+
+hog(Pid) ->
+ ?line process_flag(priority, low),
+ ?line Pid ! hog_started,
+ ?line receive
+ go -> hog_iter(0)
+ end.
+
+hog_iter(N) when N > 0 ->
+ ?line hog_iter(N-1);
+hog_iter(0) ->
+ ?line hog_iter(10000).
+
+
+garbage_collection(doc) ->
+ "Tests that statistics(garbage_collection) is callable. "
+ "It is not clear how to test anything more.";
+garbage_collection(Config) when is_list(Config) ->
+ ?line Bin = list_to_binary(lists:duplicate(19999, 42)),
+ ?line case statistics(garbage_collection) of
+ {Gcs0,R,0} when is_integer(Gcs0), is_integer(R) ->
+ ?line io:format("Reclaimed: ~p", [R]),
+ ?line Gcs = garbage_collection_1(Gcs0, Bin),
+ ?line io:format("Reclaimed: ~p",
+ [element(2, statistics(garbage_collection))]),
+ {comment,integer_to_list(Gcs-Gcs0)++" GCs"}
+ end.
+
+garbage_collection_1(Gcs0, Bin) ->
+ case statistics(garbage_collection) of
+ {Gcs,Reclaimed,0} when Gcs >= Gcs0 ->
+ if
+ Reclaimed > 16#7ffffff ->
+ Gcs;
+ true ->
+ _ = binary_to_list(Bin),
+ erlang:garbage_collect(),
+ garbage_collection_1(Gcs, Bin)
+ end
+ end.
+
+io(doc) ->
+ "Tests that statistics(io) is callable. "
+ "This could be improved to test something more.";
+io(Config) when is_list(Config) ->
+ ?line case statistics(io) of
+ {{input,In},{output,Out}} when is_integer(In), is_integer(Out) -> ok
+ end.
+
+badarg(doc) ->
+ "Tests that some illegal arguments to statistics fails.";
+badarg(Config) when is_list(Config) ->
+ ?line case catch statistics(1) of
+ {'EXIT', {badarg, _}} -> ok
+ end,
+ ?line case catch statistics(bad_atom) of
+ {'EXIT', {badarg, _}} -> ok
+ end.