%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2002-2011. 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_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("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,
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) ->
Config.
end_per_testcase(_Case, Config) ->
shutdown(),
ok.
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap, {minutes, 5}}].
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.
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() ->
Pid = setup(),
erlang:trace_pattern({?MODULE,'_','_'},[],[meta]),
[1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(),
?CTT(Pid,{?MODULE,exported,[1]}) = receive_next(),
?CTT(Pid,{?MODULE,local,[1]}) = receive_next(),
?CTT(Pid,{?MODULE,local2,[1]}) = receive_next(),
?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(),
erlang:trace_pattern({?MODULE,'_','_'},false,[meta]),
[1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
?NM,
[1,1,1,0] = lambda_slave(fun() ->
exported_wrap(1)
end),
?NM,
erlang:trace_pattern({?MODULE,'_','_'},[],[meta]),
[1,1,1,0] = lambda_slave(fun() ->
exported_wrap(1)
end),
?CTT(Pid,{?MODULE,_,_}) = receive_next(), %% The fun
?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(),
?CTT(Pid,{?MODULE,exported,[1]}) = receive_next(),
?CTT(Pid,{?MODULE,local,[1]}) = receive_next(),
?CTT(Pid,{?MODULE,local2,[1]}) = receive_next(),
?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(),
erlang:trace_pattern({?MODULE,'_','_'},false,[meta]),
shutdown(),
?NM,
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
return_test() ->
Pid = setup(),
erlang:trace_pattern({?MODULE,'_','_'},[{'_',[],[{return_trace}]}],
[meta]),
erlang:trace_pattern({erlang,phash2,'_'},[{'_',[],[{return_trace}]}],
[meta]),
[1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(),
?CTT(Pid,{?MODULE,exported,[1]}) = receive_next(),
?CTT(Pid,{?MODULE,local,[1]}) = receive_next(),
?CTT(Pid,{?MODULE,local2,[1]}) = receive_next(),
?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(),
?CTT(Pid,{erlang,phash2,[1,1]}) = receive_next(),
?RFT(Pid,{erlang,phash2,2},0) = receive_next(),
?RFT(Pid,{?MODULE,local_tail,1},[1,0]) = receive_next(),
?RFT(Pid,{?MODULE,local2,1},[1,0]) = receive_next(),
?RFT(Pid,{?MODULE,local,1},[1,1,0]) = receive_next(),
?RFT(Pid,{?MODULE,exported,1},[1,1,1,0]) = receive_next(),
?RFT(Pid,{?MODULE,exported_wrap,1},[1,1,1,0]) = receive_next(),
shutdown(),
?NM,
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
on_and_off_test() ->
Pid = setup(),
1 = erlang:trace_pattern({?MODULE,local_tail,1},[],[meta]),
LocalTail = fun() ->
local_tail(1)
end,
[1,0] = lambda_slave(LocalTail),
?CTT(Pid,{?MODULE,local_tail,[1]}) = receive_next(),
0 = erlang:trace_pattern({?MODULE,local_tail,1},[],[global]),
[1,0] = lambda_slave(LocalTail),
?NM,
1 = erlang:trace_pattern({?MODULE,exported_wrap,1},[],[meta]),
[1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(),
1 = erlang:trace_pattern({erlang,phash2,2},[],[meta]),
[1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
?CTT(Pid,{?MODULE,exported_wrap,[1]}) = receive_next(),
?CTT(Pid,{erlang,phash2,[1,1]}) = receive_next(),
shutdown(),
erlang:trace_pattern({'_','_','_'},false,[meta]),
N = erlang:trace_pattern({erlang,'_','_'},true,[meta]),
case erlang:trace_pattern({erlang,'_','_'},false,[meta]) of
N -> ok;
Else ->
exit({number_mismatch, {expected, N}, {got, Else}})
end,
case erlang:trace_pattern({erlang,'_','_'},false,[meta]) of
N -> ok;
Else2 ->
exit({number_mismatch, {expected, N}, {got, Else2}})
end,
%% ?NM,
%% Can't check for erlang:*/* stuff since common_test or test_server
%% will likely call list_to_binary in the logger. just drain any potential
%% message
ok = flush(),
ok.
flush() ->
receive _ -> flush() after 0 -> ok end.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
stack_grow_test() ->
Pid = setup(),
1 = erlang:trace_pattern({?MODULE,loop,4},
[{'_',[],[{return_trace}]}],[meta]),
Num = 1 bsl 15,
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,
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,
apply_slave(?MODULE,loop,[{hej,hopp},[a,b,c],4.5,Num]),
% apply_slave_async(?MODULE,loop,[{hej,hopp},[a,b,c],4.5,Num]),
% List = collect(test_server:seconds(5)),
ok = Dive(Dive, receive_next(), Num),
?NM,
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
info_test() ->
setup(),
Prog = [{['$1'],[{is_integer,'$1'}],[{message, false}]}, {'_',[],[]}],
Self = self(),
GoOn = make_ref(),
Pid =
spawn_link(
fun () ->
erlang:trace_pattern({?MODULE,exported_wrap,1},
Prog, [{meta, Self}]),
Self ! {self(), GoOn}
end),
receive {Pid, GoOn} -> ok end,
{traced,false} = erlang:trace_info({?MODULE,exported_wrap,1}, traced),
{match_spec, false} =
erlang:trace_info({?MODULE,exported_wrap,1}, match_spec),
{meta, Self} = erlang:trace_info({?MODULE,exported_wrap,1}, meta),
{meta_match_spec, MMS} =
erlang:trace_info({?MODULE,exported_wrap,1}, meta_match_spec),
case MMS of
Prog ->
ok;
Wrong ->
exit({bad_result, {erlang,trace_info,
[{?MODULE,exported_wrap,1},
meta_match_spec]},
{expected, Prog}, {got, Wrong}})
end,
erlang:garbage_collect(self()),
receive
after 1 ->
ok
end,
io:format("~p~n",[MMS]),
{meta_match_spec,MMS2} =
erlang:trace_info({?MODULE,exported_wrap,1}, meta_match_spec),
io:format("~p~n",[MMS2]),
case MMS2 of
Prog ->
ok;
Wrong2 ->
exit({bad_result, {erlang,trace_info,
[{?MODULE,exported_wrap,1},
meta_match_spec]},
{expected, Prog}, {got, Wrong2}})
end,
{all, [_|_]=L} = erlang:trace_info({?MODULE,exported_wrap,1}, all),
{value, {meta, Self}} = lists:keysearch(meta, 1, L),
{value, {meta_match_spec, MMS}} = lists:keysearch(meta_match_spec, 1, L),
erlang:trace_pattern({?MODULE,exported_wrap,1}, true, [meta]),
{meta_match_spec, []} =
erlang:trace_info({?MODULE,exported_wrap,1}, meta_match_spec),
erlang:trace_pattern({?MODULE,exported_wrap,1}, false, [meta]),
{meta, false} = erlang:trace_info({?MODULE,exported_wrap,1}, meta),
{meta_match_spec, false} =
erlang:trace_info({?MODULE,exported_wrap,1}, meta_match_spec),
{all, false} = erlang:trace_info({?MODULE,exported_wrap,1}, all),
shutdown(),
?NM,
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
tracer_test() ->
Slave = setup(),
Self = self(),
MatchSpec = [{'_',[],[{return_trace}]}],
Tracer1 = spawn_link(fun () -> relay_n(3, Self) end),
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),
receive {Setter, done} -> ok end,
Ref = make_ref(),
apply_slave_async(?MODULE, receiver, [Ref]),
{Tracer1,?CTT(Slave,{?MODULE,receiver,[Ref]})} = receive_next(100),
{Tracer1,?CTT(Slave,{erlang,phash2,[1,1]})} = receive_next(100),
{Tracer1,?RFT(Slave,{erlang,phash2,2},0)} = receive_next(100),
%% Initiate a return_trace that will fail since the tracer just stopped
Slave ! Ref,
receive_no_next(100),
%% The breakpoint has not been hit since the tracer stopped
{meta,Tracer1} =
erlang:trace_info({?MODULE,receiver,1}, meta),
{meta_match_spec, MatchSpec} =
erlang:trace_info({?MODULE,receiver,1}, meta_match_spec),
{meta,Tracer1} =
erlang:trace_info({erlang,phash2,2}, meta),
{meta_match_spec, MatchSpec} =
erlang:trace_info({erlang,phash2,2}, meta_match_spec),
%% Initiate trace messages that will fail
Ref2 = make_ref(),
apply_slave_async(?MODULE, receiver, [Ref2]),
Slave ! Ref2,
receive_no_next(100),
{meta,[]} =
erlang:trace_info({?MODULE,receiver,1}, meta),
{meta_match_spec, MatchSpec} =
erlang:trace_info({?MODULE,receiver,1}, meta_match_spec),
{meta,[]} =
erlang:trace_info({erlang,phash2,2}, meta),
{meta_match_spec, MatchSpec} =
erlang:trace_info({erlang,phash2,2}, meta_match_spec),
%% Change tracer
Tracer2 = spawn_link(fun () -> relay_n(4, Self) end),
erlang:trace_pattern({?MODULE,receiver,1},
MatchSpec,
[{meta,Tracer2}]),
erlang:trace_pattern({erlang,phash2,2},
MatchSpec,
[{meta,Tracer2}]),
Ref3 = make_ref(),
apply_slave_async(?MODULE, receiver, [Ref3]),
{Tracer2,?CTT(Slave,{?MODULE,receiver,[Ref3]})} = receive_next(),
{Tracer2,?CTT(Slave,{erlang,phash2,[1,1]})} = receive_next(),
{Tracer2,?RFT(Slave,{erlang,phash2,2},0)} = receive_next(),
%% Change tracer between call trace and return trace
Tracer3 = spawn_link(fun () -> relay_n(4, Self) end),
erlang:trace_pattern({?MODULE,receiver,1},
MatchSpec,
[{meta,Tracer3}]),
erlang:trace_pattern({erlang,phash2,2},
MatchSpec,
[{meta,Tracer3}]),
Slave ! Ref3,
%% The return trace should still come from Tracer2
{Tracer2,?RFT(Slave,{?MODULE,receiver,1},Ref3)} = receive_next(),
Ref4 = make_ref(),
%% Now should Tracer3 be used
apply_slave_async(?MODULE, receiver, [Ref4]),
Slave ! Ref4,
{Tracer3,?CTT(Slave,{?MODULE,receiver,[Ref4]})} = receive_next(),
{Tracer3,?CTT(Slave,{erlang,phash2,[1,1]})} = receive_next(),
{Tracer3,?RFT(Slave,{erlang,phash2,2},0)} = receive_next(),
{Tracer3,?RFT(Slave,{?MODULE,receiver,1},Ref4)} = receive_next(),
%% The breakpoint has not been hit since the tracer stopped
{meta,Tracer3} = erlang:trace_info({?MODULE,receiver,1}, meta),
{meta_match_spec, MatchSpec} =
erlang:trace_info({?MODULE,receiver,1}, meta_match_spec),
{meta,Tracer3} =
erlang:trace_info({erlang,phash2,2}, meta),
{meta_match_spec, MatchSpec} =
erlang:trace_info({erlang,phash2,2}, meta_match_spec),
shutdown(),
?NM,
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
combo_test() ->
Slave = setup(),
Self = self(),
MatchSpec = [{'_',[],[{return_trace}]}],
Flags = lists:sort([call, return_to]),
LocalTracer = spawn_link(fun () -> relay_n(6, Self) end),
MetaTracer = spawn_link(fun () -> relay_n(4, Self) end),
1 = erlang:trace_pattern({?MODULE,receiver,1},
MatchSpec,
[local,{meta,MetaTracer}]),
1 = erlang:trace_pattern({erlang,phash2,2},
MatchSpec,
[local,{meta,MetaTracer}]),
1 = erlang:trace(Slave, true,
[{tracer,LocalTracer} | Flags]),
%%
{all, TraceInfo1} =
erlang:trace_info({?MODULE,receiver,1}, all),
{meta,MetaTracer} =
erlang:trace_info({?MODULE,receiver,1}, meta),
{value,{meta,MetaTracer}} =
lists:keysearch(meta, 1, TraceInfo1),
{meta_match_spec,MatchSpec} =
erlang:trace_info({?MODULE,receiver,1}, meta_match_spec),
{value,{meta_match_spec,MatchSpec}} =
lists:keysearch(meta_match_spec, 1, TraceInfo1),
{traced,local} =
erlang:trace_info({?MODULE,receiver,1}, traced),
{value,{traced,local}} =
lists:keysearch(traced, 1, TraceInfo1),
{match_spec,MatchSpec} =
erlang:trace_info({?MODULE,receiver,1}, match_spec),
{value,{match_spec,MatchSpec}} =
lists:keysearch(match_spec, 1, TraceInfo1),
%%
{all, TraceInfo2} =
erlang:trace_info({erlang,phash2,2}, all),
{meta,MetaTracer} =
erlang:trace_info({erlang,phash2,2}, meta),
{value,{meta,MetaTracer}} =
lists:keysearch(meta, 1, TraceInfo2),
{meta_match_spec,MatchSpec} =
erlang:trace_info({erlang,phash2,2}, meta_match_spec),
{value,{meta_match_spec,MatchSpec}} =
lists:keysearch(meta_match_spec, 1, TraceInfo2),
{traced,local} =
erlang:trace_info({erlang,phash2,2}, traced),
{value,{traced,local}} =
lists:keysearch(traced, 1, TraceInfo2),
{match_spec,MatchSpec} =
erlang:trace_info({erlang,phash2,2}, match_spec),
{value,{match_spec,MatchSpec}} =
lists:keysearch(match_spec, 1, TraceInfo2),
%%
{flags,Flags1} = erlang:trace_info(Slave, flags),
Flags = lists:sort(Flags1),
{tracer,LocalTracer} = erlang:trace_info(Slave, tracer),
%%
Ref = make_ref(),
apply_slave_async(?MODULE, receiver, [Ref]),
Slave ! Ref,
?CTT(Slave,{?MODULE,receiver,[Ref]}) = receive_next_bytag(MetaTracer),
?CTT(Slave,{erlang,phash2,[1,1]}) = receive_next_bytag(MetaTracer),
?RFT(Slave,{erlang,phash2,2},0) = receive_next_bytag(MetaTracer),
?RFT(Slave,{?MODULE,receiver,1},Ref) = receive_next_bytag(MetaTracer),
?CT(Slave,{?MODULE,receiver,[Ref]}) = receive_next_bytag(LocalTracer),
?CT(Slave,{erlang,phash2,[1,1]}) = receive_next_bytag(LocalTracer),
case {receive_next_bytag(LocalTracer),
receive_next_bytag(LocalTracer)} of
{?RF(Slave,{erlang,phash2,2},0),
?RT(Slave,{?MODULE,receiver,1})} ->
ok;
{?RT(Slave,{?MODULE,receiver,1}),
?RF(Slave,{erlang,phash2,2},0)} ->
ok;
Error1 -> ct:fail({unexpected_message, Error1})
end,
case {receive_next_bytag(LocalTracer),
receive_next_bytag(LocalTracer)} of
{?RF(Slave,{?MODULE,receiver,1},Ref),
?RT(Slave,{?MODULE,slave,1})} ->
ok;
{?RT(Slave,{?MODULE,slave,1}),
?RF(Slave,{?MODULE,receiver,1},Ref)} ->
ok;
Error2 -> ct:fail({unexpected_message, Error2})
end,
shutdown(),
?NM,
ok.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Use case for Inviso meta tracing:
%% Setup silent local call tracing, and start it using meta trace.
nosilent_test() ->
Pid = setup(),
Trigger = {?MODULE,id,1},
TriggerMS = [{[start],[],[{silent,false}]},
{[stop],[],[{silent,true},{return_trace}]}],
1 = erlang:trace(Pid, true, [call,silent,return_to]),
erlang:trace_pattern({?MODULE,'_','_'},[],[local]),
1 = erlang:trace_pattern({?MODULE,local2,1},
[{'_',[],[{return_trace}]}],
[local]),
1 = erlang:trace_pattern({?MODULE,slave,1},false,[local]),
1 = erlang:trace_pattern(Trigger,false,[local]),
1 = erlang:trace_pattern(Trigger,TriggerMS,[meta]),
[1,1,1,0] = apply_slave(?MODULE,exported_wrap,[1]),
receive_no_next(17),
start = apply_slave(?MODULE, id, [start]),
?CTT(Pid,{?MODULE,id,[start]}) = receive_next(),
[2,2,2,0] = apply_slave(?MODULE,exported_wrap,[2]),
?CT(Pid,{?MODULE,exported_wrap,[2]}) = receive_next(),
?CT(Pid,{?MODULE,exported,[2]}) = receive_next(),
?CT(Pid,{?MODULE,local,[2]}) = receive_next(),
?CT(Pid,{?MODULE,local2,[2]}) = receive_next(),
?CT(Pid,{?MODULE,local_tail,[2]}) = receive_next(),
?RF(Pid,{?MODULE,local2,1}, [2,0]) = receive_next(),
?RT(Pid,{?MODULE,local,1}) = receive_next(),
?RT(Pid,{?MODULE,exported,1}) = receive_next(),
?RT(Pid,{?MODULE,slave,1}) = receive_next(),
stop = apply_slave(?MODULE, id, [stop]),
?CTT(Pid,{?MODULE,id,[stop]}) = receive_next(),
?RFT(Pid,{?MODULE,id,1}, stop) = receive_next(),
[3,3,3,0] = apply_slave(?MODULE,exported_wrap,[3]),
receive_no_next(17),
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} ->
?dbgformat("Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]),
Res = apply(M,F,A),
?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 ->
ct:fail(timeout)
end.
receive_no_next(TO) ->
receive
M ->
ct: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.