1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1999-2020. 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(multi_load_SUITE).
22-export([all/0, suite/0, many/1, on_load/1, errors/1]).
23
24-include_lib("common_test/include/ct.hrl").
25
26suite() ->
27    [{ct_hooks,[ts_install_cth]}].
28
29all() ->
30    [many,on_load,errors].
31
32many(_Config) ->
33
34    N = case erlang:system_info(build_type) of
35            valgrind ->
36                10;
37            _ ->
38                100
39        end,
40
41    Ms = make_modules(N, fun many_module/1),
42
43    io:put_chars("Light load\n"
44		 "=========="),
45    many_measure(Ms),
46
47    _ = [spawn_link(fun many_worker/0) || _ <- lists:seq(1, 8)],
48    erlang:yield(),
49    io:put_chars("Heavy load\n"
50		 "=========="),
51    many_measure(Ms),
52    ok.
53
54many_module(M) ->
55    ["-module("++M++").",
56     "-compile(export_all).",
57     "f1() -> ok.",
58     "f2() -> ok.",
59     "f3() -> ok.",
60     "f4() -> ok."].
61
62many_measure(Ms) ->
63    many_purge(Ms),
64    MsPrep1 = prepare_modules(Ms),
65    Us1 = ms(fun() -> many_load_seq(MsPrep1) end),
66    many_try_call(Ms),
67    many_purge(Ms),
68    MsPrep2 = prepare_modules(Ms),
69    Us2 = ms(fun() -> many_load_par(MsPrep2) end),
70    many_try_call(Ms),
71    io:format("# modules:  ~9w\n"
72	      "Sequential: ~9w µs\n"
73	      "Parallel:   ~9w µs\n"
74	      "Ratio:      ~9w\n",
75	      [length(Ms),Us1,Us2,divide(Us1,Us2)]),
76    ok.
77
78divide(A,B) when B > 0 -> A div B;
79divide(_,_) -> inf.
80
81many_load_seq(Ms) ->
82    [erlang:finish_loading([M]) || M <- Ms],
83    ok.
84
85many_load_par(Ms) ->
86    erlang:finish_loading(Ms).
87
88many_purge(Ms) ->
89    _ = [catch erlang:purge_module(M) || {M,_} <- Ms],
90    ok.
91
92many_try_call(Ms) ->
93    _ = [begin
94	     ok = M:f1(),
95	     ok = M:f2(),
96	     ok = M:f3(),
97	     ok = M:f4()
98	 end || {M,_} <- Ms],
99    ok.
100
101many_worker() ->
102    many_worker(lists:seq(1, 100)).
103
104many_worker(L) ->
105    N0 = length(L),
106    N1 = N0 * N0 * N0,
107    N2 = N1 div (N0 * N0),
108    N3 = N2 + 10,
109    _ = N3 - 10,
110    many_worker(L).
111
112
113on_load(_Config) ->
114    On = make_modules(2, fun on_load_module/1),
115    OnPrep = prepare_modules(On),
116    {'EXIT',{system_limit,_}} = (catch erlang:finish_loading(OnPrep)),
117
118    Normal = make_modules(1, fun on_load_normal/1),
119    Mixed = Normal ++ tl(On),
120    MixedPrep = prepare_modules(Mixed),
121    {'EXIT',{system_limit,_}} = (catch erlang:finish_loading(MixedPrep)),
122
123    [false,true] = [erlang:has_prepared_code_on_load(Code) ||
124		       Code <- MixedPrep],
125    {'EXIT',{badarg,_}} = (catch erlang:has_prepared_code_on_load(<<1,2,3>>)),
126    Magic = ets:match_spec_compile([{'_',[true],['$_']}]),
127    {'EXIT',{badarg,_}} = (catch erlang:has_prepared_code_on_load(Magic)),
128
129    SingleOnPrep = tl(OnPrep),
130    {on_load,[OnLoadMod]} = erlang:finish_loading(SingleOnPrep),
131    ok = erlang:call_on_load_function(OnLoadMod),
132    ok.
133
134on_load_module(M) ->
135    ["-module("++M++").",
136     "-on_load(f/0).",
137     "f() -> ok."].
138
139on_load_normal(M) ->
140    ["-module("++M++")."].
141
142
143errors(_Config) ->
144    finish_loading_badarg(x),
145    finish_loading_badarg([x|y]),
146    finish_loading_badarg([x]),
147    finish_loading_badarg([<<>>]),
148
149    Mods = make_modules(2, fun errors_module/1),
150    Ms = lists:sort([M || {M,_} <- Mods]),
151    Prep = prepare_modules(Mods),
152    {duplicated,Dups} = erlang:finish_loading(Prep ++ Prep),
153    Ms = lists:sort(Dups),
154    ok.
155
156finish_loading_badarg(Arg) ->
157    {'EXIT',{badarg,[{erlang,finish_loading,[Arg],_}|_]}} =
158	(catch erlang:finish_loading(Arg)).
159
160errors_module(M) ->
161    ["-module("++M++").",
162     "-export([f/0]).",
163     "f() -> ok."].
164
165%%%
166%%% Common utilities
167%%%
168
169ms(Fun) ->
170    {Ms,ok} = timer:tc(Fun),
171    Ms.
172
173make_modules(0, _) ->
174    [];
175make_modules(N, Fun) ->
176    U = erlang:unique_integer([positive]),
177    M0 = "m__" ++ integer_to_list(N) ++ "_" ++ integer_to_list(U),
178    Contents = Fun(M0),
179    Forms = [make_form(S) || S <- Contents],
180    {ok,M,Code} = compile:forms(Forms),
181    [{M,Code}|make_modules(N-1, Fun)].
182
183make_form(S) ->
184    {ok,Toks,_} = erl_scan:string(S),
185    {ok,Form} = erl_parse:parse_form(Toks),
186    Form.
187
188prepare_modules(Ms) ->
189    [erlang:prepare_loading(M, Code) || {M,Code} <- Ms].
190