diff options
author | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/kernel/test/seq_trace_SUITE.erl | |
download | otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2 otp-84adefa331c4159d432d22840663c38f155cd4c1.zip |
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/kernel/test/seq_trace_SUITE.erl')
-rw-r--r-- | lib/kernel/test/seq_trace_SUITE.erl | 760 |
1 files changed, 760 insertions, 0 deletions
diff --git a/lib/kernel/test/seq_trace_SUITE.erl b/lib/kernel/test/seq_trace_SUITE.erl new file mode 100644 index 0000000000..f582b94c97 --- /dev/null +++ b/lib/kernel/test/seq_trace_SUITE.erl @@ -0,0 +1,760 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-2009. 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/1,init_per_testcase/2,fin_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("test_server.hrl"). + +-define(default_timeout, ?t:minutes(1)). + +all(suite) -> [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]. + +init_per_testcase(_Case, Config) -> + ?line Dog = test_server:timetrap(?default_timeout), + [{watchdog, Dog}|Config]. + +fin_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 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 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 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]. |