1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2002-2016. 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%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22%%%
23%%% Define to run outside of test server
24%%%
25%%% -define(STANDALONE,1).
26%%%
27%%%
28%%% Define for debug output
29%%%
30%%% -define(debug,1).
31
32-module(cprof_SUITE).
33
34%% Exported end user tests
35-export([basic_test/0, on_load_test/1, modules_test/1]).
36
37%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
38%% Test server related stuff
39%%
40
41-ifdef(STANDALONE).
42-define(config(A,B),config(A,B)).
43-export([config/2]).
44-else.
45-include_lib("common_test/include/ct.hrl").
46-endif.
47
48-ifdef(debug).
49-ifdef(STANDALONE).
50-define(line, erlang:display({?MODULE,?LINE}), ).
51-endif.
52-define(dbgformat(A,B),io:format(A,B)).
53-else.
54-ifdef(STANDALONE).
55-define(line, noop, ).
56-endif.
57-define(dbgformat(A,B),noop).
58-endif.
59
60-ifdef(STANDALONE).
61config(priv_dir, _) ->
62    ".";
63config(data_dir, _) ->
64    "cprof_SUITE_data".
65-else.
66%% When run in test server.
67-export([all/0, suite/0,
68         init_per_testcase/2, end_per_testcase/2,
69         not_run/1]).
70-export([basic/1, on_load/1, modules/1]).
71
72init_per_testcase(_Case, Config) ->
73    Config.
74
75end_per_testcase(_Case, _Config) ->
76    erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_count]),
77    erlang:trace_pattern(on_load, false, [local,meta,call_count]),
78    erlang:trace(all, false, [all]),
79    ok.
80
81suite() ->
82    [{ct_hooks,[ts_install_cth]},
83     {timetrap,{seconds,30}}].
84
85all() ->
86    case test_server:is_native(cprof_SUITE) of
87        true -> [not_run];
88        false -> [basic, on_load, modules]
89    end.
90
91
92not_run(Config) when is_list(Config) ->
93    {skipped,"Native code"}.
94
95%% Tests basic profiling
96basic(Config) when is_list(Config) ->
97    basic_test().
98
99%% Tests profiling of unloaded module
100on_load(Config) when is_list(Config) ->
101    on_load_test(Config).
102
103%% Tests profiling of several modules
104modules(Config) when is_list(Config) ->
105    modules_test(Config).
106
107-endif. %-ifdef(STANDALONE). ... -else.
108
109%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
110%%% The Tests
111%%%
112
113basic_test() ->
114    M = 1000,
115    %%
116    M2 = M*2,
117    M3 = M*3,
118    M2__1 = M2 + 1,
119    M3__1 = M3 + 1,
120    N = cprof:stop(),
121    %%
122    2 = cprof:start(?MODULE, seq_r),
123    1 = cprof:start(?MODULE, seq, 3),
124    L = seq(1, M, fun succ/1),
125    Lr = seq_r(1, M, fun succ/1),
126    L = lists:reverse(Lr),
127    %%
128    io:format("~p~n~p~n~p~n",
129              [erlang:trace_info({?MODULE,sec_r,3}, all),
130               erlang:trace_info({?MODULE,sec_r,4}, all),
131               erlang:trace_info({?MODULE,sec,3}, all)]),
132    %%
133    ModAna1 = {?MODULE,M2__1,[{{?MODULE,seq_r,4},M},
134                              {{?MODULE,seq,3},M},
135                              {{?MODULE,seq_r,3},1}]},
136    ModAna1 = cprof:analyse(?MODULE,0),
137    {M2__1, [ModAna1]} = cprof:analyse(),
138    ModAna1 = cprof:analyse(?MODULE, 1),
139    {M2__1, [ModAna1]} = cprof:analyse(1),
140    %%
141    ModAna2 = {?MODULE,M2__1,[{{?MODULE,seq_r,4},M},
142                              {{?MODULE,seq,3},M}]},
143    ModAna2 = cprof:analyse(?MODULE, 2),
144    {M2__1, [ModAna2]} = cprof:analyse(2),
145    %%
146    2 = cprof:pause(?MODULE, seq_r),
147    L = seq(1, M, fun succ/1),
148    Lr = seq_r(1, M, fun succ/1),
149    %%
150    ModAna3 = {?MODULE,M3__1,[{{?MODULE,seq,3},M2},
151                              {{?MODULE,seq_r,4},M},
152                              {{?MODULE,seq_r,3},1}]},
153    ModAna3 = cprof:analyse(?MODULE),
154    %%
155    N = cprof:pause(),
156    L = seq(1, M, fun succ/1),
157    Lr = seq_r(1, M, fun succ/1),
158    %%
159    {M3__1, [ModAna3]} = cprof:analyse(),
160    %%
161    N = cprof:restart(),
162    L = seq(1, M, fun succ/1),
163    Lr = seq_r(1, M, fun succ/1),
164    %%
165    ModAna1 = cprof:analyse(?MODULE),
166    %%
167    N = cprof:stop(),
168    {?MODULE,0,[]} = cprof:analyse(?MODULE),
169    {0,[]} = cprof:analyse(),
170    ok.
171
172%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
173
174on_load_test(Config) ->
175    Priv = proplists:get_value(priv_dir, Config),
176    Data = proplists:get_value(data_dir, Config),
177    File = filename:join(Data, "cprof_SUITE_test"),
178    Module = cprof_SUITE_test,
179    M = 1000,
180    %%
181    M2 = M*2,
182    M2__1 = M2 + 1,
183    N1 = cprof:start(),
184
185    {ok,Module} = c:c(File, [{outdir,Priv}]),
186
187    %% If this system is hipe-enabled, the loader may have called module_info/1
188    %% when Module was loaded above. Reset the call count to avoid seeing
189    %% the call in the analysis below.
190
191    1 = cprof:restart(Module, module_info, 1),
192
193    L = Module:seq(1, M, fun succ/1),
194    Lr = Module:seq_r(1, M, fun succ/1),
195    Lr = lists:reverse(L),
196    N2 = cprof:pause(),
197    N3 = cprof:pause(Module),
198    {Module,M2__1,[{{Module,seq_r,4},M},
199                   {{Module,seq,3},M},
200                   {{Module,seq_r,3},1}]} = cprof:analyse(Module),
201    io:format("~p ~p ~p~n", [N1, N2, N3]),
202    code:purge(Module),
203    code:delete(Module),
204    N4 = N2 - N3,
205    %%
206    N4 = cprof:restart(),
207    {ok,Module} = c:c(File, [{outdir,Priv}]),
208    L = Module:seq(1, M, fun succ/1),
209    Lr = Module:seq_r(1, M, fun succ/1),
210    L = seq(1, M, fun succ/1),
211    Lr = seq_r(1, M, fun succ/1),
212    N2 = cprof:pause(),
213    {Module,0,[]} = cprof:analyse(Module),
214    M4__4 = M*4 - 4,
215    M10_7 = M*10 - 7,
216    {?MODULE,M10_7,[{{?MODULE,succ,1},M4__4},
217                    {{?MODULE,'-fun.succ/1-',1},M4__4},
218                    {{?MODULE,seq_r,4},M},
219                    {{?MODULE,seq,3},M},
220                    {{?MODULE,seq_r,3},1}]}
221    = cprof:analyse(?MODULE),
222    N2 = cprof:stop(),
223    ok.
224
225%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
226
227modules_test(Config) ->
228    Priv = proplists:get_value(priv_dir, Config),
229    Data = proplists:get_value(data_dir, Config),
230    File = filename:join(Data, "cprof_SUITE_test"),
231    Module = cprof_SUITE_test,
232    {ok,Module} = c:c(File, [{outdir,Priv}]),
233    M = 10,
234    %%
235    M2 = M*2,
236    M2__1 = M2 + 1,
237    erlang:yield(),
238    N = cprof:start(),
239    L = Module:seq(1, M, fun succ/1),
240    Lr = Module:seq_r(1, M, fun succ/1),
241    L = seq(1, M, fun succ/1),
242    Lr = seq_r(1, M, fun succ/1),
243    N = cprof:pause(),
244    Lr = lists:reverse(L),
245    M4_4 = M*4 - 4,
246    M10_7 = M*10 - 7,
247    M2__1 = M*2 + 1,
248    {Tot,ModList} = cprof:analyse(),
249    {value,{?MODULE,M10_7,[{{?MODULE,succ,1},M4_4},
250                           {{?MODULE,'-fun.succ/1-',1},M4_4},
251                           {{?MODULE,seq_r,4},M},
252                           {{?MODULE,seq,3},M},
253                           {{?MODULE,seq_r,3},1}]}} =
254      lists:keysearch(?MODULE, 1, ModList),
255    {value,{Module,M2__1,[{{Module,seq_r,4},M},
256                          {{Module,seq,3},M},
257                          {{Module,seq_r,3},1}]}} =
258      lists:keysearch(Module, 1, ModList),
259    Tot = lists:foldl(fun ({_,C,_}, A) -> C+A end, 0, ModList),
260    {cprof,_,Prof} = cprof:analyse(cprof),
261    {value,{{cprof,pause,0},1}} = lists:keysearch({cprof,pause,0}, 1, Prof),
262    N = cprof:stop(),
263    ok.
264
265
266
267%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
268%% Local helpers
269
270
271%% Stack recursive seq
272seq(Stop, Stop, Succ) when is_function(Succ) ->
273    [Stop];
274seq(Start, Stop, Succ) when is_function(Succ) ->
275    [Start | seq(Succ(Start), Stop, Succ)].
276
277
278
279%% Tail recursive seq, result list is reversed
280seq_r(Start, Stop, Succ) when is_function(Succ) ->
281    seq_r(Start, Stop, Succ, []).
282
283seq_r(Stop, Stop, _, R) ->
284    [Stop | R];
285seq_r(Start, Stop, Succ, R) ->
286    seq_r(Succ(Start), Stop, Succ, [Start | R]).
287
288
289%% Successor
290succ(X) -> X+1.
291