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_meta_SUITE).
   32: 
   33: %% Exported end user tests
   34: -export([basic_test/0, return_test/0, on_and_off_test/0, stack_grow_test/0,
   35: 	 info_test/0, tracer_test/0, combo_test/0, nosilent_test/0]).
   36: 
   37: %% Internal exports
   38: -export([exported/1, exported_wrap/1, loop/4, id/1, receiver/1]).
   39: 
   40: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   41: %% Test server related stuff
   42: %%
   43: 
   44: -ifdef(STANDALONE).
   45: -define(config(A,B),config(A,B)).
   46: -export([config/2]).
   47: -else.
   48: -include_lib("test_server/include/test_server.hrl").
   49: -endif.
   50:  
   51: -ifdef(debug).
   52: -ifdef(STANDALONE).
   53: -define(line, erlang:display({?MODULE,?LINE}), ).
   54: -endif.
   55: -define(dbgformat(A,B),io:format(A,B)).
   56: -else.
   57: -ifdef(STANDALONE).
   58: -define(line, noop, ).
   59: -endif.
   60: -define(dbgformat(A,B),noop).
   61: -endif.
   62:  
   63: -ifdef(STANDALONE).
   64: config(priv_dir,_) ->
   65:     ".".
   66: -else.
   67: %% When run in test server.
   68: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
   69: 	 init_per_group/2,end_per_group/2, 
   70: 	 init_per_testcase/2, end_per_testcase/2, not_run/1]).
   71: -export([basic/1, return/1, on_and_off/1, stack_grow/1, 
   72: 	 info/1, tracer/1, combo/1, nosilent/1]).
   73: 	 
   74: init_per_testcase(_Case, Config) ->
   75:     ?line Dog=test_server:timetrap(test_server:minutes(5)),
   76:     [{watchdog, Dog}|Config].
   77: 
   78: end_per_testcase(_Case, Config) ->
   79:     shutdown(),
   80:     Dog=?config(watchdog, Config),
   81:     test_server:timetrap_cancel(Dog),
   82:     ok.
   83: suite() -> [{ct_hooks,[ts_install_cth]}].
   84: 
   85: all() -> 
   86: case test_server:is_native(trace_meta_SUITE) of
   87:   true -> [not_run];
   88:   false ->
   89:       [basic, return, on_and_off, stack_grow, info, tracer,
   90:        combo, nosilent]
   91: end.
   92: 
   93: groups() -> 
   94:     [].
   95: 
   96: init_per_suite(Config) ->
   97:     Config.
   98: 
   99: end_per_suite(_Config) ->
  100:     ok.
  101: 
  102: init_per_group(_GroupName, Config) ->
  103: 	Config.
  104: 
  105: end_per_group(_GroupName, Config) ->
  106: 	Config.
  107: 
  108: 
  109: not_run(Config) when is_list(Config) -> 
  110:     {skipped,"Native code"}.
  111: 
  112: basic(suite) ->
  113:     [];
  114: basic(doc) ->
  115:     ["Tests basic meta trace"];
  116: basic(Config) when is_list(Config) ->
  117:     basic_test().
  118: 
  119: return(suite) ->
  120:     [];
  121: return(doc) ->
  122:     ["Tests return trace"];
  123: return(Config) when is_list(Config) ->
  124:     return_test().
  125:  
  126: on_and_off(suite) ->
  127:     [];
  128: on_and_off(doc) ->
  129:     ["Tests turning trace parameters on and off"];
  130: on_and_off(Config) when is_list(Config) ->
  131:     on_and_off_test().
  132:  
  133: stack_grow(doc) ->
  134:     ["Tests the stack growth during return traces"];
  135: stack_grow(Config) when is_list(Config) ->
  136:     stack_grow_test().
  137:  
  138: info(doc) ->
  139:     ["Tests the trace_info BIF"];
  140: info(Config) when is_list(Config) ->
  141:     info_test().
  142:  
  143: tracer(suite) ->
  144:     [];
  145: tracer(doc) ->
  146:     ["Tests stopping and changing tracer process"];
  147: tracer(Config) when is_list(Config) ->
  148:     tracer_test().
  149: 
  150: combo(suite) ->
  151:     [];
  152: combo(doc) ->
  153:     ["Tests combining local call trace with meta trace"];
  154: combo(Config) when is_list(Config) ->
  155:     combo_test().
  156: 
  157: nosilent(suite) ->
  158:     [];
  159: nosilent(doc) ->
  160:     ["Tests that meta trace is not silenced by the silent process flag"];
  161: nosilent(Config) when is_list(Config) ->
  162:     nosilent_test().
  163: 
  164: -endif.
  165: 
  166: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  167: %% Result examination macros
  168: 
  169: -define(CT(P,MFA),{trace,P,call,MFA}).
  170: -define(CTT(P, MFA),{trace_ts,P,call,MFA,{_,_,_}}).
  171: -define(RF(P,MFA,V),{trace,P,return_from,MFA,V}).
  172: -define(RFT(P,MFA,V),{trace_ts,P,return_from,MFA,V,{_,_,_}}).
  173: -define(RT(P,MFA),{trace,P,return_to,MFA}).
  174: -define(RTT(P,MFA),{trace_ts,P,return_to,MFA,{_,_,_}}).
  175: -define(NM, receive_no_next(100)).
  176: 
  177: %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  178: %%% The Tests
  179: %%%
  180: 
  181: basic_test() ->
  182:     ?line Pid = setup(),
  183:     ?line erlang:trace_pattern({?MODULE,'_','_'},[],[meta]),
  184:     ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
  185:     ?line ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(), 
  186:     ?line ?CTT(Pid,{?MODULE,exported,[1]}) = receive_next(),
  187:     ?line ?CTT(Pid,{?MODULE,local,[1]}) = receive_next(),
  188:     ?line ?CTT(Pid,{?MODULE,local2,[1]}) = receive_next(),
  189:     ?line ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(),
  190:     ?line erlang:trace_pattern({?MODULE,'_','_'},false,[meta]),
  191:     ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
  192:     ?line ?NM,
  193:     ?line [1,1,1,0] = lambda_slave(fun() ->
  194: 					   exported_wrap(1)
  195: 				   end),
  196:     ?line ?NM, 
  197:     ?line erlang:trace_pattern({?MODULE,'_','_'},[],[meta]),
  198:     ?line [1,1,1,0] = lambda_slave(fun() ->
  199: 					   exported_wrap(1)
  200: 				   end),
  201:     ?line ?CTT(Pid,{?MODULE,_,_}) = receive_next(), %% The fun
  202:     ?line ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(), 
  203:     ?line ?CTT(Pid,{?MODULE,exported,[1]}) = receive_next(),
  204:     ?line ?CTT(Pid,{?MODULE,local,[1]}) = receive_next(),
  205:     ?line ?CTT(Pid,{?MODULE,local2,[1]}) = receive_next(),
  206:     ?line ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(),
  207:     ?line erlang:trace_pattern({?MODULE,'_','_'},false,[meta]),
  208:     ?line shutdown(),
  209:     ?line ?NM,
  210:     ok.
  211: 
  212: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  213: 
  214: return_test() ->
  215:     ?line Pid = setup(),
  216:     ?line erlang:trace_pattern({?MODULE,'_','_'},[{'_',[],[{return_trace}]}],
  217: 			 [meta]),
  218:     ?line erlang:trace_pattern({erlang,phash2,'_'},[{'_',[],[{return_trace}]}],
  219: 			 [meta]),
  220:     ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
  221:     ?line ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(), 
  222:     ?line ?CTT(Pid,{?MODULE,exported,[1]}) = receive_next(),
  223:     ?line ?CTT(Pid,{?MODULE,local,[1]}) = receive_next(),
  224:     ?line ?CTT(Pid,{?MODULE,local2,[1]}) = receive_next(),
  225:     ?line ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(),
  226:     ?line ?CTT(Pid,{erlang,phash2,[1,1]}) = receive_next(),
  227:     ?line ?RFT(Pid,{erlang,phash2,2},0) = receive_next(),
  228:     ?line ?RFT(Pid,{?MODULE,local_tail,1},[1,0]) = receive_next(),
  229:     ?line ?RFT(Pid,{?MODULE,local2,1},[1,0]) = receive_next(),
  230:     ?line ?RFT(Pid,{?MODULE,local,1},[1,1,0]) = receive_next(),
  231:     ?line ?RFT(Pid,{?MODULE,exported,1},[1,1,1,0]) = receive_next(),
  232:     ?line ?RFT(Pid,{?MODULE,exported_wrap,1},[1,1,1,0]) = receive_next(),
  233:     ?line shutdown(),
  234:     ?line ?NM,
  235:     ok.
  236: 
  237: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  238: 
  239: on_and_off_test() ->
  240:     ?line Pid = setup(),
  241:     ?line 1 = erlang:trace_pattern({?MODULE,local_tail,1},[],[meta]),
  242:     ?line LocalTail = fun() ->
  243: 			      local_tail(1)
  244: 		      end,
  245:     ?line [1,0] = lambda_slave(LocalTail),
  246:     ?line ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(),
  247:     ?line 0 = erlang:trace_pattern({?MODULE,local_tail,1},[],[global]),
  248:     ?line [1,0] = lambda_slave(LocalTail),
  249:     ?line ?NM,
  250:     ?line 1 = erlang:trace_pattern({?MODULE,exported_wrap,1},[],[meta]),
  251:     ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
  252:     ?line ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(),
  253:     ?line 1 = erlang:trace_pattern({erlang,phash2,2},[],[meta]),
  254:     ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
  255:     ?line ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(),
  256:     ?line ?CTT(Pid,{erlang,phash2,[1,1]}) = receive_next(),
  257:     ?line shutdown(),
  258:     ?line erlang:trace_pattern({'_','_','_'},false,[meta]),
  259:     ?line N = erlang:trace_pattern({erlang,'_','_'},true,[meta]),
  260:     ?line case erlang:trace_pattern({erlang,'_','_'},false,[meta]) of
  261: 	      N -> 
  262: 		  ok;
  263: 	      Else ->
  264: 		  exit({number_mismatch, {expected, N}, {got, Else}})
  265: 	  end,
  266:     ?line case erlang:trace_pattern({erlang,'_','_'},false,[meta]) of
  267: 	      N -> 
  268: 		  ok;
  269: 	      Else2 ->
  270: 		  exit({number_mismatch, {expected, N}, {got, Else2}})
  271: 	  end,
  272:     ?line ?NM,
  273:     ok.
  274:     
  275: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  276: 
  277: stack_grow_test() ->    
  278:     ?line Pid = setup(),
  279:     ?line 1 = erlang:trace_pattern({?MODULE,loop,4},
  280: 				   [{'_',[],[{return_trace}]}],[meta]),
  281:     ?line Num = 1 bsl 15,
  282:     ?line Surface =
  283: 	fun (This, ?RFT(P,{?MODULE,loop,4},N), N) when P == Pid->
  284: 		if N == Num ->
  285: 			?NM,
  286: 			ok;
  287: 		   true ->
  288: 			This(This, receive_next(), N+1)
  289: 		end
  290: 	end,
  291:     ?line Dive = 
  292: 	fun (This, ?CTT(P,{?MODULE,loop,[{hej,hopp},[a,b,c],4.5,N]}), N)
  293: 	    when P == Pid->
  294: 		if N == 0 ->
  295: 			Surface(Surface, receive_next(), 0);
  296: 		   true ->
  297: 			This(This, receive_next(), N-1)
  298: 		end
  299: 	end,
  300:     ?line apply_slave(?MODULE,loop,[{hej,hopp},[a,b,c],4.5,Num]),
  301: %     ?line apply_slave_async(?MODULE,loop,[{hej,hopp},[a,b,c],4.5,Num]),
  302: %     ?line List = collect(test_server:seconds(5)),
  303:     ?line ok = Dive(Dive, receive_next(), Num),
  304:     ?line ?NM,
  305:     ok.
  306: 
  307: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  308: 
  309: info_test() ->
  310:     ?line setup(),
  311:     ?line Prog = [{['$1'],[{is_integer,'$1'}],[{message, false}]},
  312: 		  {'_',[],[]}],
  313:     ?line Self = self(),
  314:     ?line GoOn = make_ref(),
  315:     
  316:     ?line Pid = 
  317: 	spawn_link(
  318: 	  fun () ->
  319: 		  erlang:trace_pattern({?MODULE,exported_wrap,1}, 
  320: 				       Prog, [{meta, Self}]),
  321: 		  Self ! {self(), GoOn}
  322: 	  end),
  323:     ?line receive {Pid, GoOn} -> ok end,
  324:     ?line {traced,false} = erlang:trace_info({?MODULE,exported_wrap,1}, traced),
  325:     ?line {match_spec, false} = 
  326: 	erlang:trace_info({?MODULE,exported_wrap,1}, match_spec),
  327:     ?line {meta, Self} = erlang:trace_info({?MODULE,exported_wrap,1}, meta),
  328:     ?line {meta_match_spec, MMS} = 
  329: 	erlang:trace_info({?MODULE,exported_wrap,1}, meta_match_spec),
  330:     ?line case MMS of
  331: 	      Prog ->
  332: 		  ok;
  333: 	      Wrong ->
  334: 		  exit({bad_result, {erlang,trace_info,
  335: 				     [{?MODULE,exported_wrap,1},
  336: 				      meta_match_spec]},
  337: 			{expected, Prog}, {got, Wrong}})
  338: 	  end,
  339:     ?line erlang:garbage_collect(self()),
  340:     ?line receive
  341: 	  after 1 ->
  342: 		  ok
  343: 	  end,
  344:     ?line io:format("~p~n",[MMS]),
  345:     ?line {meta_match_spec,MMS2} = 
  346: 	erlang:trace_info({?MODULE,exported_wrap,1}, meta_match_spec),
  347:     ?line io:format("~p~n",[MMS2]),
  348:     ?line case MMS2 of
  349: 	      Prog ->
  350: 		  ok;
  351: 	      Wrong2 ->
  352: 		  exit({bad_result, {erlang,trace_info,
  353: 				     [{?MODULE,exported_wrap,1},
  354: 				      meta_match_spec]},
  355: 			{expected, Prog}, {got, Wrong2}})
  356: 	  end,
  357:     ?line {all, [_|_]=L} = erlang:trace_info({?MODULE,exported_wrap,1}, all),
  358:     ?line {value, {meta, Self}} = 
  359: 	lists:keysearch(meta, 1, L),
  360:     ?line {value, {meta_match_spec, MMS}} = 
  361: 	lists:keysearch(meta_match_spec, 1, L),
  362:     
  363:     ?line erlang:trace_pattern({?MODULE,exported_wrap,1}, true, [meta]),
  364:     ?line {meta_match_spec, []} = 
  365: 	erlang:trace_info({?MODULE,exported_wrap,1}, meta_match_spec),
  366:     
  367:     ?line erlang:trace_pattern({?MODULE,exported_wrap,1}, false, [meta]),
  368:     ?line {meta, false} = erlang:trace_info({?MODULE,exported_wrap,1}, meta),
  369:     ?line {meta_match_spec, false} = 
  370: 	erlang:trace_info({?MODULE,exported_wrap,1}, meta_match_spec),
  371:     ?line {all, false} = erlang:trace_info({?MODULE,exported_wrap,1}, all),
  372:     
  373:     ?line shutdown(),
  374:     ?line ?NM,
  375:     ok.
  376: 
  377: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  378: 
  379: tracer_test() ->
  380:     ?line Slave = setup(),
  381:     ?line Self = self(),
  382:     
  383:     ?line MatchSpec = [{'_',[],[{return_trace}]}],
  384:     ?line Tracer1 = spawn_link(fun () -> relay_n(3, Self) end),
  385:     ?line Setter = 
  386: 	spawn_link(
  387: 	  fun () ->
  388: 		  erlang:trace_pattern({?MODULE,receiver,1},
  389: 				       MatchSpec,
  390: 				       [{meta,Tracer1}]),
  391: 		  erlang:trace_pattern({erlang,phash2,2},
  392: 				       MatchSpec,
  393: 				       [{meta,Tracer1}]),
  394: 		  Self ! {self(), done}
  395: 	  end),
  396:     ?line receive {Setter, done} -> ok end,
  397:     ?line Ref = make_ref(),
  398:     ?line apply_slave_async(?MODULE, receiver, [Ref]),
  399:     ?line {Tracer1,?CTT(Slave,{?MODULE,receiver,[Ref]})} = receive_next(100),
  400:     ?line {Tracer1,?CTT(Slave,{erlang,phash2,[1,1]})} = receive_next(100),
  401:     ?line {Tracer1,?RFT(Slave,{erlang,phash2,2},0)} = receive_next(100),
  402:     %% Initiate a return_trace that will fail since the tracer just stopped
  403:     ?line Slave ! Ref,
  404:     ?line receive_no_next(100),
  405:     %% The breakpoint has not been hit since the tracer stopped
  406:     ?line {meta,Tracer1} = 
  407: 	erlang:trace_info({?MODULE,receiver,1}, meta),
  408:     ?line {meta_match_spec, MatchSpec} = 
  409: 	erlang:trace_info({?MODULE,receiver,1}, meta_match_spec),
  410:     ?line {meta,Tracer1} = 
  411: 	erlang:trace_info({erlang,phash2,2}, meta),
  412:     ?line {meta_match_spec, MatchSpec} = 
  413: 	erlang:trace_info({erlang,phash2,2}, meta_match_spec),
  414:     %% Initiate trace messages that will fail
  415:     ?line Ref2 = make_ref(),
  416:     ?line apply_slave_async(?MODULE, receiver, [Ref2]),
  417:     ?line Slave ! Ref2,
  418:     ?line receive_no_next(100),
  419:     ?line {meta,[]} = 
  420: 	erlang:trace_info({?MODULE,receiver,1}, meta),
  421:     ?line {meta_match_spec, MatchSpec} = 
  422: 	erlang:trace_info({?MODULE,receiver,1}, meta_match_spec),
  423:     ?line {meta,[]} =
  424: 	erlang:trace_info({erlang,phash2,2}, meta),
  425:     ?line {meta_match_spec, MatchSpec} = 
  426: 	erlang:trace_info({erlang,phash2,2}, meta_match_spec),
  427:     %% Change tracer
  428:     ?line Tracer2 = spawn_link(fun () -> relay_n(4, Self) end),
  429:     ?line erlang:trace_pattern({?MODULE,receiver,1}, 
  430: 			       MatchSpec, 
  431: 			       [{meta,Tracer2}]),
  432:     ?line erlang:trace_pattern({erlang,phash2,2},
  433: 			       MatchSpec,
  434: 			       [{meta,Tracer2}]),
  435:     ?line Ref3 = make_ref(),
  436:     ?line apply_slave_async(?MODULE, receiver, [Ref3]),
  437:     ?line {Tracer2,?CTT(Slave,{?MODULE,receiver,[Ref3]})} = receive_next(),
  438:     ?line {Tracer2,?CTT(Slave,{erlang,phash2,[1,1]})} = receive_next(),
  439:     ?line {Tracer2,?RFT(Slave,{erlang,phash2,2},0)} = receive_next(),
  440:     %% Change tracer between call trace and return trace
  441:     ?line Tracer3 = spawn_link(fun () -> relay_n(4, Self) end),
  442:     ?line erlang:trace_pattern({?MODULE,receiver,1}, 
  443: 			       MatchSpec, 
  444: 			       [{meta,Tracer3}]),
  445:     ?line erlang:trace_pattern({erlang,phash2,2},
  446: 			       MatchSpec,
  447: 			       [{meta,Tracer3}]),
  448:     ?line Slave ! Ref3, 
  449:     %% The return trace should still come from Tracer2
  450:     ?line {Tracer2,?RFT(Slave,{?MODULE,receiver,1},Ref3)} = receive_next(),
  451:     ?line Ref4 = make_ref(),
  452:     %% Now should Tracer3 be used
  453:     ?line apply_slave_async(?MODULE, receiver, [Ref4]),
  454:     ?line Slave ! Ref4,
  455:     ?line {Tracer3,?CTT(Slave,{?MODULE,receiver,[Ref4]})} = receive_next(),
  456:     ?line {Tracer3,?CTT(Slave,{erlang,phash2,[1,1]})} = receive_next(),
  457:     ?line {Tracer3,?RFT(Slave,{erlang,phash2,2},0)} = receive_next(),
  458:     ?line {Tracer3,?RFT(Slave,{?MODULE,receiver,1},Ref4)} = receive_next(),
  459:     %% The breakpoint has not been hit since the tracer stopped
  460:     ?line {meta,Tracer3} = 
  461: 	erlang:trace_info({?MODULE,receiver,1}, meta),
  462:     ?line {meta_match_spec, MatchSpec} = 
  463: 	erlang:trace_info({?MODULE,receiver,1}, meta_match_spec),
  464:     ?line {meta,Tracer3} = 
  465: 	erlang:trace_info({erlang,phash2,2}, meta),
  466:     ?line {meta_match_spec, MatchSpec} = 
  467: 	erlang:trace_info({erlang,phash2,2}, meta_match_spec),
  468:     
  469:     ?line shutdown(),
  470:     ?line ?NM,
  471:     ok.
  472: 
  473: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  474: 
  475: combo_test() ->
  476:     ?line Slave = setup(),
  477:     ?line Self = self(),
  478:     
  479:     ?line MatchSpec = [{'_',[],[{return_trace}]}],
  480:     ?line Flags = lists:sort([call, return_to]),
  481:     ?line LocalTracer = spawn_link(fun () -> relay_n(6, Self) end),
  482:     ?line MetaTracer = spawn_link(fun () -> relay_n(4, Self) end),
  483:     ?line 1 = erlang:trace_pattern({?MODULE,receiver,1}, 
  484: 				   MatchSpec,
  485: 				   [local,{meta,MetaTracer}]),
  486:     ?line 1 = erlang:trace_pattern({erlang,phash2,2}, 
  487: 				   MatchSpec,
  488: 				   [local,{meta,MetaTracer}]),
  489:     ?line 1 = erlang:trace(Slave, true, 
  490: 			   [{tracer,LocalTracer} | Flags]),
  491:     %%
  492:     ?line {all, TraceInfo1} = 
  493: 	erlang:trace_info({?MODULE,receiver,1}, all),
  494:     ?line {meta,MetaTracer} = 
  495: 	erlang:trace_info({?MODULE,receiver,1}, meta),
  496:     ?line {value,{meta,MetaTracer}} = 
  497: 	lists:keysearch(meta, 1, TraceInfo1),
  498:     ?line {meta_match_spec,MatchSpec} = 
  499: 	erlang:trace_info({?MODULE,receiver,1}, meta_match_spec),
  500:     ?line {value,{meta_match_spec,MatchSpec}} = 
  501: 	lists:keysearch(meta_match_spec, 1, TraceInfo1),
  502:     ?line {traced,local} = 
  503: 	erlang:trace_info({?MODULE,receiver,1}, traced),
  504:     ?line {value,{traced,local}} = 
  505: 	lists:keysearch(traced, 1, TraceInfo1),
  506:     ?line {match_spec,MatchSpec} = 
  507: 	erlang:trace_info({?MODULE,receiver,1}, match_spec),
  508:     ?line {value,{match_spec,MatchSpec}} = 
  509: 	lists:keysearch(match_spec, 1, TraceInfo1),
  510:     %%
  511:     ?line {all, TraceInfo2} = 
  512: 	erlang:trace_info({erlang,phash2,2}, all),
  513:     ?line {meta,MetaTracer} = 
  514: 	erlang:trace_info({erlang,phash2,2}, meta),
  515:     ?line {value,{meta,MetaTracer}} = 
  516: 	lists:keysearch(meta, 1, TraceInfo2),
  517:     ?line {meta_match_spec,MatchSpec} = 
  518: 	erlang:trace_info({erlang,phash2,2}, meta_match_spec),
  519:     ?line {value,{meta_match_spec,MatchSpec}} = 
  520: 	lists:keysearch(meta_match_spec, 1, TraceInfo2),
  521:     ?line {traced,local} = 
  522: 	erlang:trace_info({erlang,phash2,2}, traced),
  523:     ?line {value,{traced,local}} = 
  524: 	lists:keysearch(traced, 1, TraceInfo2),
  525:     ?line {match_spec,MatchSpec} = 
  526: 	erlang:trace_info({erlang,phash2,2}, match_spec),
  527:     ?line {value,{match_spec,MatchSpec}} = 
  528: 	lists:keysearch(match_spec, 1, TraceInfo2),
  529:     %%
  530:     ?line {flags,Flags1} = erlang:trace_info(Slave, flags),
  531:     ?line Flags = lists:sort(Flags1),
  532:     ?line {tracer,LocalTracer} = erlang:trace_info(Slave, tracer),
  533:     %%
  534:     ?line Ref = make_ref(),
  535:     ?line apply_slave_async(?MODULE, receiver, [Ref]),
  536:     ?line Slave ! Ref,
  537:     ?line ?CTT(Slave,{?MODULE,receiver,[Ref]}) = receive_next_bytag(MetaTracer),
  538:     ?line ?CTT(Slave,{erlang,phash2,[1,1]}) = receive_next_bytag(MetaTracer),
  539:     ?line ?RFT(Slave,{erlang,phash2,2},0) = receive_next_bytag(MetaTracer),
  540:     ?line ?RFT(Slave,{?MODULE,receiver,1},Ref) = receive_next_bytag(MetaTracer),
  541:     ?line ?CT(Slave,{?MODULE,receiver,[Ref]}) = receive_next_bytag(LocalTracer),
  542:     ?line ?CT(Slave,{erlang,phash2,[1,1]}) = receive_next_bytag(LocalTracer),
  543:     ?line case {receive_next_bytag(LocalTracer),
  544: 		receive_next_bytag(LocalTracer)} of
  545: 	      {?RF(Slave,{erlang,phash2,2},0),
  546: 	       ?RT(Slave,{?MODULE,receiver,1})} ->
  547: 		  ?line ok;
  548: 	      {?RT(Slave,{?MODULE,receiver,1}),
  549: 	       ?RF(Slave,{erlang,phash2,2},0)} ->
  550: 		?line ok;
  551: 	      Error1 -> ?t:fail({unexpected_message, Error1})
  552: 	end,
  553:     ?line case {receive_next_bytag(LocalTracer),
  554: 		receive_next_bytag(LocalTracer)} of
  555: 	      {?RF(Slave,{?MODULE,receiver,1},Ref),
  556: 	       ?RT(Slave,{?MODULE,slave,1})} ->
  557: 		  ?line ok;
  558: 	      {?RT(Slave,{?MODULE,slave,1}),
  559: 	       ?RF(Slave,{?MODULE,receiver,1},Ref)} ->
  560: 		  ?line ok;
  561: 	      Error2 -> ?t:fail({unexpected_message, Error2})
  562: 	  end,
  563:     
  564:     ?line shutdown(),
  565:     ?line ?NM,
  566:     ok.
  567: 
  568: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  569: %% Use case for Inviso meta tracing:
  570: %% Setup silent local call tracing, and start it using meta trace.
  571: 
  572: nosilent_test() ->
  573:     ?line Pid = setup(),
  574:     ?line Trigger = {?MODULE,id,1},
  575:     ?line TriggerMS = [{[start],[],[{silent,false}]},
  576: 		       {[stop],[],[{silent,true},{return_trace}]}],
  577:     ?line 1 = erlang:trace(Pid, true, [call,silent,return_to]),
  578:     ?line erlang:trace_pattern({?MODULE,'_','_'},[],[local]),
  579:     ?line 1 = erlang:trace_pattern({?MODULE,local2,1},
  580: 				   [{'_',[],[{return_trace}]}],
  581: 				   [local]),
  582:     ?line 1 = erlang:trace_pattern({?MODULE,slave,1},false,[local]),
  583:     ?line 1 = erlang:trace_pattern(Trigger,false,[local]),
  584:     ?line 1 = erlang:trace_pattern(Trigger,TriggerMS,[meta]),
  585:     ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
  586:     ?line receive_no_next(17),
  587:     ?line start = apply_slave(?MODULE, id, [start]),
  588:     ?line ?CTT(Pid,{?MODULE,id,[start]})		= receive_next(),
  589:     ?line [2,2,2,0] = apply_slave(?MODULE,exported_wrap,[2]),
  590:     ?line ?CT(Pid,{?MODULE,exported_wrap,[2]})		= receive_next(), 
  591:     ?line ?CT(Pid,{?MODULE,exported,[2]})		= receive_next(),
  592:     ?line ?CT(Pid,{?MODULE,local,[2]})			= receive_next(),
  593:     ?line ?CT(Pid,{?MODULE,local2,[2]})			= receive_next(),
  594:     ?line ?CT(Pid,{?MODULE,local_tail,[2]})		= receive_next(),
  595:     ?line ?RF(Pid,{?MODULE,local2,1}, [2,0])		= receive_next(),
  596:     ?line ?RT(Pid,{?MODULE,local,1})			= receive_next(),
  597:     ?line ?RT(Pid,{?MODULE,exported,1})			= receive_next(),
  598:     ?line ?RT(Pid,{?MODULE,slave,1})			= receive_next(),
  599:     ?line stop = apply_slave(?MODULE, id, [stop]),
  600:     ?line ?CTT(Pid,{?MODULE,id,[stop]})			= receive_next(),
  601:     ?line ?RFT(Pid,{?MODULE,id,1}, stop)		= receive_next(),
  602:     ?line [3,3,3,0] = apply_slave(?MODULE,exported_wrap,[3]),
  603:     ?line receive_no_next(17),
  604:     ?line shutdown(),
  605:     ok.
  606: 
  607: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  608: %% Trace target functions
  609: 
  610: loop(D1,D2,D3,0) ->
  611:     io:format("~p~n",[[D1,D2,D3]]),
  612:     0;
  613: loop(D1,D2,D3,N) ->
  614:     max(N,loop(D1,D2,D3,N-1)).
  615: 
  616: id(X) ->
  617:     X.
  618: 
  619: 
  620: exported_wrap(Val) ->
  621:     exported(Val).
  622: 
  623: exported(Val) ->
  624:     [Val | local(Val)]. %% Non tail recursive local call
  625: 
  626: local(Val) ->
  627:     [Val | local2(Val)]. %% Non tail recursive local call
  628: 
  629: local2(Val) ->
  630:     local_tail(Val). %% Tail recursive call
  631: 
  632: local_tail(Val) ->
  633:     [Val , erlang:phash2(1,1)]. 
  634: 
  635: 
  636: 
  637: receiver(Msg) ->
  638:     erlang:phash2(1,1),
  639:     receive Msg -> Msg end.
  640: 	    
  641: %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  642: %% Trace target process and utilities
  643: 
  644: slave(Sync) ->
  645:     Sync ! sync,
  646:     receive
  647: 	{From,apply, M, F, A} ->
  648: 	    ?line ?dbgformat("Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]),
  649: 	    ?line Res = apply(M,F,A),
  650: 	    ?line ?dbgformat("done Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]),
  651: 	    From ! {apply, Res},
  652: 	    erlang:trace_pattern({?MODULE,slave,1},false,[meta]),
  653: 	    slave(From);
  654: 	{From, lambda, Fun} ->
  655: 	    Res = Fun(),
  656: 	    From ! {lambda, Res},
  657: 	    erlang:trace_pattern({?MODULE,slave,1},false,[meta]),
  658: 	    slave(From);
  659: 	die ->
  660: 	    exit(normal)
  661:     end.
  662: 
  663: setup() ->
  664:     trace_off(),
  665:     Self = self(),
  666:     Pid = spawn(fun () -> slave(Self) end),
  667:     receive sync -> ok end,
  668:     put(slave,Pid),
  669:     Pid.
  670: 
  671: shutdown() ->
  672:     trace_off(),
  673:     Pid = get(slave),
  674:     case (catch is_process_alive(Pid)) of
  675: 	true ->
  676: 	    Ref = erlang:monitor(process,Pid),
  677: 	    Pid ! die,
  678: 	    receive
  679: 		{'DOWN',Ref,process,Pid,_} ->
  680: 		    ok
  681: 	    end;
  682: 	_ ->
  683: 	    ok
  684:     end.
  685: 
  686: trace_off() ->
  687:     erlang:trace(all, false, [all]),
  688:     erlang:trace_pattern({'_','_','_'},false,[]),
  689:     erlang:trace_pattern({'_','_','_'},false,[local]),
  690:     erlang:trace_pattern({'_','_','_'},false,[meta]),
  691:     erlang:trace_pattern(on_load,false,[]),
  692:     erlang:trace_pattern(on_load,false,[local]),
  693:     erlang:trace_pattern(on_load,false,[meta]),
  694:     ok.
  695: 
  696: apply_slave_async(M,F,A) ->
  697:     Slave = get(slave),
  698:     Pid = 
  699: 	spawn(
  700: 	  fun () ->
  701: 		  Slave ! {self(),apply, M, F, A},
  702: 		  receive
  703: 		      {apply, _} ->
  704: 			  receive
  705: 			      sync ->
  706: 				  ok
  707: 			  end
  708: 		  end
  709: 	  end),
  710:     Pid.
  711: 
  712: apply_slave(M,F,A) ->
  713:     Pid = get(slave),
  714:     Pid ! {self(),apply, M, F, A},
  715:     receive
  716: 	{apply, Res} ->
  717: 	    receive
  718: 		sync ->
  719: 		    Res
  720: 	    end
  721:     end.
  722: 
  723: lambda_slave(Fun) ->
  724:     Pid = get(slave),
  725:     Pid ! {self(), lambda, Fun},
  726:     receive
  727: 	{lambda, Res} ->
  728: 	    receive
  729: 		sync ->
  730: 		    Res
  731: 	    end
  732:     end.
  733: 
  734: relay_n(0, _) ->
  735:     ok;
  736: relay_n(N, Dest) ->
  737:     receive Msg ->
  738: 	    Dest ! {self(), Msg},
  739: 	    relay_n(N-1, Dest)
  740:     end.
  741: 
  742: 
  743: receive_next() ->
  744:     receive_next(infinity).
  745: 
  746: receive_next(TO) ->
  747:     receive
  748: 	M ->
  749: 	    M
  750:     after TO ->
  751: 	    ?t:fail(timeout)
  752:     end.
  753: 
  754: receive_no_next(TO) ->
  755:     receive
  756: 	M ->
  757: 	    ?t:fail({unexpected_message, M})
  758:     after
  759: 	TO ->
  760: 	    ok
  761:     end.
  762: 
  763: receive_next_bytag(Tag) ->
  764:     receive_next_bytag(Tag, infinity).
  765: receive_next_bytag(Tag, TO) ->
  766:     receive
  767: 	{Tag, Msg} ->
  768: 	    Msg
  769:     after
  770: 	TO ->
  771: 	    timeout
  772:     end.