aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel/test/seq_trace_SUITE.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/kernel/test/seq_trace_SUITE.erl
downloadotp-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.erl760
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].