%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2002-2013. 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%
%%
%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%
%%% Define to run outside of test server
%%%
%%% -define(STANDALONE,1).
%%%
%%%
%%% Define for debug output
%%%
%%% -define(debug,1).
-module(cprof_SUITE).
%% Exported end user tests
-export([basic_test/0, on_load_test/1, modules_test/1]).
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Test server related stuff
%%
-ifdef(STANDALONE).
-define(config(A,B),config(A,B)).
-export([config/2]).
-else.
-include_lib("common_test/include/ct.hrl").
-endif.
-ifdef(debug).
-ifdef(STANDALONE).
-define(line, erlang:display({?MODULE,?LINE}), ).
-endif.
-define(dbgformat(A,B),io:format(A,B)).
-else.
-ifdef(STANDALONE).
-define(line, noop, ).
-endif.
-define(dbgformat(A,B),noop).
-endif.
-ifdef(STANDALONE).
config(priv_dir, _) ->
".";
config(data_dir, _) ->
"cprof_SUITE_data".
-else.
%% When run in test server.
-export([all/0, suite/0,
init_per_testcase/2, end_per_testcase/2,
not_run/1]).
-export([basic/1, on_load/1, modules/1]).
init_per_testcase(_Case, Config) ->
Config.
end_per_testcase(_Case, _Config) ->
erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_count]),
erlang:trace_pattern(on_load, false, [local,meta,call_count]),
erlang:trace(all, false, [all]),
ok.
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap,{seconds,30}}].
all() ->
case test_server:is_native(cprof_SUITE) of
true -> [not_run];
false -> [basic, on_load, modules]
end.
not_run(Config) when is_list(Config) ->
{skipped,"Native code"}.
basic(suite) ->
[];
basic(doc) ->
["Tests basic profiling"];
basic(Config) when is_list(Config) ->
basic_test().
on_load(suite) ->
[];
on_load(doc) ->
["Tests profiling of unloaded module"];
on_load(Config) when is_list(Config) ->
on_load_test(Config).
modules(suite) ->
[];
modules(doc) ->
["Tests profiling of several modules"];
modules(Config) when is_list(Config) ->
modules_test(Config).
-endif. %-ifdef(STANDALONE). ... -else.
%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% The Tests
%%%
basic_test() ->
M = 1000,
%%
M2 = M*2,
M3 = M*3,
M2__1 = M2 + 1,
M3__1 = M3 + 1,
N = cprof:stop(),
%%
2 = cprof:start(?MODULE, seq_r),
1 = cprof:start(?MODULE, seq, 3),
L = seq(1, M, fun succ/1),
Lr = seq_r(1, M, fun succ/1),
L = lists:reverse(Lr),
%%
io:format("~p~n~p~n~p~n",
[erlang:trace_info({?MODULE,sec_r,3}, all),
erlang:trace_info({?MODULE,sec_r,4}, all),
erlang:trace_info({?MODULE,sec,3}, all)]),
%%
ModAna1 = {?MODULE,M2__1,[{{?MODULE,seq_r,4},M},
{{?MODULE,seq,3},M},
{{?MODULE,seq_r,3},1}]},
ModAna1 = cprof:analyse(?MODULE,0),
{M2__1, [ModAna1]} = cprof:analyse(),
ModAna1 = cprof:analyse(?MODULE, 1),
{M2__1, [ModAna1]} = cprof:analyse(1),
%%
ModAna2 = {?MODULE,M2__1,[{{?MODULE,seq_r,4},M},
{{?MODULE,seq,3},M}]},
ModAna2 = cprof:analyse(?MODULE, 2),
{M2__1, [ModAna2]} = cprof:analyse(2),
%%
2 = cprof:pause(?MODULE, seq_r),
L = seq(1, M, fun succ/1),
Lr = seq_r(1, M, fun succ/1),
%%
ModAna3 = {?MODULE,M3__1,[{{?MODULE,seq,3},M2},
{{?MODULE,seq_r,4},M},
{{?MODULE,seq_r,3},1}]},
ModAna3 = cprof:analyse(?MODULE),
%%
N = cprof:pause(),
L = seq(1, M, fun succ/1),
Lr = seq_r(1, M, fun succ/1),
%%
{M3__1, [ModAna3]} = cprof:analyse(),
%%
N = cprof:restart(),
L = seq(1, M, fun succ/1),
Lr = seq_r(1, M, fun succ/1),
%%
ModAna1 = cprof:analyse(?MODULE),
%%
N = cprof:stop(),
{?MODULE,0,[]} = cprof:analyse(?MODULE),
{0,[]} = cprof:analyse(),
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
on_load_test(Config) ->
Priv = ?config(priv_dir, Config),
Data = ?config(data_dir, Config),
File = filename:join(Data, "cprof_SUITE_test"),
Module = cprof_SUITE_test,
M = 1000,
%%
M2 = M*2,
M2__1 = M2 + 1,
N1 = cprof:start(),
{ok,Module} = c:c(File, [{outdir,Priv}]),
%% If this system is hipe-enabled, the loader may have called module_info/1
%% when Module was loaded above. Reset the call count to avoid seeing
%% the call in the analysis below.
1 = cprof:restart(Module, module_info, 1),
L = Module:seq(1, M, fun succ/1),
Lr = Module:seq_r(1, M, fun succ/1),
Lr = lists:reverse(L),
N2 = cprof:pause(),
N3 = cprof:pause(Module),
{Module,M2__1,[{{Module,seq_r,4},M},
{{Module,seq,3},M},
{{Module,seq_r,3},1}]} = cprof:analyse(Module),
io:format("~p ~p ~p~n", [N1, N2, N3]),
code:purge(Module),
code:delete(Module),
N4 = N2 - N3,
%%
N4 = cprof:restart(),
{ok,Module} = c:c(File, [{outdir,Priv}]),
L = Module:seq(1, M, fun succ/1),
Lr = Module:seq_r(1, M, fun succ/1),
L = seq(1, M, fun succ/1),
Lr = seq_r(1, M, fun succ/1),
N2 = cprof:pause(),
{Module,0,[]} = cprof:analyse(Module),
M_1 = M - 1,
M4__4 = M*4 - 4,
M10_7 = M*10 - 7,
{?MODULE,M10_7,[{{?MODULE,succ,1},M4__4},
{{?MODULE,seq_r,4},M},
{{?MODULE,seq,3},M},
{{?MODULE,'-on_load_test/1-fun-5-',1},M_1},
{{?MODULE,'-on_load_test/1-fun-4-',1},M_1},
{{?MODULE,'-on_load_test/1-fun-3-',1},M_1},
{{?MODULE,'-on_load_test/1-fun-2-',1},M_1},
{{?MODULE,seq_r,3},1}]}
= cprof:analyse(?MODULE),
N2 = cprof:stop(),
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
modules_test(Config) ->
Priv = ?config(priv_dir, Config),
Data = ?config(data_dir, Config),
File = filename:join(Data, "cprof_SUITE_test"),
Module = cprof_SUITE_test,
{ok,Module} = c:c(File, [{outdir,Priv}]),
M = 10,
%%
M2 = M*2,
M2__1 = M2 + 1,
erlang:yield(),
N = cprof:start(),
L = Module:seq(1, M, fun succ/1),
Lr = Module:seq_r(1, M, fun succ/1),
L = seq(1, M, fun succ/1),
Lr = seq_r(1, M, fun succ/1),
N = cprof:pause(),
Lr = lists:reverse(L),
M_1 = M - 1,
M4_4 = M*4 - 4,
M10_7 = M*10 - 7,
M2__1 = M*2 + 1,
{Tot,ModList} = cprof:analyse(),
{value,{?MODULE,M10_7,[{{?MODULE,succ,1},M4_4},
{{?MODULE,seq_r,4},M},
{{?MODULE,seq,3},M},
{{?MODULE,'-modules_test/1-fun-3-',1},M_1},
{{?MODULE,'-modules_test/1-fun-2-',1},M_1},
{{?MODULE,'-modules_test/1-fun-1-',1},M_1},
{{?MODULE,'-modules_test/1-fun-0-',1},M_1},
{{?MODULE,seq_r,3},1}]}} =
lists:keysearch(?MODULE, 1, ModList),
{value,{Module,M2__1,[{{Module,seq_r,4},M},
{{Module,seq,3},M},
{{Module,seq_r,3},1}]}} =
lists:keysearch(Module, 1, ModList),
Tot = lists:foldl(fun ({_,C,_}, A) -> C+A end, 0, ModList),
{cprof,_,Prof} = cprof:analyse(cprof),
{value,{{cprof,pause,0},1}} = lists:keysearch({cprof,pause,0}, 1, Prof),
N = cprof:stop(),
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Local helpers
%% Stack recursive seq
seq(Stop, Stop, Succ) when is_function(Succ) ->
[Stop];
seq(Start, Stop, Succ) when is_function(Succ) ->
[Start | seq(Succ(Start), Stop, Succ)].
%% Tail recursive seq, result list is reversed
seq_r(Start, Stop, Succ) when is_function(Succ) ->
seq_r(Start, Stop, Succ, []).
seq_r(Stop, Stop, _, R) ->
[Stop | R];
seq_r(Start, Stop, Succ, R) ->
seq_r(Succ(Start), Stop, Succ, [Start | R]).
%% Successor
succ(X) -> X+1.