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    M_1 = M - 1,
215    M4__4 = M*4 - 4,
216    M10_7 = M*10 - 7,
217    {?MODULE,M10_7,[{{?MODULE,succ,1},M4__4},
218                    {{?MODULE,seq_r,4},M},
219                    {{?MODULE,seq,3},M},
220                    {{?MODULE,'-on_load_test/1-fun-5-',1},M_1},
221                    {{?MODULE,'-on_load_test/1-fun-4-',1},M_1},
222                    {{?MODULE,'-on_load_test/1-fun-3-',1},M_1},
223                    {{?MODULE,'-on_load_test/1-fun-2-',1},M_1},
224                    {{?MODULE,seq_r,3},1}]}
225    = cprof:analyse(?MODULE),
226    N2 = cprof:stop(),
227    ok.
228
229%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
230
231modules_test(Config) ->
232    Priv = proplists:get_value(priv_dir, Config),
233    Data = proplists:get_value(data_dir, Config),
234    File = filename:join(Data, "cprof_SUITE_test"),
235    Module = cprof_SUITE_test,
236    {ok,Module} = c:c(File, [{outdir,Priv}]),
237    M = 10,
238    %%
239    M2 = M*2,
240    M2__1 = M2 + 1,
241    erlang:yield(),
242    N = cprof:start(),
243    L = Module:seq(1, M, fun succ/1),
244    Lr = Module:seq_r(1, M, fun succ/1),
245    L = seq(1, M, fun succ/1),
246    Lr = seq_r(1, M, fun succ/1),
247    N = cprof:pause(),
248    Lr = lists:reverse(L),
249    M_1 = M - 1,
250    M4_4 = M*4 - 4,
251    M10_7 = M*10 - 7,
252    M2__1 = M*2 + 1,
253    {Tot,ModList} = cprof:analyse(),
254    {value,{?MODULE,M10_7,[{{?MODULE,succ,1},M4_4},
255                           {{?MODULE,seq_r,4},M},
256                           {{?MODULE,seq,3},M},
257                           {{?MODULE,'-modules_test/1-fun-3-',1},M_1},
258                           {{?MODULE,'-modules_test/1-fun-2-',1},M_1},
259                           {{?MODULE,'-modules_test/1-fun-1-',1},M_1},
260                           {{?MODULE,'-modules_test/1-fun-0-',1},M_1},
261                           {{?MODULE,seq_r,3},1}]}} =
262      lists:keysearch(?MODULE, 1, ModList),
263    {value,{Module,M2__1,[{{Module,seq_r,4},M},
264                          {{Module,seq,3},M},
265                          {{Module,seq_r,3},1}]}} =
266      lists:keysearch(Module, 1, ModList),
267    Tot = lists:foldl(fun ({_,C,_}, A) -> C+A end, 0, ModList),
268    {cprof,_,Prof} = cprof:analyse(cprof),
269    {value,{{cprof,pause,0},1}} = lists:keysearch({cprof,pause,0}, 1, Prof),
270    N = cprof:stop(),
271    ok.
272
273
274
275%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
276%% Local helpers
277
278
279%% Stack recursive seq
280seq(Stop, Stop, Succ) when is_function(Succ) ->
281    [Stop];
282seq(Start, Stop, Succ) when is_function(Succ) ->
283    [Start | seq(Succ(Start), Stop, Succ)].
284
285
286
287%% Tail recursive seq, result list is reversed
288seq_r(Start, Stop, Succ) when is_function(Succ) ->
289    seq_r(Start, Stop, Succ, []).
290
291seq_r(Stop, Stop, _, R) ->
292    [Stop | R];
293seq_r(Start, Stop, Succ, R) ->
294    seq_r(Succ(Start), Stop, Succ, [Start | R]).
295
296
297%% Successor
298succ(X) -> X+1.
299