1: %%
    2: %% %CopyrightBegin%
    3: %% 
    4: %% Copyright Ericsson AB 2002-2011. All Rights Reserved.
    5: %% 
    6: %% The contents of this file are subject to the Erlang Public License,
    7: %% Version 1.1, (the "License"); you may not use this file except in
    8: %% compliance with the License. You should have received a copy of the
    9: %% Erlang Public License along with this software. If not, it can be
   10: %% retrieved online at http://www.erlang.org/.
   11: %% 
   12: %% Software distributed under the License is distributed on an "AS IS"
   13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   14: %% the License for the specific language governing rights and limitations
   15: %% under the License.
   16: %% 
   17: %% %CopyrightEnd%
   18: %%
   19: 
   20: %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   21: %%%
   22: %%% Define to run outside of test server
   23: %%%
   24: %%% -define(STANDALONE,1).
   25: %%%
   26: %%%
   27: %%% Define for debug output
   28: %%%
   29: %%% -define(debug,1).
   30: 
   31: -module(trace_call_count_SUITE).
   32: 
   33: %% Exported end user tests
   34: -export([basic_test/0, on_and_off_test/0, info_test/0, 
   35: 	 pause_and_restart_test/0, combo_test/0]).
   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("test_server/include/test_server.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).
   61: config(priv_dir,_) ->
   62:     ".".
   63: -else.
   64: %% When run in test server.
   65: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
   66: 	 init_per_group/2,end_per_group/2, 
   67: 	 init_per_testcase/2, end_per_testcase/2, not_run/1]).
   68: -export([basic/1, on_and_off/1, info/1, 
   69: 	 pause_and_restart/1, combo/1]).
   70: 	 
   71: init_per_testcase(_Case, Config) ->
   72:     ?line Dog=test_server:timetrap(test_server:seconds(30)),
   73:     [{watchdog, Dog}|Config].
   74: 
   75: end_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:     Dog=?config(watchdog, Config),
   80:     test_server:timetrap_cancel(Dog),
   81:     ok.
   82: 
   83: suite() -> [{ct_hooks,[ts_install_cth]}].
   84: 
   85: all() -> 
   86:     case test_server:is_native(trace_call_count_SUITE) of
   87: 	true -> [not_run];
   88: 	false ->
   89: 	    [basic, on_and_off, info, pause_and_restart, combo]
   90:     end.
   91: 
   92: groups() -> 
   93:     [].
   94: 
   95: init_per_suite(Config) ->
   96:     Config.
   97: 
   98: end_per_suite(_Config) ->
   99:     ok.
  100: 
  101: init_per_group(_GroupName, Config) ->
  102:     Config.
  103: 
  104: end_per_group(_GroupName, Config) ->
  105:     Config.
  106: 
  107: 
  108: not_run(Config) when is_list(Config) -> 
  109:     {skipped,"Native code"}.
  110: 
  111: basic(suite) ->
  112:     [];
  113: basic(doc) ->
  114:     ["Tests basic call count trace"];
  115: basic(Config) when is_list(Config) ->
  116:     basic_test().
  117: 
  118: on_and_off(suite) ->
  119:     [];
  120: on_and_off(doc) ->
  121:     ["Tests turning trace parameters on and off"];
  122: on_and_off(Config) when is_list(Config) ->
  123:     on_and_off_test().
  124:  
  125: info(suite) ->
  126:     [];
  127: info(doc) ->
  128:     ["Tests the trace_info BIF"];
  129: info(Config) when is_list(Config) ->
  130:     info_test().
  131:  
  132: pause_and_restart(suite) ->
  133:     [];
  134: pause_and_restart(doc) ->
  135:     ["Tests pausing and restarting call counters"];
  136: pause_and_restart(Config) when is_list(Config) ->
  137:     pause_and_restart_test().
  138:  
  139: combo(suite) ->
  140:     [];
  141: combo(doc) ->
  142:     ["Tests combining local call trace and meta trace with call count trace"];
  143: combo(Config) when is_list(Config) ->
  144:     combo_test().
  145: 
  146: -endif. %-ifdef(STANDALONE). ... -else.
  147: 
  148: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  149: %% Result examination macros
  150: 
  151: -define(CT(P,MFA),{trace,P,call,MFA}).
  152: -define(CTT(P, MFA),{trace_ts,P,call,MFA,{_,_,_}}).
  153: -define(RF(P,MFA,V),{trace,P,return_from,MFA,V}).
  154: -define(RFT(P,MFA,V),{trace_ts,P,return_from,MFA,V,{_,_,_}}).
  155: -define(RT(P,MFA),{trace,P,return_to,MFA}).
  156: -define(RTT(P,MFA),{trace_ts,P,return_to,MFA,{_,_,_}}).
  157: 
  158: %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  159: %%% The Tests
  160: %%%
  161: 
  162: basic_test() ->
  163:     ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
  164:     ?line M = 1000,
  165:     %%
  166:     ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_count]),
  167:     ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, true, [call_count]),
  168:     ?line L = seq(1, M, fun(X) -> X+1 end),
  169:     ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
  170:     ?line {call_count,0} = erlang:trace_info({?MODULE,seq_r,3}, call_count),
  171:     ?line Lr = seq_r(1, M, fun(X) -> X+1 end),
  172:     ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
  173:     ?line {call_count,1} = erlang:trace_info({?MODULE,seq_r,3}, call_count),
  174:     ?line {call_count,M} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
  175:     ?line L = lists:reverse(Lr),
  176:     %%
  177:     ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
  178:     ok.
  179: 
  180: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  181: 
  182: on_and_off_test() ->
  183:     ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
  184:     ?line M = 100,
  185:     %%
  186:     ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_count]),
  187:     ?line L = seq(1, M, fun(X) -> X+1 end),
  188:     ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
  189:     ?line N = erlang:trace_pattern({?MODULE,'_','_'}, true, [call_count]),
  190:     ?line L = seq(1, M, fun(X) -> X+1 end),
  191:     ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
  192:     ?line P = erlang:trace_pattern({'_','_','_'}, true, [call_count]),
  193:     ?line L = seq(1, M, fun(X) -> X+1 end),
  194:     ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
  195:     ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, false, [call_count]),
  196:     ?line {call_count,false} = erlang:trace_info({?MODULE,seq,3}, call_count),
  197:     ?line L = seq(1, M, fun(X) -> X+1 end),
  198:     ?line {call_count,false} = erlang:trace_info({?MODULE,seq,3}, call_count),
  199:     ?line {call_count,0} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
  200:     ?line Lr = seq_r(1, M, fun(X) -> X+1 end),
  201:     ?line {call_count,M} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
  202:     ?line N = erlang:trace_pattern({?MODULE,'_','_'}, false, [call_count]),
  203:     ?line {call_count,false} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
  204:     ?line Lr = seq_r(1, M, fun(X) -> X+1 end),
  205:     ?line {call_count,false} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
  206:     ?line L = lists:reverse(Lr),
  207:     %%
  208:     ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
  209:     ok.
  210: 
  211: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  212: 
  213: info_test() ->
  214:     ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
  215:     %%
  216:     ?line 1 = erlang:trace_pattern({?MODULE,seq,3}, true, [call_count]),
  217:     ?line {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count),
  218:     ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, pause, [call_count]),
  219:     ?line {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count),
  220:     ?line {all,[_|_]=L} = erlang:trace_info({?MODULE,seq,3}, all),
  221:     ?line {value,{call_count,0}} = lists:keysearch(call_count, 1, L),
  222:     ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, restart, [call_count]),
  223:     ?line {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count),
  224:     ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, false, [call_count]),
  225:     ?line {call_count,false} = erlang:trace_info({?MODULE,seq,3}, call_count),
  226:     ?line {all,false} = erlang:trace_info({?MODULE,seq,3}, all),
  227:     %%
  228:     ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
  229:     ok.
  230: 
  231: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  232: 
  233: pause_and_restart_test() ->
  234:     ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
  235:     ?line M = 100,
  236:     %%
  237:     ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, true, [call_count]),
  238:     ?line {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count),
  239:     ?line L = seq(1, M, fun(X) -> X+1 end),
  240:     ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
  241:     ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, pause, [call_count]),
  242:     ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
  243:     ?line L = seq(1, M, fun(X) -> X+1 end),
  244:     ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
  245:     ?line 1 = erlang:trace_pattern({?MODULE,seq,'_'}, restart, [call_count]),
  246:     ?line {call_count,0} = erlang:trace_info({?MODULE,seq,3}, call_count),
  247:     ?line L = seq(1, M, fun(X) -> X+1 end),
  248:     ?line {call_count,M} = erlang:trace_info({?MODULE,seq,3}, call_count),
  249:     %%
  250:     ?line P = erlang:trace_pattern({'_','_','_'}, false, [call_count]),
  251:     ok.
  252: 
  253: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  254: 
  255: combo_test() ->
  256:     ?line Self = self(),
  257:     
  258:     ?line MetaMatchSpec = [{'_',[],[{return_trace}]}],
  259:     ?line Flags = lists:sort([call, return_to]),
  260:     ?line LocalTracer = spawn_link(fun () -> relay_n(5, Self) end),
  261:     ?line MetaTracer = spawn_link(fun () -> relay_n(9, Self) end),
  262:     ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'}, [], [local]),
  263:     ?line 2 = erlang:trace_pattern({?MODULE,seq_r,'_'},
  264: 				   MetaMatchSpec,
  265: 				   [{meta,MetaTracer}, call_count]),
  266:     ?line 1 = erlang:trace(Self, true, [{tracer,LocalTracer} | Flags]),
  267:     %%
  268:     ?line {traced,local} = 
  269: 	erlang:trace_info({?MODULE,seq_r,3}, traced),
  270:     ?line {match_spec,[]} = 
  271: 	erlang:trace_info({?MODULE,seq_r,3}, match_spec),
  272:     ?line {meta,MetaTracer} = 
  273: 	erlang:trace_info({?MODULE,seq_r,3}, meta),
  274:     ?line {meta_match_spec,MetaMatchSpec} = 
  275: 	erlang:trace_info({?MODULE,seq_r,3}, meta_match_spec),
  276:     ?line {call_count,0} = 
  277: 	erlang:trace_info({?MODULE,seq_r,3}, call_count),
  278:     %%
  279:     ?line {all,[_|_]=TraceInfo} = 
  280: 	erlang:trace_info({?MODULE,seq_r,3}, all),
  281:     ?line {value,{traced,local}} = 
  282: 	lists:keysearch(traced, 1, TraceInfo),
  283:     ?line {value,{match_spec,[]}} = 
  284: 	lists:keysearch(match_spec, 1, TraceInfo),
  285:     ?line {value,{meta,MetaTracer}} = 
  286: 	lists:keysearch(meta, 1, TraceInfo),
  287:     ?line {value,{meta_match_spec,MetaMatchSpec}} = 
  288: 	lists:keysearch(meta_match_spec, 1, TraceInfo),
  289:     ?line {value,{call_count,0}} = 
  290: 	lists:keysearch(call_count, 1, TraceInfo),
  291:     %%
  292:     ?line [3,2,1] = seq_r(1, 3, fun(X) -> X+1 end),
  293:     %%
  294:     ?line List = collect(100),
  295:     ?line {MetaR, LocalR} = 
  296: 	lists:foldl(
  297: 	  fun ({P,X}, {M,L}) when P == MetaTracer ->
  298: 		  {[X|M],L};
  299: 	      ({P,X}, {M,L}) when P == LocalTracer ->
  300: 		  {M,[X|L]}
  301: 	  end,
  302: 	  {[],[]},
  303: 	  List),
  304:     ?line Meta = lists:reverse(MetaR),
  305:     ?line Local = lists:reverse(LocalR),
  306:     ?line [?CTT(Self,{?MODULE,seq_r,[1,3,_]}),
  307: 	   ?CTT(Self,{?MODULE,seq_r,[1,3,_,[]]}),
  308: 	   ?CTT(Self,{?MODULE,seq_r,[2,3,_,[1]]}),
  309: 	   ?CTT(Self,{?MODULE,seq_r,[3,3,_,[2,1]]}),
  310: 	   ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]),
  311: 	   ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]),
  312: 	   ?RFT(Self,{?MODULE,seq_r,4},[3,2,1]),
  313: 	   ?RFT(Self,{?MODULE,seq_r,3},[3,2,1])] = Meta,
  314:     ?line [?CT(Self,{?MODULE,seq_r,[1,3,_]}),
  315: 	   ?CT(Self,{?MODULE,seq_r,[1,3,_,[]]}),
  316: 	   ?CT(Self,{?MODULE,seq_r,[2,3,_,[1]]}),
  317: 	   ?CT(Self,{?MODULE,seq_r,[3,3,_,[2,1]]}),
  318: 	   ?RT(Self,{?MODULE,combo_test,0})] = Local,
  319:     ?line {call_count,1} = erlang:trace_info({?MODULE,seq_r,3}, call_count),
  320:     ?line {call_count,3} = erlang:trace_info({?MODULE,seq_r,4}, call_count),
  321:     %%
  322:     ?line erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_count]),
  323:     ?line erlang:trace_pattern(on_load, false, [local,meta,call_count]),
  324:     ?line erlang:trace(all, false, [all]),
  325:     ok.
  326: 
  327: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  328: %% Local helpers
  329: 
  330: %% Stack recursive seq
  331: seq(Stop, Stop, Succ) when is_function(Succ) ->
  332:     [Stop];
  333: seq(Start, Stop, Succ) when is_function(Succ) ->
  334:     [Start | seq(Succ(Start), Stop, Succ)].
  335: 
  336: 
  337: 
  338: %% Tail recursive seq, result list is reversed
  339: seq_r(Start, Stop, Succ) when is_function(Succ) ->
  340:     seq_r(Start, Stop, Succ, []).
  341: 
  342: seq_r(Stop, Stop, _, R) ->
  343:     [Stop | R];
  344: seq_r(Start, Stop, Succ, R) ->
  345:     seq_r(Succ(Start), Stop, Succ, [Start | R]).
  346: 
  347: 
  348: 
  349: %% Message relay process
  350: relay_n(0, _) ->
  351:     ok;
  352: relay_n(N, Dest) ->
  353:     receive Msg ->
  354: 	    Dest ! {self(), Msg},
  355: 	    relay_n(N-1, Dest)
  356:     end.
  357: 
  358: 
  359: 
  360: %% Collect received messages
  361: collect(Time) ->
  362:     Ref = erlang:start_timer(Time, self(), done),
  363:     L = lists:reverse(collect([], Ref)),
  364:     ?dbgformat("Got: ~p~n",[L]),
  365:     L.
  366: 
  367: collect(A, 0) ->
  368:     receive
  369: 	Mess ->
  370: 	    collect([Mess | A], 0)
  371:     after 0 ->
  372: 	    A
  373:     end;
  374: collect(A, Ref) ->
  375:     receive
  376: 	{timeout, Ref, done} ->
  377: 	    collect(A, 0);
  378: 	Mess ->
  379: 	    collect([Mess | A], Ref)
  380:     end.