%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2010-2011. 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(dbg_SUITE). %% Test functions -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, big/1, tiny/1, simple/1, message/1, distributed/1, ip_port/1, file_port/1, file_port2/1, file_port_schedfix/1, ip_port_busy/1, wrap_port/1, wrap_port_time/1, with_seq_trace/1, dead_suspend/1, local_trace/1, saved_patterns/1]). -export([init_per_testcase/2, end_per_testcase/2]). -export([tracee1/1, tracee2/1]). -export([dummy/0, exported/1]). -include_lib("test_server/include/test_server.hrl"). -define(default_timeout, ?t:minutes(1)). init_per_testcase(_Case, Config) -> ?line Dog=test_server:timetrap(?default_timeout), [{watchdog, Dog}|Config]. end_per_testcase(_Case, Config) -> Dog=?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [big, tiny, simple, message, distributed, ip_port, file_port, file_port2, file_port_schedfix, ip_port_busy, wrap_port, wrap_port_time, with_seq_trace, dead_suspend, local_trace, saved_patterns]. groups() -> []. init_per_suite(Config) -> Config. end_per_suite(_Config) -> ok. init_per_group(_GroupName, Config) -> Config. end_per_group(_GroupName, Config) -> Config. big(suite) -> []; big(doc) -> ["Rudimentary interface test"]; big(Config) when is_list(Config) -> ?line {ok,OldCurDir} = file:get_cwd(), Datadir=?config(data_dir, Config), Privdir=?config(priv_dir, Config), ?line ok=file:set_cwd(Privdir), try %% make sure dbg is stopped (and returns correctly) ?line ok = dbg:stop(), %% compile test module and make sure it is loaded. ?line {ok,Mod} = compile:file(Datadir++"/dbg_test",[trace]), ?line code:purge(dbg_test), ?line {module, Mod}=code:load_file(dbg_test), %% run/debug a named test function. ?line Pid = spawn_link(dbg_test, loop, [Config]), ?line true = register(dbg_test_loop, Pid), ?line {ok,_} = dbg:tracer(), ?line {ok,[{matched, _node, 1}]} = dbg:p(dbg_test_loop, [m,p,c]), ?line ok = dbg:c(dbg_test, test, [Config]), ?line ok = dbg:i(), ?line dbg_test_loop ! {dbg_test, stop}, unregister(dbg_test_loop), ?line ok = dbg:stop(), %% run/debug a Pid. ?line Pid2=spawn_link(dbg_test,loop,[Config]), ?line {ok,_} = dbg:tracer(), ?line {ok,[{matched, _node, 1}]} = dbg:p(Pid2,[s,r,p]), ?line ok = dbg:c(dbg_test, test, [Config]), ?line ok = dbg:i(), ?line Pid2 ! {dbg_test, stop}, ?line ok=file:set_cwd(OldCurDir) after ?line dbg:stop() end, ok. tiny(suite) -> []; tiny(doc) -> ["Rudimentary interface test"]; tiny(Config) when is_list(Config) -> ?line {ok,OldCurDir} = file:get_cwd(), Datadir=?config(data_dir, Config), Privdir=?config(priv_dir, Config), ?line ok=file:set_cwd(Privdir), try %% compile test module and make sure it is loaded. ?line {ok, Mod} = compile:file(Datadir++"/dbg_test",[trace]), ?line code:purge(dbg_test), ?line {module, Mod}=code:load_file(dbg_test), ?line Pid=spawn_link(dbg_test,loop,[Config]), if is_pid(Pid) -> ?line dbg:tracer(), ?line {ok,[{matched, _node, 1}]} = dbg:p(Pid,[s,r,m,p,c]), ?line ok = dbg:c(dbg_test,test,[Config]), ?line ok = dbg:i(), ?line Pid ! {dbg_test, stop}; true -> ?line ok=file:set_cwd(OldCurDir), ?t:fail("Could not spawn external test process.~n"), failure end after ?line ok = dbg:stop(), ?line ok = file:set_cwd(OldCurDir) end, ok. simple(suite) -> []; simple(doc) -> ["Simple interface test with own handler"]; simple(Config) when is_list(Config) -> try ?line start(), ?line dbg:p(self(),call), ?line dbg:tp(dbg,ltp,[]), ?line dbg:ltp(), ?line stop(), ?line S = self(), ?line [{trace,S,call,{dbg,ltp,[]}}] = flush() after ?line dbg:stop() end, ok. message(suite) -> []; message(doc) -> ["Simple interface test with pam code that appends a message"]; message(Config) when is_list(Config) -> ?line {ok, _} = start(), try ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call), ?line {ok, X} = dbg:tp(dbg,ltp,[{'_',[],[{message, {self}}]}]), ?line {value, {saved, Saved}} = lists:keysearch(saved, 1, X), ?line {ok, Y} = dbg:tp(dbg,ln,Saved), ?line {value, {saved, Saved}} = lists:keysearch(saved, 1, Y), ?line ok = dbg:ltp(), ?line ok = dbg:ln() after ?line stop() end, ?line S = self(), ?line [{trace,S,call,{dbg,ltp,[]},S}, {trace,S,call,{dbg,ln,[]},S}] = flush(), ok. distributed(suite) -> []; distributed(doc) -> ["Simple test of distributed tracing"]; distributed(Config) when is_list(Config) -> ?line {ok, _} = start(), ?line Node = start_slave(), try ?line RexPid = rpc:call(Node, erlang, whereis, [rex]), ?line RexPidList = pid_to_list(RexPid), ?line {ok, Node} = dbg:n(Node), ?line {ok, X} = dbg:p(all,call), ?line {value, {matched, Node, _}} = lists:keysearch(Node, 2, X), ?line {ok, Y} = dbg:p(RexPidList, s), ?line {value, {matched, Node, 1}} = lists:keysearch(Node, 2, Y), ?line {ok, Z} = dbg:tp(dbg,ltp,[]), ?line {value, {matched, Node, 1}} = lists:keysearch(Node, 2, Z), ?line dbg:cn(Node), ?line dbg:tp(dbg,ln,[]), ?line ok = rpc:call(Node, dbg, ltp, []), ?line ok = rpc:call(Node, dbg, ln, []), ?line ok = dbg:ln(), ?line S = self(), ?line {TraceSend, TraceCall} = lists:partition(fun ({trace,RP,send,_,_}) when RP =:= RexPid -> true; (_) -> false end, flush()), ?line [_|_] = TraceSend, ?line [{trace,Pid,call,{dbg,ltp,[]}}, {trace,S,call,{dbg,ln,[]}}] = TraceCall, ?line Node = node(Pid), %% ?line stop() after ?line stop_slave(Node), ?line stop() end, ok. local_trace(suite) -> []; local_trace(doc) -> ["Tests tracing of local function calls."]; local_trace(Config) when is_list(Config) -> ?line {ok, _} = start(), try ?line S = self(), ?line %% Split "<X.Y.Z>" into {X, Y, Z} ?line "<"++L1 = L = pid_to_list(S), ?line NoDot = fun ($.) -> false; (_) -> true end, ?line {LX,"."++L2} = lists:splitwith(NoDot, L1), ?line {LY,"."++L3} = lists:splitwith(NoDot, L2), ?line ">"++L4 = lists:reverse(L3), ?line LZ = lists:reverse(L4), ?line X = 0 = list_to_integer(LX), ?line Y = list_to_integer(LY), ?line Z = list_to_integer(LZ), ?line XYZ = {X, Y, Z}, ?line io:format("Self = ~w = ~w~n", [S,XYZ]), ?line {ok, [{matched, _node, 1}]} = dbg:p(S,call), ?line {ok, [{matched, _node, 1}]} = dbg:p(XYZ,call), if Z =:= 0 -> ?line {ok, [{matched, _node, 1}]} = dbg:p(Y,call); true -> ok end, ?line {ok, [{matched, _node, 1}]} = dbg:p(L,call), ?line {ok, _} = dbg:tpl(?MODULE,not_exported,[]), ?line 4 = not_exported(2), ?line [{trace,S,call,{?MODULE,not_exported,[2]}}] = flush(), ?line {ok, _} = dbg:tp(?MODULE,exported,[]), ?line 4 = ?MODULE:exported(2), ?line [{trace,S,call,{?MODULE,exported,[2]}}, {trace,S,call,{?MODULE,not_exported,[2]}}] = flush(), ?line {ok, _} = dbg:ctpl(?MODULE), ?line 4 = ?MODULE:exported(2), ?line [{trace,S,call,{?MODULE,exported,[2]}}] = flush(), ?line {ok, _} = dbg:tpl(?MODULE,not_exported,[]), ?line {ok, _} = dbg:ctp(?MODULE), ?line 4 = ?MODULE:exported(2), ?line [] = flush(), ?line {ok, _} = dbg:tpl(?MODULE,not_exported,x), ?line catch ?MODULE:exported(x), ?line [{trace,S,call,{dbg_SUITE,not_exported,[x]}}, {trace,S,exception_from, {dbg_SUITE,not_exported,1}, {error,badarith}}] = flush() after ?line stop() end, ok. saved_patterns(suite) -> []; saved_patterns(doc) -> ["Tests saving of match_spec's."]; saved_patterns(Config) when is_list(Config) -> ?line dbg:stop(), ?line {ok,[{saved,1}]} = dbg:tp(dbg,ctp,1,[{'_',[],[{message, blahonga}]}]), ?line {ok,[{saved,2}]} = dbg:tp(dbg,ctp,1,[{['_'],[],[{message, blahonga}]}]), ?line Privdir=?config(priv_dir, Config), ?line file:make_dir(Privdir), ?line File = filename:join([Privdir, "blahonga.ms"]), ?line dbg:wtp(File), ?line dbg:stop(), ?line dbg:ctp('_','_','_'), ?line {ok, _} = start(), try ?line dbg:rtp(File), ?line {ok,[{matched,_node,1},{saved,1}]} = dbg:tp(dbg,ltp,0,1), ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call), ?line dbg:ltp(), ?line S = self(), ?line [{trace,S,call,{dbg,ltp,[]},blahonga}] = flush() after ?line stop() end, ok. not_exported(N) -> N * 2. exported(N) -> not_exported(N). ip_port(suite) -> []; ip_port(doc) -> ["Test tracing to IP port"]; ip_port(Config) when is_list(Config) -> ?line stop(), ?line Port = dbg:trace_port(ip, 0), ?line {ok, _} = dbg:tracer(port, Port), try ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call), ?line {ok, X} = dbg:tp(dbg, ltp,[{'_',[],[{message, {self}}]}]), ?line {value, {saved, _Saved}} = lists:keysearch(saved, 1, X), ?line {ok, Y} = dbg:tp(dbg, ln, [{'_',[],[{message, hej}]}]), ?line {value, {saved, _}} = lists:keysearch(saved, 1, Y), ?line ok = dbg:ltp(), ?line ok = dbg:ln(), ?line {ok, IpPort} = dbg:trace_port_control(get_listen_port), ?line io:format("IpPort = ~p~n", [IpPort]), ?line dbg:trace_client(ip, IpPort, {fun myhandler/2, self()}), ?line S = self(), ?line [{trace,S,call,{dbg,ltp,[]},S}, {trace,S,call,{dbg,ln,[]},hej}] = flush() after ?line stop() end, ok. ip_port_busy(suite) -> []; ip_port_busy(doc) -> ["Test that the dbg server does not hang if the tracer don't start ", "(OTP-3592)"]; ip_port_busy(Config) when is_list(Config) -> ?line stop(), ?line Tracer = dbg:trace_port(ip, 4745), ?line Port = Tracer(), ?line {error, Reason} = dbg:tracer(port, Tracer), try ?line io:format("Error reason = ~p~n", [Reason]), ?line true = port_close(Port) after ?line dbg:stop() end, ?line ok. file_port(suite) -> []; file_port(doc) -> ["Test tracing to file port (simple)"]; file_port(Config) when is_list(Config) -> ?line stop(), ?line {A,B,C} = erlang:now(), ?line FTMP = atom_to_list(?MODULE) ++ integer_to_list(A) ++ "-" ++ integer_to_list(B) ++ "-" ++ integer_to_list(C), ?line FName = filename:join([?config(data_dir, Config), FTMP]), ?line Port = dbg:trace_port(file, FName), ?line {ok, _} = dbg:tracer(port, Port), try ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call), ?line {ok, X} = dbg:tp(dbg, ltp,[{'_',[],[{message, {self}}]}]), ?line {value, {saved, _Saved}} = lists:keysearch(saved, 1, X), ?line {ok, Y} = dbg:tp(dbg, ln, [{'_',[],[{message, hej}]}]), ?line {value, {saved, _}} = lists:keysearch(saved, 1, Y), ?line ok = dbg:ltp(), ?line ok = dbg:ln(), ?line stop(), ?line dbg:trace_client(file, FName, {fun myhandler/2, self()}), ?line S = self(), ?line [{trace,S,call,{dbg,ltp,[]},S}, {trace,S,call,{dbg,ln,[]},hej}, end_of_trace] = flush() after ?line stop(), ?line file:delete(FName) end, ok. file_port2(suite) -> []; file_port2(doc) -> ["Test tracing to file port with 'follow_file'"]; file_port2(Config) when is_list(Config) -> case os:type() of vxworks -> {skipped, "VxWorks NFS cache ruins it all."}; _ -> ?line stop(), ?line {A,B,C} = erlang:now(), ?line FTMP = atom_to_list(?MODULE) ++ integer_to_list(A) ++ "-" ++ integer_to_list(B) ++ "-" ++ integer_to_list(C), ?line FName = filename:join([?config(data_dir, Config), FTMP]), %% Ok, lets try with flush and follow_file, not a chance on VxWorks %% with NFS caching... ?line Port2 = dbg:trace_port(file, FName), ?line {ok, _} = dbg:tracer(port, Port2), try ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call), ?line {ok, _} = dbg:tp(dbg, ltp,[{'_',[],[{message, {self}}]}]), ?line {ok, _} = dbg:tp(dbg, ln, [{'_',[],[{message, hej}]}]), ?line ok = dbg:ltp(), ?line ok = dbg:flush_trace_port(), ?line dbg:trace_client(follow_file, FName, {fun myhandler/2, self()}), ?line S = self(), ?line [{trace,S,call,{dbg,ltp,[]},S}] = flush(), ?line ok = dbg:ln(), ?line ok = dbg:flush_trace_port(), ?line receive after 1000 -> ok end, %% Polls every second... ?line [{trace,S,call,{dbg,ln,[]},hej}] = flush(), ?line stop(), ?line [] = flush() after ?line stop(), ?line file:delete(FName) end, ok end. file_port_schedfix(suite) -> []; file_port_schedfix(doc) -> ["Test that the scheduling timestamp fix for trace flag 'running' works."]; file_port_schedfix(Config) when is_list(Config) -> ?line case (catch erlang:system_info(smp_support)) of true -> {skip, "No schedule fix on SMP"}; _ -> try file_port_schedfix1(Config) after dbg:stop() end end. file_port_schedfix1(Config) when is_list(Config) -> ?line stop(), ?line {A,B,C} = erlang:now(), ?line FTMP = atom_to_list(?MODULE) ++ integer_to_list(A) ++ "-" ++ integer_to_list(B) ++ "-" ++ integer_to_list(C), ?line FName = filename:join([?config(data_dir, Config), FTMP]), %% ?line Port = dbg:trace_port(file, {FName, wrap, ".wraplog", 8*1024, 4}), ?line {ok, _} = dbg:tracer(port, Port), ?line {ok,[{matched,_node,0}]} = dbg:p(new,[running,procs,send,timestamp]), %% %% Generate the trace data %% %% This starts 3 processes that sends a message to each other in a ring, %% 4 laps. Prior to sending the message to the next in the ring, each %% process send 8 messages to itself, just to generate some trace data, %% and to lower the possibility that the trace log wraps just after %% a schedule out message (which would not burden any process and hence %% not show up in the result) %% %% The wrap file trace is used because it burns a lot of time when the %% driver swaps files, a lot more than the regular file trace. The test %% case is dimensioned so that the log fills two files and just starts %% on the third (out of four wrap files). This gives two file swaps, %% and there are three processes, so one process will NOT be burdened. %% The criterion for trace success is then that the max process %% execution time must not be more than twice the min process %% execution time. Wallclock. A normal result is about 10 times more %% without schedule in - schedule out compensation (OTP-3938). %% ?line ok = token_volleyball(3, 4, 8), %% ?line {ok,[{matched,_,_}]} = dbg:p(all, [clear]), ?line stop(), % Some debug code to run on all platforms, for finding the fault on genny % Dont touch please /PaN ?line io:format("Trace dump by PaN BEGIN~n"), ?line dbg:trace_client(file,{FName, wrap, ".wraplog"},{fun(end_of_trace,Pid)-> Pid ! done; (Mesg,Pid) -> io:format("~w~n",[Mesg]),Pid end,self()}), receive done -> ok end, ?line io:format("Trace dump by PaN END~n"), %% %% Get the trace result %% ?line Tag = make_ref(), ?line dbg:trace_client(file, {FName, wrap, ".wraplog"}, {fun schedstat_handler/2, {self(), Tag, []}}), ?line Result = receive {Tag, D} -> lists:map( fun({Pid, {A1, B1, C1}}) -> {Pid, C1/1000000 + B1 + A1*1000000} end, D) end, ?line ok = io:format("Result=~p", [Result]), % erlang:display({?MODULE, ?LINE, Result}), %% %% Analyze the result %% ?line {Min, Max} = lists:foldl( fun({_Pid, M}, {Mi, Ma}) -> {if M < Mi -> M; true -> Mi end, if M > Ma -> M; true -> Ma end} end, {void, 0}, Result), % More PaN debug ?line io:format("Min = ~f, Max = ~f~n",[Min,Max]), %% %% Cleanup %% ?line ToBeDeleted = filelib:wildcard(FName++"*"++".wraplog"), ?line lists:map({file, delete}, ToBeDeleted), % io:format("ToBeDeleted=~p", [ToBeDeleted]), %% %% Present the result %% P = (Max / Min - 1) * 100, BottomLine = lists:flatten(io_lib:format("~.2f %", [P])), if P > 100 -> Reason = {BottomLine, '>', "100%"}, erlang:display({file_port_schedfix, fail, Reason}), test_server:fail(Reason); true -> {comment, BottomLine} end. wrap_port(suite) -> []; wrap_port(doc) -> ["Test tracing to wrapping file port"]; wrap_port(Config) when is_list(Config) -> ?line Self = self(), ?line stop(), ?line {A,B,C} = erlang:now(), ?line FTMP = atom_to_list(?MODULE) ++ integer_to_list(A) ++ "-" ++ integer_to_list(B) ++ "-" ++ integer_to_list(C) ++ "-", ?line FName = filename:join([?config(data_dir, Config), FTMP]), ?line FNameWildcard = FName++"*"++".trace", %% WrapSize=0 and WrapCnt=11 will force the trace to wrap after %% every trace message, and to contain only the last 10 entries %% after trace stop since the last file will be empty waiting %% for its first trace message. ?line WrapSize = 0, ?line WrapCnt = 11, ?line WrapFilesSpec = {FName, wrap, ".trace", WrapSize, WrapCnt}, ?line wrap_port_init(WrapFilesSpec), %% The number of iterations, N, is tested to place wrap the log, %% giving a gap in the filename sequence at index 3. %% This should be a difficult case for %% the trace_client file sorting functionality. N = 7, ?line lists:foreach( fun(Cnt) -> ?MODULE:tracee1(Cnt), ?MODULE:tracee2(Cnt) end, lists:seq(1, N)), ?line stop(), try ?line Files1 = filelib:wildcard(FNameWildcard), ?line io:format("~p~n", [Files1]), ?line Tc1 = dbg:trace_client(file, WrapFilesSpec, {fun myhandler/2, {wait_for_go,Self}}), ?line Tref1 = erlang:monitor(process, Tc1), Tc1 ! {go,Self}, ?line [{'DOWN',Tref1,_,_,normal}, end_of_trace |Result] = lists:reverse(flush()), ?line M = N - (WrapCnt-1) div 2, ?line M = wrap_port_result(Result, Self, N), %% %% Start a new wrap log with the same name to verify that %% all files are cleared at wrap log start. Only produce %% two trace messages to also place the gap at index 3, %% so the trace log will be misinterpreted. %% ?line wrap_port_init(WrapFilesSpec), ?line Files2 = filelib:wildcard(FNameWildcard), ?line io:format("~p~n", [Files2]), ?line -1 = ?MODULE:tracee1(-1), ?line -1 = ?MODULE:tracee2(-1), ?line stop(), ?line Files = filelib:wildcard(FNameWildcard), ?line io:format("~p~n", [Files]), ?line Tc2 = dbg:trace_client(file, WrapFilesSpec, {fun myhandler/2, {wait_for_go,Self}}), ?line Tref2 = erlang:monitor(process, Tc2), Tc2 ! {go,Self}, ?line [{trace,Self,call,{?MODULE,tracee1,[-1]},Self}, {trace,Self,call,{?MODULE,tracee2,[-1]},hej}, end_of_trace, {'DOWN',Tref2,_,_,normal}] = flush(), %% ?line lists:map(fun(F) -> file:delete(F) end, Files) after ?line stop() end, ok. wrap_port_init(WrapFilesSpec) -> ?line Port = dbg:trace_port(file, WrapFilesSpec), ?line {ok, _} = dbg:tracer(port, Port), ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call), ?line {ok, X} = dbg:tp(?MODULE, tracee1,[{'_',[],[{message, {self}}]}]), ?line {value, {saved, _Saved}} = lists:keysearch(saved, 1, X), ?line {ok, Y} = dbg:tp(?MODULE, tracee2, [{'_',[],[{message, hej}]}]), ?line {value, {saved, _}} = lists:keysearch(saved, 1, Y), ok. tracee1(X) -> X. tracee2(X) -> X. wrap_port_result([], _S, M) -> M; wrap_port_result([{trace, S, call, {?MODULE, tracee2, [M]}, hej}, {trace, S, call, {?MODULE, tracee1, [M]}, S} | Tail], S, M) -> wrap_port_result(Tail, S, M-1). wrap_port_time(suite) -> []; wrap_port_time(doc) -> ["Test tracing to time limited wrapping file port"]; wrap_port_time(Config) when is_list(Config) -> ?line stop(), ?line {A,B,C} = erlang:now(), ?line FTMP = atom_to_list(?MODULE) ++ integer_to_list(A) ++ "-" ++ integer_to_list(B) ++ "-" ++ integer_to_list(C) ++ "-", ?line FName = filename:join([?config(data_dir, Config), FTMP]), %% WrapTime=2 and WrapCnt=4 will force the trace to wrap after %% every 2 seconds, and to contain between 3*2 and 4*2 seconds %% of trace entries. ?line WrapFilesSpec = {FName, wrap, ".trace", {time, 2000}, 4}, ?line Port = dbg:trace_port(file, WrapFilesSpec), ?line {ok, _} = dbg:tracer(port, Port), try ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call), ?line {ok, X} = dbg:tp(?MODULE, tracee1,[{'_',[],[{message, {self}}]}]), ?line {value, {saved, _Saved1}} = lists:keysearch(saved, 1, X), ?line {ok, Y} = dbg:tp(?MODULE, tracee2, [{'_',[],[{message, hej}]}]), ?line {value, {saved, _Saved2}} = lists:keysearch(saved, 1, Y), %% The delays in the iterations places two trace messages in each %% trace file, but the last which is empty waiting for its first %% trace message. The number of iterations is chosen so that %% one trace file has been wasted, and therefore the first pair %% of trace messages. ?line lists:foreach( fun(Cnt) -> receive after 1000 -> ok end, ?MODULE:tracee1(Cnt), ?MODULE:tracee2(Cnt), receive after 1100 -> ok end end, lists:seq(1, 4)), ?line stop(), ?line Files = filelib:wildcard(FName ++ "*" ++ ".trace"), ?line io:format("~p~n", [Files]), ?line dbg:trace_client(file, WrapFilesSpec, {fun myhandler/2, self()}), ?line S = self(), ?line [{trace, S, call, {?MODULE, tracee1, [2]}, S}, {trace, S, call, {?MODULE, tracee2, [2]}, hej}, {trace, S, call, {?MODULE, tracee1, [3]}, S}, {trace, S, call, {?MODULE, tracee2, [3]}, hej}, {trace, S, call, {?MODULE, tracee1, [4]}, S}, {trace, S, call, {?MODULE, tracee2, [4]}, hej}, end_of_trace] = flush(), ?line lists:map(fun(F) -> file:delete(F) end, Files) after ?line stop() end, ok. with_seq_trace(suite) -> []; with_seq_trace(doc) -> ["Test ordinary tracing combined with seq_trace"]; with_seq_trace(Config) when is_list(Config) -> try ?line {ok, Server} = start(), ?line {ok, Tracer} = dbg:get_tracer(), ?line {ok, X} = dbg:tp(dbg, get_tracer, [{[],[], [{set_seq_token, send, true}]}]), ?line {value, {saved, _}} = lists:keysearch(saved, 1, X), ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call), ?line seq_trace:set_system_tracer(Tracer), ?line dbg:get_tracer(), receive after 1 -> ok end, ?line S = self(), ?line ThisNode = node(), ?line [{trace,S,call,{dbg,get_tracer,[]}}, {seq_trace,0,{send,_,S,Server,{S,{get_tracer,ThisNode}}}}, {seq_trace,0,{send,_,Server,S,{dbg,{ok,Tracer}}}}] = flush() after ?line stop() end, ok. dead_suspend(suite) -> []; dead_suspend(doc) -> ["Test that trace messages concerning a now dead process does " "not crash dbg."]; dead_suspend(Config) when is_list(Config) -> ?line start(), try survived = run_dead_suspend() after ?line stop() end. run_dead_suspend() -> dbg:p(new, call), dbg:tp(?MODULE, dummy, []), spawn(?MODULE, dummy, []), spawn(?MODULE, dummy, []), spawn(?MODULE, dummy, []), spawn(?MODULE, dummy, []), spawn(?MODULE, dummy, []), receive after 1000 -> ok end, case whereis(dbg) of undefined -> died; _ -> survived end. dummy() -> ok. %% %% Support functions %% start_slave() -> {A, B, C} = now(), Name = "asdkxlkmd" ++ integer_to_list(A+B+C), {ok, Node} = test_server:start_node(Name,slave,[]), ok = wait_node(Node, 15), Node. stop_slave(Node) -> test_server:stop_node(Node). wait_node(_,0) -> no; wait_node(Node, N) -> case net_adm:ping(Node) of pong -> ok; pang -> receive after 1000 -> ok end, wait_node(Node, N - 1) end. myhandler(Message, {wait_for_go,Pid}) -> receive {go,Pid} -> myhandler(Message, Pid) end; myhandler(Message, Relay) -> Relay ! Message, case Message of end_of_trace -> ok; _ -> Relay end. flush() -> flush([]). flush(Acc) -> receive X -> flush(Acc ++ [X]) after 1000 -> Acc end. start() -> stop(), dbg:tracer(process, {fun myhandler/2, self()}). stop() -> dbg:stop(). schedstat_handler(TraceMsg, {Parent, Tag, Data} = State) -> case TraceMsg of {trace_ts, Pid, in, _, Ts} -> NewData = case lists:keysearch(Pid, 1, Data) of {value, {Pid, Acc}} -> [{Pid, Acc, Ts} | lists:keydelete(Pid, 1, Data)]; false -> [{Pid, {0, 0, 0}, Ts} | Data]; Other -> exit(Parent, {?MODULE, ?LINE, Other}), erlang:display({?MODULE, ?LINE, Other}), Data end, {Parent, Tag, NewData}; {trace_ts, Pid, out, _, {A3, B3, C3}} -> NewData = case lists:keysearch(Pid, 1, Data) of {value, {Pid, {A1, B1, C1}, {A2, B2, C2}}} -> [{Pid, {A3-A2+A1, B3-B2+B1, C3-C2+C1}} | lists:keydelete(Pid, 1, Data)]; Other -> exit(Parent, {?MODULE, ?LINE, Other}), erlang:display({?MODULE, ?LINE, Other}), Data end, {Parent, Tag, NewData}; {trace_ts, Pid, exit, normal, {A3, B3, C3}} -> NewData = case lists:keysearch(Pid, 1, Data) of {value, {Pid, {A1, B1, C1}, {A2, B2, C2}}} -> [{Pid, {A3-A2+A1, B3-B2+B1, C3-C2+C1}} | lists:keydelete(Pid, 1, Data)]; {value, {Pid, _Acc}} -> Data; false -> [{Pid, {0, 0, 0}} | Data]; Other -> exit(Parent, {?MODULE, ?LINE, Other}), erlang:display({?MODULE, ?LINE, Other}), Data end, {Parent, Tag, NewData}; {trace_ts, _Pid, send, _Msg, _OtherPid, _Ts} -> State; end_of_trace -> Parent ! {Tag, Data}, State end. pass_token(Token, Next, Loops) -> receive {Token, 1} = Msg -> sendloop(Loops), Next ! Msg; {Token, _Cnt} = Msg-> sendloop(Loops), Next ! Msg, pass_token(Token, Next, Loops) end. pass_token(Token, Final, Cnt, Loops) -> receive {Token, start, Next} -> sendloop(Loops), Msg = {Token, Cnt}, Next ! Msg, pass_token(Token, Final, Next, Cnt, Loops) end. pass_token(Token, Final, Next, Cnt, Loops) -> receive {Token, 1} -> sendloop(Loops), Msg = {Token, done}, Final ! Msg; {Token, Cnt} -> sendloop(Loops), NextCnt = Cnt-1, Msg = {Token, NextCnt}, Next ! Msg, pass_token(Token, Final, Next, NextCnt, Loops) end. sendloop(Loops) -> sendloop(make_ref(), Loops). sendloop(_Tag, 0) -> ok; sendloop(Tag, Loops) -> self() ! {Tag, Loops}, receive {Tag, Loops} -> ok end, sendloop(Tag, Loops-1). token_volleyball(N, Cnt, Loops) when is_integer(N), N >= 1, is_integer(Cnt), Cnt >= 1, is_integer(Loops), Loops >= 0 -> Self = self(), Token = make_ref(), Last = spawn_link(fun() -> pass_token(Token, Self, Cnt, Loops) end), First = token_volleyball(Token, Last, N-1, Loops), Last ! {Token, start, First}, receive {Token, done} -> ok end. token_volleyball(Token, Next, 1, Loops) -> spawn_link(fun() -> pass_token(Token, Next, Loops) end); token_volleyball(Token, Next, N, Loops) -> Pid = spawn_link(fun() -> pass_token(Token, Next, Loops) end), token_volleyball(Token, Pid, N-1, Loops).