aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/test/trace_port_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 /erts/emulator/test/trace_port_SUITE.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'erts/emulator/test/trace_port_SUITE.erl')
-rw-r--r--erts/emulator/test/trace_port_SUITE.erl686
1 files changed, 686 insertions, 0 deletions
diff --git a/erts/emulator/test/trace_port_SUITE.erl b/erts/emulator/test/trace_port_SUITE.erl
new file mode 100644
index 0000000000..5febe177f9
--- /dev/null
+++ b/erts/emulator/test/trace_port_SUITE.erl
@@ -0,0 +1,686 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-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(trace_port_SUITE).
+
+-export([all/1,init_per_testcase/2,fin_per_testcase/2,
+ call_trace/1,
+ return_trace/1,
+ send/1,
+ receive_trace/1,
+ process_events/1,
+ schedule/1,
+ fake_schedule/1,
+ fake_schedule_after_register/1,
+ fake_schedule_after_getting_linked/1,
+ fake_schedule_after_getting_unlinked/1,
+ gc/1,
+ default_tracer/1]).
+
+-include("test_server.hrl").
+
+test_cases() ->
+ [call_trace,
+ return_trace,
+ send,
+ receive_trace,
+ process_events,
+ schedule,
+ fake_schedule,
+ fake_schedule_after_register,
+ fake_schedule_after_getting_linked,
+ fake_schedule_after_getting_unlinked,
+ gc,
+ default_tracer].
+
+all(suite) -> test_cases().
+
+init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
+ Dog = ?t:timetrap(?t:seconds(30)),
+ [{watchdog, Dog}|Config].
+
+fin_per_testcase(_Func, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog).
+
+call_trace(doc) -> "Test sending call trace messages to a port.";
+call_trace(Config) when is_list(Config) ->
+ case test_server:is_native(?MODULE) of
+ true ->
+ {skip,"Native code"};
+ false ->
+ ?line start_tracer(Config),
+ Self = self(),
+ ?line trace_func({lists,reverse,1}, []),
+ ?line trace_pid(Self, true, [call]),
+ ?line trace_info(Self, flags),
+ ?line trace_info(Self, tracer),
+ ?line [b,a] = lists:reverse([a,b]),
+ ?line expect({trace,Self,call,{lists,reverse,[[a,b]]}}),
+
+ ?line trace_pid(Self, true, [timestamp]),
+ ?line trace_info(Self, flags),
+ ?line Huge = huge_data(),
+ ?line lists:reverse(Huge),
+ ?line expect({trace_ts,Self,call,{lists,reverse,[Huge]},ts}),
+
+ ?line trace_pid(Self, true, [arity]),
+ ?line trace_info(Self, flags),
+ ?line [y,x] = lists:reverse([x,y]),
+ ?line expect({trace_ts,Self,call,{lists,reverse,1},ts}),
+
+ ?line trace_pid(Self, false, [timestamp]),
+ ?line trace_info(Self, flags),
+ ?line [z,y,x] = lists:reverse([x,y,z]),
+ ?line expect({trace,Self,call,{lists,reverse,1}}),
+
+ %% OTP-7399. Delayed sub-binary creation optimization.
+ ?line trace_pid(Self, false, [arity]),
+ ?line trace_info(Self, flags),
+ ?line trace_func({?MODULE,bs_sum_c,2}, [], [local]),
+ ?line 26 = bs_sum_c(<<3:4,5:4,7:4,11:4>>, 0),
+ ?line trace_func({?MODULE,bs_sum_c,2}, false, [local]),
+ ?line expect({trace,Self,call,{?MODULE,bs_sum_c,[<<3:4,5:4,7:4,11:4>>,0]}}),
+ ?line expect({trace,Self,call,{?MODULE,bs_sum_c,[<<5:4,7:4,11:4>>,3]}}),
+ ?line expect({trace,Self,call,{?MODULE,bs_sum_c,[<<7:4,11:4>>,8]}}),
+ ?line expect({trace,Self,call,{?MODULE,bs_sum_c,[<<11:4>>,15]}}),
+ ?line expect({trace,Self,call,{?MODULE,bs_sum_c,[<<>>,26]}}),
+
+ ?line trace_func({lists,reverse,1}, false),
+ ok
+ end.
+
+bs_sum_c(<<H:4,T/bits>>, Acc) -> bs_sum_c(T, H+Acc);
+bs_sum_c(<<>>, Acc) -> Acc.
+
+
+return_trace(doc) -> "Test the new return trace.";
+return_trace(Config) when is_list(Config) ->
+ case test_server:is_native(?MODULE) of
+ true ->
+ {skip,"Native code"};
+ false ->
+ ?line start_tracer(Config),
+ Self = self(),
+ MFA = {lists,reverse,1},
+
+ %% Plain (no timestamp, small data).
+
+ ?line trace_func(MFA, [{['$1'],[],[{return_trace},
+ {message,false}]}]),
+ ?line trace_pid(Self, true, [call]),
+ ?line trace_info(Self, flags),
+ ?line trace_info(Self, tracer),
+ ?line trace_info(MFA, match_spec),
+ ?line {traced,global} = trace_info(MFA, traced),
+ ?line [b,a] = lists:reverse([a,b]),
+ ?line expect({trace,Self,return_from,MFA,[b,a]}),
+
+ %% Timestamp, huge data.
+ ?line trace_pid(Self, true, [timestamp]),
+ ?line Result = lists:reverse(huge_data()),
+ ?line expect({trace_ts,Self,return_from,MFA,Result,ts}),
+
+ %% Turn off trace.
+ ?line trace_func(MFA, false),
+ ?line trace_info(MFA, match_spec),
+ ?line {traced,false} = trace_info(MFA, traced),
+ ok
+ end.
+
+send(doc) -> "Test sending send trace messages to a port.";
+send(Config) when is_list(Config) ->
+ ?line Tracer = start_tracer(Config),
+ Self = self(),
+ ?line Sender = fun_spawn(fun sender/0),
+ ?line trac(Sender, true, [send]),
+
+ %% Simple message, no timestamp.
+
+ ?line Bin = list_to_binary(lists:seq(1, 10)),
+ ?line Msg = {some_data,Bin},
+ Sender ! {send_please,self(),Msg},
+ receive Msg -> ok end,
+ ?line expect({trace,Sender,send,Msg,Self}),
+
+ %% Timestamp.
+
+ BiggerMsg = {even_bigger,Msg},
+ ?line trac(Sender, true, [send,timestamp]),
+ Sender ! {send_please,self(),BiggerMsg},
+ receive BiggerMsg -> ok end,
+ ?line expect({trace_ts,Sender,send,BiggerMsg,Self,ts}),
+
+ %% Huge message.
+
+ ?line HugeMsg = huge_data(),
+ Sender ! {send_please,self(),HugeMsg},
+ receive HugeMsg -> ok end,
+ ?line expect({trace_ts,Sender,send,HugeMsg,Self,ts}),
+
+ %% Kill trace port and force a trace. The emulator should not crasch.
+
+ ?line unlink(Tracer),
+ ?line exit(Tracer, kill),
+ erlang:yield(), % Make sure that port gets killed.
+ Sender ! {send_please,Self,good_bye},
+ receive good_bye -> ok end,
+ ok.
+
+receive_trace(doc) -> "Test sending receive traces to a port.";
+receive_trace(Config) when is_list(Config) ->
+ ?line start_tracer(Config),
+ ?line Receiver = fun_spawn(fun receiver/0),
+ ?line trac(Receiver, true, ['receive']),
+
+ Receiver ! {hello,world},
+ ?line expect({trace,Receiver,'receive',{hello,world}}),
+
+ ?line trac(Receiver, true, ['receive',timestamp]),
+ Huge = {hello,huge_data()},
+ Receiver ! {hello,huge_data()},
+ ?line expect({trace_ts,Receiver,'receive',Huge,ts}),
+ ok.
+
+process_events(doc) -> "Tests a few process events (like getting linked).";
+process_events(Config) when is_list(Config) ->
+ ?line start_tracer(Config),
+ Self = self(),
+ ?line Receiver = fun_spawn(fun receiver/0),
+ ?line trac(Receiver, true, [procs]),
+
+ unlink(Receiver), %It is already linked.
+ ?line expect({trace,Receiver,getting_unlinked,Self}),
+ link(Receiver),
+ ?line expect({trace,Receiver,getting_linked,Self}),
+ ?line trac(Receiver, true, [procs,timestamp]),
+ unlink(Receiver),
+ ?line expect({trace_ts,Receiver,getting_unlinked,Self,ts}),
+ link(Receiver),
+ ?line expect({trace_ts,Receiver,getting_linked,Self,ts}),
+
+ unlink(Receiver),
+ ?line expect({trace_ts,Receiver,getting_unlinked,Self,ts}),
+ Huge = huge_data(),
+ exit(Receiver, Huge),
+ ?line expect({trace_ts,Receiver,exit,Huge,ts}),
+
+ ok.
+
+schedule(doc) -> "Test sending scheduling events to a port.";
+schedule(Config) when is_list(Config) ->
+ ?line start_tracer(Config),
+ ?line Receiver = fun_spawn(fun receiver/0),
+ ?line trac(Receiver, true, [running]),
+
+ Receiver ! hi,
+ expect({trace,Receiver,in,{?MODULE,receiver,0}}),
+ expect({trace,Receiver,out,{?MODULE,receiver,0}}),
+
+ ?line trac(Receiver, true, [running,timestamp]),
+
+ Receiver ! hi_again,
+ expect({trace_ts,Receiver,in,{?MODULE,receiver,0},ts}),
+ expect({trace_ts,Receiver,out,{?MODULE,receiver,0},ts}),
+
+ ok.
+
+run_fake_sched_test(Fun, Config) when is_function(Fun), is_list(Config) ->
+ ?line case catch erlang:system_info(smp_support) of
+ true ->
+ ?line {skipped,
+ "No need for faked schedule out/in trace messages "
+ "when smp support is enabled"};
+ _ ->
+ ?line Fun(Config)
+ end.
+
+fake_schedule(doc) -> "Tests time compensating fake out/in scheduling.";
+fake_schedule(Config) when is_list(Config) ->
+ ?line run_fake_sched_test(fun fake_schedule_test/1, Config).
+
+fake_schedule_test(Config) when is_list(Config) ->
+ ?line Tracer = start_tracer(Config),
+ ?line Port = get(tracer_port),
+ ?line General = fun_spawn(fun general/0),
+ %%
+ ?line trac(General, true, [send, running]),
+ %%
+ %% Test that fake out/in scheduling is not generated unless
+ %% both 'running' and 'timestamp' is active.
+ ?line [] = erlang:port_control(Port, $h, []),
+ ?line General ! nop,
+ ?line expect({trace, General, in, {?MODULE, general, 0}}),
+ ?line expect({trace, General, out, {?MODULE, general, 0}}),
+ ?line expect(),
+ %%
+ ?line trac(General, false, [running]),
+ ?line trac(General, true, [timestamp]),
+ %%
+ ?line Ref1 = make_ref(),
+ ?line Msg1 = {Port, {data, term_to_binary(Ref1)}},
+ ?line [] = erlang:port_control(Port, $h, []),
+ ?line General ! {send, Tracer, Msg1},
+ ?line expect({trace_ts, General, send, Msg1, Tracer, ts}),
+ ?line expect(Ref1),
+ ?line expect(),
+ %%
+ ?line trac(General, true, [running]),
+ %%
+ %% Test that fake out/in scheduling can be generated by the driver
+ ?line Ref2 = make_ref(),
+ ?line Msg2 = {Port, {data, term_to_binary(Ref2)}},
+ ?line [] = erlang:port_control(Port, $h, []),
+ ?line General ! {send, Tracer, Msg2},
+ ?line {_,_,_,_,Ts} =
+ expect({trace_ts, General, in, {?MODULE, general, 0}, ts}),
+ ?line expect({trace_ts, General, out, 0, Ts}),
+ ?line expect({trace_ts, General, in, 0, ts}),
+ ?line expect({trace_ts, General, send, Msg2, Tracer, ts}),
+ ?line expect(Ref2),
+ ?line expect({trace_ts, General, out, {?MODULE, general, 0}, ts}),
+ ?line expect(),
+ %%
+ %% Test that fake out/in scheduling is not generated after an
+ %% 'out' scheduling event
+ ?line Ref3 = make_ref(),
+ ?line Msg3 = {Port, {data, term_to_binary(Ref3)}},
+ ?line General ! {apply, {erlang, port_control, [Port, $h, []]}},
+ ?line expect({trace_ts, General, in, {?MODULE, general, 0}, ts}),
+ ?line expect({trace_ts, General, out, {?MODULE, general, 0}, ts}),
+ ?line General ! {send, Tracer, Msg3},
+ ?line expect({trace_ts, General, in, {?MODULE, general, 0}, ts}),
+ ?line expect({trace_ts, General, send, Msg3, Tracer, ts}),
+ ?line expect(Ref3),
+ ?line expect({trace_ts, General, out, {?MODULE, general, 0}, ts}),
+ ?line expect(),
+ %%
+ ok.
+
+fake_schedule_after_register(doc) ->
+ "Tests fake out/in scheduling contents.";
+fake_schedule_after_register(Config) when is_list(Config) ->
+ ?line run_fake_sched_test(fun fake_schedule_after_register_test/1, Config).
+
+fake_schedule_after_register_test(Config) when is_list(Config) ->
+ ?line start_tracer(Config),
+ ?line Port = get(tracer_port),
+ ?line G1 = fun_spawn(fun general/0),
+ ?line G2 = fun_spawn(fun general/0),
+ %%
+ ?line trac(G1, true, [running, timestamp, procs]),
+ ?line trac(G2, true, [running, timestamp]),
+ %%
+ %% Test fake out/in scheduling after certain messages
+ ?line erlang:yield(),
+ ?line G2 ! {apply, {erlang, port_control, [Port, $h, []]}},
+ ?line G2 ! {apply, {erlang, register, [fake_schedule_after_register, G1]}},
+ ?line expect({trace_ts, G2, in, {?MODULE, general, 0}, ts}),
+ ?line {_,_,_,_,Ts} =
+ expect({trace_ts, G1, register, fake_schedule_after_register, ts}),
+ ?line expect({trace_ts, G2, out, 0, Ts}),
+ ?line expect({trace_ts, G2, in, 0, ts}),
+ ?line expect({trace_ts, G2, out, {?MODULE, general, 0}, ts}),
+ ?line expect(),
+ %%
+ ok.
+
+fake_schedule_after_getting_linked(doc) ->
+ "Tests fake out/in scheduling contents.";
+fake_schedule_after_getting_linked(Config) when is_list(Config) ->
+ ?line run_fake_sched_test(fun fake_schedule_after_getting_linked_test/1,
+ Config).
+
+fake_schedule_after_getting_linked_test(Config) when is_list(Config) ->
+ ?line start_tracer(Config),
+ ?line Port = get(tracer_port),
+ ?line G1 = fun_spawn(fun general/0),
+ ?line G2 = fun_spawn(fun general/0),
+ %%
+ ?line trac(G1, true, [running, timestamp, procs]),
+ ?line trac(G2, true, [running, timestamp]),
+ %%
+ %% Test fake out/in scheduling after certain messages
+ ?line erlang:yield(),
+ ?line G2 ! {apply, {erlang, port_control, [Port, $h, []]}},
+ ?line G2 ! {apply, {erlang, link, [G1]}},
+ ?line expect({trace_ts, G2, in, {?MODULE, general, 0}, ts}),
+ ?line {_,_,_,_,Ts} =
+ expect({trace_ts, G1, getting_linked, G2, ts}),
+ ?line expect({trace_ts, G2, out, 0, Ts}),
+ ?line expect({trace_ts, G2, in, 0, ts}),
+ ?line expect({trace_ts, G2, out, {?MODULE, general, 0}, ts}),
+ ?line expect(),
+ %%
+ ok.
+
+fake_schedule_after_getting_unlinked(doc) ->
+ "Tests fake out/in scheduling contents.";
+fake_schedule_after_getting_unlinked(Config) when is_list(Config) ->
+ ?line run_fake_sched_test(fun fake_schedule_after_getting_unlinked_test/1,
+ Config).
+
+fake_schedule_after_getting_unlinked_test(Config) when is_list(Config) ->
+ ?line start_tracer(Config),
+ ?line Port = get(tracer_port),
+ ?line G1 = fun_spawn(fun general/0),
+ ?line G2 = fun_spawn(fun general/0),
+ %%
+ ?line trac(G1, true, [running, procs]),
+ ?line trac(G2, true, [running, timestamp]),
+ %%
+ %% Test fake out/in scheduling after certain messages
+ ?line erlang:yield(),
+ ?line G2 ! {apply, {erlang, link, [G1]}},
+ ?line G2 ! {apply, {erlang, port_control, [Port, $h, []]}},
+ ?line G2 ! {apply, {erlang, unlink, [G1]}},
+ ?line expect({trace_ts, G2, in, {?MODULE, general, 0}, ts}),
+ ?line expect({trace, G1, getting_linked, G2}),
+ ?line expect({trace, G1, getting_unlinked, G2}),
+ ?line expect({trace_ts, G2, out, 0, ts}),
+ ?line expect({trace_ts, G2, in, 0, ts}),
+ ?line expect({trace_ts, G2, out, {?MODULE, general, 0}, ts}),
+ ?line expect(),
+ %%
+ ok.
+
+gc(doc) -> "Test sending garbage collection events to a port.";
+gc(Config) when is_list(Config) ->
+ ?line start_tracer(Config),
+ ?line Garber = fun_spawn(fun garber/0, [{min_heap_size, 5000}]),
+ ?line trac(Garber, true, [garbage_collection]),
+ ?line trace_info(Garber, flags),
+
+ ?line trace_info(Garber, flags),
+ Garber ! hi,
+ expect({trace,Garber,gc_start,info}),
+ expect({trace,Garber,gc_end,info}),
+
+ ?line trac(Garber, true, [garbage_collection,timestamp]),
+ Garber ! hi,
+ expect({trace_ts,Garber,gc_start,info,ts}),
+ expect({trace_ts,Garber,gc_end,info,ts}),
+
+ ok.
+
+default_tracer(doc) ->
+ "Test a port as default tracer.";
+default_tracer(Config) when is_list(Config) ->
+ ?line Tracer = start_tracer(Config),
+ ?line TracerMonitor = erlang:monitor(process, Tracer),
+ ?line Port = get(tracer_port),
+ %%
+ ?line N = erlang:trace(all, true, [send, {tracer, Port}]),
+ ?line {flags, [send]} = erlang:trace_info(self(), flags),
+ ?line {tracer, Port} = erlang:trace_info(self(), tracer),
+ ?line {flags, [send]} = erlang:trace_info(new, flags),
+ ?line {tracer, Port} = erlang:trace_info(new, tracer),
+ ?line G1 = fun_spawn(fun general/0),
+ ?line {flags, [send]} = erlang:trace_info(G1, flags),
+ ?line {tracer, Port} = erlang:trace_info(G1, tracer),
+ ?line unlink(Tracer),
+ ?line exit(Port, done),
+ ?line receive
+ {'DOWN', TracerMonitor, process, Tracer, TracerExitReason} ->
+ ?line done = TracerExitReason
+ end,
+ ?line {flags, []} = erlang:trace_info(self(), flags),
+ ?line {tracer, []} = erlang:trace_info(self(), tracer),
+ ?line {flags, []} = erlang:trace_info(new, flags),
+ ?line {tracer, []} = erlang:trace_info(new, tracer),
+ ?line M = erlang:trace(all, false, [all]),
+ ?line {flags, []} = erlang:trace_info(self(), flags),
+ ?line {tracer, []} = erlang:trace_info(self(), tracer),
+ ?line {flags, []} = erlang:trace_info(G1, flags),
+ ?line {tracer, []} = erlang:trace_info(G1, tracer),
+ ?line G1 ! {apply,{erlang,exit,[normal]}},
+ ?line io:format("~p = ~p.~n", [M, N]),
+ ?line M = N,
+ ok.
+
+
+%%% Help functions.
+
+huge_data() ->
+ case os:type() of
+ vxworks -> huge_data(4711);
+ _ -> huge_data(16384)
+ end.
+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].
+
+expect() ->
+ receive
+ Other ->
+ ok = io:format("Unexpected; got ~p", [Other]),
+ test_server:fail({unexpected, Other})
+ after 200 ->
+ ok
+ end.
+
+expect({trace_ts,E1,E2,info,ts}=Message) ->
+ receive
+ {trace_ts,E1,E2,_Info,_Ts}=MessageTs ->
+ ok = io:format("Expected and got ~p", [MessageTs]),
+ MessageTs;
+ Other ->
+ io:format("Expected ~p; got ~p", [Message,Other]),
+ test_server:fail({unexpected,Other})
+ after 5000 ->
+ io:format("Expected ~p; got nothing", [Message]),
+ test_server:fail(no_trace_message)
+ end;
+expect({trace,E1,E2,info}=Message) ->
+ receive
+ {trace,E1,E2,_Info}=MessageTs ->
+ ok = io:format("Expected and got ~p", [MessageTs]),
+ MessageTs;
+ Other ->
+ io:format("Expected ~p; got ~p", [Message,Other]),
+ test_server:fail({unexpected,Other})
+ after 5000 ->
+ io:format("Expected ~p; got nothing", [Message]),
+ test_server:fail(no_trace_message)
+ end;
+expect({trace_ts,E1,E2,E3,ts}=Message) ->
+ receive
+ {trace_ts,E1,E2,E3,_Ts}=MessageTs ->
+ ok = io:format("Expected and got ~p", [MessageTs]),
+ MessageTs;
+ Other ->
+ io:format("Expected ~p; got ~p", [Message,Other]),
+ test_server:fail({unexpected,Other})
+ after 5000 ->
+ io:format("Expected ~p; got nothing", [Message]),
+ test_server:fail(no_trace_message)
+ end;
+expect({trace_ts,E1,E2,E3,E4,ts}=Message) ->
+ receive
+ {trace_ts,E1,E2,E3,E4,_Ts}=MessageTs ->
+ ok = io:format("Expected and got ~p", [MessageTs]),
+ MessageTs;
+ Other ->
+ io:format("Expected ~p; got ~p", [Message,Other]),
+ test_server:fail({unexpected,Other})
+ after 5000 ->
+ io:format("Expected ~p; got nothing", [Message]),
+ test_server:fail(no_trace_message)
+ end;
+expect(Message) ->
+ receive
+ Message ->
+ ok = io:format("Expected and got ~p", [Message]),
+ Message;
+ Other ->
+ io:format("Expected ~p; got ~p", [Message,Other]),
+ test_server:fail({unexpected,Other})
+ after 5000 ->
+ io:format("Expected ~p; got nothing", [Message]),
+ test_server:fail(no_trace_message)
+ end.
+
+trac(What, On, Flags0) ->
+ Flags = [{tracer,get(tracer_port)}|Flags0],
+ get(tracer) ! {apply,self(),{erlang,trace,[What,On,Flags]}},
+ Res = receive
+ {apply_result,Result} -> Result
+ end,
+ ok = io:format("erlang:trace(~p, ~p, ~p) -> ~p",
+ [What,On,Flags,Res]),
+ Res.
+
+trace_info(What, Key) ->
+ get(tracer) ! {apply,self(),{erlang,trace_info,[What,Key]}},
+ Res = receive
+ {apply_result,Result} -> Result
+ end,
+ ok = io:format("erlang:trace_info(~p, ~p) -> ~p",
+ [What,Key,Res]),
+ Res.
+
+trace_func(MFA, MatchProg) ->
+ get(tracer) ! {apply,self(),{erlang,trace_pattern,[MFA,MatchProg]}},
+ Res = receive
+ {apply_result,Result} -> Result
+ end,
+ ok = io:format("erlang:trace_pattern(~p, ~p) -> ~p", [MFA,MatchProg,Res]),
+ Res.
+
+trace_func(MFA, MatchProg, Flags) ->
+ get(tracer) ! {apply,self(),{erlang,trace_pattern,[MFA,MatchProg,Flags]}},
+ Res = receive
+ {apply_result,Result} -> Result
+ end,
+ ok = io:format("erlang:trace_pattern(~p, ~p) -> ~p", [MFA,MatchProg,Res]),
+ Res.
+
+trace_pid(Pid, On, Flags0) ->
+ Flags = [{tracer,get(tracer_port)}|Flags0],
+ get(tracer) ! {apply,self(),{erlang,trace,[Pid,On,Flags]}},
+ Res = receive
+ {apply_result,Result} -> Result
+ end,
+ ok = io:format("erlang:trace(~p, ~p, ~p) -> ~p",
+ [Pid,On,Flags,Res]),
+ Res.
+
+start_tracer(Config) ->
+ Path = ?config(data_dir, Config),
+ ok = load_driver(Path, echo_drv),
+ Self = self(),
+ put(tracer, fun_spawn(fun() -> tracer(Self) end)),
+ receive
+ {started,Port} ->
+ put(tracer_port, Port)
+ end,
+ get(tracer).
+
+load_driver(Dir, Driver) ->
+ case erl_ddll:load_driver(Dir, Driver) of
+ ok -> ok;
+ {error, Error} = Res ->
+ io:format("~s\n", [erl_ddll:format_error(Error)]),
+ Res
+ end.
+
+tracer(RelayTo) ->
+ Port = open_port({spawn,echo_drv}, [eof,binary]),
+ RelayTo ! {started,Port},
+ tracer_loop(RelayTo, Port).
+
+tracer_loop(RelayTo, Port) ->
+ receive
+ {apply,From,{M,F,A}} ->
+ From ! {apply_result,apply(M, F, A)},
+ tracer_loop(RelayTo, Port);
+ {Port,{data,Msg}} ->
+ RelayTo ! binary_to_term(Msg),
+ tracer_loop(RelayTo, Port);
+ Other ->
+ exit({bad_message,Other})
+ end.
+
+fun_spawn(Fun) ->
+ spawn_link(erlang, apply, [Fun,[]]).
+
+fun_spawn(Fun, Opts) ->
+ spawn_opt(erlang, apply, [Fun,[]], [link | Opts]).
+
+% flush() ->
+% receive
+% X ->
+% [X | flush()]
+% after 2000 ->
+% []
+% end.
+
+
+%%% Models for various kinds of processes.
+
+%% Sends messages when ordered to.
+
+sender() ->
+ receive
+ {send_please, To, What} ->
+ To ! What,
+ sender()
+ end.
+
+%% Just consumes messages from its message queue.
+
+receiver() ->
+ receive
+ _Any -> receiver()
+ end.
+
+%% Does a garbage collection when it receives a message.
+
+garber() ->
+ receive
+ _Any ->
+ lists:seq(1, 100),
+ erlang:garbage_collect(),
+ garber()
+ end.
+
+%% All-purpose process
+
+general() ->
+ receive
+ {apply, {M, F, Args}} ->
+ erlang:apply(M, F, Args),
+ general();
+ {send, Dest, Msg} ->
+ Dest ! Msg,
+ general();
+ {call_f_1, Arg} ->
+ f(Arg),
+ general();
+ nop ->
+ general()
+ end.
+
+f(Arg) ->
+ Arg.