%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2010-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(lcnt_SUITE).
-include_lib("common_test/include/ct.hrl").
%% Test server specific exports
-export([all/0, suite/0]).
-export([init_per_testcase/2, end_per_testcase/2]).
%% Test cases
-export([t_load/1,
t_conflicts/1,
t_locations/1,
t_swap_keys/1,
t_implicit_start/1,
t_crash_before_collect/1,
smoke_lcnt/1]).
init_per_testcase(_Case, Config) ->
Config.
end_per_testcase(_Case, _Config) ->
catch lcnt:stop(),
ok.
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap,{minutes,4}}].
all() ->
[t_load, t_conflicts, t_locations, t_swap_keys, t_implicit_start,
t_crash_before_collect, smoke_lcnt].
%%----------------------------------------------------------------------
%% Tests
%%----------------------------------------------------------------------
%% Load data from file.
t_load(Config) when is_list(Config) ->
Path = proplists:get_value(data_dir, Config),
Files = [filename:join([Path,"big_bang_40.lcnt"]),
filename:join([Path,"ehb_3_3_hist.lcnt"])],
ok = t_load_file(Files),
ok.
t_load_file([]) -> ok;
t_load_file([File|Files]) ->
{ok, _} = lcnt:start(),
ok = lcnt:load(File),
ok = lcnt:stop(),
t_load_file(Files).
%% API: conflicts
t_conflicts(Config) when is_list(Config) ->
Path = proplists:get_value(data_dir, Config),
Files = [filename:join([Path,"big_bang_40.lcnt"]),
filename:join([Path,"ehb_3_3_hist.lcnt"])],
ok = t_conflicts_file(Files),
ok.
t_conflicts_file([]) -> ok;
t_conflicts_file([File|Files]) ->
{ok, _} = lcnt:start(),
ok = lcnt:load(File),
ok = lcnt:conflicts(),
THs = [-1, 5],
Print = [name , id , type , entry , tries , colls , ratio , time , duration],
Opts = [
[{sort, Sort}, {reverse, Rev}, {max_locks, ML}, {combine, Combine}, {thresholds, [TH]}, {print, [Print]}] ||
Sort <- [name , type , tries , colls , ratio , time],
ML <- [none, 32],
Combine <- [true, false],
TH <- [{tries, Tries} || Tries <- THs] ++ [{colls, Colls} || Colls <- THs] ++ [{time, Time} || Time <- THs],
Rev <- [true, false]
],
ok = test_conflicts_opts(Opts),
ok = lcnt:stop(),
t_conflicts_file(Files).
test_conflicts_opts([]) -> ok;
test_conflicts_opts([Opt|Opts]) ->
ok = lcnt:conflicts(Opt),
test_conflicts_opts(Opts).
%% API: locations
t_locations(Config) when is_list(Config) ->
Path = proplists:get_value(data_dir, Config),
Files = [filename:join([Path,"big_bang_40.lcnt"]),
filename:join([Path,"ehb_3_3_hist.lcnt"])],
ok = t_locations_file(Files),
ok.
t_locations_file([]) -> ok;
t_locations_file([File|Files]) ->
{ok, _} = lcnt:start(),
ok = lcnt:load(File),
ok = lcnt:locations(),
THs = [-1, 0, 100],
Print = [name , id , type , entry , tries , colls , ratio , time , duration],
Opts = [
[{full_id, Id}, {sort, Sort}, {max_locks, ML}, {combine, Combine}, {thresholds, [TH]}, {print, Print}] ||
Sort <- [name , id , type , tries , colls , ratio , time , entry],
ML <- [none, 64],
Combine <- [true, false],
TH <- [{tries, Tries} || Tries <- THs] ++ [{colls, Colls} || Colls <- THs] ++ [{time, Time} || Time <- THs],
Id <- [true, false]
],
ok = test_locations_opts(Opts),
ok = lcnt:stop(),
t_locations_file(Files).
test_locations_opts([]) -> ok;
test_locations_opts([Opt|Opts]) ->
ok = lcnt:locations(Opt),
test_locations_opts(Opts).
%% Test interchanging port/process id with class
t_swap_keys(Config) when is_list(Config) ->
Path = proplists:get_value(data_dir, Config),
Files = [filename:join([Path,"big_bang_40.lcnt"]),
filename:join([Path,"ehb_3_3_hist.lcnt"])],
ok = t_swap_keys_file(Files),
ok.
t_swap_keys_file([]) -> ok;
t_swap_keys_file([File|Files]) ->
{ok, _} = lcnt:start(),
ok = lcnt:load(File),
ok = lcnt:conflicts(),
ok = lcnt:swap_pid_keys(),
ok = lcnt:conflicts(),
ok = lcnt:stop(),
t_swap_keys_file(Files).
%% Prior to OTP-14913 this would crash with 'noproc' as the lcnt server hadn't
%% been started yet.
t_implicit_start(Config) when is_list(Config) ->
ok = lcnt:conflicts().
t_crash_before_collect(Config) when is_list(Config) ->
{ok, _} = lcnt:start(),
ok = lcnt:information().
%% Simple smoke test of actual lock-counting, if running on
%% a run-time with lock-counting enabled.
smoke_lcnt(Config) ->
case catch erlang:system_info(lock_counting) of
true ->
do_smoke_lcnt(Config);
_ ->
{skip,"Lock counting is not enabled"}
end.
do_smoke_lcnt(Config) ->
PrivDir = proplists:get_value(priv_dir, Config),
SaveFile = filename:join(PrivDir, atom_to_list(?FUNCTION_NAME)),
{Time,ok} = timer:tc(fun() -> lcnt:apply(fun() -> big_bang(200) end) end),
io:format("~p ms\n", [Time]),
ok = lcnt:conflicts(),
ok = lcnt:save(SaveFile),
ok = lcnt:load(SaveFile),
ok = lcnt:conflicts(),
lcnt:stop().
%%%
%%% A slightly modified version of Rickard Green's Big Bang benchmark.
%%%
big_bang(N) when is_integer(N) ->
Procs = spawn_procs(N),
RMsgs = lists:map(fun (P) -> {done, P} end, Procs),
send_procs(Procs, {procs, Procs, self()}),
receive_msgs(RMsgs),
lists:foreach(fun (P) -> exit(P, normal) end, Procs).
pinger([], [], true) ->
receive
{procs, Procs, ReportTo} ->
pinger(Procs, [], ReportTo)
end;
pinger([], [], false) ->
receive {ping, From} -> From ! {pong, self()} end,
pinger([],[],false);
pinger([], [], ReportTo) ->
ReportTo ! {done, self()},
pinger([],[],false);
pinger([],[Po|Pos] = Pongers, ReportTo) ->
receive
{ping, From} ->
From ! {pong, self()},
pinger([], Pongers, ReportTo);
{pong, Po} ->
pinger([], Pos, ReportTo)
end;
pinger([Pi|Pis], Pongers, ReportTo) ->
receive {ping, From} -> From ! {pong, self()}
after 0 -> ok
end,
Pi ! {ping, self()},
pinger(Pis, [Pi|Pongers], ReportTo).
spawn_procs(N) when N =< 0 ->
[];
spawn_procs(N) ->
[spawn_link(fun () -> pinger([], [], true) end) | spawn_procs(N-1)].
send_procs([], Msg) ->
Msg;
send_procs([P|Ps], Msg) ->
P ! Msg,
send_procs(Ps, Msg).
receive_msgs([]) ->
ok;
receive_msgs([M|Ms]) ->
receive
M ->
receive_msgs(Ms)
end.