%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1998-2010. 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%
%%
-module(seq_trace_SUITE).
-export([all/0,groups/0,init_per_group/2,end_per_group/2,init_per_testcase/2,end_per_testcase/2]).
-export([token_set_get/1, tracer_set_get/1, print/1,
send/1, distributed_send/1, recv/1, distributed_recv/1,
trace_exit/1, distributed_exit/1, call/1, port/1,
match_set_seq_token/1, gc_seq_token/1]).
% internal exports
-export([simple_tracer/2, one_time_receiver/0, one_time_receiver/1,
start_tracer/0, stop_tracer/1,
do_match_set_seq_token/1, do_gc_seq_token/1, countdown_start/2]).
%-define(line_trace, 1).
-include_lib("test_server/include/test_server.hrl").
-define(default_timeout, ?t:minutes(1)).
all() ->
[token_set_get, tracer_set_get, print, send,
distributed_send, recv, distributed_recv, trace_exit,
distributed_exit, call, port, match_set_seq_token,
gc_seq_token].
groups() ->
[].
init_per_group(_GroupName, Config) ->
Config.
end_per_group(_GroupName, Config) ->
Config.
init_per_testcase(_Case, Config) ->
?line Dog = test_server:timetrap(?default_timeout),
[{watchdog, Dog}|Config].
end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
%% Verifies that the set_token and get_token functions work as expected
token_set_get(doc) -> [];
token_set_get(suite) -> [];
token_set_get(Config) when is_list(Config) ->
?line Self = self(),
?line seq_trace:reset_trace(),
%% Test that initial seq_trace is disabled
?line [] = seq_trace:get_token(),
%% Test setting and reading the different fields
?line 0 = seq_trace:set_token(label,17),
?line {label,17} = seq_trace:get_token(label),
?line false = seq_trace:set_token(print,true),
?line {print,true} = seq_trace:get_token(print),
?line false = seq_trace:set_token(send,true),
?line {send,true} = seq_trace:get_token(send),
?line false = seq_trace:set_token('receive',true),
?line {'receive',true} = seq_trace:get_token('receive'),
?line false = seq_trace:set_token(timestamp,true),
?line {timestamp,true} = seq_trace:get_token(timestamp),
%% Check the whole token
?line {15,17,0,Self,0} = seq_trace:get_token(), % all flags are set
%% Test setting and reading the 'serial' field
?line {0,0} = seq_trace:set_token(serial,{3,5}),
?line {serial,{3,5}} = seq_trace:get_token(serial),
%% Check the whole token, test that a whole token can be set and get
?line {15,17,5,Self,3} = seq_trace:get_token(),
?line seq_trace:set_token({15,19,7,Self,5}),
?line {15,19,7,Self,5} = seq_trace:get_token(),
%% Check that receive timeout does not reset token
?line receive after 0 -> ok end,
?line {15,19,7,Self,5} = seq_trace:get_token(),
%% Check that token can be unset
?line {15,19,7,Self,5} = seq_trace:set_token([]),
?line [] = seq_trace:get_token(),
%% Check that Previous serial counter survived unset token
?line 0 = seq_trace:set_token(label, 17),
?line {0,17,0,Self,5} = seq_trace:get_token(),
%% Check that reset_trace resets the token and clears
%% the Previous serial counter
?line seq_trace:reset_trace(),
?line [] = seq_trace:get_token(),
?line 0 = seq_trace:set_token(label, 19),
?line {0,19,0,Self,0} = seq_trace:get_token(),
%% Cleanup
?line seq_trace:reset_trace(),
ok.
tracer_set_get(doc) -> [];
tracer_set_get(suite) -> [];
tracer_set_get(Config) when is_list(Config) ->
?line Self = self(),
?line seq_trace:set_system_tracer(self()),
?line Self = seq_trace:get_system_tracer(),
?line Self = seq_trace:set_system_tracer(false),
?line false = seq_trace:get_system_tracer(),
%% Set the system tracer to a port.
?line Port = load_tracer(Config),
?line seq_trace:set_system_tracer(Port),
?line Port = seq_trace:get_system_tracer(),
?line Port = seq_trace:set_system_tracer(false),
?line false = seq_trace:get_system_tracer(),
ok.
print(doc) -> [];
print(suite) -> [];
print(Config) when is_list(Config) ->
?line start_tracer(),
?line seq_trace:set_token(print,true),
?line seq_trace:print(0,print1),
?line seq_trace:print(1,print2),
?line seq_trace:print(print3),
?line seq_trace:reset_trace(),
?line [{0,{print,_,_,[],print1}},
{0,{print,_,_,[],print3}}] = stop_tracer(2).
send(doc) -> [];
send(suite) -> [];
send(Config) when is_list(Config) ->
?line seq_trace:reset_trace(),
?line start_tracer(),
?line Receiver = spawn(?MODULE,one_time_receiver,[]),
?line seq_trace:set_token(send,true),
?line Receiver ! send,
?line Self = self(),
?line seq_trace:reset_trace(),
?line [{0,{send,_,Self,Receiver,send}}] = stop_tracer(1).
distributed_send(doc) -> [];
distributed_send(suite) -> [];
distributed_send(Config) when is_list(Config) ->
?line {ok,Node} = start_node(seq_trace_other,[]),
?line {_,Dir} = code:is_loaded(?MODULE),
?line Mdir = filename:dirname(Dir),
?line true = rpc:call(Node,code,add_patha,[Mdir]),
?line seq_trace:reset_trace(),
?line start_tracer(),
?line Receiver = spawn(Node,?MODULE,one_time_receiver,[]),
?line seq_trace:set_token(send,true),
?line Receiver ! send,
?line Self = self(),
?line seq_trace:reset_trace(),
?line stop_node(Node),
?line [{0,{send,_,Self,Receiver,send}}] = stop_tracer(1).
recv(doc) -> [];
recv(suite) -> [];
recv(Config) when is_list(Config) ->
?line seq_trace:reset_trace(),
?line start_tracer(),
?line Receiver = spawn(?MODULE,one_time_receiver,[]),
?line seq_trace:set_token('receive',true),
?line Receiver ! 'receive',
%% let the other process receive the message:
?line receive after 1 -> ok end,
?line Self = self(),
?line seq_trace:reset_trace(),
?line [{0,{'receive',_,Self,Receiver,'receive'}}] = stop_tracer(1).
distributed_recv(doc) -> [];
distributed_recv(suite) -> [];
distributed_recv(Config) when is_list(Config) ->
?line {ok,Node} = start_node(seq_trace_other,[]),
?line {_,Dir} = code:is_loaded(?MODULE),
?line Mdir = filename:dirname(Dir),
?line true = rpc:call(Node,code,add_patha,[Mdir]),
?line seq_trace:reset_trace(),
?line rpc:call(Node,?MODULE,start_tracer,[]),
?line Receiver = spawn(Node,?MODULE,one_time_receiver,[]),
?line seq_trace:set_token('receive',true),
?line Receiver ! 'receive',
%% let the other process receive the message:
?line receive after 1 -> ok end,
?line Self = self(),
?line seq_trace:reset_trace(),
?line Result = rpc:call(Node,?MODULE,stop_tracer,[1]),
?line stop_node(Node),
?line ok = io:format("~p~n",[Result]),
?line [{0,{'receive',_,Self,Receiver,'receive'}}] = Result.
trace_exit(doc) -> [];
trace_exit(suite) -> [];
trace_exit(Config) when is_list(Config) ->
?line seq_trace:reset_trace(),
?line start_tracer(),
?line Receiver = spawn_link(?MODULE, one_time_receiver, [exit]),
?line process_flag(trap_exit, true),
?line seq_trace:set_token(send,true),
?line Receiver ! {before, exit},
%% let the other process receive the message:
?line receive
{'EXIT', Receiver, {exit, {before, exit}}} ->
seq_trace:set_token([]);
Other ->
seq_trace:set_token([]),
?t:fail({received, Other})
end,
?line Self = self(),
?line Result = stop_tracer(2),
?line seq_trace:reset_trace(),
?line ok = io:format("~p~n", [Result]),
?line [{0, {send, {0,1}, Self, Receiver, {before, exit}}},
{0, {send, {1,2}, Receiver, Self,
{'EXIT', Receiver, {exit, {before, exit}}}}}] = Result.
distributed_exit(doc) -> [];
distributed_exit(suite) -> [];
distributed_exit(Config) when is_list(Config) ->
?line {ok, Node} = start_node(seq_trace_other, []),
?line {_, Dir} = code:is_loaded(?MODULE),
?line Mdir = filename:dirname(Dir),
?line true = rpc:call(Node, code, add_patha, [Mdir]),
?line seq_trace:reset_trace(),
?line rpc:call(Node, ?MODULE, start_tracer,[]),
?line Receiver = spawn_link(Node, ?MODULE, one_time_receiver, [exit]),
?line process_flag(trap_exit, true),
?line seq_trace:set_token(send, true),
?line Receiver ! {before, exit},
%% let the other process receive the message:
?line receive
{'EXIT', Receiver, {exit, {before, exit}}} ->
seq_trace:set_token([]);
Other ->
seq_trace:set_token([]),
?t:fail({received, Other})
end,
?line Self = self(),
?line Result = rpc:call(Node, ?MODULE, stop_tracer, [1]),
?line seq_trace:reset_trace(),
?line stop_node(Node),
?line ok = io:format("~p~n", [Result]),
?line [{0, {send, {1, 2}, Receiver, Self,
{'EXIT', Receiver, {exit, {before, exit}}}}}] = Result.
call(doc) ->
"Tests special forms {is_seq_trace} and {get_seq_token} "
"in trace match specs.";
call(suite) ->
[];
call(Config) when is_list(Config) ->
?line Self = self(),
?line seq_trace:reset_trace(),
?line TrA = transparent_tracer(),
?line 1 =
erlang:trace(Self, true,
[call, set_on_spawn, {tracer, TrA(pid)}]),
?line 1 =
erlang:trace_pattern({?MODULE, call_tracee_1, 1},
[{'_',
[],
[{message, {{{self}, {get_seq_token}}}}]}],
[local]),
?line 1 =
erlang:trace_pattern({?MODULE, call_tracee_2, 1},
[{'_',
[{is_seq_trace}],
[{message, {{{self}, {get_seq_token}}}}]}],
[local]),
?line RefA = make_ref(),
?line Pid2A = spawn_link(
fun() ->
receive {_, msg, RefA} -> ok end,
RefA = call_tracee_2(RefA),
Self ! {self(), msg, RefA}
end),
?line Pid1A = spawn_link(
fun() ->
receive {_, msg, RefA} -> ok end,
RefA = call_tracee_1(RefA),
Pid2A ! {self(), msg, RefA}
end),
?line Pid1A ! {Self, msg, RefA},
%% The message is passed Self -> Pid1B -> Pid2B -> Self.
%% Traced functions are called in Pid1B and Pid2B.
?line receive {Pid2A, msg, RefA} -> ok end,
%% Only call_tracee1 will be traced since the guard for
%% call_tracee2 requires a sequential trace. The trace
%% token is undefined.
?line Token2A = [],
?line {ok, [{trace, Pid1A, call,
{?MODULE, call_tracee_1, [RefA]},
{Pid1A, Token2A}}]} =
TrA({stop, 1}),
?line seq_trace:reset_trace(),
?line TrB = transparent_tracer(),
?line 1 =
erlang:trace(Self, true,
[call, set_on_spawn, {tracer, TrB(pid)}]),
?line Label = 17,
?line seq_trace:set_token(label, Label), % Token enters here!!
?line RefB = make_ref(),
?line Pid2B = spawn_link(
fun() ->
receive {_, msg, RefB} -> ok end,
RefB = call_tracee_2(RefB),
Self ! {self(), msg, RefB}
end),
?line Pid1B = spawn_link(
fun() ->
receive {_, msg, RefB} -> ok end,
RefB = call_tracee_1(RefB),
Pid2B ! {self(), msg, RefB}
end),
?line Pid1B ! {Self, msg, RefB},
%% The message is passed Self -> Pid1B -> Pid2B -> Self, and the
%% seq_trace token follows invisibly. Traced functions are
%% called in Pid1B and Pid2B. Seq_trace flags == 0 so no
%% seq_trace messages are generated.
?line receive {Pid2B, msg, RefB} -> ok end,
%% The values of these counters {.., 1, _, 0}, {.., 2, _, 1}
%% depend on that seq_trace has been reset just before this test.
?line Token1B = {0, Label, 1, Self, 0},
?line Token2B = {0, Label, 2, Pid1B, 1},
?line {ok, [{trace, Pid1B, call,
{?MODULE, call_tracee_1, [RefB]},
{Pid1B, Token1B}},
{trace, Pid2B, call,
{?MODULE, call_tracee_2, [RefB]},
{Pid2B, Token2B}}]} =
TrB({stop,2}),
?line seq_trace:reset_trace(),
ok.
port(doc) ->
"Send trace messages to a port.";
port(suite) -> [];
port(Config) when is_list(Config) ->
?line Port = load_tracer(Config),
?line seq_trace:set_system_tracer(Port),
?line seq_trace:set_token(print, true),
?line Small = [small,term],
?line seq_trace:print(0, Small),
?line case get_port_message(Port) of
{seq_trace,0,{print,_,_,[],Small}} ->
ok;
Other ->
?line seq_trace:reset_trace(),
?line ?t:fail({unexpected,Other})
end,
%% OTP-4218 Messages from ports should not affect seq trace token.
%%
%% Check if trace token still is active on this process after
%% the get_port_message/1 above that receives from a port.
?line OtherSmall = [other | Small],
?line seq_trace:print(0, OtherSmall),
?line seq_trace:reset_trace(),
?line case get_port_message(Port) of
{seq_trace,0,{print,_,_,[],OtherSmall}} ->
ok;
Other1 ->
?line ?t:fail({unexpected,Other1})
end,
?line seq_trace:set_token(print, true),
?line Huge = huge_data(),
?line seq_trace:print(0, Huge),
?line seq_trace:reset_trace(),
?line case get_port_message(Port) of
{seq_trace,0,{print,_,_,[],Huge}} ->
ok;
Other2 ->
?line ?t:fail({unexpected,Other2})
end,
ok.
get_port_message(Port) ->
receive
{Port,{data,Bin}} when is_binary(Bin) ->
binary_to_term(Bin);
Other ->
?t:fail({unexpected,Other})
after 5000 ->
?t:fail(timeout)
end.
match_set_seq_token(suite) ->
[];
match_set_seq_token(doc) ->
["Tests that match spec function set_seq_token does not "
"corrupt the heap"];
match_set_seq_token(Config) when is_list(Config) ->
?line Parent = self(),
?line Timetrap = test_server:timetrap(test_server:seconds(20)),
%% OTP-4222 Match spec 'set_seq_token' corrupts heap
%%
%% This test crashes the emulator if the bug in question is present,
%% it is therefore done in a slave node.
%%
%% All the timeout stuff is here to get decent accuracy of the error
%% return value, instead of just 'timeout'.
%
?line {ok, Sandbox} = start_node(seq_trace_other, []),
?line true = rpc:call(Sandbox, code, add_patha,
[filename:dirname(code:which(?MODULE))]),
?line Lbl = 4711,
%% Do the possibly crashing test
?line P1 =
spawn(
fun () ->
Parent ! {self(),
rpc:call(Sandbox,
?MODULE, do_match_set_seq_token, [Lbl])}
end),
%% Probe the node with a simple rpc request, to see if it is alive.
?line P2 =
spawn(
fun () ->
receive after 4000 -> ok end,
Parent ! {self(), rpc:call(Sandbox, erlang, abs, [-1])}
end),
%% If the test node hangs completely, this timer expires.
?line R3 = erlang:start_timer(8000, self(), void),
%%
?line {ok, Log} =
receive
{P1, Result} ->
exit(P2, done),
erlang:cancel_timer(R3),
Result;
{P2, 1} ->
exit(P1, timeout),
erlang:cancel_timer(R3),
{error, "Test process hung"};
{timeout, R3, _} ->
exit(P1, timeout),
exit(P2, timeout),
{error, "Test node hung"}
end,
?line ok = check_match_set_seq_token_log(Lbl, Log),
%%
?line stop_node(Sandbox),
?line test_server:timetrap_cancel(Timetrap),
ok.
%% OTP-4222 Match spec 'set_seq_token' corrupts heap
%%
%% The crashing test goes as follows:
%%
%% One trigger function calls match spec function {set_seq_token, _, _},
%% which when faulty corrupts the heap. It is assured that the process
%% in question has a big heap and recently garbage collected so there
%% will be room on the heap, which is necessary for the crash to happen.
%%
%% Then two processes bounces a few messages between each other, and if
%% the heap is crashed the emulator crashes, or the triggering process's
%% loop data gets corrupted so the loop never ends.
do_match_set_seq_token(Label) ->
seq_trace:reset_trace(),
Tr = transparent_tracer(),
TrPid = Tr(pid),
erlang:trace_pattern({?MODULE, '_', '_'},
[{'_',
[{is_seq_trace}],
[{message, {get_seq_token}}]}],
[local]),
erlang:trace_pattern({?MODULE, countdown, 2},
[{'_',
[],
[{set_seq_token, label, Label},
{message, {get_seq_token}}]}],
[local]),
erlang:trace(new, true, [call, {tracer, TrPid}]),
Ref = make_ref(),
Bounce = spawn(fun () -> bounce(Ref) end),
Mref = erlang:monitor(process, Bounce),
_Countdown = erlang:spawn_opt(?MODULE, countdown_start, [Bounce, Ref],
[{min_heap_size, 4192}]),
receive
{'DOWN', Mref, _, _, normal} ->
Result = Tr({stop, 0}),
seq_trace:reset_trace(),
erlang:trace(new, false, [call]),
Result;
{'DOWN', Mref, _, _, Reason} ->
Tr({stop, 0}),
seq_trace:reset_trace(),
erlang:trace(new, false, [call]),
{error, Reason}
end.
check_match_set_seq_token_log(
Label,
[{trace,C,call,{?MODULE,countdown,[B,Ref]}, {0,Label,0,C,0}},
{trace,C,call,{?MODULE,countdown,[B,Ref,3]},{0,Label,0,C,0}},
{trace,B,call,{?MODULE,bounce, [Ref]}, {0,Label,2,B,1}},
{trace,C,call,{?MODULE,countdown,[B,Ref,2]},{0,Label,2,B,1}},
{trace,B,call,{?MODULE,bounce, [Ref]}, {0,Label,4,B,3}},
{trace,C,call,{?MODULE,countdown,[B,Ref,1]},{0,Label,4,B,3}},
{trace,B,call,{?MODULE,bounce, [Ref]}, {0,Label,6,B,5}},
{trace,C,call,{?MODULE,countdown,[B,Ref,0]},{0,Label,6,B,5}}
]) ->
ok;
check_match_set_seq_token_log(_Label, Log) ->
{error, Log}.
countdown_start(Bounce, Ref) ->
%% This gc and the increased heap size of this process ensures that
%% the match spec executed for countdown/2 has got heap space for
%% the trace token, so the heap gets trashed according to OTP-4222.
erlang:garbage_collect(),
countdown(Bounce, Ref).
countdown(Bounce, Ref) ->
countdown(Bounce, Ref, 3).
countdown(Bounce, Ref, 0) ->
Bounce ! Ref;
countdown(Bounce, Ref, Cnt) ->
Tag = make_ref(),
Bounce ! {Ref, self(), {Tag, Cnt}},
receive {Tag, Cnt} -> countdown(Bounce, Ref, Cnt-1) end.
bounce(Ref) ->
receive
Ref ->
ok;
{Ref, Dest, Msg} ->
Dest ! Msg,
bounce(Ref)
end.
gc_seq_token(suite) ->
[];
gc_seq_token(doc) ->
["Tests that a seq_trace token on a message in the inqueue ",
"can be garbage collected."];
gc_seq_token(Config) when is_list(Config) ->
?line Parent = self(),
?line Timetrap = test_server:timetrap(test_server:seconds(20)),
%% OTP-4555 Seq trace token causes free mem read in gc
%%
%% This test crashes the emulator if the bug in question is present,
%% it is therefore done in a slave node.
%%
%% All the timeout stuff is here to get decent accuracy of the error
%% return value, instead of just 'timeout'.
%
?line {ok, Sandbox} = start_node(seq_trace_other, []),
?line true = rpc:call(Sandbox, code, add_patha,
[filename:dirname(code:which(?MODULE))]),
?line Label = 4711,
%% Do the possibly crashing test
?line P1 =
spawn(
fun () ->
Parent ! {self(),
rpc:call(Sandbox,
?MODULE, do_gc_seq_token, [Label])}
end),
%% Probe the node with a simple rpc request, to see if it is alive.
?line P2 =
spawn(
fun () ->
receive after 4000 -> ok end,
Parent ! {self(), rpc:call(Sandbox, erlang, abs, [-1])}
end),
%% If the test node hangs completely, this timer expires.
?line R3 = erlang:start_timer(8000, self(), void),
%%
?line ok =
receive
{P1, Result} ->
exit(P2, done),
erlang:cancel_timer(R3),
Result;
{P2, 1} ->
exit(P1, timeout),
erlang:cancel_timer(R3),
{error, "Test process hung"};
{timeout, R3, _} ->
exit(P1, timeout),
exit(P2, timeout),
{error, "Test node hung"}
end,
%%
?line stop_node(Sandbox),
?line test_server:timetrap_cancel(Timetrap),
ok.
do_gc_seq_token(Label) ->
Parent = self(),
Comment =
{"OTP-4555 Seq trace token causes free mem read in gc\n"
"\n"
"The crashing test goes as follows:\n"
"\n"
"Put a message with seq_trace token in the inqueue,\n"
"Grow the process heap big enough to become mmap'ed\n"
"and force a garbage collection using large terms\n"
"to get a test_heap instruction with a big size value.\n"
"Then try to trick the heap into shrinking.\n"
"\n"
"All this to make the GC move the heap between memory blocks.\n"},
seq_trace:reset_trace(),
Child = spawn_link(
fun() ->
receive {Parent, no_seq_trace_token} -> ok end,
do_grow(Comment, 256*1024, []),
do_shrink(10),
receive {Parent, seq_trace_token} -> ok end,
Parent ! {self(), {token, seq_trace:get_token(label)}}
end),
seq_trace:set_token(label, Label),
Child ! {Parent, seq_trace_token},
seq_trace:set_token([]),
Child ! {Parent, no_seq_trace_token},
receive
{Child, {token, {label, Label}}} ->
ok;
{Child, {token, Other}} ->
{error, Other}
end.
do_grow(_, 0, Acc) ->
Acc;
do_grow(E, N, Acc) ->
do_grow(E, N-1, [E | Acc]).
do_shrink(0) ->
ok;
do_shrink(N) ->
erlang:garbage_collect(),
do_shrink(N-1).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Internal help functions
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Call trace targets
call_tracee_1(X) ->
X.
call_tracee_2(X) ->
X.
transparent_tracer() ->
Ref = make_ref(),
Loop =
fun(Fun, Log, LN) ->
receive
{stop, MinLN, Ref, From} when LN >= MinLN ->
From ! {log, Ref, lists:reverse(Log)};
Entry when is_tuple(Entry) == false; element(1, Entry) /= stop ->
Fun(Fun, [Entry | Log], LN+1)
end
end,
Self = self(),
Pid =
spawn(fun() ->
seq_trace:set_system_tracer(self()),
Self ! {started, Ref},
Loop(Loop, [], 0)
end),
receive {started, Ref} -> ok end,
fun(pid) ->
Pid;
({stop, N}) when is_integer(N), N >= 0 ->
Mref = erlang:monitor(process, Pid),
receive
{'DOWN', Mref, _, _, _} ->
{error, not_started}
after 0 ->
DeliverRef = erlang:trace_delivered(all),
receive
{trace_delivered,_,DeliverRef} -> ok
end,
Pid ! {stop, N, Ref, self()},
receive {'DOWN', Mref, _, _, _} -> ok end,
receive {log, Ref, Log} ->
{ok, Log}
end
end
end.
one_time_receiver() ->
receive _Term -> ok
end.
one_time_receiver(exit) ->
receive Term ->
exit({exit, Term})
end.
simple_tracer(Data, DN) ->
receive
{seq_trace,Label,Info,Ts} ->
simple_tracer([{Label,Info,Ts}|Data], DN+1);
{seq_trace,Label,Info} ->
simple_tracer([{Label,Info}|Data], DN+1);
{stop,N,From} when DN >= N ->
From ! {tracerlog,lists:reverse(Data)}
end.
stop_tracer(N) when is_integer(N) ->
case catch (seq_trace_SUITE_tracer ! {stop,N,self()}) of
{'EXIT', _} ->
{error, not_started};
_ ->
receive
{tracerlog,Data} ->
Data
after 1000 ->
{error,timeout}
end
end.
start_tracer() ->
stop_tracer(0),
Pid = spawn(?MODULE,simple_tracer,[[], 0]),
register(seq_trace_SUITE_tracer,Pid),
seq_trace:set_system_tracer(Pid),
Pid.
start_node(Name, Param) ->
test_server:start_node(Name, slave, [{args, Param}]).
stop_node(Node) ->
test_server:stop_node(Node).
load_tracer(Config) ->
Path = ?config(data_dir, Config),
ok = erl_ddll:load_driver(Path, echo_drv),
open_port({spawn,echo_drv}, [eof,binary]).
huge_data() -> huge_data(16384).
huge_data(0) -> [];
huge_data(N) when N rem 2 == 0 ->
P = huge_data(N div 2),
[P|P];
huge_data(N) ->
P = huge_data(N div 2),
[16#1234566,P|P].