1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2010-2018. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21-module(lcnt_SUITE).
22-include_lib("common_test/include/ct.hrl").
23
24%% Test server specific exports
25-export([all/0, suite/0]).
26-export([init_per_testcase/2, end_per_testcase/2]).
27
28%% Test cases
29-export([t_load/1,
30         t_conflicts/1,
31         t_locations/1,
32         t_swap_keys/1,
33         t_implicit_start/1,
34         t_crash_before_collect/1,
35         smoke_lcnt/1]).
36
37init_per_testcase(_Case, Config) ->
38    Config.
39
40end_per_testcase(_Case, _Config) ->
41    catch lcnt:stop(),
42    ok.
43
44suite() ->
45    [{ct_hooks,[ts_install_cth]},
46     {timetrap,{minutes,4}}].
47
48all() ->
49    [t_load, t_conflicts, t_locations, t_swap_keys, t_implicit_start,
50     t_crash_before_collect, smoke_lcnt].
51
52%%----------------------------------------------------------------------
53%% Tests
54%%----------------------------------------------------------------------
55
56%% Load data from file.
57t_load(Config) when is_list(Config) ->
58    Path = proplists:get_value(data_dir, Config),
59    Files = [filename:join([Path,"big_bang_40.lcnt"]),
60	     filename:join([Path,"ehb_3_3_hist.lcnt"])],
61    ok = t_load_file(Files),
62    ok.
63
64t_load_file([]) -> ok;
65t_load_file([File|Files]) ->
66    {ok, _} = lcnt:start(),
67    ok = lcnt:load(File),
68    ok = lcnt:stop(),
69    t_load_file(Files).
70
71%% API: conflicts
72t_conflicts(Config) when is_list(Config) ->
73    Path = proplists:get_value(data_dir, Config),
74    Files = [filename:join([Path,"big_bang_40.lcnt"]),
75	     filename:join([Path,"ehb_3_3_hist.lcnt"])],
76    ok = t_conflicts_file(Files),
77    ok.
78
79t_conflicts_file([]) -> ok;
80t_conflicts_file([File|Files]) ->
81    {ok, _} = lcnt:start(),
82    ok = lcnt:load(File),
83    ok = lcnt:conflicts(),
84    THs   = [-1, 5],
85    Print = [name , id , type , entry , tries , colls , ratio , time , duration],
86    Opts  = [
87	[{sort, Sort}, {reverse, Rev}, {max_locks, ML}, {combine, Combine}, {thresholds, [TH]}, {print, [Print]}] ||
88	    Sort    <- [name , type , tries , colls , ratio , time],
89	    ML      <- [none, 32],
90	    Combine <- [true, false],
91	    TH      <- [{tries, Tries} || Tries <- THs] ++ [{colls, Colls} || Colls <- THs] ++ [{time, Time} || Time <- THs],
92	    Rev     <- [true, false]
93	],
94    ok = test_conflicts_opts(Opts),
95    ok = lcnt:stop(),
96    t_conflicts_file(Files).
97
98
99test_conflicts_opts([]) -> ok;
100test_conflicts_opts([Opt|Opts]) ->
101    ok = lcnt:conflicts(Opt),
102    test_conflicts_opts(Opts).
103
104%% API: locations
105t_locations(Config) when is_list(Config) ->
106    Path = proplists:get_value(data_dir, Config),
107    Files = [filename:join([Path,"big_bang_40.lcnt"]),
108	     filename:join([Path,"ehb_3_3_hist.lcnt"])],
109    ok = t_locations_file(Files),
110    ok.
111
112t_locations_file([]) -> ok;
113t_locations_file([File|Files]) ->
114    {ok, _} = lcnt:start(),
115    ok = lcnt:load(File),
116    ok = lcnt:locations(),
117    THs   = [-1, 0, 100],
118    Print = [name , id , type , entry , tries , colls , ratio , time , duration],
119    Opts  = [
120	[{full_id, Id}, {sort, Sort}, {max_locks, ML}, {combine, Combine}, {thresholds, [TH]}, {print, Print}] ||
121	    Sort    <- [name , id , type , tries , colls , ratio , time , entry],
122	    ML      <- [none, 64],
123	    Combine <- [true, false],
124	    TH      <- [{tries, Tries} || Tries <- THs] ++ [{colls, Colls} || Colls <- THs] ++ [{time, Time} || Time <- THs],
125	    Id      <- [true, false]
126	],
127    ok = test_locations_opts(Opts),
128    ok = lcnt:stop(),
129    t_locations_file(Files).
130
131test_locations_opts([]) -> ok;
132test_locations_opts([Opt|Opts]) ->
133    ok = lcnt:locations(Opt),
134    test_locations_opts(Opts).
135
136%% Test interchanging port/process id with class
137t_swap_keys(Config) when is_list(Config) ->
138    Path = proplists:get_value(data_dir, Config),
139    Files = [filename:join([Path,"big_bang_40.lcnt"]),
140	     filename:join([Path,"ehb_3_3_hist.lcnt"])],
141    ok = t_swap_keys_file(Files),
142    ok.
143
144t_swap_keys_file([]) -> ok;
145t_swap_keys_file([File|Files]) ->
146    {ok, _} = lcnt:start(),
147    ok = lcnt:load(File),
148    ok = lcnt:conflicts(),
149    ok = lcnt:swap_pid_keys(),
150    ok = lcnt:conflicts(),
151    ok = lcnt:stop(),
152    t_swap_keys_file(Files).
153
154%% Prior to OTP-14913 this would crash with 'noproc' as the lcnt server hadn't
155%% been started yet.
156t_implicit_start(Config) when is_list(Config) ->
157    ok = lcnt:conflicts().
158
159t_crash_before_collect(Config) when is_list(Config) ->
160    {ok, _} = lcnt:start(),
161    ok = lcnt:information().
162
163%% Simple smoke test of actual lock-counting, if running on
164%% a run-time with lock-counting enabled.
165smoke_lcnt(Config) ->
166    case catch erlang:system_info(lock_counting) of
167        true ->
168            do_smoke_lcnt(Config);
169        _ ->
170            {skip,"Lock counting is not enabled"}
171    end.
172
173do_smoke_lcnt(Config) ->
174    PrivDir = proplists:get_value(priv_dir, Config),
175    SaveFile = filename:join(PrivDir, atom_to_list(?FUNCTION_NAME)),
176    {Time,ok} = timer:tc(fun() -> lcnt:apply(fun() -> big_bang(200) end) end),
177    io:format("~p ms\n", [Time]),
178    ok = lcnt:conflicts(),
179    ok = lcnt:save(SaveFile),
180    ok = lcnt:load(SaveFile),
181    ok = lcnt:conflicts(),
182    lcnt:stop().
183
184
185%%%
186%%% A slightly modified version of Rickard Green's Big Bang benchmark.
187%%%
188
189big_bang(N) when is_integer(N) ->
190    Procs = spawn_procs(N),
191    RMsgs = lists:map(fun (P) -> {done, P} end, Procs),
192    send_procs(Procs, {procs, Procs, self()}),
193    receive_msgs(RMsgs),
194    lists:foreach(fun (P) -> exit(P, normal) end, Procs).
195
196pinger([], [], true) ->
197    receive
198        {procs, Procs, ReportTo} ->
199            pinger(Procs, [], ReportTo)
200    end;
201pinger([], [], false) ->
202    receive {ping, From} -> From ! {pong, self()} end,
203    pinger([],[],false);
204pinger([], [], ReportTo) ->
205    ReportTo ! {done, self()},
206    pinger([],[],false);
207pinger([],[Po|Pos] = Pongers, ReportTo) ->
208    receive
209        {ping, From} ->
210            From ! {pong, self()},
211            pinger([], Pongers, ReportTo);
212        {pong, Po} ->
213            pinger([], Pos, ReportTo)
214    end;
215pinger([Pi|Pis], Pongers, ReportTo) ->
216    receive {ping, From} -> From ! {pong, self()}
217    after 0 -> ok
218    end,
219    Pi ! {ping, self()},
220    pinger(Pis, [Pi|Pongers], ReportTo).
221
222spawn_procs(N) when N =< 0 ->
223    [];
224spawn_procs(N) ->
225    [spawn_link(fun () -> pinger([], [], true) end) | spawn_procs(N-1)].
226
227send_procs([], Msg) ->
228    Msg;
229send_procs([P|Ps], Msg) ->
230    P ! Msg,
231    send_procs(Ps, Msg).
232
233receive_msgs([]) ->
234    ok;
235receive_msgs([M|Ms]) ->
236    receive
237        M ->
238            receive_msgs(Ms)
239    end.
240