%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2002-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. %% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights 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_meta_SUITE). %% Exported end user tests -export([basic_test/0, return_test/0, on_and_off_test/0, stack_grow_test/0, info_test/0, tracer_test/0, combo_test/0, nosilent_test/0]). %% Internal exports -export([exported/1, exported_wrap/1, loop/4, id/1, receiver/1]). %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Test server related stuff %% -ifdef(STANDALONE). -define(config(A,B),config(A,B)). -export([config/2]). -else. -include_lib("test_server/include/test_server.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, return/1, on_and_off/1, stack_grow/1, info/1, tracer/1, combo/1, nosilent/1]). init_per_testcase(_Case, Config) -> ?line Dog=test_server:timetrap(test_server:minutes(5)), [{watchdog, Dog}|Config]. end_per_testcase(_Case, Config) -> shutdown(), Dog=?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> case test_server:is_native(trace_meta_SUITE) of true -> [not_run]; false -> [basic, return, on_and_off, stack_grow, info, tracer, combo, nosilent] 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"}. basic(suite) -> []; basic(doc) -> ["Tests basic meta trace"]; basic(Config) when is_list(Config) -> basic_test(). return(suite) -> []; return(doc) -> ["Tests return trace"]; return(Config) when is_list(Config) -> return_test(). on_and_off(suite) -> []; on_and_off(doc) -> ["Tests turning trace parameters on and off"]; on_and_off(Config) when is_list(Config) -> on_and_off_test(). stack_grow(doc) -> ["Tests the stack growth during return traces"]; stack_grow(Config) when is_list(Config) -> stack_grow_test(). info(doc) -> ["Tests the trace_info BIF"]; info(Config) when is_list(Config) -> info_test(). tracer(suite) -> []; tracer(doc) -> ["Tests stopping and changing tracer process"]; tracer(Config) when is_list(Config) -> tracer_test(). combo(suite) -> []; combo(doc) -> ["Tests combining local call trace with meta trace"]; combo(Config) when is_list(Config) -> combo_test(). nosilent(suite) -> []; nosilent(doc) -> ["Tests that meta trace is not silenced by the silent process flag"]; nosilent(Config) when is_list(Config) -> nosilent_test(). -endif. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% 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,{_,_,_}}). -define(NM, receive_no_next(100)). %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% The Tests %%% basic_test() -> ?line Pid = setup(), ?line erlang:trace_pattern({?MODULE,'_','_'},[],[meta]), ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]), ?line ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(), ?line ?CTT(Pid,{?MODULE,exported,[1]}) = receive_next(), ?line ?CTT(Pid,{?MODULE,local,[1]}) = receive_next(), ?line ?CTT(Pid,{?MODULE,local2,[1]}) = receive_next(), ?line ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(), ?line erlang:trace_pattern({?MODULE,'_','_'},false,[meta]), ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]), ?line ?NM, ?line [1,1,1,0] = lambda_slave(fun() -> exported_wrap(1) end), ?line ?NM, ?line erlang:trace_pattern({?MODULE,'_','_'},[],[meta]), ?line [1,1,1,0] = lambda_slave(fun() -> exported_wrap(1) end), ?line ?CTT(Pid,{?MODULE,_,_}) = receive_next(), %% The fun ?line ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(), ?line ?CTT(Pid,{?MODULE,exported,[1]}) = receive_next(), ?line ?CTT(Pid,{?MODULE,local,[1]}) = receive_next(), ?line ?CTT(Pid,{?MODULE,local2,[1]}) = receive_next(), ?line ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(), ?line erlang:trace_pattern({?MODULE,'_','_'},false,[meta]), ?line shutdown(), ?line ?NM, ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% return_test() -> ?line Pid = setup(), ?line erlang:trace_pattern({?MODULE,'_','_'},[{'_',[],[{return_trace}]}], [meta]), ?line erlang:trace_pattern({erlang,phash2,'_'},[{'_',[],[{return_trace}]}], [meta]), ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]), ?line ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(), ?line ?CTT(Pid,{?MODULE,exported,[1]}) = receive_next(), ?line ?CTT(Pid,{?MODULE,local,[1]}) = receive_next(), ?line ?CTT(Pid,{?MODULE,local2,[1]}) = receive_next(), ?line ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(), ?line ?CTT(Pid,{erlang,phash2,[1,1]}) = receive_next(), ?line ?RFT(Pid,{erlang,phash2,2},0) = receive_next(), ?line ?RFT(Pid,{?MODULE,local_tail,1},[1,0]) = receive_next(), ?line ?RFT(Pid,{?MODULE,local2,1},[1,0]) = receive_next(), ?line ?RFT(Pid,{?MODULE,local,1},[1,1,0]) = receive_next(), ?line ?RFT(Pid,{?MODULE,exported,1},[1,1,1,0]) = receive_next(), ?line ?RFT(Pid,{?MODULE,exported_wrap,1},[1,1,1,0]) = receive_next(), ?line shutdown(), ?line ?NM, ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% on_and_off_test() -> ?line Pid = setup(), ?line 1 = erlang:trace_pattern({?MODULE,local_tail,1},[],[meta]), ?line LocalTail = fun() -> local_tail(1) end, ?line [1,0] = lambda_slave(LocalTail), ?line ?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(), ?line 0 = erlang:trace_pattern({?MODULE,local_tail,1},[],[global]), ?line [1,0] = lambda_slave(LocalTail), ?line ?NM, ?line 1 = erlang:trace_pattern({?MODULE,exported_wrap,1},[],[meta]), ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]), ?line ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(), ?line 1 = erlang:trace_pattern({erlang,phash2,2},[],[meta]), ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]), ?line ?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(), ?line ?CTT(Pid,{erlang,phash2,[1,1]}) = receive_next(), ?line shutdown(), ?line erlang:trace_pattern({'_','_','_'},false,[meta]), ?line N = erlang:trace_pattern({erlang,'_','_'},true,[meta]), ?line case erlang:trace_pattern({erlang,'_','_'},false,[meta]) of N -> ok; Else -> exit({number_mismatch, {expected, N}, {got, Else}}) end, ?line case erlang:trace_pattern({erlang,'_','_'},false,[meta]) of N -> ok; Else2 -> exit({number_mismatch, {expected, N}, {got, Else2}}) end, ?line ?NM, ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% stack_grow_test() -> ?line Pid = setup(), ?line 1 = erlang:trace_pattern({?MODULE,loop,4}, [{'_',[],[{return_trace}]}],[meta]), ?line Num = 1 bsl 15, ?line Surface = fun (This, ?RFT(P,{?MODULE,loop,4},N), N) when P == Pid-> if N == Num -> ?NM, ok; true -> This(This, receive_next(), N+1) end end, ?line Dive = fun (This, ?CTT(P,{?MODULE,loop,[{hej,hopp},[a,b,c],4.5,N]}), N) when P == Pid-> if N == 0 -> Surface(Surface, receive_next(), 0); true -> This(This, receive_next(), N-1) end end, ?line apply_slave(?MODULE,loop,[{hej,hopp},[a,b,c],4.5,Num]), % ?line apply_slave_async(?MODULE,loop,[{hej,hopp},[a,b,c],4.5,Num]), % ?line List = collect(test_server:seconds(5)), ?line ok = Dive(Dive, receive_next(), Num), ?line ?NM, ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% info_test() -> ?line setup(), ?line Prog = [{['$1'],[{is_integer,'$1'}],[{message, false}]}, {'_',[],[]}], ?line Self = self(), ?line GoOn = make_ref(), ?line Pid = spawn_link( fun () -> erlang:trace_pattern({?MODULE,exported_wrap,1}, Prog, [{meta, Self}]), Self ! {self(), GoOn} end), ?line receive {Pid, GoOn} -> ok end, ?line {traced,false} = erlang:trace_info({?MODULE,exported_wrap,1}, traced), ?line {match_spec, false} = erlang:trace_info({?MODULE,exported_wrap,1}, match_spec), ?line {meta, Self} = erlang:trace_info({?MODULE,exported_wrap,1}, meta), ?line {meta_match_spec, MMS} = erlang:trace_info({?MODULE,exported_wrap,1}, meta_match_spec), ?line case MMS of Prog -> ok; Wrong -> exit({bad_result, {erlang,trace_info, [{?MODULE,exported_wrap,1}, meta_match_spec]}, {expected, Prog}, {got, Wrong}}) end, ?line erlang:garbage_collect(self()), ?line receive after 1 -> ok end, ?line io:format("~p~n",[MMS]), ?line {meta_match_spec,MMS2} = erlang:trace_info({?MODULE,exported_wrap,1}, meta_match_spec), ?line io:format("~p~n",[MMS2]), ?line case MMS2 of Prog -> ok; Wrong2 -> exit({bad_result, {erlang,trace_info, [{?MODULE,exported_wrap,1}, meta_match_spec]}, {expected, Prog}, {got, Wrong2}}) end, ?line {all, [_|_]=L} = erlang:trace_info({?MODULE,exported_wrap,1}, all), ?line {value, {meta, Self}} = lists:keysearch(meta, 1, L), ?line {value, {meta_match_spec, MMS}} = lists:keysearch(meta_match_spec, 1, L), ?line erlang:trace_pattern({?MODULE,exported_wrap,1}, true, [meta]), ?line {meta_match_spec, []} = erlang:trace_info({?MODULE,exported_wrap,1}, meta_match_spec), ?line erlang:trace_pattern({?MODULE,exported_wrap,1}, false, [meta]), ?line {meta, false} = erlang:trace_info({?MODULE,exported_wrap,1}, meta), ?line {meta_match_spec, false} = erlang:trace_info({?MODULE,exported_wrap,1}, meta_match_spec), ?line {all, false} = erlang:trace_info({?MODULE,exported_wrap,1}, all), ?line shutdown(), ?line ?NM, ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% tracer_test() -> ?line Slave = setup(), ?line Self = self(), ?line MatchSpec = [{'_',[],[{return_trace}]}], ?line Tracer1 = spawn_link(fun () -> relay_n(3, Self) end), ?line Setter = spawn_link( fun () -> erlang:trace_pattern({?MODULE,receiver,1}, MatchSpec, [{meta,Tracer1}]), erlang:trace_pattern({erlang,phash2,2}, MatchSpec, [{meta,Tracer1}]), Self ! {self(), done} end), ?line receive {Setter, done} -> ok end, ?line Ref = make_ref(), ?line apply_slave_async(?MODULE, receiver, [Ref]), ?line {Tracer1,?CTT(Slave,{?MODULE,receiver,[Ref]})} = receive_next(100), ?line {Tracer1,?CTT(Slave,{erlang,phash2,[1,1]})} = receive_next(100), ?line {Tracer1,?RFT(Slave,{erlang,phash2,2},0)} = receive_next(100), %% Initiate a return_trace that will fail since the tracer just stopped ?line Slave ! Ref, ?line receive_no_next(100), %% The breakpoint has not been hit since the tracer stopped ?line {meta,Tracer1} = erlang:trace_info({?MODULE,receiver,1}, meta), ?line {meta_match_spec, MatchSpec} = erlang:trace_info({?MODULE,receiver,1}, meta_match_spec), ?line {meta,Tracer1} = erlang:trace_info({erlang,phash2,2}, meta), ?line {meta_match_spec, MatchSpec} = erlang:trace_info({erlang,phash2,2}, meta_match_spec), %% Initiate trace messages that will fail ?line Ref2 = make_ref(), ?line apply_slave_async(?MODULE, receiver, [Ref2]), ?line Slave ! Ref2, ?line receive_no_next(100), ?line {meta,[]} = erlang:trace_info({?MODULE,receiver,1}, meta), ?line {meta_match_spec, MatchSpec} = erlang:trace_info({?MODULE,receiver,1}, meta_match_spec), ?line {meta,[]} = erlang:trace_info({erlang,phash2,2}, meta), ?line {meta_match_spec, MatchSpec} = erlang:trace_info({erlang,phash2,2}, meta_match_spec), %% Change tracer ?line Tracer2 = spawn_link(fun () -> relay_n(4, Self) end), ?line erlang:trace_pattern({?MODULE,receiver,1}, MatchSpec, [{meta,Tracer2}]), ?line erlang:trace_pattern({erlang,phash2,2}, MatchSpec, [{meta,Tracer2}]), ?line Ref3 = make_ref(), ?line apply_slave_async(?MODULE, receiver, [Ref3]), ?line {Tracer2,?CTT(Slave,{?MODULE,receiver,[Ref3]})} = receive_next(), ?line {Tracer2,?CTT(Slave,{erlang,phash2,[1,1]})} = receive_next(), ?line {Tracer2,?RFT(Slave,{erlang,phash2,2},0)} = receive_next(), %% Change tracer between call trace and return trace ?line Tracer3 = spawn_link(fun () -> relay_n(4, Self) end), ?line erlang:trace_pattern({?MODULE,receiver,1}, MatchSpec, [{meta,Tracer3}]), ?line erlang:trace_pattern({erlang,phash2,2}, MatchSpec, [{meta,Tracer3}]), ?line Slave ! Ref3, %% The return trace should still come from Tracer2 ?line {Tracer2,?RFT(Slave,{?MODULE,receiver,1},Ref3)} = receive_next(), ?line Ref4 = make_ref(), %% Now should Tracer3 be used ?line apply_slave_async(?MODULE, receiver, [Ref4]), ?line Slave ! Ref4, ?line {Tracer3,?CTT(Slave,{?MODULE,receiver,[Ref4]})} = receive_next(), ?line {Tracer3,?CTT(Slave,{erlang,phash2,[1,1]})} = receive_next(), ?line {Tracer3,?RFT(Slave,{erlang,phash2,2},0)} = receive_next(), ?line {Tracer3,?RFT(Slave,{?MODULE,receiver,1},Ref4)} = receive_next(), %% The breakpoint has not been hit since the tracer stopped ?line {meta,Tracer3} = erlang:trace_info({?MODULE,receiver,1}, meta), ?line {meta_match_spec, MatchSpec} = erlang:trace_info({?MODULE,receiver,1}, meta_match_spec), ?line {meta,Tracer3} = erlang:trace_info({erlang,phash2,2}, meta), ?line {meta_match_spec, MatchSpec} = erlang:trace_info({erlang,phash2,2}, meta_match_spec), ?line shutdown(), ?line ?NM, ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% combo_test() -> ?line Slave = setup(), ?line Self = self(), ?line MatchSpec = [{'_',[],[{return_trace}]}], ?line Flags = lists:sort([call, return_to]), ?line LocalTracer = spawn_link(fun () -> relay_n(6, Self) end), ?line MetaTracer = spawn_link(fun () -> relay_n(4, Self) end), ?line 1 = erlang:trace_pattern({?MODULE,receiver,1}, MatchSpec, [local,{meta,MetaTracer}]), ?line 1 = erlang:trace_pattern({erlang,phash2,2}, MatchSpec, [local,{meta,MetaTracer}]), ?line 1 = erlang:trace(Slave, true, [{tracer,LocalTracer} | Flags]), %% ?line {all, TraceInfo1} = erlang:trace_info({?MODULE,receiver,1}, all), ?line {meta,MetaTracer} = erlang:trace_info({?MODULE,receiver,1}, meta), ?line {value,{meta,MetaTracer}} = lists:keysearch(meta, 1, TraceInfo1), ?line {meta_match_spec,MatchSpec} = erlang:trace_info({?MODULE,receiver,1}, meta_match_spec), ?line {value,{meta_match_spec,MatchSpec}} = lists:keysearch(meta_match_spec, 1, TraceInfo1), ?line {traced,local} = erlang:trace_info({?MODULE,receiver,1}, traced), ?line {value,{traced,local}} = lists:keysearch(traced, 1, TraceInfo1), ?line {match_spec,MatchSpec} = erlang:trace_info({?MODULE,receiver,1}, match_spec), ?line {value,{match_spec,MatchSpec}} = lists:keysearch(match_spec, 1, TraceInfo1), %% ?line {all, TraceInfo2} = erlang:trace_info({erlang,phash2,2}, all), ?line {meta,MetaTracer} = erlang:trace_info({erlang,phash2,2}, meta), ?line {value,{meta,MetaTracer}} = lists:keysearch(meta, 1, TraceInfo2), ?line {meta_match_spec,MatchSpec} = erlang:trace_info({erlang,phash2,2}, meta_match_spec), ?line {value,{meta_match_spec,MatchSpec}} = lists:keysearch(meta_match_spec, 1, TraceInfo2), ?line {traced,local} = erlang:trace_info({erlang,phash2,2}, traced), ?line {value,{traced,local}} = lists:keysearch(traced, 1, TraceInfo2), ?line {match_spec,MatchSpec} = erlang:trace_info({erlang,phash2,2}, match_spec), ?line {value,{match_spec,MatchSpec}} = lists:keysearch(match_spec, 1, TraceInfo2), %% ?line {flags,Flags1} = erlang:trace_info(Slave, flags), ?line Flags = lists:sort(Flags1), ?line {tracer,LocalTracer} = erlang:trace_info(Slave, tracer), %% ?line Ref = make_ref(), ?line apply_slave_async(?MODULE, receiver, [Ref]), ?line Slave ! Ref, ?line ?CTT(Slave,{?MODULE,receiver,[Ref]}) = receive_next_bytag(MetaTracer), ?line ?CTT(Slave,{erlang,phash2,[1,1]}) = receive_next_bytag(MetaTracer), ?line ?RFT(Slave,{erlang,phash2,2},0) = receive_next_bytag(MetaTracer), ?line ?RFT(Slave,{?MODULE,receiver,1},Ref) = receive_next_bytag(MetaTracer), ?line ?CT(Slave,{?MODULE,receiver,[Ref]}) = receive_next_bytag(LocalTracer), ?line ?CT(Slave,{erlang,phash2,[1,1]}) = receive_next_bytag(LocalTracer), ?line case {receive_next_bytag(LocalTracer), receive_next_bytag(LocalTracer)} of {?RF(Slave,{erlang,phash2,2},0), ?RT(Slave,{?MODULE,receiver,1})} -> ?line ok; {?RT(Slave,{?MODULE,receiver,1}), ?RF(Slave,{erlang,phash2,2},0)} -> ?line ok; Error1 -> ?t:fail({unexpected_message, Error1}) end, ?line case {receive_next_bytag(LocalTracer), receive_next_bytag(LocalTracer)} of {?RF(Slave,{?MODULE,receiver,1},Ref), ?RT(Slave,{?MODULE,slave,1})} -> ?line ok; {?RT(Slave,{?MODULE,slave,1}), ?RF(Slave,{?MODULE,receiver,1},Ref)} -> ?line ok; Error2 -> ?t:fail({unexpected_message, Error2}) end, ?line shutdown(), ?line ?NM, ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Use case for Inviso meta tracing: %% Setup silent local call tracing, and start it using meta trace. nosilent_test() -> ?line Pid = setup(), ?line Trigger = {?MODULE,id,1}, ?line TriggerMS = [{[start],[],[{silent,false}]}, {[stop],[],[{silent,true},{return_trace}]}], ?line 1 = erlang:trace(Pid, true, [call,silent,return_to]), ?line erlang:trace_pattern({?MODULE,'_','_'},[],[local]), ?line 1 = erlang:trace_pattern({?MODULE,local2,1}, [{'_',[],[{return_trace}]}], [local]), ?line 1 = erlang:trace_pattern({?MODULE,slave,1},false,[local]), ?line 1 = erlang:trace_pattern(Trigger,false,[local]), ?line 1 = erlang:trace_pattern(Trigger,TriggerMS,[meta]), ?line [1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]), ?line receive_no_next(17), ?line start = apply_slave(?MODULE, id, [start]), ?line ?CTT(Pid,{?MODULE,id,[start]}) = receive_next(), ?line [2,2,2,0] = apply_slave(?MODULE,exported_wrap,[2]), ?line ?CT(Pid,{?MODULE,exported_wrap,[2]}) = receive_next(), ?line ?CT(Pid,{?MODULE,exported,[2]}) = receive_next(), ?line ?CT(Pid,{?MODULE,local,[2]}) = receive_next(), ?line ?CT(Pid,{?MODULE,local2,[2]}) = receive_next(), ?line ?CT(Pid,{?MODULE,local_tail,[2]}) = receive_next(), ?line ?RF(Pid,{?MODULE,local2,1}, [2,0]) = receive_next(), ?line ?RT(Pid,{?MODULE,local,1}) = receive_next(), ?line ?RT(Pid,{?MODULE,exported,1}) = receive_next(), ?line ?RT(Pid,{?MODULE,slave,1}) = receive_next(), ?line stop = apply_slave(?MODULE, id, [stop]), ?line ?CTT(Pid,{?MODULE,id,[stop]}) = receive_next(), ?line ?RFT(Pid,{?MODULE,id,1}, stop) = receive_next(), ?line [3,3,3,0] = apply_slave(?MODULE,exported_wrap,[3]), ?line receive_no_next(17), ?line shutdown(), ok. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Trace target functions loop(D1,D2,D3,0) -> io:format("~p~n",[[D1,D2,D3]]), 0; loop(D1,D2,D3,N) -> max(N,loop(D1,D2,D3,N-1)). id(X) -> X. exported_wrap(Val) -> exported(Val). exported(Val) -> [Val | local(Val)]. %% Non tail recursive local call local(Val) -> [Val | local2(Val)]. %% Non tail recursive local call local2(Val) -> local_tail(Val). %% Tail recursive call local_tail(Val) -> [Val , erlang:phash2(1,1)]. receiver(Msg) -> erlang:phash2(1,1), receive Msg -> Msg end. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Trace target process and utilities slave(Sync) -> Sync ! sync, receive {From,apply, M, F, A} -> ?line ?dbgformat("Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]), ?line Res = apply(M,F,A), ?line ?dbgformat("done Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]), From ! {apply, Res}, erlang:trace_pattern({?MODULE,slave,1},false,[meta]), slave(From); {From, lambda, Fun} -> Res = Fun(), From ! {lambda, Res}, erlang:trace_pattern({?MODULE,slave,1},false,[meta]), slave(From); die -> exit(normal) end. setup() -> trace_off(), Self = self(), Pid = spawn(fun () -> slave(Self) end), receive sync -> ok end, put(slave,Pid), Pid. shutdown() -> trace_off(), Pid = get(slave), case (catch is_process_alive(Pid)) of true -> Ref = erlang:monitor(process,Pid), Pid ! die, receive {'DOWN',Ref,process,Pid,_} -> ok end; _ -> ok end. trace_off() -> erlang:trace(all, false, [all]), erlang:trace_pattern({'_','_','_'},false,[]), erlang:trace_pattern({'_','_','_'},false,[local]), erlang:trace_pattern({'_','_','_'},false,[meta]), erlang:trace_pattern(on_load,false,[]), erlang:trace_pattern(on_load,false,[local]), erlang:trace_pattern(on_load,false,[meta]), ok. apply_slave_async(M,F,A) -> Slave = get(slave), Pid = spawn( fun () -> Slave ! {self(),apply, M, F, A}, receive {apply, _} -> receive sync -> ok end end end), Pid. apply_slave(M,F,A) -> Pid = get(slave), Pid ! {self(),apply, M, F, A}, receive {apply, Res} -> receive sync -> Res end end. lambda_slave(Fun) -> Pid = get(slave), Pid ! {self(), lambda, Fun}, receive {lambda, Res} -> receive sync -> Res end end. relay_n(0, _) -> ok; relay_n(N, Dest) -> receive Msg -> Dest ! {self(), Msg}, relay_n(N-1, Dest) end. receive_next() -> receive_next(infinity). receive_next(TO) -> receive M -> M after TO -> ?t:fail(timeout) end. receive_no_next(TO) -> receive M -> ?t:fail({unexpected_message, M}) after TO -> ok end. receive_next_bytag(Tag) -> receive_next_bytag(Tag, infinity). receive_next_bytag(Tag, TO) -> receive {Tag, Msg} -> Msg after TO -> timeout end.