%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2002-2016. 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(trace_call_count_SUITE).
%% Exported end user tests
-export([basic_test/0, on_and_off_test/0, info_test/0,
pause_and_restart_test/0, combo_test/0]).
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 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,_) ->
".".
-else.
%% When run in test server.
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
init_per_testcase/2, end_per_testcase/2, not_run/1]).
-export([basic/1, on_and_off/1, info/1,
pause_and_restart/1, combo/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, {minutes, 4}}].
all() ->
case test_server:is_native(trace_call_count_SUITE) of
true -> [not_run];
false ->
[basic, on_and_off, info, pause_and_restart, combo]
end.
groups() ->
[].
init_per_suite(Config) ->
Config.
end_per_suite(_Config) ->
ok.
init_per_group(_GroupName, Config) ->
Config.
end_per_group(_GroupName, Config) ->
Config.
not_run(Config) when is_list(Config) ->
{skipped,"Native code"}.
%% Tests basic call count trace
basic(Config) when is_list(Config) ->
basic_test().
%% Tests turning trace parameters on and off
on_and_off(Config) when is_list(Config) ->
on_and_off_test().
%% Tests the trace_info BIF
info(Config) when is_list(Config) ->
info_test().
%% Tests pausing and restarting call counters
pause_and_restart(Config) when is_list(Config) ->
pause_and_restart_test().
%% Tests combining local call trace and meta trace with call count trace
combo(Config) when is_list(Config) ->
combo_test().
-endif. %-ifdef(STANDALONE). ... -else.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Result examination macros
-define(CT(P,MFA),{trace,P,call,MFA}).
-define(CTT(P, MFA),{trace_ts,P,call,MFA,{_,_,_}}).
-define(RF(P,MFA,V),{trace,P,return_from,MFA,V}).
-define(RFT(P,MFA,V),{trace_ts,P,return_from,MFA,V,{_,_,_}}).
-define(RT(P,MFA),{trace,P,return_to,MFA}).
-define(RTT(P,MFA),{trace_ts,P,return_to,MFA,{_,_,_}}).
%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% The Tests
%%%
basic_test() ->
P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
M = 1000,
%%
1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_count]),
2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, true, [call_count]),
L = seq(1, M, fun(X) -> X+1 end),
{call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
{call_count,0} = erlang:trace_info({?MODULE,seq_r,3}, call_count),
Lr = seq_r(1, M, fun(X) -> X+1 end),
{call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
{call_count,1} = erlang:trace_info({?MODULE,seq_r,3}, call_count),
{call_count,M} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
L = lists:reverse(Lr),
%%
P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
on_and_off_test() ->
P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
M = 100,
%%
1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_count]),
L = seq(1, M, fun(X) -> X+1 end),
{call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
N = erlang:trace_pattern({?MODULE,'_','_'}, true, [call_count]),
L = seq(1, M, fun(X) -> X+1 end),
{call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
P = erlang:trace_pattern({'_','_','_'}, true, [call_count]),
L = seq(1, M, fun(X) -> X+1 end),
{call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
1 = erlang:trace_pattern({?MODULE,seq,'_'}, false, [call_count]),
{call_count,false} = erlang:trace_info({?MODULE,seq,3}, call_count),
L = seq(1, M, fun(X) -> X+1 end),
{call_count,false} = erlang:trace_info({?MODULE,seq,3}, call_count),
{call_count,0} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
Lr = seq_r(1, M, fun(X) -> X+1 end),
{call_count,M} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
N = erlang:trace_pattern({?MODULE,'_','_'}, false, [call_count]),
{call_count,false} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
Lr = seq_r(1, M, fun(X) -> X+1 end),
{call_count,false} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
L = lists:reverse(Lr),
%%
P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
info_test() ->
P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
%%
1 = erlang:trace_pattern({?MODULE,seq,3}, true, [call_count]),
{call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count),
1 = erlang:trace_pattern({?MODULE,seq,'_'}, pause, [call_count]),
{call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count),
{all,[_|_]=L} = erlang:trace_info({?MODULE,seq,3}, all),
{value,{call_count,0}} = lists:keysearch(call_count, 1, L),
1 = erlang:trace_pattern({?MODULE,seq,'_'}, restart, [call_count]),
{call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count),
1 = erlang:trace_pattern({?MODULE,seq,'_'}, false, [call_count]),
{call_count,false} = erlang:trace_info({?MODULE,seq,3}, call_count),
{all,false} = erlang:trace_info({?MODULE,seq,3}, all),
%%
P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
pause_and_restart_test() ->
P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
M = 100,
%%
1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_count]),
{call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count),
L = seq(1, M, fun(X) -> X+1 end),
{call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
1 = erlang:trace_pattern({?MODULE,seq,'_'}, pause, [call_count]),
{call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
L = seq(1, M, fun(X) -> X+1 end),
{call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
1 = erlang:trace_pattern({?MODULE,seq,'_'}, restart, [call_count]),
{call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count),
L = seq(1, M, fun(X) -> X+1 end),
{call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
%%
P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
combo_test() ->
Self = self(),
MetaMatchSpec = [{'_',[],[{return_trace}]}],
Flags = lists:sort([call, return_to]),
LocalTracer = spawn_link(fun () -> relay_n(5, Self) end),
MetaTracer = spawn_link(fun () -> relay_n(9, Self) end),
2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, [], [local]),
2 = erlang:trace_pattern({?MODULE,seq_r,'_'},
MetaMatchSpec,
[{meta,MetaTracer}, call_count]),
1 = erlang:trace(Self, true, [{tracer,LocalTracer} | Flags]),
%%
{traced,local} =
erlang:trace_info({?MODULE,seq_r,3}, traced),
{match_spec,[]} =
erlang:trace_info({?MODULE,seq_r,3}, match_spec),
{meta,MetaTracer} =
erlang:trace_info({?MODULE,seq_r,3}, meta),
{meta_match_spec,MetaMatchSpec} =
erlang:trace_info({?MODULE,seq_r,3}, meta_match_spec),
{call_count,0} =
erlang:trace_info({?MODULE,seq_r,3}, call_count),
%%
{all,[_|_]=TraceInfo} =
erlang:trace_info({?MODULE,seq_r,3}, all),
{value,{traced,local}} =
lists:keysearch(traced, 1, TraceInfo),
{value,{match_spec,[]}} =
lists:keysearch(match_spec, 1, TraceInfo),
{value,{meta,MetaTracer}} =
lists:keysearch(meta, 1, TraceInfo),
{value,{meta_match_spec,MetaMatchSpec}} =
lists:keysearch(meta_match_spec, 1, TraceInfo),
{value,{call_count,0}} =
lists:keysearch(call_count, 1, TraceInfo),
%%
[3,2,1] = seq_r(1, 3, fun(X) -> X+1 end),
%%
List = collect(100),
{MetaR, LocalR} =
lists:foldl(
fun ({P,X}, {M,L}) when P == MetaTracer ->
{[X|M],L};
({P,X}, {M,L}) when P == LocalTracer ->
{M,[X|L]}
end,
{[],[]},
List),
Meta = lists:reverse(MetaR),
Local = lists:reverse(LocalR),
[?CTT(Self,{?MODULE,seq_r,[1,3,_]}),
?CTT(Self,{?MODULE,seq_r,[1,3,_,[]]}),
?CTT(Self,{?MODULE,seq_r,[2,3,_,[1]]}),
?CTT(Self,{?MODULE,seq_r,[3,3,_,[2,1]]}),
?RFT(Self,{?MODULE,seq_r,4},[3,2,1]),
?RFT(Self,{?MODULE,seq_r,4},[3,2,1]),
?RFT(Self,{?MODULE,seq_r,4},[3,2,1]),
?RFT(Self,{?MODULE,seq_r,3},[3,2,1])] = Meta,
[?CT(Self,{?MODULE,seq_r,[1,3,_]}),
?CT(Self,{?MODULE,seq_r,[1,3,_,[]]}),
?CT(Self,{?MODULE,seq_r,[2,3,_,[1]]}),
?CT(Self,{?MODULE,seq_r,[3,3,_,[2,1]]}),
?RT(Self,{?MODULE,combo_test,0})] = Local,
{call_count,1} = erlang:trace_info({?MODULE,seq_r,3}, call_count),
{call_count,3} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
%%
erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_count]),
erlang:trace_pattern(on_load, false, [local,meta,call_count]),
erlang:trace(all, false, [all]),
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]).
%% Message relay process
relay_n(0, _) ->
ok;
relay_n(N, Dest) ->
receive Msg ->
Dest ! {self(), Msg},
relay_n(N-1, Dest)
end.
%% Collect received messages
collect(Time) ->
Ref = erlang:start_timer(Time, self(), done),
L = lists:reverse(collect([], Ref)),
?dbgformat("Got: ~p~n",[L]),
L.
collect(A, 0) ->
receive
Mess ->
collect([Mess | A], 0)
after 0 ->
A
end;
collect(A, Ref) ->
receive
{timeout, Ref, done} ->
collect(A, 0);
Mess ->
collect([Mess | A], Ref)
end.