aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/test/trace_local_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/test/trace_local_SUITE.erl')
-rw-r--r--erts/emulator/test/trace_local_SUITE.erl1259
1 files changed, 1259 insertions, 0 deletions
diff --git a/erts/emulator/test/trace_local_SUITE.erl b/erts/emulator/test/trace_local_SUITE.erl
new file mode 100644
index 0000000000..24005774ba
--- /dev/null
+++ b/erts/emulator/test/trace_local_SUITE.erl
@@ -0,0 +1,1259 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-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_local_SUITE).
+-compile({nowarn_deprecated_function, {erlang,hash,2}}).
+
+-export([basic_test/0, bit_syntax_test/0, return_test/0,
+ on_and_off_test/0, stack_grow_test/0,
+ info_test/0, delete_test/1, exception_test/1,
+ not_run/1]).
+
+-export([exported/1, exported_wrap/1, loop/4, apply_slave_async/5,
+ match/2, clause/2, id/1, undef/1, lists_reverse/2]).
+
+%%
+%% Define to run outside of test server
+%%
+%% (rotten feature)
+%%
+%%-define(STANDALONE,1).
+
+%%
+%% Define for debug output
+%%
+%%-define(debug,1).
+
+-ifdef(STANDALONE).
+-define(config(A,B),config(A,B)).
+-export([config/2]).
+-define(DEFAULT_RECEIVE_TIMEOUT, 1000).
+-else.
+-include("test_server.hrl").
+-define(DEFAULT_RECEIVE_TIMEOUT, infinity).
+-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/1, basic/1, bit_syntax/1,
+ return/1, on_and_off/1, stack_grow/1,info/1, delete/1,
+ exception/1, exception_apply/1,
+ exception_function/1, exception_apply_function/1,
+ exception_nocatch/1, exception_nocatch_apply/1,
+ exception_nocatch_function/1, exception_nocatch_apply_function/1,
+ exception_meta/1, exception_meta_apply/1,
+ exception_meta_function/1, exception_meta_apply_function/1,
+ exception_meta_nocatch/1, exception_meta_nocatch_apply/1,
+ exception_meta_nocatch_function/1,
+ exception_meta_nocatch_apply_function/1,
+ init_per_testcase/2, fin_per_testcase/2]).
+init_per_testcase(_Case, Config) ->
+ ?line Dog=test_server:timetrap(test_server:minutes(2)),
+ [{watchdog, Dog}|Config].
+
+fin_per_testcase(_Case, Config) ->
+ shutdown(),
+ Dog=?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+all(doc) ->
+ ["Test tracing of local function calls and return traces."];
+all(suite) ->
+ case test_server:is_native(?MODULE) of
+ true -> [not_run];
+ false -> [basic, bit_syntax, return, on_and_off, stack_grow, info, delete,
+ exception, exception_apply,
+ exception_function, exception_apply_function,
+ exception_nocatch, exception_nocatch_apply,
+ exception_nocatch_function,
+ exception_nocatch_apply_function,
+ exception_meta, exception_meta_apply,
+ exception_meta_function, exception_meta_apply_function,
+ exception_meta_nocatch, exception_meta_nocatch_apply,
+ exception_meta_nocatch_function,
+ exception_meta_nocatch_apply_function]
+ end.
+
+not_run(Config) when is_list(Config) ->
+ {skipped,"Native code"}.
+
+basic(doc) ->
+ ["Tests basic local call-trace"];
+basic(Config) when is_list(Config) ->
+ basic_test().
+
+bit_syntax(doc) ->
+ "OTP-7399: Make sure that code that uses the optimized bit syntax matching "
+ "can be traced without crashing the emulator.";
+bit_syntax(Config) when is_list(Config) ->
+ bit_syntax_test().
+
+return(doc) ->
+ ["Tests the different types of return trace"];
+return(Config) when is_list(Config) ->
+ return_test().
+
+on_and_off(doc) ->
+ ["Tests turning trace parameters on and off, "
+ "both for trace and trace_pattern"];
+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().
+
+delete(doc) ->
+ ["Tests putting trace on deleted modules"];
+delete(Config) when is_list(Config) ->
+ delete_test(Config).
+
+exception(doc) ->
+ ["Tests exception_trace"];
+exception(Config) when is_list(Config) ->
+ exception_test([]).
+
+exception_apply(doc) ->
+ ["Tests exception_trace"];
+exception_apply(Config) when is_list(Config) ->
+ exception_test([apply]).
+
+exception_function(doc) ->
+ ["Tests exception_trace"];
+exception_function(Config) when is_list(Config) ->
+ exception_test([function]).
+
+exception_apply_function(doc) ->
+ ["Tests exception_trace"];
+exception_apply_function(Config) when is_list(Config) ->
+ exception_test([apply,function]).
+
+exception_nocatch(doc) ->
+ ["Tests exception_trace"];
+exception_nocatch(Config) when is_list(Config) ->
+ exception_test([nocatch]).
+
+exception_nocatch_apply(doc) ->
+ ["Tests exception_trace"];
+exception_nocatch_apply(Config) when is_list(Config) ->
+ exception_test([nocatch,apply]).
+
+exception_nocatch_function(doc) ->
+ ["Tests exception_trace"];
+exception_nocatch_function(Config) when is_list(Config) ->
+ exception_test([nocatch,function]).
+
+exception_nocatch_apply_function(doc) ->
+ ["Tests exception_trace"];
+exception_nocatch_apply_function(Config) when is_list(Config) ->
+ exception_test([nocatch,apply,function]).
+
+exception_meta(doc) ->
+ ["Tests meta exception_trace"];
+exception_meta(Config) when is_list(Config) ->
+ exception_test([meta]).
+
+exception_meta_apply(doc) ->
+ ["Tests meta exception_trace"];
+exception_meta_apply(Config) when is_list(Config) ->
+ exception_test([meta,apply]).
+
+exception_meta_function(doc) ->
+ ["Tests meta exception_trace"];
+exception_meta_function(Config) when is_list(Config) ->
+ exception_test([meta,function]).
+
+exception_meta_apply_function(doc) ->
+ ["Tests meta exception_trace"];
+exception_meta_apply_function(Config) when is_list(Config) ->
+ exception_test([meta,apply,function]).
+
+exception_meta_nocatch(doc) ->
+ ["Tests meta exception_trace"];
+exception_meta_nocatch(Config) when is_list(Config) ->
+ exception_test([meta,nocatch]).
+
+exception_meta_nocatch_apply(doc) ->
+ ["Tests meta exception_trace"];
+exception_meta_nocatch_apply(Config) when is_list(Config) ->
+ exception_test([meta,nocatch,apply]).
+
+exception_meta_nocatch_function(doc) ->
+ ["Tests meta exception_trace"];
+exception_meta_nocatch_function(Config) when is_list(Config) ->
+ exception_test([meta,nocatch,function]).
+
+exception_meta_nocatch_apply_function(doc) ->
+ ["Tests meta exception_trace"];
+exception_meta_nocatch_apply_function(Config) when is_list(Config) ->
+ exception_test([meta,nocatch,apply,function]).
+
+-endif.
+
+
+
+%%% Message patterns and expect functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-define(pCT(P,M,F,A), {trace, P,call,{M,F,A}}).
+-define(pCTT(P,M,F,A), {trace_ts,P,call,{M,F,A},{_,_,_}}).
+-define(pRF(P,M,F,A,V), {trace, P,return_from,{M,F,A},V}).
+-define(pRFT(P,M,F,A,V),{trace_ts,P,return_from,{M,F,A},V,{_,_,_}}).
+-define(pEF(P,M,F,A,V), {trace, P,exception_from,{M,F,A},V}).
+-define(pEFT(P,M,F,A,V),{trace_ts,P,exception_from,{M,F,A},V,{_,_,_}}).
+-define(pRT(P,M,F,A), {trace, P,return_to,{M,F,A}}).
+-define(pRTT(P,M,F,A), {trace_ts,P,return_to,{M,F,A},{_,_,_}}).
+
+-define(CT(M,F,A), ?pCT(_,M,F,A) = receive_next()).
+-define(CTT(M,F,A), ?pCTT(_,M,F,A) = receive_next()).
+-define(RF(M,F,A,V), ?pRF(_,M,F,A,V) = receive_next()).
+-define(RFT(M,F,A,V), ?pRFT(_,M,F,A,V) = receive_next()).
+-define(EF(M,F,A,V), ?pEF(_,M,F,A,V) = receive_next()).
+-define(EFT(M,F,A,V), ?pEFT(_,M,F,A,V) = receive_next()).
+-define(RT(M,F,A), ?pRT(_,M,F,A) = receive_next()).
+-define(RTT(M,F,A), ?pRTT(_,M,F,A) = receive_next()).
+-define(NM, receive_no_next(100)).
+
+expect() ->
+ {Pid,_} = get(slave),
+ expect_receive(Pid).
+
+expect(Msg) ->
+ {Pid,_} = get(slave),
+ expect_pid(Pid, Msg).
+
+
+
+expect_pid(_Pid, []) ->
+ ok;
+expect_pid(Pid, [Line|T]) when is_integer(Line) ->
+ put(test_server_loc, {?MODULE,Line}),
+ expect_pid(Pid, T);
+expect_pid(Pid, [true|[_|_]=T]) ->
+ expect_pid(Pid, T);
+expect_pid(Pid, [false|[_|T]]) ->
+ expect_pid(Pid, T);
+expect_pid(Pid, [H|T]) ->
+ expect_pid(Pid, H),
+ expect_pid(Pid, T);
+expect_pid(Pid, Msg) when is_tuple(Msg) ->
+ same(Msg, expect_receive(Pid));
+expect_pid(Pid, Fun) when is_function(Fun, 1) ->
+ case Fun(expect_receive(Pid)) of
+ next ->
+ expect_pid(Pid, Fun);
+ done ->
+ ok;
+ Other ->
+ expect_pid(Pid, Other)
+ end.
+
+expect_receive(Pid) when is_pid(Pid) ->
+ receive
+ Msg when is_tuple(Msg),
+ element(1, Msg) == trace,
+ element(2, Msg) =/= Pid;
+ %%
+ is_tuple(Msg),
+ element(1, Msg) == trace_ts,
+ element(2, Msg) =/= Pid ->
+ expect_receive(Pid);
+ Msg ->
+ expect_msg(Pid, Msg)
+ after 100 ->
+ {nm}
+ end.
+
+expect_msg(P, ?pCT(P,M,F,Args)) -> {ct,{M,F},Args};
+expect_msg(P, ?pCTT(P,M,F,Args)) -> {ctt,{M,F},Args};
+expect_msg(P, ?pRF(P,M,F,Arity,V)) -> {rf,{M,F,Arity},V};
+expect_msg(P, ?pRFT(P,M,F,Arity,V)) -> {rft,{M,F,Arity},V};
+expect_msg(P, ?pEF(P,M,F,Arity,V)) -> {ef,{M,F,Arity},V};
+expect_msg(P, ?pEFT(P,M,F,Arity,V)) -> {eft,{M,F,Arity},V};
+expect_msg(P, ?pRT(P,M,F,Arity)) -> {rt,{M,F,Arity}};
+expect_msg(P, ?pRTT(P,M,F,Arity)) -> {rtt,{M,F,Arity}};
+expect_msg(P, Msg) when is_tuple(Msg) ->
+ case tuple_to_list(Msg) of
+ [trace,P|T] ->
+ list_to_tuple([trace|T]);
+ [trace_ts,P|[_|_]=T] ->
+ list_to_tuple([trace_ts|reverse(tl(reverse(T)))]);
+ _ ->
+ Msg
+ end.
+
+same(A, B) ->
+ case [A|B] of
+ [X|X] ->
+ ok
+ end.
+
+
+
+%%% tests %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+basic_test() ->
+ ?line setup([call]),
+ ?line erlang:trace_pattern({?MODULE,'_','_'},[],[local]),
+ ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
+ ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?line ?CT(?MODULE,exported_wrap,[1]),
+ ?line ?CT(?MODULE,exported,[1]),
+ ?line ?CT(?MODULE,local,[1]),
+ ?line ?CT(?MODULE,local2,[1]),
+ ?line ?CT(?MODULE,local_tail,[1]),
+ ?line erlang:trace_pattern({?MODULE,'_','_'},[],[]),
+ ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
+ ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?line ?CT(?MODULE,exported_wrap,[1]),
+ ?line [1,1,1,1] = lambda_slave(fun() ->
+ exported_wrap(1)
+ end),
+ ?line ?NM,
+ ?line erlang:trace_pattern({?MODULE,'_','_'},[],[local]),
+ ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
+ ?line [1,1,1,1] = lambda_slave(fun() ->
+ exported_wrap(1)
+ end),
+ ?line ?CT(?MODULE,_,_), %% The fun
+ ?line ?CT(?MODULE,exported_wrap,[1]),
+ ?line ?CT(?MODULE,exported,[1]),
+ ?line ?CT(?MODULE,local,[1]),
+ ?line ?CT(?MODULE,local2,[1]),
+ ?line ?CT(?MODULE,local_tail,[1]),
+ ?line erlang:trace_pattern({?MODULE,'_','_'},false,[local]),
+ ?line shutdown(),
+ ?line ?NM,
+ ok.
+
+%% OTP-7399.
+bit_syntax_test() ->
+ ?line setup([call]),
+ ?line erlang:trace_pattern({?MODULE,'_','_'},[],[local]),
+ ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
+
+ ?line lambda_slave(fun() ->
+ 6 = bs_sum_a(<<1,2,3>>, 0),
+ 10 = bs_sum_b(0, <<1,2,3,4>>),
+ 26 = bs_sum_c(<<3:4,5:4,7:4,11:4>>, 0)
+ end),
+ ?line ?CT(?MODULE,_,[]), %Ignore call to the fun.
+
+ ?line ?CT(?MODULE,bs_sum_a,[<<1,2,3>>,0]),
+ ?line ?CT(?MODULE,bs_sum_a,[<<2,3>>,1]),
+ ?line ?CT(?MODULE,bs_sum_a,[<<3>>,3]),
+ ?line ?CT(?MODULE,bs_sum_a,[<<>>,6]),
+
+ ?line ?CT(?MODULE,bs_sum_b,[0,<<1,2,3,4>>]),
+ ?line ?CT(?MODULE,bs_sum_b,[1,<<2,3,4>>]),
+ ?line ?CT(?MODULE,bs_sum_b,[3,<<3,4>>]),
+ ?line ?CT(?MODULE,bs_sum_b,[6,<<4>>]),
+ ?line ?CT(?MODULE,bs_sum_b,[10,<<>>]),
+
+ ?line ?CT(?MODULE,bs_sum_c,[<<3:4,5:4,7:4,11:4>>, 0]),
+ ?line ?CT(?MODULE,bs_sum_c,[<<5:4,7:4,11:4>>, 3]),
+ ?line ?CT(?MODULE,bs_sum_c,[<<7:4,11:4>>, 8]),
+ ?line ?CT(?MODULE,bs_sum_c,[<<11:4>>, 15]),
+ ?line ?CT(?MODULE,bs_sum_c,[<<>>, 26]),
+
+ ?line erlang:trace_pattern({?MODULE,'_','_'},false,[local]),
+ ?line shutdown(),
+ ?line ?NM,
+
+ ok.
+
+bs_sum_a(<<H,T/binary>>, Acc) -> bs_sum_a(T, H+Acc);
+bs_sum_a(<<>>, Acc) -> Acc.
+
+bs_sum_b(Acc, <<H,T/binary>>) -> bs_sum_b(H+Acc, T);
+bs_sum_b(Acc, <<>>) -> Acc.
+
+bs_sum_c(<<H:4,T/bits>>, Acc) -> bs_sum_c(T, H+Acc);
+bs_sum_c(<<>>, Acc) -> Acc.
+
+return_test() ->
+ ?line setup([call]),
+ ?line erlang:trace_pattern({?MODULE,'_','_'},[{'_',[],[{return_trace}]}],
+ [local]),
+ ?line erlang:trace_pattern({erlang,hash,'_'},[{'_',[],[{return_trace}]}],
+ [local]),
+ ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
+ ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?line ?CT(?MODULE,exported_wrap,[1]),
+ ?line ?CT(?MODULE,exported,[1]),
+ ?line ?CT(?MODULE,local,[1]),
+ ?line ?CT(?MODULE,local2,[1]),
+ ?line ?CT(?MODULE,local_tail,[1]),
+ ?line ?CT(erlang,hash,[1,1]),
+ ?line ?RF(erlang,hash,2,1),
+ ?line ?RF(?MODULE,local_tail,1,[1,1]),
+ ?line ?RF(?MODULE,local2,1,[1,1]),
+ ?line ?RF(?MODULE,local,1,[1,1,1]),
+ ?line ?RF(?MODULE,exported,1,[1,1,1,1]),
+ ?line ?RF(?MODULE,exported_wrap,1,[1,1,1,1]),
+ ?line shutdown(),
+ ?line setup([call,return_to]),
+ ?line erlang:trace_pattern({?MODULE,'_','_'},[],
+ [local]),
+ ?line erlang:trace_pattern({erlang,hash,'_'},[],
+ [local]),
+ ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
+ ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?line ?CT(?MODULE,exported_wrap,[1]),
+ ?line ?CT(?MODULE,exported,[1]),
+ ?line ?CT(?MODULE,local,[1]),
+ ?line ?CT(?MODULE,local2,[1]),
+ ?line ?CT(?MODULE,local_tail,[1]),
+ ?line ?CT(erlang,hash,[1,1]),
+ ?line ?RT(?MODULE,local_tail,1),
+ ?line ?RT(?MODULE,local,1),
+ ?line ?RT(?MODULE,exported,1),
+ ?line ?RT(?MODULE,slave,2),
+ ?line shutdown(),
+ ?line setup([call,return_to]),
+ ?line erlang:trace_pattern({?MODULE,'_','_'},[{'_',[],[{return_trace}]}],
+ [local]),
+ ?line erlang:trace_pattern({erlang,hash,'_'},[{'_',[],[{return_trace}]}],
+ [local]),
+ ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
+ ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?line ?CT(?MODULE,exported_wrap,[1]),
+ ?line ?CT(?MODULE,exported,[1]),
+ ?line ?CT(?MODULE,local,[1]),
+ ?line ?CT(?MODULE,local2,[1]),
+ ?line ?CT(?MODULE,local_tail,[1]),
+ ?line ?CT(erlang,hash,[1,1]),
+ ?line ?RF(erlang,hash,2,1),
+ ?line ?RT(?MODULE,local_tail,1),
+ ?line ?RF(?MODULE,local_tail,1,[1,1]),
+ ?line ?RF(?MODULE,local2,1,[1,1]),
+ ?line ?RT(?MODULE,local,1),
+ ?line ?RF(?MODULE,local,1,[1,1,1]),
+ ?line ?RT(?MODULE,exported,1),
+ ?line ?RF(?MODULE,exported,1,[1,1,1,1]),
+ ?line ?RF(?MODULE,exported_wrap,1,[1,1,1,1]),
+ ?line ?RT(?MODULE,slave,2),
+ ?line shutdown(),
+ ?line ?NM,
+ ok.
+
+on_and_off_test() ->
+ ?line Pid = setup([call]),
+ ?line 1 = erlang:trace_pattern({?MODULE,local_tail,1},[],[local]),
+ ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
+ ?line LocalTail = fun() ->
+ local_tail(1)
+ end,
+ ?line [1,1] = lambda_slave(LocalTail),
+ ?line ?CT(?MODULE,local_tail,[1]),
+ ?line erlang:trace(Pid,true,[return_to]),
+ ?line [1,1] = lambda_slave(LocalTail),
+ ?line ?CT(?MODULE,local_tail,[1]),
+ ?line ?RT(?MODULE,_,_),
+ ?line 0 = erlang:trace_pattern({?MODULE,local_tail,1},[],[global]),
+ ?line [1,1] = lambda_slave(LocalTail),
+ ?line ?NM,
+ ?line 1 = erlang:trace_pattern({?MODULE,exported_wrap,1},[],[global]),
+ ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?line ?CT(?MODULE,exported_wrap,[1]),
+ ?line 1 = erlang:trace_pattern({?MODULE,exported_wrap,1},[],[local]),
+ ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?line ?CT(?MODULE,exported_wrap,[1]),
+ ?line ?RT(?MODULE,slave,2),
+ ?line 1 = erlang:trace_pattern({erlang,hash,2},[],[local]),
+ ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?line ?CT(?MODULE,exported_wrap,[1]),
+ ?line ?CT(erlang,hash,[1,1]),
+ ?line ?RT(?MODULE,local_tail,1),
+ ?line ?RT(?MODULE,slave,2),
+ ?line erlang:trace(Pid,true,[timestamp]),
+ ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?line ?CTT(?MODULE,exported_wrap,[1]),
+ ?line ?CTT(erlang,hash,[1,1]),
+ ?line ?RTT(?MODULE,local_tail,1),
+ ?line ?RTT(?MODULE,slave,2),
+ ?line erlang:trace(Pid,false,[return_to,timestamp]),
+ ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?line ?CT(?MODULE,exported_wrap,[1]),
+ ?line ?CT(erlang,hash,[1,1]),
+ ?line erlang:trace(Pid,true,[return_to]),
+ ?line 1 = erlang:trace_pattern({erlang,hash,2},[],[]),
+ ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?line ?CT(?MODULE,exported_wrap,[1]),
+ ?line ?CT(erlang,hash,[1,1]),
+ ?line ?RT(?MODULE,slave,2),
+ ?line 1 = erlang:trace_pattern({?MODULE,exported_wrap,1},[],[]),
+ ?line [1,1,1,1] = apply_slave(?MODULE,exported_wrap,[1]),
+ ?line ?CT(?MODULE,exported_wrap,[1]),
+ ?line ?CT(erlang,hash,[1,1]),
+ ?line shutdown(),
+ ?line erlang:trace_pattern({'_','_','_'},false,[local]),
+ ?line N = erlang:trace_pattern({erlang,'_','_'},true,[local]),
+ ?line case erlang:trace_pattern({erlang,'_','_'},false,[local]) of
+ N ->
+ ok;
+ Else ->
+ exit({number_mismatch, {expected, N}, {got, Else}})
+ end,
+ ?line case erlang:trace_pattern({erlang,'_','_'},false,[local]) of
+ N ->
+ ok;
+ Else2 ->
+ exit({number_mismatch, {expected, N}, {got, Else2}})
+ end,
+ ?line M = erlang:trace_pattern({erlang,'_','_'},true,[]),
+ ?line case erlang:trace_pattern({erlang,'_','_'},false,[]) of
+ M ->
+ ok;
+ Else3 ->
+ exit({number_mismatch, {expected, N}, {got, Else3}})
+ end,
+ ?line case erlang:trace_pattern({erlang,'_','_'},false,[]) of
+ M ->
+ ok;
+ Else4 ->
+ exit({number_mismatch, {expected, N}, {got, Else4}})
+ end,
+ ?line ?NM,
+ ok.
+
+
+stack_grow_test() ->
+ ?line setup([call,return_to]),
+ ?line 1 = erlang:trace_pattern({?MODULE,loop,4},
+ [{'_',[],[{return_trace}]}],[local]),
+ ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
+ ?line Num = 1 bsl 15,
+ ?line Fun =
+ fun(_F,0) -> ok;
+ (F,N) ->
+ receive _A ->
+ receive _B ->
+ receive _C ->
+ F(F,N-1)
+ end
+ end
+ end
+ end,
+ ?line apply_slave_async(?MODULE,loop,[{hej,hopp},[a,b,c],4.5,Num]),
+ ?line Fun(Fun,Num + 1),
+ ?line ?NM,
+ ok.
+
+
+info_test() ->
+ ?line Flags1 = lists:sort([call,return_to]),
+ ?line Pid = setup(Flags1),
+ ?line Prog = [{['$1'],[{is_integer,'$1'}],[{message, false}]},
+ {'_',[],[]}],
+ ?line erlang:trace_pattern({?MODULE,exported_wrap,1},Prog,[local]),
+ ?line erlang:trace_pattern({?MODULE,slave,'_'},false,[local]),
+ ?line Self = self(),
+ ?line {flags,L} = erlang:trace_info(Pid,flags),
+ ?line case lists:sort(L) of
+ Flags1 ->
+ ok;
+ Wrong1 ->
+ exit({bad_result, {erlang,trace_info,[Pid,flags]},
+ {expected, Flags1}, {got, Wrong1}})
+ end,
+ ?line {tracer,Tracer} = erlang:trace_info(Pid,tracer),
+ ?line case Tracer of
+ Self ->
+ ok;
+ Wrong2 ->
+ exit({bad_result, {erlang,trace_info,[Pid,tracer]},
+ {expected, Self}, {got, Wrong2}})
+ end,
+ ?line {traced,local} = erlang:trace_info({?MODULE,exported_wrap,1},traced),
+ ?line {match_spec, MS} =
+ erlang:trace_info({?MODULE,exported_wrap,1},match_spec),
+ ?line case MS of
+ Prog ->
+ ok;
+ Wrong3 ->
+ exit({bad_result, {erlang,trace_info,
+ [{?MODULE,exported_wrap,1},
+ match_spec]},
+ {expected, Prog}, {got, Wrong3}})
+ end,
+ ?line erlang:garbage_collect(self()),
+ ?line receive
+ after 1 ->
+ ok
+ end,
+ ?line io:format("~p~n",[MS]),
+ ?line {match_spec,MS2} =
+ erlang:trace_info({?MODULE,exported_wrap,1},match_spec),
+ ?line io:format("~p~n",[MS2]),
+ ?line erlang:trace_pattern({?MODULE,exported_wrap,1},[],[]),
+ ?line {traced,global} =
+ erlang:trace_info({?MODULE,exported_wrap,1},traced),
+ ?line {match_spec,[]} =
+ erlang:trace_info({?MODULE,exported_wrap,1},match_spec),
+ ?line {traced,undefined} =
+ erlang:trace_info({?MODULE,exported_wrap,2},traced),
+ ?line {match_spec,undefined} =
+ erlang:trace_info({?MODULE,exported_wrap,2},match_spec),
+ ?line {traced,false} = erlang:trace_info({?MODULE,exported,1},traced),
+ ?line {match_spec,false} =
+ erlang:trace_info({?MODULE,exported,1},match_spec),
+ ?line shutdown(),
+ ok.
+
+delete_test(Config) ->
+ ?line Priv = ?config(priv_dir, Config),
+ ?line Data = ?config(data_dir, Config),
+ ?line File = filename:join(Data, "trace_local_dummy"),
+ ?line {ok,trace_local_dummy} = c:c(File, [{outdir,Priv}]),
+ ?line code:purge(trace_local_dummy),
+ ?line code:delete(trace_local_dummy),
+ ?line 0 = erlang:trace_pattern({trace_local_dummy,'_','_'},true,[local]),
+ ?line ?NM,
+ ok.
+
+
+
+%%% exception_test %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+exception_test(Opts) ->
+ ?line {ProcFlags,PatFlags} =
+ case proplists:get_bool(meta, Opts) of
+ true -> {[timestamp],[meta]};
+ false -> {[call,return_to,timestamp],[local]}
+ end,
+ ?line case proplists:get_bool(nocatch, Opts) of
+ false ->
+ ?line Exceptions = exceptions(),
+ ?line exception_test_setup(ProcFlags, PatFlags),
+ ?line lists:foreach(
+ fun ({Func,Args}) ->
+ ?line exception_test(Opts, Func, Args)
+ end,
+ Exceptions),
+ ?line shutdown();
+ true ->
+ ?line Exceptions = exceptions(),
+ ?line lists:foreach(
+ fun ({Func,Args}) ->
+ ?line exception_test_setup(
+ [procs|ProcFlags],
+ PatFlags),
+ ?line exception_test(Opts, Func, Args),
+ ?line shutdown()
+ end,
+ Exceptions)
+ end,
+ ?line ok.
+
+exceptions() ->
+ ?line Ref = make_ref(),
+ ?line N = case os:type() of
+ vxworks ->
+ ?line 2000; % Limited memory on themachines, not actually
+ % VxWorks' fault /PaN
+ _ ->
+ ?line 200000
+ end,
+ ?line LiL = seq(1, N-1, N), % Long Improper List
+ ?line LL = seq(1, N, []), % Long List
+ [{{erlang,exit}, [done]},
+ {{erlang,error}, [1.0]},
+ {{erlang,error}, [Ref,[]]},
+ {{erlang,throw}, [4711]},
+ {{erlang,'++'}, [[17],seventeen]},
+ {{erlang,'++'}, [Ref,[125.125]]},
+ {{?MODULE,match}, [ref,Ref]},
+ {{?MODULE,match}, [Ref,Ref]},
+ {{?MODULE,clause}, [ref,Ref]},
+ {{?MODULE,clause}, [Ref,Ref]},
+ {{?MODULE,id}, [4711.0]},
+ {{?MODULE,undef}, [[Ref|Ref]]},
+ {{?MODULE,lists_reverse}, [LiL,[]]},
+ {{?MODULE,lists_reverse}, [LL,[]]}].
+
+exception_test_setup(ProcFlags, PatFlags) ->
+ ?line Pid = setup(ProcFlags),
+ ?line io:format("=== exception_test_setup(~p, ~p): ~p~n",
+ [ProcFlags,PatFlags,Pid]),
+ ?line Mprog = [{'_',[],[{exception_trace}]}],
+ ?line erlang:trace_pattern({?MODULE,'_','_'}, Mprog, PatFlags),
+ ?line erlang:trace_pattern({?MODULE,slave,'_'},false,PatFlags),
+ ?line [1,1,1,1,1] =
+ [erlang:trace_pattern({erlang,F,A}, Mprog, PatFlags)
+ || {F,A} <- [{exit,1},{error,1},{error,2},{throw,1},{'++',2}]],
+ ?line 1 = erlang:trace_pattern({lists,reverse,2}, Mprog, PatFlags),
+ ?line ok.
+
+-record(exc_opts, {nocatch=false, meta=false}).
+
+exception_test(Opts, Func0, Args0) ->
+ ?line io:format("=== exception_test(~p, ~p, ~p)~n",
+ [Opts,Func0,abbr(Args0)]),
+ ?line Apply = proplists:get_bool(apply, Opts),
+ ?line Function = proplists:get_bool(function, Opts),
+ ?line Nocatch = proplists:get_bool(nocatch, Opts),
+ ?line Meta = proplists:get_bool(meta, Opts),
+ ?line ExcOpts = #exc_opts{nocatch=Nocatch,meta=Meta},
+
+ %% Func0 and Args0 are for the innermost call, now we will
+ %% wrap them in wrappers...
+ ?line {Func1,Args1} =
+ case Function of
+ true -> {fun exc/2,[Func0,Args0]};
+ false -> {Func0,Args0}
+ end,
+
+ ?line {Func,Args} =
+ case Apply of
+ true -> {{erlang,apply},[Func1,Args1]};
+ false -> {Func1,Args1}
+ end,
+
+ ?line R1 = exc_slave(ExcOpts, Func, Args),
+ ?line Stack2 = [{?MODULE,exc_top,3},{?MODULE,slave,2}],
+ ?line Stack3 = [{?MODULE,exc,2}|Stack2],
+ ?line Rs =
+ case x_exc_top(ExcOpts, Func, Args) of % Emulation
+ {crash,{Reason,Stack}}=R when is_list(Stack) ->
+ [R,
+ {crash,{Reason,Stack++Stack2}},
+ {crash,{Reason,Stack++Stack3}}];
+ R ->
+ [R]
+ end,
+ ?line exception_validate(R1, Rs),
+ ?line case R1 of
+ {crash,Crash} ->
+ ?line expect({trace_ts,exit,Crash});
+ _ when not Meta ->
+ ?line expect({rtt,{?MODULE,slave,2}});
+ _ ->
+ ok
+ end,
+ ?line expect({nm}).
+
+exception_validate(R1, [R2|Rs]) ->
+ case [R1|R2] of
+ [R|R] ->
+ ok;
+ [{crash,{badarg,[{lists,reverse,[L1a,L1b]}|T]}}|
+ {crash,{badarg,[{lists,reverse,[L2a,L2b]}|T]}}] ->
+ same({crash,{badarg,[{lists,reverse,
+ [lists:reverse(L1b, L1a),[]]}|T]}},
+ {crash,{badarg,[{lists,reverse,
+ [lists:reverse(L2b, L2a),[]]}|T]}});
+ _ when is_list(Rs), Rs =/= [] ->
+ exception_validate(R1, Rs)
+ end.
+
+
+
+%%% Tracee 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)).
+
+max(A, B) when A > B -> A;
+max(_, B) -> B.
+
+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:hash(1,1)].
+
+
+
+%%% exc_slave/3 tracee target functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%
+
+exc_top(ExcOpts, Func, Args) ->
+ case ExcOpts#exc_opts.nocatch of
+ false ->
+ try exc_jump(Func, Args) of
+ Value ->
+ {value,Value}
+ catch
+ Class:Reason ->
+ {Class,Reason}
+ end;
+ true ->
+ {value,exc_jump(Func, Args)}
+ end.
+
+%% x_* functions emulate the non-x_* ones.
+%% x_* functions below x_exc_top
+%% return {value,Value} or {Class,Reason}.
+%% The only possible place for exception
+%% is below exc/2.
+x_exc_top(ExcOpts, Func, Args) ->
+ ?line Rtt = not ExcOpts#exc_opts.meta,
+ ?line expect({ctt,{?MODULE,exc_top},[ExcOpts,Func,Args]}),
+ ?line case x_exc_jump(ExcOpts, Func, Args) of
+ Result when not ExcOpts#exc_opts.nocatch ->
+ ?line expect([Rtt,{rtt,{?MODULE,exc_top,3}},
+ ?LINE,{rft,{?MODULE,exc_top,3},Result}]),
+ ?line Result;
+ {value,_}=Result ->
+
+ ?line expect([Rtt,{rtt,{?MODULE,exc_top,3}},
+ ?LINE,{rft,{?MODULE,exc_top,3},Result}]),
+ ?line Result;
+ {exit,Reason}=CR ->
+ ?line expect({eft,{?MODULE,exc_top,3},CR}),
+ ?line {crash,Reason};
+ {error,Reason}=CR ->
+ ?line expect({eft,{?MODULE,exc_top,3},CR}),
+ ?line {crash,{Reason,x_exc_stacktrace()}};
+ CR ->
+ ?line expect({eft,{?MODULE,exc_top,3},CR}),
+ ?line {crash,CR}
+ end.
+
+exc_jump(Func, Args) ->
+ exc(Func, Args, jump).
+
+x_exc_jump(ExcOpts, Func, Args) ->
+ ?line expect({ctt,{?MODULE,exc_jump},[Func,Args]}),
+ ?line case x_exc(ExcOpts, Func, Args, jump) of
+ {value,Value}=Result ->
+ ?line expect({rft,{?MODULE,exc_jump,2},Value}),
+ ?line Result;
+ CR ->
+ ?line expect({eft,{?MODULE,exc_jump,2},CR}),
+ ?line CR
+ end.
+
+exc(Func, Args, jump) ->
+ exc(Func, Args, do);
+exc(Func, Args, do) ->
+ exc(Func, Args).
+
+x_exc(ExcOpts, Func, Args, jump) ->
+ ?line expect({ctt,{?MODULE,exc},[Func,Args,jump]}),
+ ?line case x_exc(ExcOpts, Func, Args, do) of
+ {value,Value}=Result ->
+ ?line expect({rft,{?MODULE,exc,3},Value}),
+ ?line Result;
+ CR ->
+ ?line expect({eft,{?MODULE,exc,3},CR}),
+ ?line CR
+ end;
+x_exc(ExcOpts, Func, Args, do) ->
+ ?line expect({ctt,{?MODULE,exc},[Func,Args,do]}),
+ ?line case x_exc(ExcOpts, Func, Args) of
+ {value,Value}=Result ->
+ ?line expect({rft,{?MODULE,exc,3},Value}),
+ ?line Result;
+ CR ->
+ ?line expect({eft,{?MODULE,exc,3},CR}),
+ ?line CR
+ end.
+
+exc({erlang,apply}, [{M,F},A]) ->
+ erlang:apply(M, F, id(A));
+exc({erlang,apply}, [F,A]) ->
+ erlang:apply(F, id(A));
+exc({erlang,error}, [E]) ->
+ erlang:error(id(E));
+exc({erlang,error}, [E,S]) ->
+ erlang:error(E, id(S));
+exc({erlang,exit}, [E]) ->
+ erlang:exit(id(E));
+exc({erlang,throw}, [E]) ->
+ erlang:throw(id(E));
+exc({erlang,'++'}, [A,B]) ->
+ erlang:'++'(A, id(B));
+exc({?MODULE,match}, [A,B]) ->
+ match(A, id(B));
+exc({?MODULE,clause}, [A,B]) ->
+ clause(A, id(B));
+exc({?MODULE,id}, [E]) ->
+ id(id(E));
+exc({?MODULE,undef}, [E]) ->
+ undef(id(E));
+exc({?MODULE,lists_reverse}, [A,B]) ->
+ lists_reverse(A, id(B));
+exc(Func, [A,B]) when is_function(Func, 2) ->
+ Func(A, id(B)).
+
+x_exc(ExcOpts, {erlang,apply}=Func0, [{_,_}=Func,Args]=Args0) ->
+ ?line expect({ctt,{?MODULE,exc},[Func0,Args0]}),
+ ?line x_exc_body(ExcOpts, Func, Args, true);
+x_exc(ExcOpts, {erlang,apply}=Func0, [Func,Args]=Args0)
+ when is_function(Func, 2)->
+ ?line expect({ctt,{?MODULE,exc},[Func0,Args0]}),
+ ?line x_exc_func(ExcOpts, Func, Args, Args);
+x_exc(ExcOpts, {_,_}=Func, Args) ->
+ ?line expect({ctt,{?MODULE,exc},[Func,Args]}),
+ ?line x_exc_body(ExcOpts, Func, Args, false);
+x_exc(ExcOpts, Func0, [_,Args]=Args0)
+ when is_function(Func0, 2) ->
+ ?line expect({ctt,{?MODULE,exc},[Func0,Args0]}),
+ ?line x_exc_func(ExcOpts, Func0, Args0, Args).
+
+x_exc_func(ExcOpts, Func, [Func1,Args1]=Args, Id) ->
+ %% Assumes the called fun =:= fun exc/2,
+ %% will utterly fail otherwise.
+ ?line Rtt = not ExcOpts#exc_opts.meta,
+ ?line {module,M} = erlang:fun_info(Func, module),
+ ?line {name,F} = erlang:fun_info(Func, name),
+ ?line expect([{ctt,{?MODULE,id},[Id]},
+ ?LINE,{rft,{?MODULE,id,1},Id},
+ ?LINE,Rtt,{rtt,{?MODULE,exc,2}},
+ ?LINE,{ctt,{M,F},Args}]),
+ ?line case x_exc(ExcOpts, Func1, Args1) of
+ {value,Value}=Result ->
+ ?line expect([{rft,{M,F,2},Value},
+ ?LINE,{rft,{?MODULE,exc,2},Value}]),
+ ?line Result;
+ CR ->
+ ?line expect([{eft,{M,F,2},CR},
+ ?LINE,{eft,{?MODULE,exc,2},CR}]),
+ ?line CR
+ end.
+
+x_exc_body(ExcOpts, {M,F}=Func, Args, Apply) ->
+ ?line Nocatch = ExcOpts#exc_opts.nocatch,
+ ?line Rtt = not ExcOpts#exc_opts.meta,
+ ?line Id = case Apply of
+ true -> Args;
+ false -> lists:last(Args)
+ end,
+ ?line expect([{ctt,{?MODULE,id},[Id]},
+ ?LINE,{rft,{?MODULE,id,1},Id},
+ ?LINE,Rtt,{rtt,{?MODULE,exc,2}},
+ ?LINE,{ctt,{M,F},Args}]),
+ ?line Arity = length(Args),
+ ?line try exc(Func, Args) of
+ Value ->
+ ?line x_exc_value(Rtt, M, F, Args, Arity, Value),
+ ?line case expect() of
+ {rtt,{M,F,Arity}} when Rtt, Apply ->
+ %% We may get the above when
+ %% applying a BIF.
+ ?line expect({rft,{?MODULE,exc,2},Value});
+ {rtt,{?MODULE,exc,2}} when Rtt, not Apply ->
+ %% We may get the above when
+ %% calling a BIF.
+ ?line expect({rft,{?MODULE,exc,2},Value});
+ {rft,{?MODULE,exc,2},Value} ->
+ ?line ok
+ end,
+ ?line {value,Value}
+ catch
+ Thrown when Nocatch ->
+ ?line CR = {error,{nocatch,Thrown}},
+ ?line x_exc_exception(Rtt, M, F, Args, Arity, CR),
+ ?line expect({eft,{?MODULE,exc,2},CR}),
+ ?line CR;
+ Class:Reason ->
+ ?line CR = {Class,Reason},
+ ?line x_exc_exception(Rtt, M, F, Args, Arity, CR),
+ ?line expect({eft,{?MODULE,exc,2},CR}),
+ ?line CR
+ end.
+
+x_exc_value(Rtt, ?MODULE, lists_reverse, [La,Lb], 2, R) ->
+ ?line L = lists:reverse(Lb, La),
+ ?line expect([fun ({ctt,{lists,reverse},[L1,L2]}) ->
+ ?line same(L, lists:reverse(L2, L1)),
+ ?line next;
+ (Msg) ->
+ ?line same({rft,{lists,reverse,2},R}, Msg),
+ ?line same(R, lists:reverse(L, [])),
+ ?line done
+ end,
+ ?LINE,Rtt,{rtt,{?MODULE,lists_reverse,2}},
+ ?LINE,{rft,{?MODULE,lists_reverse,2},R}]);
+x_exc_value(_Rtt, M, F, _, Arity, Value) ->
+ ?line expect({rft,{M,F,Arity},Value}).
+
+x_exc_exception(_Rtt, ?MODULE, lists_reverse, [La,Lb], 2, CR) ->
+ ?line L = lists:reverse(Lb, La),
+ ?line expect([fun ({ctt,{lists,reverse},[L1,L2]}) ->
+ ?line same(L, lists:reverse(L2, L1)),
+ ?line next;
+ (Msg) ->
+ ?line same({eft,{lists,reverse,2},CR}, Msg),
+ ?line done
+ end,
+ ?LINE,{eft,{?MODULE,lists_reverse,2},CR}]);
+x_exc_exception(Rtt, ?MODULE, undef, [_], 1, {Class,Reason}=CR) ->
+ ?line expect([{ctt,{erlang,Class},[Reason]},
+ ?LINE,{eft,{erlang,Class,1},CR},
+ ?LINE,Rtt,{rtt,{error_handler,crash,1}},
+ ?LINE,{eft,{?MODULE,undef,1},CR}]);
+x_exc_exception(_Rtt, M, F, _, Arity, CR) ->
+ ?line expect({eft,{M,F,Arity},CR}).
+
+x_exc_stacktrace() ->
+ x_exc_stacktrace(erlang:get_stacktrace()).
+%% Truncate stacktrace to below exc/2
+x_exc_stacktrace([{?MODULE,x_exc,4}|_]) -> [];
+x_exc_stacktrace([{?MODULE,x_exc_func,4}|_]) -> [];
+x_exc_stacktrace([{?MODULE,x_exc_body,4}|_]) -> [];
+x_exc_stacktrace([{?MODULE,exc,2}|_]) -> [];
+x_exc_stacktrace([H|T]) ->
+ [H|x_exc_stacktrace(T)].
+
+
+
+match(A, B) ->
+ A = B.
+
+clause(A, A) ->
+ A.
+
+id(Id) ->
+ Id.
+
+undef(X) ->
+ ?MODULE:undef(X, X). % undef
+
+lists_reverse(A, B) ->
+ lists:reverse(A, B).
+
+
+
+%%% Tracee (slave) handling %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%
+
+slave(Dest, Sync) ->
+ Dest ! Sync,
+ receive
+ {From,Tag,{apply,M,F,A}} when is_pid(From) ->
+ ?line ?dbgformat("Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]),
+ ?line Res = apply(M,F,A),
+ ?line ?dbgformat("done Apply: ~p:~p/~p (~p)~n",[M,F,length(A),A]),
+ From ! {Tag,Res},
+ slave(From, Tag);
+ {From,Tag,{lambda,Fun}} when is_pid(From) ->
+ Res = Fun(),
+ From ! {Tag,Res},
+ slave(From, Tag);
+ {From,Tag,{exc_top,Catch,Func,Args}} when is_pid(From) ->
+ ?line ?dbgformat("Exc: ~p ~p~p ~n",[Catch,Func,Args]),
+ ?line Res = exc_top(Catch, Func, Args),
+ ?line ?dbgformat("done Exc: ~p ~p~p ~n",[Catch,Func,Args]),
+ From ! {Tag,Res},
+ slave(From,Tag);
+ die ->
+ exit(normal)
+ end.
+
+setup(ProcFlags) ->
+ trace_off(),
+ flush(100),
+ Self = self(),
+ Sync = make_ref(),
+ Pid = spawn(fun () -> slave(Self, Sync) end),
+ Mref = erlang:monitor(process, Pid),
+ receive
+ Sync ->
+ put(slave, {Pid,Mref}),
+ case ProcFlags of
+ [] -> ok;
+ _ ->
+ erlang:trace(Pid, true, ProcFlags)
+ end,
+ Pid
+ end.
+
+shutdown() ->
+ trace_off(),
+ {Pid,Mref} = get(slave),
+ try erlang:is_process_alive(Pid) of
+ true ->
+ Pid ! die,
+ receive
+ {'DOWN',Mref,process,Pid,Reason} ->
+ Reason
+ end;
+ _ ->
+ not_alive
+ catch _:_ ->
+ undefined
+ end.
+
+trace_off() ->
+ erlang:trace_pattern({'_','_','_'},false,[]),
+ erlang:trace_pattern({'_','_','_'},false,[local]),
+ erlang:trace_pattern({'_','_','_'},false,[meta]),
+ erlang:trace(all, false, [all]).
+
+
+apply_slave_async(M,F,A) ->
+ {Pid,Mref} = get(slave),
+ spawn(?MODULE,apply_slave_async,[M,F,A,Pid,Mref]),
+ Pid.
+
+apply_slave_async(M,F,A,Pid,Mref) ->
+ Tag = make_ref(),
+ Pid ! {self(),Tag,{apply,M,F,A}},
+ result(Tag, Mref).
+
+apply_slave(M,F,A) ->
+ request({apply,M,F,A}).
+
+lambda_slave(Fun) ->
+ request({lambda,Fun}).
+
+exc_slave(Opts, Func, Args) ->
+ try request({exc_top,Opts,Func,Args})
+ catch
+ Reason ->
+ {crash,Reason}
+ end.
+
+request(Request) ->
+ Tag = make_ref(),
+ {Pid,Mref} = get(slave),
+ Pid ! {self(),Tag,Request},
+ result(Tag, Mref).
+
+result(Tag, Mref) ->
+ receive
+ {Tag,Result} ->
+ receive
+ Tag ->
+ Result
+ end;
+ {'DOWN',Mref,process,_Pid,Reason} ->
+ throw(Reason)
+ end.
+
+
+
+%%% Some receive helpers %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%
+
+receive_next() ->
+ receive_next(?DEFAULT_RECEIVE_TIMEOUT).
+
+receive_next(TO) ->
+ receive
+ M ->
+ M
+ after TO ->
+ ?t:fail(timeout)
+ end.
+
+receive_no_next(TO) ->
+ receive M ->
+ ?t:fail({unexpected_message,[M|flush(TO)]})
+ after TO ->
+ ok
+ end.
+
+flush(T) ->
+ receive
+ M ->
+ [M|flush(T)]
+ after T ->
+ []
+ end.
+
+
+
+%%% Helpers %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%
+
+%% Do not build garbage
+%%
+seq(M, N, R) when M =< N ->
+ seq(M, N-1, [N|R]);
+seq(_, _, R) -> R.
+
+%% Do not call traced lists:reverse
+reverse(L) ->
+ reverse(L, []).
+%%
+reverse([], R) -> R;
+reverse([H|T], R) ->
+ reverse(T, [H|R]).
+
+%% Abbreviate large complex terms to avoid croaking printout
+%%
+abbr(Term) ->
+ abbr(Term, 20).
+%%
+abbr(Tuple, N) when is_tuple(Tuple) ->
+ list_to_tuple(abbr_tuple(Tuple, N, 1));
+abbr(List, N) when is_list(List) ->
+ abbr_list(List, N, []);
+abbr(Term, _) -> Term.
+%%
+abbr_tuple(Tuple, N, J) when J =< size(Tuple) ->
+ if J > N; N =< 0 ->
+ ['...'];
+ true ->
+ [abbr(element(J, Tuple), N-1)|abbr_tuple(Tuple, J+1, N)]
+ end;
+abbr_tuple(_, _, _) ->
+ [].
+%%
+abbr_list(_, 0, R) ->
+ case io_lib:printable_list(R) of
+ true ->
+ reverse(R, "...");
+ false ->
+ reverse(R, '...')
+ end;
+abbr_list([H|T], N, R) ->
+ M = N-1,
+ abbr_list(T, M, [abbr(H, M)|R]);
+abbr_list(T, _, R) ->
+ reverse(R, T).