diff options
-rw-r--r-- | lib/runtime_tools/test/Makefile | 65 | ||||
-rw-r--r-- | lib/runtime_tools/test/dbg_SUITE.erl | 901 | ||||
-rw-r--r-- | lib/runtime_tools/test/dbg_SUITE_data/dbg_test.erl | 87 | ||||
-rw-r--r-- | lib/runtime_tools/test/dbg_SUITE_data/exref_td.erl | 50 | ||||
-rw-r--r-- | lib/runtime_tools/test/erts_alloc_config_SUITE.erl | 206 | ||||
-rw-r--r-- | lib/runtime_tools/test/inviso_SUITE.erl | 2840 | ||||
-rw-r--r-- | lib/runtime_tools/test/inviso_testmodule1_foo.erl | 9 | ||||
-rw-r--r-- | lib/runtime_tools/test/runtime_tools.cover | 1 | ||||
-rw-r--r-- | lib/runtime_tools/test/runtime_tools.spec | 1 | ||||
-rw-r--r-- | lib/runtime_tools/test/runtime_tools_SUITE.erl | 50 |
10 files changed, 4210 insertions, 0 deletions
diff --git a/lib/runtime_tools/test/Makefile b/lib/runtime_tools/test/Makefile new file mode 100644 index 0000000000..873d395277 --- /dev/null +++ b/lib/runtime_tools/test/Makefile @@ -0,0 +1,65 @@ +# +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +MODULES = \ + runtime_tools_SUITE \ + inviso_testmodule1_foo \ + inviso_SUITE \ + dbg_SUITE \ + erts_alloc_config_SUITE + +ERL_FILES= $(MODULES:%=%.erl) + +TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) +INSTALL_PROGS= $(TARGET_FILES) + +EMAKEFILE=Emakefile + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/runtime_tools_test + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- + +ERL_MAKE_FLAGS += +ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include + +EBIN = . + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +make_emakefile: + $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES)\ + > $(EMAKEFILE) + +tests debug opt: make_emakefile + erl $(ERL_MAKE_FLAGS) -make + +clean: + rm -f $(EMAKEFILE) + rm -f $(TARGET_FILES) + rm -f core + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + +release_tests_spec: make_emakefile + $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DATA) runtime_tools.spec $(ERL_FILES) $(RELSYSDIR) + $(INSTALL_DATA) $(EMAKEFILE) runtime_tools.cover $(RELSYSDIR) + chmod -f -R u+w $(RELSYSDIR) + @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + +release_docs_spec: diff --git a/lib/runtime_tools/test/dbg_SUITE.erl b/lib/runtime_tools/test/dbg_SUITE.erl new file mode 100644 index 0000000000..ff96af5e86 --- /dev/null +++ b/lib/runtime_tools/test/dbg_SUITE.erl @@ -0,0 +1,901 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010. 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/1, 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, fin_per_testcase/2]). +-export([tracee1/1, tracee2/1]). +-export([dummy/0, exported/1]). + +-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]. +fin_per_testcase(_Case, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +all(suite) -> [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]. + +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). diff --git a/lib/runtime_tools/test/dbg_SUITE_data/dbg_test.erl b/lib/runtime_tools/test/dbg_SUITE_data/dbg_test.erl new file mode 100644 index 0000000000..2edbf6f99a --- /dev/null +++ b/lib/runtime_tools/test/dbg_SUITE_data/dbg_test.erl @@ -0,0 +1,87 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010. 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% +%% +%%%---------------------------------------------------------------------- +%%% Purpose : A priority queue. +%%%---------------------------------------------------------------------- +%%% This module implements a priority queue as defined in +%%% "Priority Queues and the STL" by Mark Nelson in Dr.Dobb's Journal, Jan 1996 +%%% see http://web2.airmail.net/markn/articles/pq_stl/priority.htm for more +%%% information. (A heap implementation is planned aswell) +%%%---------------------------------------------------------------------- +%%% The items of the queue is kept priority sorted, and because of that, +%%% a push() operation costs more than a pop() operation (wich only +%%% needs to return the top item of the queue(read: list)). +%%%---------------------------------------------------------------------- +%%% The priority queue can be deceptively nice to use when creating for +%%% example a Huffman coding tree. +%%% See http://web2.airmail.net/markn/articles/pq_stl/priority.htm or +%%% Dr.Dobb's Journal Jan, 96 for more information on this. +%%%---------------------------------------------------------------------- + +%%% Used here strictly as something for dbg_SUITE to run. + +-module(dbg_test). +-export([test/1, loop/1]). +-export([new/0, push/3, pop/1]). + +loop(Config) -> + test(Config), + receive + {dbg_test, stop} -> + ok; + Other -> + loop(Config) + end, + ok. + +test(Config) -> + Q1=new(), + Q2=push(Q1, "monkey", 3), + Q3=push(Q2, "banana", 4), + Q4=push(Q3, "jungle", 2), + Q5=push(Q4, "world", 5), + Q6=push(Q5, "universe",6), + Q7=push(Q6, "peanut", 1), +% io:format("~p~n",[Q7]), + {Itm, Q8}=pop(Q7), + ok. + +%% Returns a new priority queue. +new() -> + []. + +%% Pushes a new item with a set priority into the queue. +push(Queue, Itm, Pri) -> + insert(Queue, Itm, Pri, []). + +%% Pops the item with the highest priority out of the queue. +pop([{Itm, Pri}|Queue]) -> + {Itm, Queue}. + +%% --- -- - +%% Support functions. +insert([], Itm, Pri, NewQ) -> + lists:flatten([lists:reverse(NewQ)|[{Itm, Pri}]]); +% Itm>QItm>NewQ>Queue +insert([{QItm,QPri}|Queue], Itm, Pri, NewQ) when Pri>QPri-> + A = [{Itm, Pri}|[{QItm, QPri}]], + lists:flatten([[A|NewQ]|Queue]); +insert([QItm|Rest], Itm, Pri, NewQ) -> + insert(Rest, Itm, Pri, [QItm|NewQ]). +%% --- -- - diff --git a/lib/runtime_tools/test/dbg_SUITE_data/exref_td.erl b/lib/runtime_tools/test/dbg_SUITE_data/exref_td.erl new file mode 100644 index 0000000000..85faf620aa --- /dev/null +++ b/lib/runtime_tools/test/dbg_SUITE_data/exref_td.erl @@ -0,0 +1,50 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010. 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(exref_td). + +-export([a/1,b/1,c/1,d/1,e/1,my_analyse/1]). + +a("edcba") -> + true; +a(_) -> + lists:reverse("abcde"). + +b(_) -> + a(nil). + +c(_) -> + nonexistentmodule:f(). + +d(_) -> + lists:nonexistentfunction(). + +e(_) -> + [a(x),b(x),c(x)]. + +localfuncnotcalled() -> + true. + +localfunccalledbyf(A) -> + A. + +f() -> + lists:filter(fun localfunccalledbyf/1,"aaabba"), + F = fun localfuncnotcalled/0. + +my_analyse(Graph) -> ok. diff --git a/lib/runtime_tools/test/erts_alloc_config_SUITE.erl b/lib/runtime_tools/test/erts_alloc_config_SUITE.erl new file mode 100644 index 0000000000..32483dbe73 --- /dev/null +++ b/lib/runtime_tools/test/erts_alloc_config_SUITE.erl @@ -0,0 +1,206 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010. 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(erts_alloc_config_SUITE). + +%-define(line_trace, 1). + +-include("test_server.hrl"). + +%-compile(export_all). +-export([all/1, init_per_testcase/2, fin_per_testcase/2]). + +%% Testcases +-export([basic/1]). + +%% internal export +-export([make_basic_config/1]). + +-define(DEFAULT_TIMEOUT, ?t:minutes(2)). + +all(doc) -> []; +all(suite) -> [basic]. + +init_per_testcase(Case, Config) when is_list(Config) -> + [{testcase, Case}, + {watchdog, ?t:timetrap(?DEFAULT_TIMEOUT)}, + {erl_flags_env, save_env()} | Config]. + +fin_per_testcase(_Case, Config) when is_list(Config) -> + ?t:timetrap_cancel(?config(watchdog, Config)), + restore_env(?config(erl_flags_env, Config)), + ok. + +%%% +%%% The test cases ------------------------------------------------------------ +%%% + +basic(doc) -> []; +basic(suite) -> []; +basic(Config) when is_list(Config) -> + ?line ErtsAllocConfig = privfile("generated", Config), + + SbctMod = " +MBsbct 1024 +MHsbct 4096", + + %% Make sure we have enabled allocators + ZFlgs = case os:getenv("ERL_ZFLAGS") of + FlgString when is_list(FlgString) -> + FlgString; + _ -> + "" + end ++ " +Mea max +Mea config", + + ?line os:putenv("ERL_ZFLAGS", ZFlgs ++ SbctMod), + + ?line {ok, Node1} = start_node(Config), + ?line ok = rpc:call(Node1, ?MODULE, make_basic_config, [ErtsAllocConfig]), + ?line stop_node(Node1), + + ?line display_file(ErtsAllocConfig), + + ?line ManualConfig = privfile("manual", Config), + ?line {ok, IOD} = file:open(ManualConfig, [write]), + ?line io:format(IOD, "~s", ["+MBsbct 2048"]), + ?line file:close(IOD), + ?line display_file(ManualConfig), + + ?line os:putenv("ERL_ZFLAGS", ZFlgs), + + ?line {ok, Node2} = start_node(Config, + "-args_file " ++ ErtsAllocConfig + ++ " -args_file " ++ ManualConfig), + + ?line {_, _, _, Cfg} = rpc:call(Node2, erlang, system_info, [allocator]), + + ?line stop_node(Node2), + + ?line {value,{binary_alloc, BCfg}} = lists:keysearch(binary_alloc, 1, Cfg), + ?line {value,{sbct, 2097152}} = lists:keysearch(sbct, 1, BCfg), + ?line {value,{eheap_alloc, HCfg}} = lists:keysearch(eheap_alloc, 1, Cfg), + ?line {value,{sbct, 4194304}} = lists:keysearch(sbct, 1, HCfg), + + ?line ok. + +make_basic_config(ErtsAllocConfig) -> + %% Save some different scenarios + Tester = self(), + SSBegun = make_ref(), + SSDone = make_ref(), + SSFun = fun (F) -> + receive + SSDone -> + ok = erts_alloc_config:save_scenario(), + Tester ! SSDone + after 500 -> + ok = erts_alloc_config:save_scenario(), + F(F) + end + end, + SS = spawn_link(fun () -> + ok = erts_alloc_config:save_scenario(), + Tester ! SSBegun, + SSFun(SSFun) + end), + receive SSBegun -> ok end, + Ref = make_ref(), + Tab = ets:new(?MODULE, [bag, public]), + Ps = lists:map( + fun (_) -> + spawn_link( + fun () -> + ets:insert(Tab, + {self(), + lists:seq(1, 1000)}), + receive after 1000 -> ok end, + Tester ! {Ref, self()} + end) + end, + lists:seq(1, 10000)), + lists:foreach(fun (P) -> receive {Ref, P} -> ok end end, Ps), + ets:delete(Tab), + SS ! SSDone, + receive SSDone -> ok end, + + ok = erts_alloc_config:make_config(ErtsAllocConfig). + + + +%% +%% Utils ---------------------------------------------------------------------- +%% + +display_file(FileName) -> + ?t:format("filename: ~s~n", [FileName]), + {ok, Bin} = file:read_file(FileName), + io:format("~s", [binary_to_list(Bin)]), + ?t:format("eof: ~s~n", [FileName]), + ok. + +mk_name(Config) when is_list(Config) -> + {A, B, C} = now(), + list_to_atom(atom_to_list(?MODULE) + ++ "-" ++ atom_to_list(?config(testcase, Config)) + ++ "-" ++ integer_to_list(A) + ++ "-" ++ integer_to_list(B) + ++ "-" ++ integer_to_list(C)). + +start_node(Config) -> + start_node(Config, ""). + +start_node(Config, Args) -> + ?line Pa = filename:dirname(code:which(?MODULE)), + ?line ?t:start_node(mk_name(Config), + slave, + [{args, "-pa " ++ Pa ++ " " ++ Args}]). + +stop_node(Node) -> + ?line true = ?t:stop_node(Node). + +privfile(Name, Config) -> + filename:join([?config(priv_dir, Config), + atom_to_list(?config(testcase, Config)) ++ "." ++ Name]). + +save_env() -> + {erl_flags, + os:getenv("ERL_AFLAGS"), + os:getenv("ERL_FLAGS"), + os:getenv("ERL_"++erlang:system_info(otp_release)++"_FLAGS"), + os:getenv("ERL_ZFLAGS")}. + +restore_env(EVar, false) when is_list(EVar) -> + restore_env(EVar, ""); +restore_env(EVar, "") when is_list(EVar) -> + case os:getenv(EVar) of + false -> ok; + "" -> ok; + " " -> ok; + _ -> os:putenv(EVar, " ") + end; +restore_env(EVar, Value) when is_list(EVar), is_list(Value) -> + case os:getenv(EVar) of + Value -> ok; + _ -> os:putenv(EVar, Value) + end. + +restore_env({erl_flags, AFlgs, Flgs, RFlgs, ZFlgs}) -> + restore_env("ERL_AFLAGS", AFlgs), + restore_env("ERL_FLAGS", Flgs), + restore_env("ERL_"++erlang:system_info(otp_release)++"_FLAGS", RFlgs), + restore_env("ERL_ZFLAGS", ZFlgs), + ok. diff --git a/lib/runtime_tools/test/inviso_SUITE.erl b/lib/runtime_tools/test/inviso_SUITE.erl new file mode 100644 index 0000000000..1c5c887b62 --- /dev/null +++ b/lib/runtime_tools/test/inviso_SUITE.erl @@ -0,0 +1,2840 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010. 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% +%% +%% Description: +%% Test suite for inviso (basic parts, i.e not inviso tools). Note that +%% inviso basic parts have modules in both the runtime_tools and +%% inviso applications. +%% +%% Authors: +%% Ann-Marie L�f, ann-marie.lof@st.se +%% Lennart �hman, lennart.ohman@st.se +%% ----------------------------------------------------------------------------- + +-module(inviso_SUITE). +-compile(export_all). + +-include("test_server.hrl"). +-include_lib("kernel/include/file.hrl"). + +-define(l,?line). + +all(suite) -> + [ + basic_dist_trace_1, + basic_dist_trace_2, + basic_dist_trace_3, + basic_dist_trace_ti_1, + basic_dist_trace_ti_2, + basic_dist_trace_ti_3, + suspend_dist_trace_ti_1, + suspend_dist_trace_ti_2, + meta_cleanfunc_dist_1, + basic_handlerfun_dist_1, + delete_log_dist_1, + autostart_dist_1, + autostart_dist_2, + autostart_dist_3, + running_alone_dist_1, + running_alone_dist_2, + running_alone_dist_3, + running_alone_dist_4, + running_alone_dist_5, + overload_dist_1, + overload_dist_2, + overload_dist_3, + overload_dist_4, + overload_dist_5, + subscribe_dist_1, + lfm_trace_dist_1, + lfm_trace_ti_dist_2, + handle_logfile_sort_wrapset, + fetch_log_dist_trace_1, + fetch_log_dist_trace_2, + fetch_log_dist_trace_3, + fetch_log_dist_error_1, + fetch_log_dist_error_2, + expand_regexp_dist_1, + only_loaded_dist_1 + ]. + + +init_per_suite(Config) -> + %% No never know who skrewed up this node before this suite! :-) + erlang:trace_pattern({'_','_','_'},[],[local]), + erlang:trace_pattern({'_','_','_'},[],[global]), + erlang:trace(all,false,[all]), + + ?l ok=application:start(runtime_tools), + Config. + +end_per_suite(_Config) -> + ?l ok=application:stop(runtime_tools). + + +%% For each distributed testcase, we need two other distributed nodes to run the +%% runtime components on. Since they are freshly started every time there is no +%% need to clean them up first. +init_per_testcase(_Case,Config) -> + ?l TH=test_server:timetrap(100000), + ?l {ok,Node1}=test_server:start_node(inviso1,peer,[]), + ?l {ok,Node2}=test_server:start_node(inviso2,peer,[]), + ?l SuiteDir=filename:dirname(code:which(?MODULE)), + + %% Otherwise peer nodes will not find this module! + ?l true=rpc:call(Node1,code,add_patha,[SuiteDir]), + ?l true=rpc:call(Node2,code,add_patha,[SuiteDir]), + + ?l start_side_effect_logger(node()), + ?l start_side_effect_logger(Node1), + ?l start_side_effect_logger(Node2), + + + %% SPECIAL FOR MY PRIVATE TEST ENVIROMENT +% ?l rpc:call(Node1,code,add_patha,["/clearcase/otp/tools/runtime_tools/ebin"]), +% ?l rpc:call(Node1,code,add_patha,["/clearcase/otp/tools/inviso/ebin"]), +% ?l rpc:call(Node2,code,add_patha,["/clearcase/otp/tools/runtime_tools/ebin"]), +% ?l rpc:call(Node2,code,add_patha,["/clearcase/otp/tools/inviso/ebin"]), + +% %% SPECIAL FOR MY PRIVATE TEST ENVIROMENT, windows. +% ?l rpc:call(Node1,code,add_patha,["Z:/DATA/PROJECTS/inviso_project/runtime_tools/ebin"]), +% ?l rpc:call(Node1,code,add_patha,["Z:/DATA/PROJECTS/inviso_project/inviso/ebin"]), +% ?l rpc:call(Node2,code,add_patha,["Z:/DATA/PROJECTS/inviso_project/runtime_tools/ebin"]), +% ?l rpc:call(Node2,code,add_patha,["Z:/DATA/PROJECTS/inviso_project/inviso/ebin"]), + + ?l ok=rpc:call(Node1,application,start,[runtime_tools]), + ?l ok=rpc:call(Node2,application,start,[runtime_tools]), + ?l timer:sleep(100), % Problem with autostarted runtime. + %% The following is a test that the inviso_rt processes which are autostarted + %% are now gone. + + ?l ok=poll(rpc,call,[Node1,erlang,whereis,[inviso_rt]],undefined,20), + ?l ok=poll(rpc,call,[Node2,erlang,whereis,[inviso_rt]],undefined,20), + +% ?l ok=poll(rpc,call,[Node1,supervisor,which_children,[runtime_tools_sup]],[],20), +% ?l ok=poll(rpc,call,[Node2,supervisor,which_children,[runtime_tools_sup]],[],20), + NewConfig1=insert_remotenode_config(inviso1,Node1,Config), + NewConfig2=insert_remotenode_config(inviso2,Node2,NewConfig1), + insert_timetraphandle_config(TH,NewConfig2). +%% ----------------------------------------------------------------------------- + +fin_per_testcase(Case,Config) -> + ?l test_server:stop_node(get_remotenode_config(inviso1,Config)), + ?l test_server:stop_node(get_remotenode_config(inviso2,Config)), + + case whereis(inviso_c) of + undefined -> % Should not exist. + true; + Pid when is_pid(Pid) -> % But if it exists... + exit(Pid,kill), % Remove it! + io:format("Had to kill the control component in fin_per_testcase,~p.~n",[Case]) + end, + case whereis(inviso_rt) of + undefined -> % Should not exist. + true; + Pid2 when is_pid(Pid2) -> % But if it exists... + exit(Pid2,kill), % Remove it! + io:format("Had to kill local runtime component in fin_per_testcase,~p.~n",[Case]) + end, + ?l process_killer([inviso_test_proc, + inviso_tab_proc, + inviso_collector_proc, + global_inviso_test_proc]), + ?l test_server:timetrap_cancel(get_timetraphandle_config(Config)), + + NewConfig1=remove_remotenode_config(inviso1,Config), + NewConfig2=remove_remotenode_config(inviso2,NewConfig1), + remove_timetraphandle_config(NewConfig2). +%% ----------------------------------------------------------------------------- + +%% ============================================================================== +%% Testcases. +%% ============================================================================== + +%% TEST CASE: Basic, distributed, trace only. +basic_dist_trace_1(suite) -> []; +basic_dist_trace_1(doc) -> + ["Basic case, start of distributed tracing, using only trac."]; +basic_dist_trace_1(Config) when is_list(Config) -> + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + PrivDir=filename:join(?config(priv_dir,Config),""), + TracerDataList=lists:map(fun(N)->{N,{file,filename:join([PrivDir, + "tf1_"++ + atom_to_list(N) + ])}} end, + Nodes), + start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}), + activate_local_tracing(Nodes), + deactivate_local_tracing(Nodes), + stop_tracing(Nodes), + stop(Nodes), + ok. +%% ----------------------------------------------------------------------------- + + +%% TEST CASE: Basic, distributed, activate global tracing for functions in modules +%% pointed out using a regexp. No tracing will be done. +basic_dist_trace_2(suite) -> []; +basic_dist_trace_2(doc) -> + [""]; +basic_dist_trace_2(Config) when is_list(Config) -> + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + PrivDir=filename:join(?config(priv_dir,Config),""), + TracerDataList=lists:map(fun(N)->{N,{file,filename:join([PrivDir, + "tf1a_"++ + atom_to_list(N) + ])}} end, + Nodes), + start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}), + Funcs1=activate_global_tracing_regexp(Nodes), + deactivate_global_tracing_regexp(Nodes,Funcs1), + stop_tracing(Nodes), + stop(Nodes), + ok. +%% ----------------------------------------------------------------------------- + +%% TEST CASE: Basic, distributed, activate global tracing for functions in modules +%% pointed out using a dir-regexp. No tracing will be done. +basic_dist_trace_3(suite) -> []; +basic_dist_trace_3(doc) -> + [""]; +basic_dist_trace_3(Config) when is_list(Config) -> + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + PrivDir=filename:join(?config(priv_dir,Config),""), + TracerDataList=lists:map(fun(N)->{N,{file,filename:join([PrivDir, + "tf1b_"++ + atom_to_list(N) + ])}} end, + Nodes), + start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}), + Funcs1=activate_global_tracing_regexp_dir(Nodes), + deactivate_global_tracing_regexp_dir(Nodes,Funcs1), + stop_tracing(Nodes), + stop(Nodes), + ok. +%% ----------------------------------------------------------------------------- + +%% TEST CASE: Basic, distributed, trace and ti. +basic_dist_trace_ti_1(suite) -> []; +basic_dist_trace_ti_1(doc) -> + [""]; +basic_dist_trace_ti_1(Config) when is_list(Config) -> + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + PrivDir=filename:join(?config(priv_dir,Config),""), + TracerDataFun= + fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf2_"++atom_to_list(N)])}}, + {ti,{file,filename:join([PrivDir,"tf2_"++atom_to_list(N)++".ti"])}}]} + end, + TracerDataList=lists:map(TracerDataFun,Nodes), + start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}), + activate_local_tracing(Nodes), + activate_meta_tracing(Nodes), + ?l true=(is_pid(whereis(inviso_rt))), + ?l true=(is_pid(whereis(inviso_rt_meta))), + deactivate_meta_tracing(Nodes), + deactivate_local_tracing(Nodes), + stop_tracing(Nodes), + ?l true=(is_pid(whereis(inviso_rt))), % Shall still be running. + ?l ok=poll(erlang,whereis,[inviso_rt_meta],undefined,3), + stop(Nodes), + timer:sleep(200), % Give it time to terminate. + ?l ok=poll(erlang,whereis,[inviso_rt],undefined,3),% Shall be gone now. + ?l undefined=whereis(inviso_rt_meta), % Still gone. + ok. +%% ----------------------------------------------------------------------------- + +%% Test CASE: Testing that the tpm_tracer functionality works. That is appending +%% {tracer,Tracer} to a meta match spec. +basic_dist_trace_ti_2(suite) -> []; +basic_dist_trace_ti_2(doc) -> + [""]; +basic_dist_trace_ti_2(Config) when is_list(Config) -> + case erlang:system_info(version) of + "5.4"++_ -> % Perhaps not perfect, but work now :-) + {skip,"Old emulator"}; + _ -> + basic_dist_trace_ti_2_do(Config) + end. + +basic_dist_trace_ti_2_do(Config) -> + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + PrivDir=filename:join(?config(priv_dir,Config),""), + TracerDataFun= + fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf3_"++atom_to_list(N)])}}, + {ti,{file,filename:join([PrivDir,"tf3_"++atom_to_list(N)++".ti"])}}]} + end, + TracerDataList=lists:map(TracerDataFun,Nodes), + start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}), + activate_deactivate_meta_tracing_tracer(Nodes), + stop_tracing(Nodes), + stop(Nodes), + ok. +%% ----------------------------------------------------------------------------- + +%% TEST CASE: Basic, distributed, trace and ti, where we try to use ctp_all to +%% check that all global and local patterns are removed but that meta patterns +%% remain. +%% This test also checks that if the meta tracer is terminated an error value +%% is generated when trying to do meta tracing at that node. +basic_dist_trace_ti_3(suite) -> []; +basic_dist_trace_ti_3(doc) -> + [""]; +basic_dist_trace_ti_3(Config) when is_list(Config) -> + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + PrivDir=filename:join(?config(priv_dir,Config),""), + TracerDataFun= + fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf4_"++atom_to_list(N)])}}, + {ti,{file,filename:join([PrivDir,"tf4_"++atom_to_list(N)++".ti"])}}]} + end, + TracerDataList=lists:map(TracerDataFun,Nodes), + start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}), + activate_local_tracing(Nodes), + activate_global_tracing(Nodes), + activate_meta_tracing(Nodes), + ?l true=(is_pid(whereis(inviso_rt))), + ?l true=(is_pid(whereis(inviso_rt_meta))), + ?l {ok,NodeResults1}=inviso:ctp_all(Nodes), % Removes local and global patterns. + ?l true=check_noderesults(Nodes,ok,NodeResults1), + ?l true=check_on_nodes(Nodes,erlang,trace_info,[{code,which,1},traced],{traced,false}), + ?l true=check_on_nodes(Nodes,erlang,trace_info,[{code,get_path,0},traced],{traced,false}), + %% But meta patters shall remain. + ?l true=check_on_nodes(Nodes, + erlang, + trace_info, + [{lists,module_info,0},meta_match_spec], + fun({meta_match_spec,L})when length(L)>0 ->true end), + ?l true=check_on_nodes(Nodes, + erlang, + trace_info, + [{lists,module_info,0},meta], + fun({meta,P})when is_pid(P) -> + P=rpc:call(node(P),erlang,whereis,[inviso_rt_meta]), + true + end), + %% Now kill the meta tracer somewhere and try to activate meta tracing. + ?l [ANode|_]=Nodes, + ?l AMetaPid=rpc:call(ANode,erlang,whereis,[inviso_rt_meta]), + ?l rpc:call(ANode,erlang,exit,[AMetaPid,kill]), + ?l {ok,NodeResults2}=inviso:tpm(Nodes,math,pi,0,[],void), + ?l {value,{ANode,{error,_}}}=lists:keysearch(ANode,1,NodeResults2), + + ?l stop_tracing(Nodes), + ?l stop(Nodes), + ok. +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% Test cases for SUSPEND +%% ----------------------------------------------------------------------------- + +%% TEST CASE: In this test case a trace with ti is started. Trace flags are set, +%% trace patterns are set and meta trace patterns. We then check that the trace +%% flags and the meta patterns are removed when tracing suspended. +%% The suspension is cancelled and we check that it is possible to reactivate +%% tracing by setting the process flags and meta patterns again. +suspend_dist_trace_ti_1(suite) -> []; +suspend_dist_trace_ti_1(doc) -> + [""]; +suspend_dist_trace_ti_1(Config) when is_list(Config) -> + ?l RemoteNodes=get_remotenodes_config(Config), + ?l Nodes=[node()|RemoteNodes], + ?l PrivDir=filename:join(?config(priv_dir,Config),""), + ?l TracerDataFun= + fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf_suspend1_"++atom_to_list(N)])}}, + {ti,{file,filename:join([PrivDir,"tf_suspend1_"++atom_to_list(N)++".ti"])}}]} + end, + ?l TracerDataList=lists:map(TracerDataFun,Nodes), + start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}), + activate_local_tracing(Nodes), + activate_meta_tracing(Nodes), + ?l true=(is_pid(whereis(inviso_rt))), + ?l true=(is_pid(whereis(inviso_rt_meta))), + %% Set some trace flags on some newly started test procs. + activate_traceflags(Nodes), + + %% Now suspend the tracing on all nodes. That shall result in the removal + %% of trace flags and meta trace patterns, but not local trace patterns. + ?l {ok,NodeResults1}=inviso:suspend(Nodes,test), + ?l true=check_noderesults(Nodes,ok,NodeResults1), + %% Trace flags gone? + ?l TestProcs=lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_test_proc]) end,Nodes), + ?l lists:foreach(fun(P)-> + {flags,[]}= + rpc:call(node(P),erlang,trace_info,[P,flags]) + end, + TestProcs), + %% Meta patterns shall be gone too, but local functions still there. + ?l lists:foreach(fun(N)-> + {meta,false}= + rpc:call(N, + erlang, + trace_info, + [{math,module_info,1},meta]), + {traced,local}= + rpc:call(N, + erlang, + trace_info, + [{code,which,1},traced]) + end, + Nodes), + + %% Try to activate trace flags, trace patterns and meta tracing while + %% suspended. Should not succeed of course! + ?l ThisNode=node(), + ?l {ok,[{ThisNode,{error,suspended}}]}= + inviso:tf([ThisNode],inviso_test_proc,[call]), + ?l {ok,[{ThisNode,{error,suspended}}]}= + inviso:tpl([ThisNode],math,module_info,1,[]), + ?l {ok,[{ThisNode,{error,suspended}}]}= + inviso:init_tpm([ThisNode], + math, + module_info, + 1, + {?MODULE,tpm_init_func2}, % Does not exist on purpose. + {?MODULE,tpm_call_func2}, % Does not exist on purpose. + {?MODULE,tpm_return_func2}, % Does not exist on purpose. + {?MODULE,tpm_remove_func2}), % Does not exist on purpose. + + %% Now we want to cancel suspension and see that we can reactivate tracing. + ?l {ok,NodeResults2}=inviso:cancel_suspension(Nodes), + ?l true=check_noderesults(Nodes,ok,NodeResults2), + + ?l {ok,NodeResults3}= + inviso:init_tpm(math, + module_info, + 1, + {?MODULE,tpm_init_func2}, % Does not exist on purpose. + {?MODULE,tpm_call_func2}, % Does not exist on purpose. + {?MODULE,tpm_return_func2}, % Does not exist on purpose. + {?MODULE,tpm_remove_func2}), % Does not exist on purpose. + ?l true=check_noderesults(Nodes,ok,NodeResults3), + ?l {ok,NodeResults5}= + inviso:tpm_ms(math,module_info,1,ms1,[{'_',[],[{return_trace}]}]), + ?l true=check_noderesults(Nodes,{ok,1},NodeResults5), + ?l true=check_on_nodes(Nodes, + erlang, + trace_info, + [{math,module_info,1},meta_match_spec], + {meta_match_spec,[{'_',[],[{return_trace}]}]}), + ?l {ok,NodeResults6}=inviso:tf(Nodes,inviso_test_proc,[call]), + ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults6), + + %deactivate_meta_tracing(Nodes), + %deactivate_local_tracing(Nodes), + stop_tracing(Nodes), + ?l true=(is_pid(whereis(inviso_rt))), % Shall still be running. + ?l ok=poll(erlang,whereis,[inviso_rt_meta],undefined,3), + stop(Nodes), + ?l timer:sleep(200), % Give it time to terminate. + ?l ok=poll(erlang,whereis,[inviso_rt],undefined,3),% Shall be gone now. + ?l undefined=whereis(inviso_rt_meta), % Still gone. + ok. +%% ----------------------------------------------------------------------------- + +%% TEST CASE: In this test case a trace with ti is started. Trace flags are set, +%% trace patterns are set and meta trace patterns. We then suspend tracing at +%% all nodes, then stop tracing which shall be allowed. We then try to initiate +%% tracing again which shall not be possible. +suspend_dist_trace_ti_2(suite) -> []; +suspend_dist_trace_ti_2(doc) -> + [""]; +suspend_dist_trace_ti_2(Config) when is_list(Config) -> + ?l RemoteNodes=get_remotenodes_config(Config), + ?l Nodes=[node()|RemoteNodes], + ?l PrivDir=filename:join(?config(priv_dir,Config),""), + ?l TracerDataFun= + fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf_suspend2_"++atom_to_list(N)])}}, + {ti,{file,filename:join([PrivDir,"tf_suspend2_"++atom_to_list(N)++".ti"])}}]} + end, + ?l TracerDataList=lists:map(TracerDataFun,Nodes), + start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}), + activate_local_tracing(Nodes), + activate_meta_tracing(Nodes), + ?l true=(is_pid(whereis(inviso_rt))), + ?l true=(is_pid(whereis(inviso_rt_meta))), + %% Set some trace flags on some newly started test procs. + activate_traceflags(Nodes), + + %% Now suspend the tracing on all nodes. That shall result in the removal + %% of trace flags and meta trace patterns, but not local trace patterns. + ?l {ok,NodeResults1}=inviso:suspend(Nodes,test), + ?l true=check_noderesults(Nodes,ok,NodeResults1), + + %% Now stop tracing. + ?l {ok,NodeResults3}=inviso:stop_tracing(Nodes), + ?l true=check_noderesults(Nodes,{ok,idle},NodeResults3), + %% Now try to initiate tracing again. + ThisNode=node(), + ?l {ok,[{ThisNode,{error,suspended}}]}= + inviso:init_tracing([ThisNode], + [{trace,{file,filename:join([PrivDir,"tf_suspend3_"++ + atom_to_list(ThisNode)])}}, + {ti,{file,{filename:join([PrivDir,"tf_suspend3_"++ + atom_to_list(ThisNode)])}}}]), + + %% Cancel the suspension and initiate tracing again. + ?l {ok,NodeResults2}=inviso:cancel_suspension(Nodes), + ?l true=check_noderesults(Nodes,ok,NodeResults2), + ?l TracerDataFun2= + fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf_suspend4_"++atom_to_list(N)])}}, + {ti,{file,filename:join([PrivDir,"tf_suspend4_"++atom_to_list(N)++".ti"])}}]} + end, + ?l TracerDataList2=lists:map(TracerDataFun2,Nodes), + ?l {ok,NodeResults4}=inviso:init_tracing(TracerDataList2), + ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok},{ti_log,ok}]},NodeResults4), + stop_tracing(Nodes), + ?l true=(is_pid(whereis(inviso_rt))), % Shall still be running. + stop(Nodes), + ?l timer:sleep(200), % Give it time to terminate. + ?l ok=poll(erlang,whereis,[inviso_rt],undefined,3),% Shall be gone now. + ok. +%% ----------------------------------------------------------------------------- + + + +%% TEST CASE: This test case tests that the clean function removes (prosumed) +%% expired data from the internal public-loopdata structure in the inviso_rt_meta +%% process. +meta_cleanfunc_dist_1(Config) when is_list(Config) -> + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + PrivDir=filename:join(?config(priv_dir,Config),""), + TracerDataFun= + fun(N)->{N,[{trace,{file,filename:join([PrivDir,"mcf1_"++atom_to_list(N)])}}, + {ti,{file,filename:join([PrivDir,"mcf1_"++atom_to_list(N)++".ti"])}}]} + end, + TracerDataList=lists:map(TracerDataFun,Nodes), + start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}), + %% Now initialize meta tracing, but the call_func is a bit "fixed". + ?l {ok,NodeResults1}= + inviso:tpm(Nodes,math,module_info,1,[], + {?MODULE,meta_cleanfunc_initfunc_1}, + {?MODULE,meta_cleanfunc_callfunc_1}, + void,void), + ?l true=check_noderesults(Nodes,{ok,1},NodeResults1), + %% Nothing in the "our" part of the public loop data. + ?l true=check_on_nodes(Nodes, + inviso_rt_meta,get_state,[inviso_rt_meta], + fun({ok,_LD,{{_,[]},_}})->true end), + ?l lists:foreach(fun(N)->rpc:call(N,math,module_info,[exports]) end,Nodes), + %% Check that it has been added to the public loopdata structure. + ?l true=check_on_nodes(Nodes, + ?MODULE,poll,[inviso_rt_meta, + get_state, + [inviso_rt_meta], + fun({ok,_LD,{{_,[{meta_cleanfunc_test1,_Now}]},_}})-> + true; + (_)->false + end, + 20], + ok), + %% While we wait for 60 seconds to pass, we test a few other things. + ?l {ok,NodeResults2}= + inviso:tpm(Nodes,?MODULE,slowfunction2,0,[{'_',[],[{return_trace}]}], + {?MODULE,meta_cleanfunc_initfunc_2}, + {?MODULE,meta_cleanfunc_callfunc_2}, + {?MODULE,meta_cleanfunc_returnfunc_2}, + void), + ?l true=check_noderesults(Nodes,{ok,1},NodeResults2), + ?l lists:foreach(fun(N)->rpc:call(N,?MODULE,slowfunction,[]) end,Nodes), + %% Believe it or not but slowfunction is still running, in its own process, + %% we are therefore free now to examine the meta tracer. + ?l true=check_on_nodes(Nodes, + ?MODULE,poll,[inviso_rt_meta, + get_state, + [inviso_rt_meta], + fun({ok,_LD,{{[],Tuples},_}})-> + {value,_}= + lists:keysearch(meta_cleanfunc_test2, + 1, + Tuples), + {value,_}= + lists:keysearch(meta_cleanfunc_test1, + 1, + Tuples), + true; + (_)-> + false + end, + 20], + ok), + %% Now we wait for slowfunction to return and that the meta_cleanfunc_test2 + %% to be removed from public loopdata strucuture. + ?l timer:sleep(10000), + %% The only thing remaining should be the meta_cleanfunc_test1 which will not + %% go away for less than that the clean functionality removes it. + ?l true=check_on_nodes(Nodes, + ?MODULE,poll,[inviso_rt_meta, + get_state, + [inviso_rt_meta], + fun({ok,_LD,{{_,[{meta_cleanfunc_test1,_Now}]},_}})-> + true; + (_)-> + false + end, + 20], + ok), + %% Wait for the clean function to clean meta_cleanfunc_test1 away. + ?l timer:sleep(51000), % Shall be gone after 5 seconds. + ?l true=check_on_nodes(Nodes, + ?MODULE,poll,[inviso_rt_meta, + get_state, + [inviso_rt_meta], + fun({ok,_LD,{{_,[]},_}})->true; + (_)->false + end, + 20], + ok), + stop_tracing(Nodes), + stop(Nodes), + ok. + +%% This function acts as tpm initialization function when we are going to test +%% that the clean function works. Note that we here assume standard public loop +%% datastructure. +meta_cleanfunc_initfunc_1(_M,_F,_Arity,{E1,_E2}) -> + {ok,{E1,[]},void}. +%% Function that is supposed to be called when the meta traced function is +%% called. +meta_cleanfunc_callfunc_1(_Pid,_Args,{{E1,E2},Global}) -> + {ok,{{E1,[{meta_cleanfunc_test1,now()}|E2]},Global},void}. + +meta_cleanfunc_initfunc_2(_M,_F,_Arity,PublLD) -> + {ok,PublLD,void}. +meta_cleanfunc_callfunc_2(_Pid,_Args,{{E1,E2},Global}) -> + {ok,{{E1,[{meta_cleanfunc_test2,now()}|E2]},Global},void}. +meta_cleanfunc_returnfunc_2(_Pid,_,{{E1,E2},Global}) -> + {value,_}=lists:keysearch(meta_cleanfunc_test2,1,E2), + {ok,{{E1,lists:keydelete(meta_cleanfunc_test2,1,E2)},Global},void}. + +slowfunction() -> + spawn(?MODULE,slowfunction1,[]). +slowfunction1() -> + slowfunction2(). % Meta trace on this function call. +slowfunction2() -> + timer:sleep(2000), + true. +%% ----------------------------------------------------------------------------- + +%% TEST CASE: Testing that a runtime component can be started instructing it +%% to use a handler fun. Checks that the handler fun is called if a trace +%% message comes in. +basic_handlerfun_dist_1(suite) -> []; +basic_handlerfun_dist_1(doc) -> + [""]; +basic_handlerfun_dist_1(Config) when is_list(Config) -> + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + ?l lists:foreach(fun(N)->rpc:call(N,ets,insert,[inviso_sideeffect_tab,{bhf1,0}]) end, + Nodes), + TracerDataFun= + fun(N)->{N,{fun basic_handlerfun_dist_1_fun/2,inviso_sideeffect_tab}} end, + TracerDataList=lists:map(TracerDataFun,Nodes), + start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}), + activate_local_tracing(Nodes), + activate_traceflags(Nodes), + ?l lists:foreach(fun(N)->[{bhf1,0}]= + rpc:call(N,ets,lookup,[inviso_sideeffect_tab,bhf1]) + end, + Nodes), + ?l inviso_test_proc ! {apply,code,which,[lists]}, + ok=poll(ets,lookup,[inviso_sideeffect_tab,bhf1],[{bhf1,1}],20), + deactivate_traceflags(Nodes), + deactivate_local_tracing(Nodes), + stop_tracing(Nodes), + timer:sleep(100), + ?l [{bhf1,1}]=ets:lookup(inviso_sideeffect_tab,bhf1), + stop(Nodes), + ok. + +%% Function used as handler fun for testcase above. +basic_handlerfun_dist_1_fun(_Msg,TId) -> + ets:update_counter(TId,bhf1,1), + TId. +%% ----------------------------------------------------------------------------- + +%% TEST CASE: Here we test that delete_log removes the files at the involved +%% runtime nodes. In this case we test that we remove logs according to last +%% used tracer data. +delete_log_dist_1(suite) -> []; +delete_log_dist_1(doc) -> [""]; +delete_log_dist_1(Config) when is_list(Config) -> + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + PrivDir=filename:join(?config(priv_dir,Config),""), + TracerDataFun= + fun(N)->{N,[{trace,{file,filename:join([PrivDir,"dl1_"++atom_to_list(N)])}}, + {ti,{file,filename:join([PrivDir,"dl1_"++atom_to_list(N)++".ti"])}}]} + end, + TracerDataList=lists:map(TracerDataFun,Nodes), + start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}), + ?l Files=lists:map(fun({N,TD})-> + ?l {value,{_,{_,TraceFile}}}=lists:keysearch(trace,1,TD), + ?l {value,{_,{_,TiFile}}}=lists:keysearch(ti,1,TD), + ?l {N,{TraceFile,TiFile}} + end, + TracerDataList), + io:format("The Files is:~w~n",[Files]), + ?l {ok,NodeResults1}=inviso:delete_log(Nodes), % Should not work! + ?l true=check_noderesults(Nodes,{error,tracing},NodeResults1), + stop_tracing(Nodes), + %% Files still here. + ?l lists:foreach(fun({N,{F1,F2}})-> + ?l {ok,_}=rpc:call(N,file,read_file_info,[F1]), + ?l {ok,_}=rpc:call(N,file,read_file_info,[F2]) + end, + Files), + ?l {ok,NodeResults2}=inviso:delete_log(Nodes), + ?l true=check_noderesults(Nodes, + fun({_N,{ok,LogInfos}})-> + ?l {value,{_,[{ok,_FName1}]}}= + lists:keysearch(trace_log,1,LogInfos), + ?l {value,{_,[{ok,_FName2}]}}= + lists:keysearch(ti_log,1,LogInfos), + true + end, + NodeResults2), + %% The files shall be gone now. + ?l lists:foreach(fun({N,{F1,F2}})-> + ?l {error,enoent}=rpc:call(N,file,read_file_info,[F1]), + ?l {error,enoent}=rpc:call(N,file,read_file_info,[F2]) + end, + Files), + stop(Nodes), + ok. +%% ----------------------------------------------------------------------------- + + +%% TEST CASE: Test of the autostart behaviour of the runtime component. +%% Here we test that a runtime component is started according to the autostart.conf +%% file. Note that the repeat parameter is set to 2. +autostart_dist_1(suite) -> []; +autostart_dist_1(doc) -> + [""]; +autostart_dist_1(Config) when is_list(Config) -> + RemoteNodes=get_remotenodes_config(Config), + PrivDir=filename:join(?config(priv_dir,Config),""), + AutoConfFile=filename:join(PrivDir,"autostart1.conf"), + [RNode|_]=RemoteNodes, + ?l ok=rpc:call(RNode,application,stop,[runtime_tools]), + ?l ok=rpc:call(RNode,application,set_env,[runtime_tools, + inviso_autostart_conf, + AutoConfFile]), + ?l {ok,FD}=file:open(AutoConfFile,[write]), + ?l ok=io:format(FD,"~w.~n~w.~n",[{repeat,2},{tag,c_ref}]), + ?l file:close(FD), + ?l ok=rpc:call(RNode,application,start,[runtime_tools]), + timer:sleep(1000), + ?l P1=rpc:call(RNode,erlang,whereis,[inviso_rt]), + ?l true=is_pid(P1), + ?l rpc:call(RNode,erlang,exit,[P1,kill]), + ?l ok=rpc:call(RNode,application,stop,[runtime_tools]), + ?l ok=rpc:call(RNode,application,start,[runtime_tools]), + timer:sleep(1000), + ?l P2=rpc:call(RNode,erlang,whereis,[inviso_rt]), + ?l true=is_pid(P2), + ?l rpc:call(RNode,erlang,exit,[P2,kill]), + ?l ok=rpc:call(RNode,application,stop,[runtime_tools]), + ?l ok=rpc:call(RNode,application,start,[runtime_tools]), + timer:sleep(1000), + ?l undefined=rpc:call(RNode,erlang,whereis,[inviso_rt]), + ok. +%% ----------------------------------------------------------------------------- + +%% TEST CASE: Test of autostart. Here we focus on that an autostarted +%% runtime component actually follows the trace case command file and +%% initiates tracing. +autostart_dist_2(suite) -> []; +autostart_dist_2(doc) -> + [""]; +autostart_dist_2(Config) when is_list(Config) -> + RemoteNodes=get_remotenodes_config(Config), + PrivDir=filename:join(?config(priv_dir,Config),""), + AutoConfFile=filename:join(PrivDir,"autostart2.conf"), + [RNode|_]=RemoteNodes, + ?l ok=rpc:call(RNode,application,stop,[runtime_tools]), + ?l ok=rpc:call(RNode,application,set_env,[runtime_tools, + inviso_autostart_conf, + AutoConfFile]), + ?l CmdFileName=filename:join(PrivDir,"autostart_cmd_as1"), + ?l {ok,FD}=file:open(CmdFileName,[write]), + ?l ok=io:format(FD, + "inviso:tpl(Nodes,M,F,Arity,[]).~n" + "inviso:tf(Nodes,inviso_test_proc,[call]).~n", + []), + ?l file:close(FD), + ?l TraceFileName=filename:join([PrivDir,"as1_"++atom_to_list(RNode)]), + ?l TiFileName=filename:join([PrivDir,"as1_"++atom_to_list(RNode)++".ti"]), + ?l inviso_as_lib:setup_autostart(RNode, + 2, + [], + [{trace,{file,TraceFileName}}, + {ti,{file,TiFileName}}], + [[CmdFileName]], + [{'M',code},{'F',which},{'Arity',1}], + [{{inviso,tpl,5},{inviso_rt,tpl,{erlang,tl}}}, + {{inviso,tf,3},{inviso_rt,tf,{erlang,tl}}}]), + ?l TestP=spawn(RNode,?MODULE,test_proc_init,[]), + ?l ok=rpc:call(RNode,application,start,[runtime_tools]), + ?l timer:sleep(1000), + ?l {ok,_}=file:read_file_info(TraceFileName), + ?l {ok,_}=file:read_file_info(TiFileName), + ?l true=is_pid(P=rpc:call(RNode,erlang,whereis,[inviso_rt])), + ?l ok=poll(rpc,call,[RNode,erlang,trace_info,[{code,which,1},traced]],{traced,local},10), + ?l {flags,[call]}=rpc:call(RNode,erlang,trace_info,[TestP,flags]), + ?l rpc:call(RNode,erlang,exit,[P,kill]), + ok. +%% ----------------------------------------------------------------------------- + +%% TEST CASE: Here we test that an autostarted runtime component with a dependency +%% to a specific control component tries to connect to that control component +%% during its start-up. +autostart_dist_3(suite) -> []; +autostart_dist_3(doc) -> + [""]; +autostart_dist_3(Config) when is_list(Config) -> + RemoteNodes=get_remotenodes_config(Config), + PrivDir=filename:join(?config(priv_dir,Config),""), + AutoConfFile=filename:join(PrivDir,"autostart3.conf"), + [RNode|_]=RemoteNodes, + ?l ok=rpc:call(RNode,application,stop,[runtime_tools]), + ?l ok=rpc:call(RNode,application,set_env,[runtime_tools, + inviso_autostart_conf, + AutoConfFile]), + ?l {ok,FD}=file:open(AutoConfFile,[write]), + ?l ok=io:format(FD,"~w.~n~w.~n~w.~n", + [{options,[{dependency,{infinity,node()}}]},{repeat,2},{tag,c_ref}]), + ?l file:close(FD), + %% Now start inviso at this node here for the runtime to connect. + ?l {ok,_Pid}=inviso:start(), + ?l ok=poll(erlang,whereis,[inviso_c],fun(P) when is_pid(P)->true;(_)->false end,10), + %% Make the runtime component start. + ?l ok=rpc:call(RNode,application,start,[runtime_tools]), + ?l ok=poll(rpc,call,[RNode,erlang,whereis,[inviso_rt]], + fun(P) when is_pid(P)->true;(_)->false end,10), + %% Check that the runtime component started. + ?l ok=poll(inviso,get_status,[[RNode]],{ok,[{RNode,{ok,{new,running}}}]},20), +% ?l {ok,[{RNode,{ok,{new,running}}}]}=inviso:get_status([RNode]), + stop([RNode]), + ok. +%% ----------------------------------------------------------------------------- + + + +%% TEST CASE: Test of the dependency mechanism in the runtime component. +%% Default behaviour is dependency=infinity, i.e the runtime components remains. +%% We also test here that we can reconnect to the runtime. +running_alone_dist_1(suite) -> []; +running_alone_dist_1(doc) -> + [""]; +running_alone_dist_1(Config) when is_list(Config) -> + ?l {ok,_Pid1}=inviso:start(), % Start a control component. + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,[]), + ?l true=check_noderesults(Nodes,{ok,new},NodeResults1), + ?l shutdown=inviso:stop(), % Stop the control component! + ?l undefined=whereis(inviso_c), + timer:sleep(3000), % How long shall we wait? :-) + ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end, + Nodes), + ?l {ok,_Pid2}=inviso:start(), + ?l {ok,NodeResults2}=inviso:add_nodes(Nodes,b_ref,[]), + ?l true=check_noderesults(Nodes,{ok,{adopted,new,running,a_ref}},NodeResults2), + stop(Nodes), + ok. +%% ----------------------------------------------------------------------------- + +%% TEST CASE: Test of the dependency mechanism in the runtime component. +%% Test that the runtime components terminates after the specified 5000 ms. +running_alone_dist_2(suite) -> []; +running_alone_dist_2(doc) -> + [""]; +running_alone_dist_2(Config) when is_list(Config) -> + ?l {ok,_Pid1}=inviso:start(), % Start a control component. + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,[{dependency,5000}]), + ?l true=check_noderesults(Nodes,{ok,new},NodeResults1), + ?l shutdown=inviso:stop(), % Stop the control component! + ?l undefined=whereis(inviso_c), + timer:sleep(2000), + ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end, + Nodes), + timer:sleep(4000), % Now they shall be dead! + ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt]) end, + Nodes), + ok. +%% ----------------------------------------------------------------------------- + +%% TEST CASE: Test of the dependency mechanism in the runtime component. +%% Test that the runtime components terminates after the specified 5000 ms. +running_alone_dist_3(suite) -> []; +running_alone_dist_3(doc) -> + [""]; +running_alone_dist_3(Config) when is_list(Config) -> + ?l {ok,_Pid1}=inviso:start(), % Start a control component. + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,[{dependency,1000}]), + ?l true=check_noderesults(Nodes,{ok,new},NodeResults1), + ?l {ok,NodeResults2}=inviso:change_options(Nodes,[{dependency,5000}]), + ?l true=check_noderesults(Nodes,ok,NodeResults2), + ?l shutdown=inviso:stop(), % Stop the control component! + ?l undefined=whereis(inviso_c), + timer:sleep(3000), + ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end, + Nodes), + timer:sleep(3000), % Now they shall be dead! + ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt]) end, + Nodes), + ok. +%% ----------------------------------------------------------------------------- + +%% TEST CASE: Test of the dependency mechanism in the runtime component. +%% Test that the runtime components terminates after the specified 5000 ms, +%% like we did in running_alone_dist_2. But now we also start tracing and checks +%% that all inviso processes actually disappears when the time-out is reached. +running_alone_dist_4(suite) -> []; +running_alone_dist_4(doc) -> + [""]; +running_alone_dist_4(Config) when is_list(Config) -> + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + %% Start some tracing! + PrivDir=filename:join(?config(priv_dir,Config),""), + TracerDataFun= + fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf_ra4"++atom_to_list(N)])}}, + {ti,{file,filename:join([PrivDir,"tf_ra4_"++atom_to_list(N)++".ti"])}}]} + end, + TracerDataList=lists:map(TracerDataFun,Nodes), + start_and_init_tracing2(Nodes, + [{dependency,5000}], + TracerDataList, + {ok,[{trace_log,ok},{ti_log,ok}]}), + + ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end, + Nodes), + ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt_meta])) end, + Nodes), + %% Stop control component and wait for the runtimes to terminate after + %% running alone timer has expired. + ?l shutdown=inviso:stop(), % Stop the control component! + ?l undefined=whereis(inviso_c), + timer:sleep(2000), + ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end, + Nodes), + ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt_meta])) end, + Nodes), + timer:sleep(4000), % Now they shall be dead! + ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt]) end, + Nodes), + ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt_meta]) end, + Nodes), + ok. +%% ----------------------------------------------------------------------------- + +%% TEST CASE: Test of the dependency mechanism in the runtime component. +%% Test that the runtime components terminates imeediately when the control +%% component is stopped. Check that all processes are gone. +running_alone_dist_5(suite) -> []; +running_alone_dist_5(doc) -> + [""]; +running_alone_dist_5(Config) when is_list(Config) -> + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + %% Start some tracing! + PrivDir=filename:join(?config(priv_dir,Config),""), + TracerDataFun= + fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf_ra5"++atom_to_list(N)])}}, + {ti,{file,filename:join([PrivDir,"tf_ra5_"++atom_to_list(N)++".ti"])}}]} + end, + TracerDataList=lists:map(TracerDataFun,Nodes), + start_and_init_tracing2(Nodes, + [{dependency,0}], + TracerDataList, + {ok,[{trace_log,ok},{ti_log,ok}]}), + + ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end, + Nodes), + ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt_meta])) end, + Nodes), + %% Stop control component and check that all runtime component processes have + %% terminate more or less immediately afterwards, since dependency==0. + ?l shutdown=inviso:stop(), % Stop the control component! + timer:sleep(100), + ?l undefined=whereis(inviso_c), + timer:sleep(500), + ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt]) end, + Nodes), + ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt_meta]) end, + Nodes), + ok. +%% ----------------------------------------------------------------------------- + +%% TEST CASE: Test of the overload protection mechanism. The mechanism checks +%% for overload using the callback approximately at the interval specified. +%% Check that it does not start protection until start of tracing. +overload_dist_1(suite) -> []; +overload_dist_1(doc) -> + [""]; +overload_dist_1(Config) when is_list(Config) -> + ?l {ok,_Pid1}=inviso:start(), % Start a control component. + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + ?l lists:foreach(fun(N)->true=rpc:call(N,ets,insert,[inviso_sideeffect_tab,{ovl1,0}]) end, + Nodes), % Initiate the counter. + ?l {ok,NodeResults1}=inviso:add_nodes(Nodes, + a_ref, + [{overload,{{?MODULE,overload1},500}}]), + ?l true=check_noderesults(Nodes,{ok,new},NodeResults1), + timer:sleep(1000), % Give the loadcheck time to perform. + ?l [{_,0}]=ets:lookup(inviso_sideeffect_tab,ovl1), % Nothing should have happened. + + %% Overload check shall not start until we start tracing. + PrivDir=filename:join(?config(priv_dir,Config),""), + TracerDataList=lists:map(fun(N)->{N,[{trace, + {file,filename:join([PrivDir, + "tf_ovl1."++atom_to_list(N) + ])}}]} + end, + Nodes), + ?l {ok,NodeResults2}=inviso:init_tracing(TracerDataList), + ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok}]},NodeResults2), + timer:sleep(1500), % Give the loadcheck time to perform. + ?l [{_,N}]=ets:lookup(inviso_sideeffect_tab,ovl1), + ?l true=(N>=2), % After 1,5 seconds, at least 2 checks. + + %% Now change options and remove overload checking! + ?l {ok,NodeResults3}=inviso:change_options(Nodes,[overload]), + ?l true=check_noderesults(Nodes,ok,NodeResults3), + ?l [{_,N2}]=ets:lookup(inviso_sideeffect_tab,ovl1), + timer:sleep(1000), + ?l [{_,N2}]=ets:lookup(inviso_sideeffect_tab,ovl1), % No more loadchecks! + + stop_tracing(Nodes), + stop(Nodes), + ok. +%% ----------------------------------------------------------------------------- + +%% TEST CASE: Test of the overload protection mechanism. In this case we focus +%% in that the init and remove functions are carried out at change_options and +%% when starting and stoping the runtime component. +overload_dist_2(suite) -> []; +overload_dist_2(doc) -> + [""]; +overload_dist_2(Config) when is_list(Config) -> + ?l {ok,_Pid1}=inviso:start(), % Start a control component. + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + ?l {ok,NodeResults1}=inviso:add_nodes(Nodes, + a_ref, + [{overload,{{?MODULE,overload2}, + 500, + {?MODULE,overload2i,[]}, + {?MODULE,overload2r,[]}}}]), + ?l true=check_noderesults(Nodes,{ok,new},NodeResults1), + ?l [{_,0}]=ets:lookup(inviso_sideeffect_tab,ovl2), + + PrivDir=filename:join(?config(priv_dir,Config),""), + TracerDataList=lists:map(fun(N)->{N,[{trace, + {file,filename:join([PrivDir, + "tf_ovl2."++atom_to_list(N) + ])}}]} + end, + Nodes), + ?l {ok,NodeResults2}=inviso:init_tracing(TracerDataList), + ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok}]},NodeResults2), + timer:sleep(1500), % Give the loadcheck time to perform. + ?l [{_,N}]=ets:lookup(inviso_sideeffect_tab,ovl2), + io:format("� is:~p~n",[N]), + ?l true=(N>=2), % After 1,5 seconds, at least 2 checks. + ?l {ok,NodeResults3}=inviso:change_options(Nodes,[{overload,{{?MODULE,overload3}, + 500, + {?MODULE,overload3i,[]}, + {?MODULE,overload3r,[]}}}]), + ?l true=check_noderesults(Nodes,ok,NodeResults3), + ?l []=ets:lookup(inviso_sideeffect_tab,ovl2), + timer:sleep(1500), + ?l [{_,N2}]=ets:lookup(inviso_sideeffect_tab,ovl3), + ?l true=(N2>=2), % After 1,5 seconds, at least 2 checks. + stop_tracing(Nodes), + ?l []=ets:lookup(inviso_sideeffect_tab,ovl3r), % Remove function shall not be called. + ?l [{_,N3}]=ets:lookup(inviso_sideeffect_tab,ovl3), + timer:sleep(1000), % Check that overloadchecking has stopped. + ?l [{_,N3}]=ets:lookup(inviso_sideeffect_tab,ovl3), + stop(Nodes), + ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,ovl3r],[{ovl3r,done}],20), + ok. +%% ----------------------------------------------------------------------------- + +%% TEST CASE: Test of the overload protections mechanism. Here we focus on testing +%% that if overload is reached tracing is really suspended. +overload_dist_3(suite) -> []; +overload_dist_3(doc) -> + [""]; +overload_dist_3(Config) when is_list(Config) -> + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + PrivDir=filename:join(?config(priv_dir,Config),""), + TracerDataList= + lists:map(fun(N)->{N,[{trace,{file,filename:join([PrivDir, + "tf_ovl3."++atom_to_list(N)])}}, + {ti,{file,filename:join([PrivDir, + "tf_ovl3_ti."++atom_to_list(N)])}}]} + end, + Nodes), + ?l lists:foreach(fun(N)-> + true=rpc:call(N,ets,insert,[inviso_sideeffect_tab,{ovl4,0}]) + end, + Nodes), + start_and_init_tracing2(Nodes, + [{overload,{{?MODULE,overload4},500}}], + TracerDataList, + {ok,[{trace_log,ok},{ti_log,ok}]}), + activate_local_tracing(Nodes), + activate_meta_tracing(Nodes), + activate_traceflags(Nodes), + timer:sleep(600), + ?l [{_,N1}]=ets:lookup(inviso_sideeffect_tab,ovl4), + ?l true=(N1>=1), % Overload check has been done! + ?l Node=node(), + ?l {ok,[{Node,{ok,{tracing,running}}}]}=inviso:get_status([node()]), + ?l true=ets:insert(inviso_sideeffect_tab,{ovl4_suspend,true}), + timer:sleep(600), + ?l {ok,[{Node,{ok,{tracing,{suspended,test}}}}]}=inviso:get_status([node()]), + ?l [{_,N2}]=ets:lookup(inviso_sideeffect_tab,ovl4), + ?l {flags,[]}=erlang:trace_info(whereis(inviso_test_proc),flags), + ?l {meta,false}=erlang:trace_info({lists,module_info,0},meta), + ?l {traced,local}=erlang:trace_info({code,which,1},traced), + ?l true=(is_pid(whereis(inviso_rt_meta))), + ?l true=ets:delete(inviso_sideeffect_tab,ovl4_suspend), + timer:sleep(600), + ?l [{_,N2}]=ets:lookup(inviso_sideeffect_tab,ovl4), % No checking while suspended! + ?l {ok,[{Node,ok}]}=inviso:cancel_suspension([node()]), + ?l {ok,NodeResults1}=inviso:get_status(Nodes), + ?l true=check_noderesults(Nodes,{ok,{tracing,running}},NodeResults1), + timer:sleep(600), + ?l [{_,N3}]=ets:lookup(inviso_sideeffect_tab,ovl4), + ?l true=(N3>N2), + ?l deactivate_local_tracing(Nodes), + ?l stop_tracing(Nodes), + ?l stop(Nodes), + ok. +%% ----------------------------------------------------------------------------- + +%% TEST CASE. Test that the overload mechanism is triggered by to the runtime +%% component incomming messages, and nothing else. +overload_dist_4(suite) -> []; +overload_dist_4(doc) -> + [""]; +overload_dist_4(Config) when is_list(Config) -> + ?l {ok,_Pid1}=inviso:start(), % Start a control component. + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + ?l {ok,NodeResults1}=inviso:add_nodes(Nodes, + a_ref, + [{overload,{{?MODULE,overload5}, + infinity, + {?MODULE,overload5i,[]}, + {?MODULE,overload5r,[]}}}]), + ?l true=check_noderesults(Nodes,{ok,new},NodeResults1), + ?l [{_,0}]=ets:lookup(inviso_sideeffect_tab,ovl5), + + PrivDir=filename:join(?config(priv_dir,Config),""), + TracerDataList=lists:map(fun(N)->{N,[{trace, + {file,filename:join([PrivDir, + "tf_ovl4."++atom_to_list(N) + ])}}]} + end, + Nodes), + ?l {ok,NodeResults2}=inviso:init_tracing(TracerDataList), + ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok}]},NodeResults2), + timer:sleep(2000), % Give the loadcheck time to perform. + ?l [{_,N}]=ets:lookup(inviso_sideeffect_tab,ovl5), + ?l true=(N==0), % And nothing shall have happend! + %% Now we send a message to the inviso_rt, then the load check function + %% shall be called. + ?l whereis(inviso_rt) ! test_of_loadcheck, + timer:sleep(200), % Make sure the inviso_rt gets scheduled. + ?l [{_,1}]=ets:lookup(inviso_sideeffect_tab,ovl5), + stop_tracing(Nodes), + ?l []=ets:lookup(inviso_sideeffect_tab,ovl5r), % Remove function shall not be called. + ?l [{_,N3}]=ets:lookup(inviso_sideeffect_tab,ovl5), + ?l whereis(inviso_rt) ! test_of_loadcheck, + timer:sleep(1000), % Check that overloadchecking has stopped. + ?l [{_,N3}]=ets:lookup(inviso_sideeffect_tab,ovl5), + stop(Nodes), + ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,ovl5r],[{ovl5r,done}],20), + ok. +%% ----------------------------------------------------------------------------- + +%% TEST CASE. Test that the overload mechanism correctly calculates remaining time +%% to next load check if a message comes into the runtime component "interupting" +%% the waiting for loadcheck timeout. (Loadcheck timeout is implemented as an after +%% in the receive). +overload_dist_5(suite) -> []; +overload_dist_5(doc) -> + [""]; +overload_dist_5(Config) when is_list(Config) -> + ?l {ok,_Pid1}=inviso:start(), % Start a control component. + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + ?l lists:foreach(fun(N)->true=rpc:call(N,ets,insert,[inviso_sideeffect_tab,{ovl6,0}]) end, + Nodes), % Initiate the counter. + ?l {ok,NodeResults1}=inviso:add_nodes(Nodes, + a_ref, + [{overload,{{?MODULE,overload6},1000}}]), + ?l true=check_noderesults(Nodes,{ok,new},NodeResults1), + %% Overload check shall not start until we start tracing. + PrivDir=filename:join(?config(priv_dir,Config),""), + TracerDataList=lists:map(fun(N)->{N,[{trace, + {file,filename:join([PrivDir, + "tf_ovl5."++atom_to_list(N) + ])}}]} + end, + Nodes), + ?l {ok,NodeResults2}=inviso:init_tracing(TracerDataList), + ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok}]},NodeResults2), + ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,ovl6],[{ovl6,2}],25), + %% Now we know that exactly 2 checks have been made. Try to Distract the runtime :-) + ?l inviso_rt:state(whereis(inviso_rt)), % Make it have to receive a message. + timer:sleep(500), + ?l [{_,2}]=ets:lookup(inviso_sideeffect_tab,ovl6), % Should still be 2. + timer:sleep(600), + ?l [{_,3}]=ets:lookup(inviso_sideeffect_tab,ovl6), % We expect yet one check. + timer:sleep(1100), + ?l [{_,4}]=ets:lookup(inviso_sideeffect_tab,ovl6), + + stop_tracing(Nodes), + stop(Nodes), + ok. +%% ----------------------------------------------------------------------------- + + +%% TEST CASE: Test of the subscription mechanism. +subscribe_dist_1(Config) when is_list(Config) -> + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + PrivDir=filename:join(?config(priv_dir,Config),""), + Pid=spawn(?MODULE,inviso_msg_collector,[]), + CtrlPid=whereis(inviso_c), + + ?l {ok,_Pid}=inviso:start(), % Start a control component. + ?l ok=inviso:subscribe(Pid), + ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,[]), + ?l true=check_noderesults(Nodes,{ok,new},NodeResults1), + ?l {ok,NodeResults2}=inviso:get_status(Nodes), + ?l true=check_noderesults(Nodes,{ok,{new,running}},NodeResults2), + check_msg_collector(Nodes, + fun({inviso_event,CP,_,{connected,N,{_Tag,{idle,running}}}}) + when CP==CtrlPid -> + {true,N}; + (_) -> + false + end, + 13), + TracerDataList=lists:map(fun(N)->{N,{file, + filename:join([PrivDir, + "tf_sub1"++atom_to_list(N)])}} + end, + Nodes), + ?l {ok,NodeResults3}=inviso:init_tracing(TracerDataList), + ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok}]},NodeResults3), + check_msg_collector(Nodes, + fun({inviso_event,CP,_,{state_change,N,{tracing,running}}}) + when CP==CtrlPid -> + {true,N}; + (_) -> + false + end, + 13), + ?l {ok,NodeResults4}=inviso:suspend(Nodes,test), + ?l true=check_noderesults(Nodes,ok,NodeResults4), + check_msg_collector(Nodes, + fun({inviso_event,CP,_,{state_change,N,{tracing,{suspended,test}}}}) + when CP==CtrlPid -> + {true,N}; + (_) -> + false + end, + 13), + ?l [RNode|_]=RemoteNodes, + ?l RInvisoPid=rpc:call(RNode,erlang,whereis,[inviso_rt]), + ?l rpc:call(RNode,erlang,exit,[RInvisoPid,kill]), + check_msg_collector([RNode], + fun({inviso_event,CP,_,{disconnected,N,_Info}}) + when CP==CtrlPid -> + {true,N}; + (_) -> + false + end, + 11), + + ?l {ok,_NodeResults5}=inviso:stop_tracing(Nodes), + ?l {ok,_NodeResults6}=inviso:stop_nodes(Nodes), + ?l shutdown=inviso:stop(), + ok. +%% ----------------------------------------------------------------------------- + + +%% TEST CASE: fetch_log test of single straight trace_log file in distributed +%% environment. +fetch_log_dist_trace_1(suite) -> []; +fetch_log_dist_trace_1(doc) -> + ["fetch_log test of single straight trace_log file in distributed" + "environment."]; +fetch_log_dist_trace_1(Config) when is_list(Config) -> + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + PrivDir=filename:join(?config(priv_dir,Config),""), + TracerDataList=lists:map(fun(N)->{N,[{trace,{file,filename:join([PrivDir, + "testfile1."++ + atom_to_list(N) + ])}}]} end, + Nodes), + start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}), + + %% Put some output in the logs. + ?l inviso:tp(Nodes,math,module_info,0,[]), + ?l inviso:tf(Nodes,all,[call]), + ?l lists:foreach(fun(N)->rpc:call(N,math,module_info,[]) end,Nodes), + + stop_tracing(Nodes), + {H,M,S}=time(), + FetchToDir=filename:join([PrivDir, + "fetch_log_test1_"++integer_to_list(H)++"_"++ + integer_to_list(M)++"_"++integer_to_list(S)]), + ?l ok=file:make_dir(FetchToDir), + ?l {ok,NodeResults}=inviso:fetch_log(RemoteNodes,FetchToDir,"p1"), + io:format("~p~n",[NodeResults]), + ?l true=check_noderesults(RemoteNodes, + fun({N,{complete,[{trace_log,[{ok,File}]},{ti_log,[]}]}}) -> + ?l File="p1testfile1."++atom_to_list(N), + true; + (_)-> + false + end, + NodeResults), + ?l ON=filename:join(PrivDir,"testfile1."), + ?l FN=filename:join(FetchToDir,"p1testfile1."), + ?l lists:foreach(fun(N)-> + {ok,#file_info{size=Size}}= + file:read_file_info(ON++atom_to_list(N)), + {ok,#file_info{size=Size}}= + file:read_file_info(FN++atom_to_list(N)) + end, + RemoteNodes), + %% Now we wish to see that we get an incomplete if we try to fetch to a + %% directory that does not exist. + ?l FetchToErrorDir=filename:join([PrivDir,nonexistingingdir]), + ?l {ok,NodeResults2}=inviso:fetch_log(RemoteNodes,FetchToErrorDir,"p1"), + ?l io:format("NodeResults2:~w~n",[NodeResults2]), + ?l true=check_noderesults(RemoteNodes, + fun({_,{incomplete,_}}) -> + true; + (_)-> + false + end, + NodeResults2), + stop(Nodes), + ok. +%% ----------------------------------------------------------------------------- + +fetch_log_dist_trace_2(suite) -> []; +fetch_log_dist_trace_2(doc) -> + [""]; +fetch_log_dist_trace_2(Config) -> + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + PrivDir=filename:join(?config(priv_dir,Config),""), + + {H,M,S}=time(), + ?l Name="wrap"++integer_to_list(H)++"_"++integer_to_list(M)++"_"++integer_to_list(S), + ?l BaseName=filename:join(PrivDir,Name), + Fun=fun(N)->{N,[{trace,{file,{BaseName++atom_to_list(N),wrap,".log",512,2}}}, + {ti,{file,BaseName++"_ti_"++atom_to_list(N)++".ti"}}]} + end, + ?l TracerDataList=lists:map(Fun,Nodes), + start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}), + fill_and_reach_two_wrapfiles(PrivDir,"^"++Name,Nodes), + + stop_tracing(Nodes), + FetchToDir=filename:join([PrivDir, + "fetch_log_test2_"++integer_to_list(H)++"_"++ + integer_to_list(M)++"_"++integer_to_list(S)]), + ?l ok=file:make_dir(FetchToDir), + ?l {ok,NodeResults}=inviso:fetch_log(RemoteNodes,FetchToDir,"p1"), + io:format("~p~n",[NodeResults]), + CheckFun=fun({N,{complete,[{trace_log,FileResults1},{ti_log,[{ok,TiFile}]}]}}) -> + Fun2=fun({ok,File}) -> + {match,1,_}= + regexp:first_match(File, + "^"++"p1"++Name++atom_to_list(N)), + true; + (_) -> + false + end, + ?l true=lists:all(Fun2,FileResults1), + ?l TiFile="p1"++Name++"_ti_"++atom_to_list(N)++".ti", + true; + (_)-> + false + end, + ?l true=check_noderesults(RemoteNodes,CheckFun,NodeResults), + stop(Nodes), + ok. +%% ----------------------------------------------------------------------------- + +fetch_log_dist_trace_3(suite) -> []; +fetch_log_dist_trace_3(doc) -> + [""]; +fetch_log_dist_trace_3(Config) -> + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + PrivDir=filename:join(?config(priv_dir,Config),""), + + {H,M,S}=time(), + ?l Name="wrap2_"++integer_to_list(H)++"_"++integer_to_list(M)++"_"++integer_to_list(S), + ?l BaseName=filename:join(PrivDir,Name), + Fun=fun(N)->{N,[{trace,{file,{BaseName++atom_to_list(N),wrap,".log",512,2}}}, + {ti,{file,BaseName++"_ti_"++atom_to_list(N)++".ti"}}]} + end, + ?l TracerDataList=lists:map(Fun,Nodes), + start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}), + fill_and_reach_two_wrapfiles(PrivDir,"^"++Name,Nodes), + + stop_tracing(Nodes), + FetchToDir=filename:join([PrivDir, + "fetch_log_test3_"++integer_to_list(H)++"_"++ + integer_to_list(M)++"_"++integer_to_list(S)]), + ?l ok=file:make_dir(FetchToDir), + ?l {ok,NodeResults1}=inviso:list_logs(Nodes), + CheckFun=fun({N,{ok,[{trace_log,PrivDir2,[F1,F2]},{ti_log,PrivDir2,[F3]}]}})-> + PrivDir2=PrivDir, + RegExp="^"++Name++atom_to_list(N)++"[0-9]+"++"\.log", + {match,1,_}=regexp:first_match(F1,RegExp), + {match,1,_}=regexp:first_match(F2,RegExp), + F3=Name++"_ti_"++atom_to_list(N)++".ti", + true; + (_) -> + false + end, + ?l true=check_noderesults(Nodes,CheckFun,NodeResults1), + ?l NodeFileSpecList=lists:map(fun({N,{ok,L}})->{N,L} end, + lists:keydelete(node(),1,NodeResults1)), + ?l {ok,NodeResults2}=inviso:fetch_log(NodeFileSpecList,FetchToDir,"p1"), +io:format("~p~n",[NodeResults2]), + CheckFun2=fun({N,{complete,[{trace_log,FileResults1},{ti_log,[{ok,TiFile}]}]}}) -> + Fun2=fun({ok,File}) -> + {match,1,_}= + regexp:first_match(File, + "^"++"p1"++Name++atom_to_list(N)), + true; + (_) -> + false + end, + ?l true=lists:all(Fun2,FileResults1), + ?l TiFile="p1"++Name++"_ti_"++atom_to_list(N)++".ti", + true; + (_)-> + false + end, + ?l true=check_noderesults(RemoteNodes,CheckFun2,NodeResults2), + stop(Nodes), + ok. +%% ----------------------------------------------------------------------------- + +fetch_log_dist_error_1(suite) -> []; +fetch_log_dist_error_1(doc) -> + [""]; +fetch_log_dist_error_1(Config) when is_list(Config) -> + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + ?l {ok,_Pid}=inviso:start(), % Start a control component. + ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref), + ?l true=check_noderesults(Nodes,{ok,new},NodeResults1), + ?l {ok,NodeResults2}=inviso:fetch_log(RemoteNodes,"foo","bar"), +io:format("~p~n",[NodeResults2]), + ?l true=check_noderesults(RemoteNodes, + fun({_N,{error,no_tracerdata}})->true; + (_)->false + end, + NodeResults2), + stop(Nodes), + ok. +%% ----------------------------------------------------------------------------- + +fetch_log_dist_error_2(suite) -> []; +fetch_log_dist_error_2(doc) -> + [""]; +fetch_log_dist_error_2(Config) when is_list(Config) -> + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + PrivDir=filename:join(?config(priv_dir,Config),""), + ?l {ok,_Pid}=inviso:start(), % Start a control component. + ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref), + ?l true=check_noderesults(Nodes,{ok,new},NodeResults1), + ?l NodeLogList=lists:map(fun(N)->{N,[{trace_log, + PrivDir, + ["f1,fil","f2.fil"]}, + {ti_log, + PrivDir, + ["f.ti"]}]} + end, + RemoteNodes), + ?l {ok,NodeResults2}=inviso:fetch_log(NodeLogList,"foo","bar"), + io:format("~p~n",[NodeResults2]), + ?l true=check_noderesults(RemoteNodes, + fun({_N,{incomplete,_}}) -> + true; + (_) -> + false + end, + NodeResults2), + ?l NodeTracerData=lists:map(fun(N)->{N, + [{trace,{file,filename:join(PrivDir,"foo")}}, + {ti,{file,filename:join(PrivDir,"bar.ti")}}]} + end, + RemoteNodes), + {ok,NodeResults3}=inviso:fetch_log(NodeTracerData,"foo","bar"), + io:format("~p~n",[NodeResults3]), +%% This should work this way. Now it says complete [], which is not entirely +%% incorrect. But to follow the sematics of when fetching named files should +%% say incomplete. +%% Must do some rework to make that work. No real danger leaving it this way +%% for now. +% ?l true=check_noderesults(RemoteNodes, +% fun({_N,{incomplete,_}}) -> +% true; +% (_) -> +% false +% end, +% NodeResults3), + stop(Nodes), + ok. +%% ----------------------------------------------------------------------------- + +%% TEST CASE: This case tests that the log file merger merges files in the +%% correct order, based on the timestamps. +lfm_trace_dist_1(suite) -> []; +lfm_trace_dist_1(doc) -> + [""]; +lfm_trace_dist_1(Config) when is_list(Config) -> + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + [RNode1,RNode2|_]=RemoteNodes, + PrivDir=filename:join(?config(priv_dir,Config),""), + TracerDataList= + lists:map(fun(N)->{N,{file,filename:join([PrivDir,"lfm1_"++atom_to_list(N)])}} end, + Nodes), + start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}), + activate_local_tracing(Nodes), + activate_traceflags(Nodes), + + {inviso_test_proc,RNode2} ! {apply,code,which,[lists]}, + timer:sleep(300), + {inviso_test_proc,RNode1} ! {apply,code,which,[lists]}, + timer:sleep(300), + {inviso_test_proc,RNode1} ! {apply,code,which,[lists]}, + timer:sleep(300), + inviso_test_proc ! {apply,code,which,[lists]}, + timer:sleep(300), + {inviso_test_proc,RNode2} ! {apply,code,which,[lists]}, + timer:sleep(300), + inviso_test_proc ! {apply,code,which,[lists]}, + + deactivate_traceflags(Nodes), + deactivate_local_tracing(Nodes), + stop_tracing(Nodes), + stop(Nodes), + + DestFile=filename:join(PrivDir,"lfm1_out.txt"), + ?l {ok,6}= + inviso_lfm:merge([{node(), + [{trace_log, + [filename:join(PrivDir,"lfm1_"++atom_to_list(node()))]}]}, + {RNode1, + [{trace_log, + [filename:join(PrivDir,"lfm1_"++atom_to_list(RNode1))]}]}, + {RNode2, + [{trace_log, + [filename:join(PrivDir,"lfm1_"++atom_to_list(RNode2))]}]}], + DestFile), + ?l {ok,FD}=file:open(DestFile,[read]), + ?l S1=io:get_line(FD,""), + ?l true=lists:prefix(atom_to_list(RNode2),S1), + ?l S2=io:get_line(FD,""), + ?l true=lists:prefix(atom_to_list(RNode1),S2), + ?l S3=io:get_line(FD,""), + ?l true=lists:prefix(atom_to_list(RNode1),S3), + ?l S4=io:get_line(FD,""), + ?l true=lists:prefix(atom_to_list(node()),S4), + ?l S5=io:get_line(FD,""), + ?l true=lists:prefix(atom_to_list(RNode2),S5), + ?l S6=io:get_line(FD,""), + ?l true=lists:prefix(atom_to_list(node()),S6), + ?l file:close(FD), + ok. +%% ----------------------------------------------------------------------------- + +%% TEST CASE: Testing to the full extent that pid-mappings work with both +%% local and global registration. Also checks that pidmappings can be removed +%% and that consequently the mappings in the resulting merged file stops. +lfm_trace_ti_dist_2(suite) -> []; +lfm_trace_ti_dist_2(doc) -> + [""]; +lfm_trace_ti_dist_2(Config) when is_list(Config) -> + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + [RNode1,RNode2|_]=RemoteNodes, + PrivDir=filename:join(?config(priv_dir,Config),""), + TracerDataList= + lists:map(fun(N)->{N,[{trace,{file,filename:join(PrivDir,"lfm2_"++atom_to_list(N))}}, + {ti,{file,filename:join(PrivDir,"lfm2_ti_"++atom_to_list(N))}}]} + end, + Nodes), + start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}), + activate_local_tracing(Nodes), + activate_meta_tracing(Nodes), + activate_traceflags(Nodes), + + {inviso_test_proc,RNode2} ! {apply,code,which,[lists]}, + timer:sleep(300), + {inviso_test_proc,RNode1} ! {apply,code,which,[lists]}, + timer:sleep(300), + {inviso_test_proc,RNode1} ! {apply,code,which,[lists]}, + timer:sleep(300), + inviso_test_proc ! {apply,code,which,[lists]}, + timer:sleep(300), + + P2=spawn(RNode2,?MODULE,test_proc_loop,[]), + P1=spawn(RNode1,?MODULE,test_proc_loop,[]), + P0=spawn_link(?MODULE,test_proc_loop,[]), + ThisNode=node(), + ?l {ok,[{ThisNode,{ok,[1]}}]}=inviso:tf([node()],P0,[call,timestamp]), + ?l {ok,[{RNode1,{ok,[1]}}]}=inviso:tf([RNode1],P1,[call,timestamp]), + ?l {ok,[{RNode2,{ok,[1]}}]}=inviso:tf([RNode2],P2,[call,timestamp]), + P2 ! {apply,code,which,[lists]}, + timer:sleep(300), + P1 ! {apply,code,which,[lists]}, + timer:sleep(300), + P0 ! {apply,code,which,[lists]}, + timer:sleep(300), + + P3=spawn(RNode2,?MODULE,test_proc_loop,[]), + ?l yes=global:register_name(inviso_test_proc_globalname,P3), + ?l {ok,[{RNode2,{ok,[1]}}]}=inviso:tf([RNode2],P3,[call,timestamp]), + timer:sleep(300), + P3 ! {apply,code,which,[lists]}, + timer:sleep(300), + + P4=rpc:call(RNode1,erlang,whereis,[inviso_test_proc]), + ?l true=rpc:call(RNode1,erlang,unregister,[inviso_test_proc]), + timer:sleep(300), + P4 ! {apply,code,which,[lists]}, + timer:sleep(300), + + ?l true=rpc:call(RNode1,erlang,register,[inviso_test_proc,P4]), + + ?l global:unregister_name(inviso_test_proc_globalname), + timer:sleep(300), + ?l P3 ! {apply,code,which,[lists]}, + timer:sleep(300), + + deactivate_traceflags(Nodes), + deactivate_local_tracing(Nodes), + stop_tracing(Nodes), + stop(Nodes), + + DestFile=filename:join(PrivDir,"lfm2_out.txt"), + ?l {ok,10}= + inviso_lfm:merge([ + {node(), + [{trace_log, + [filename:join(PrivDir,"lfm2_"++atom_to_list(node()))]}, + {ti_log, + [filename:join(PrivDir,"lfm2_ti_"++atom_to_list(node()))]}]}, + {RNode1, + [{trace_log, + [filename:join(PrivDir,"lfm2_"++atom_to_list(RNode1))]}, + {ti_log, + [filename:join(PrivDir,"lfm2_ti_"++atom_to_list(RNode1))]}]}, + {RNode2, + [{trace_log, + [filename:join(PrivDir,"lfm2_"++atom_to_list(RNode2))]}, + {ti_log, + [filename:join(PrivDir,"lfm2_ti_"++atom_to_list(RNode2))]}]} + ], + DestFile), + ?l {ok,FD}=file:open(DestFile,[read]), + ?l S1=io:get_line(FD,""), +io:format("S1 is:~p~n",[S1]), + ?l true=lists:prefix(atom_to_list(RNode2)++" [inviso_test_proc",S1), + ?l S2=io:get_line(FD,""), + ?l true=lists:prefix(atom_to_list(RNode1)++" [inviso_test_proc",S2), + ?l S3=io:get_line(FD,""), + ?l true=lists:prefix(atom_to_list(RNode1)++" [inviso_test_proc",S3), + ?l S4=io:get_line(FD,""), + ?l true=lists:prefix(atom_to_list(node())++" [inviso_test_proc",S4), + ?l S5=io:get_line(FD,""), + ?l true=lists:prefix(atom_to_list(RNode2)++" []",S5), + ?l S6=io:get_line(FD,""), + ?l true=lists:prefix(atom_to_list(RNode1)++" []",S6), + ?l S7=io:get_line(FD,""), + ?l true=lists:prefix(atom_to_list(node())++" []",S7), + ?l S8=io:get_line(FD,""), + ?l true=lists:prefix(atom_to_list(RNode2)++" [{global,inviso_test_proc_globalname}]",S8), + ?l S9=io:get_line(FD,""), + ?l true=lists:prefix(atom_to_list(RNode1)++" []",S9), + ?l S10=io:get_line(FD,""), + ?l true=lists:prefix(atom_to_list(RNode2)++" []",S10), + ?l file:close(FD), + ok. +%% ----------------------------------------------------------------------------- + +%% TEST CASE: This tests that the wrapset sorter works. +handle_logfile_sort_wrapset(suite) -> []; +handle_logfile_sort_wrapset(doc) -> + [""]; +handle_logfile_sort_wrapset(Config) when is_list(Config) -> + File0="prefix10.fil", + File1="prefix11.fil", + File2="prefix12.fil", + File3="prefix13.fil", + ?l [File0,File1,File2,File3]= + inviso_lfm_tpfreader:handle_logfile_sort_wrapset([File2,File1,File0,File3]), + File5="prefix15.fil", + ?l [File5,File0,File1,File2,File3]= + inviso_lfm_tpfreader:handle_logfile_sort_wrapset([File2,File5,File1,File0,File3]), + ok. +%% ----------------------------------------------------------------------------- + +%% TEST CASE: This case tests that the regexp mechanism in the inviso_rt_lib can +%% find modules using regexps and that its only_loaded mechanism works. +%% This test case can not be run when using cover because cover will make the +%% modules no longer loaded from the path containing "runtime_tools". +expand_regexp_dist_1(suite) -> []; +expand_regexp_dist_1(doc) -> + [""]; +expand_regexp_dist_1(Config) when is_list(Config) -> + case ?t:is_cover() of + true -> + {skip,"Cover is running"}; + false -> + expand_regexp_dist_1_nocover(Config) + end. + +expand_regexp_dist_1_nocover(Config) -> + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + [RNode1|_]=RemoteNodes, + ?l NodeResults1=inviso_rt_lib:expand_regexp(Nodes,"^inviso_rt.*",[]), + ?l L1=length(Nodes), + ?l L1=length(NodeResults1), + ?l true=lists:all(fun({_,Mods})-> + ?l 3=length(Mods), + ?l true=lists:member(inviso_rt,Mods), + ?l true=lists:member(inviso_rt_lib,Mods), + ?l true=lists:member(inviso_rt_meta,Mods), + true; + (_) -> + false + end, + NodeResults1), + %% Check the dir-option. In the following inviso_tool_lib shall not be found. + ?l NodeResults2=inviso_rt_lib:expand_regexp(Nodes,"runtime_tools","invi.*lib.*",[]), +?l io:format("NodeResults2:~w~n",[NodeResults2]), + ?l L1=length(NodeResults2), % Same number of nodes replying. + ?l true=lists:all(fun({_,Mods})-> + 2=length(Mods), + true=lists:member(inviso_as_lib,Mods), + true=lists:member(inviso_rt_lib,Mods), + true; + (_) -> + false + end, + NodeResults2), + ?l [{RNode1,[]}]= + inviso_rt_lib:expand_regexp([RNode1],"^inviso_testmodule1.*",[only_loaded]), + ?l [{RNode1,[inviso_testmodule1_foo]}]= + inviso_rt_lib:expand_regexp([RNode1],"^inviso_testmodule1.*",[]), + ok. +%% ----------------------------------------------------------------------------- + + +only_loaded_dist_1(suite) -> []; +only_loaded_dist_1(doc) -> + [""]; +only_loaded_dist_1(Config) when is_list(Config) -> + RemoteNodes=get_remotenodes_config(Config), + Nodes=[node()|RemoteNodes], + [RNode1|_]=RemoteNodes, + PrivDir=filename:join(?config(priv_dir,Config),""), + TracerDataList= + lists:map(fun(N)->{N,[{trace,{file,filename:join(PrivDir,"ol_1_"++atom_to_list(N))}}]} + end, + Nodes), + start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}), + ?l false=rpc:call(RNode1,erlang,module_loaded,[inviso_testmodule1_foo]), + ?l {ok,[{RNode1,{ok,[0]}}]}= + inviso:tpl([RNode1],inviso_testmodule1_foo,'_','_',[],[only_loaded]), + ?l false=rpc:call(RNode1,erlang,module_loaded,[inviso_testmodule1_foo]), + ?l {ok,[{RNode1,{ok,[3]}}]}= + inviso:tpl([RNode1],inviso_testmodule1_foo,'_','_',[],[]), + stop_tracing(Nodes), + stop(Nodes), + ok. + + +%% ============================================================================== +%% Common functions setting up inviso. +%% ============================================================================== + +%% Starts controlcomponent and adds runtime components on the nodes specified. +%% Also initiates tracing on the nodes. +start_and_init_tracing1(Nodes,Options,TracerData,Reply) when is_list(Nodes) -> + ?l {ok,_Pid}=inviso:start(), % Start a control component. + ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,Options), + io:format("~p~n",[NodeResults1]), + ?l true=check_noderesults(Nodes,{ok,new},NodeResults1), + ?l {ok,NodeResults2}=inviso:get_status(Nodes), + ?l true=check_noderesults(Nodes,{ok,{new,running}},NodeResults2), + ?l {ok,NodeResults3}=inviso:init_tracing(Nodes,TracerData), + ?l true=check_noderesults(Nodes,Reply,NodeResults3), + ok. +start_and_init_tracing2(Nodes,Options,TracerDataList,Reply) -> + ?l {ok,_Pid}=inviso:start(), % Start a control component. + ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,Options), + io:format("~p~n",[NodeResults1]), + ?l true=check_noderesults(Nodes,{ok,new},NodeResults1), + ?l {ok,NodeResults2}=inviso:get_status(Nodes), + ?l true=check_noderesults(Nodes,{ok,{new,running}},NodeResults2), + ?l {ok,NodeResults4}=inviso:get_tracerdata(Nodes), + ?l true=check_noderesults(Nodes,{ok,no_tracerdata},NodeResults4), + ?l {ok,NodeResults3}=inviso:init_tracing(TracerDataList), + io:format("Tracerdatalist:~p~n",[TracerDataList]), + ?l true=check_noderesults(Nodes,Reply,NodeResults3), + + ?l Fun1=fun({N,{ok,TD}}) when is_list(TD)-> + ?l {value,{trace,Trace}}=lists:keysearch(trace,1,TD), + ?l {value,{N,TD2}}=lists:keysearch(N,1,TracerDataList), + ?l true=lists:member({trace,Trace},TD2), + %% Check that the trace file really exists. + ?l case Trace of % Trace={file,FilePortParameters} + {file,FileName1} when is_list(FileName1) -> + ?l {ok,_}=rpc:call(N,file,read_file_info,[FileName1]); + _ -> % This should be extended with more cases. + true + end, + ?l case lists:keysearch(ti,1,TD2) of + {value,{_,Ti}} -> % Ok, we have ti too. + ?l {value,{_,Ti}}=lists:keysearch(ti,1,TD), + ?l FileName2=element(2,Ti), + ?l {ok,_}=rpc:call(N,file,read_file_info,[FileName2]), + true; + false -> % No ti, we are done now. + true + end; + ({N,{ok,{file,FileName}}}) -> + ?l {value,{N,{file,FileName}}}=lists:keysearch(N,1,TracerDataList), + ?l {ok,_}=rpc:call(N,file,read_file_info,[FileName]), + true; + ({N,{ok,LogTD}}) -> % The case using a fun. + ?l {value,{N,LogTD}}=lists:keysearch(N,1,TracerDataList), + true + end, + ?l {ok,NodeResults5}=inviso:get_tracerdata(Nodes), + ?l true=check_noderesults(Nodes,Fun1,NodeResults5), + ok. +%% ------------------------------------------------------------------------------ + +%% Stops tracing on Nodes. +stop_tracing(Nodes) when is_list(Nodes) -> + ?l {ok,NodeResults1}=inviso:stop_tracing(Nodes), + ?l true=check_noderesults(Nodes,{ok,idle},NodeResults1), + ?l {ok,NodeResults2}=inviso:get_status(Nodes), + ?l true=check_noderesults(Nodes,{ok,{idle,running}},NodeResults2), + %% The implementation says that the meta tracer shall be stopped when + %% tracing is stopped. Check that. + ?l lists:foreach(fun(N)-> + ok=poll(erlang,whereis,[inviso_rt_meta],undefined,20) + end, + Nodes). +%% ------------------------------------------------------------------------------ + +%% Stops the runtime components on Nodes and stops the control component at this +%% Erlang node. +stop(Nodes) when is_list(Nodes) -> + ?l true=check_on_nodes(Nodes,erlang,whereis,[inviso_rt],fun(P) when is_pid(P)->true end), + ?l {ok,NodeResults}=inviso:stop_nodes(Nodes), + ?l true=check_noderesults(Nodes,ok,NodeResults), + ?l true=check_on_nodes(Nodes,erlang,whereis,[inviso_rt],fun(undefined)->true end), + ?l true=is_pid(whereis(inviso_c)), + ?l shutdown=inviso:stop(), + ?l ok=poll(erlang,whereis,[inviso_c],undefined,20). +%% ------------------------------------------------------------------------------ + +%% Help function activating local tracing. +activate_local_tracing(Nodes) when is_list(Nodes) -> + ?l true=check_on_nodes(Nodes, + erlang, + trace_info, + [{code,which,1},traced], + {traced,false}), + ?l {ok,NodeResults}=inviso:tpl(Nodes,code,which,1,[]), + ?l true=check_noderesults(Nodes,fun({_,{ok,[1]}})->true end,NodeResults), + ?l true=check_on_nodes(Nodes, + erlang, + trace_info, + [{code,which,1},traced], + {traced,local}). +%% ------------------------------------------------------------------------------ + +%% Help function activating global tracing. +activate_global_tracing(Nodes) when is_list(Nodes) -> + ?l true=check_on_nodes(Nodes, + erlang, + trace_info, + [{code,get_path,0},traced], + {traced,false}), + ?l {ok,NodeResults}=inviso:tp(Nodes,code,get_path,0,[]), + ?l true=check_noderesults(Nodes,fun({_,{ok,[1]}})->true end,NodeResults), + ?l true=check_on_nodes(Nodes, + erlang, + trace_info, + [{code,get_path,0},traced], + {traced,global}). +%% ------------------------------------------------------------------------------ + + +%% Help function activating local tracing and using a regexp to point out modules. +%% Returns the structure of modules and functions that were activated. Must be used +%% when deactivating. +activate_global_tracing_regexp(Nodes) when is_list(Nodes) -> + %% First find out which modules will be effected. + ?l Mods1=inviso_rt_lib:expand_regexp("application.*",[]), + ?l true=(length(Mods1)>1), % Should find more than one module! + ?l Funcs1=lists:foldl(fun(M,Acc)->[{M,M:module_info(exports)}|Acc] end,[],Mods1), + %% Check that these functions are not traced. + io:format("Modules:~w~n",[Mods1]), + ?l {ok,NodeResults}=inviso:tp(Nodes,"application.*",'_','_',[],[]), + io:format("Here 2~w~n",[NodeResults]), + ?l N=lists:foldl(fun({_,L1},A1)->lists:foldl(fun(_,A2)->A2+1 end,A1,L1) end,0,Funcs1), + ?l true=check_noderesults(Nodes,fun({_,{ok,L}})-> N==lists:sum(L) end,NodeResults), + io:format("Here 3~n",[]), + %% Check again! + ?l lists:foreach(fun({M,Funcs})-> + lists:foreach(fun({F,Arity})-> + true=check_on_nodes(Nodes, + erlang, + trace_info, + [{M,F,Arity},traced], + {traced,global}) + end, + Funcs) + end, + Funcs1), + Funcs1. +%% ------------------------------------------------------------------------------ + +%% Help function as above but uses the dir feature as well. +activate_global_tracing_regexp_dir(Nodes) when is_list(Nodes) -> + %% First find out which modules will be effected. + ?l Mods1=inviso_rt_lib:expand_regexp(".*kernel.*","application.*",[]), + ?l true=(length(Mods1)>1), % Should find more than one module! + ?l Funcs1=lists:foldl(fun(M,Acc)->[{M,M:module_info(exports)}|Acc] end,[],Mods1), + %% Check that these functions are not traced. + io:format("Modules:~w~n",[Mods1]), + ?l {ok,NodeResults}=inviso:tp(Nodes,{".*kernel.*","application.*"},'_','_',[],[]), + io:format("Here 2~w~n",[NodeResults]), + ?l N=lists:foldl(fun({_,L1},A1)->lists:foldl(fun(_,A2)->A2+1 end,A1,L1) end,0,Funcs1), + ?l true=check_noderesults(Nodes,fun({_,{ok,L}})-> N==lists:sum(L) end,NodeResults), + io:format("Here 3~n",[]), + %% Check again! + ?l lists:foreach(fun({M,Funcs})-> + lists:foreach(fun({F,Arity})-> + true=check_on_nodes(Nodes, + erlang, + trace_info, + [{M,F,Arity},traced], + {traced,global}) + end, + Funcs) + end, + Funcs1), + Funcs1. +%% ------------------------------------------------------------------------------ + +deactivate_local_tracing(Nodes) when is_list(Nodes) -> + ?l true=check_on_nodes(Nodes, + erlang, + trace_info, + [{code,which,1},traced], + {traced,local}), + ?l {ok,NodeResults}=inviso:ctpl(Nodes,code,'_','_'), + ?l true=check_noderesults(Nodes,fun({_,{ok,[N]}})when is_integer(N)->true end,NodeResults), + ?l true=check_on_nodes(Nodes, + erlang, + trace_info, + [{code,which,1},traced], + {traced,false}). +%% ------------------------------------------------------------------------------ + +deactivate_global_tracing(Nodes) when is_list(Nodes) -> + ?l true=check_on_nodes(Nodes, + erlang, + trace_info, + [{code,get_path,0},traced], + {traced,global}), + ?l {ok,NodeResults}=inviso:ctp(Nodes,code,'_','_'), + ?l true=check_noderesults(Nodes,fun({_,{ok,[N]}})when is_integer(N)->true end,NodeResults), + ?l true=check_on_nodes(Nodes, + erlang, + trace_info, + [{code,get_path,0},traced], + {traced,false}). +%% ------------------------------------------------------------------------------ + + +%% Function deactivating the functions activated by activate_global_tracing_regexp/1. +deactivate_global_tracing_regexp(Nodes,Funcs1) -> + ?l lists:foreach(fun({M,Funcs})-> + lists:foreach(fun({F,Arity})-> + true=check_on_nodes(Nodes, + erlang, + trace_info, + [{M,F,Arity},traced], + {traced,global}) + end, + Funcs) + end, + Funcs1), + ?l {ok,NodeResults}=inviso:ctp(Nodes,"application.*",'_','_'), + ?l N=lists:foldl(fun({_,L1},A1)->lists:foldl(fun(_,A2)->A2+1 end,A1,L1) end,0,Funcs1), + io:format("Noderesult from deactivate;~w~n",[NodeResults]), + ?l true=check_noderesults(Nodes,fun({_,{ok,L}})-> N==lists:sum(L) end,NodeResults), + ?l lists:foreach(fun({M,Funcs})-> + lists:foreach(fun({F,Arity})-> + true=check_on_nodes(Nodes, + erlang, + trace_info, + [{M,F,Arity},traced], + {traced,false}) + end, + Funcs) + end, + Funcs1). +%% ------------------------------------------------------------------------------ + +%% Function deactivating the functions activated by activate_global_tracing_regexp_dir/1. +deactivate_global_tracing_regexp_dir(Nodes,Funcs1) -> + ?l lists:foreach(fun({M,Funcs})-> + lists:foreach(fun({F,Arity})-> + true=check_on_nodes(Nodes, + erlang, + trace_info, + [{M,F,Arity},traced], + {traced,global}) + end, + Funcs) + end, + Funcs1), + ?l {ok,NodeResults}=inviso:ctp(Nodes,{".*kernel.*","application.*"},'_','_'), + ?l N=lists:foldl(fun({_,L1},A1)->lists:foldl(fun(_,A2)->A2+1 end,A1,L1) end,0,Funcs1), + io:format("Noderesult from deactivate;~w~n",[NodeResults]), + ?l true=check_noderesults(Nodes,fun({_,{ok,L}})-> N==lists:sum(L) end,NodeResults), + ?l lists:foreach(fun({M,Funcs})-> + lists:foreach(fun({F,Arity})-> + true=check_on_nodes(Nodes, + erlang, + trace_info, + [{M,F,Arity},traced], + {traced,false}) + end, + Funcs) + end, + Funcs1). +%% ------------------------------------------------------------------------------ + +%% Help function which starts the inviso_test_proc on all nodes and then sets +%% the call flag on that process. +activate_traceflags(Nodes) -> + ?l lists:foreach(fun(N)->spawn(N,?MODULE,test_proc_init,[]) end,Nodes), + ?l lists:foreach(fun(N)-> + P=rpc:call(N,erlang,whereis,[inviso_test_proc]), + {flags,[]}=rpc:call(N,erlang,trace_info,[P,flags]) + end, + Nodes), + ?l {ok,NodeResults}=inviso:tf(Nodes,inviso_test_proc,[call,timestamp]), + ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults), + ?l lists:foreach(fun(N)-> + P=rpc:call(N,erlang,whereis,[inviso_test_proc]), + {flags,Flags}=rpc:call(N,erlang,trace_info,[P,flags]), + true=lists:member(call,Flags), + true=lists:member(timestamp,Flags) + end, + Nodes), + %% Now try a globally registered process. + ?l [ANode|_]=Nodes, + ?l GPid=spawn(ANode,?MODULE,global_test_proc_init,[]), + ?l ok=poll(global,whereis_name,[global_inviso_test_proc], + fun(P) when is_pid(P)->true;(_)->false end, + 10), + ?l {ok,NodeResults2}= + inviso:tf(Nodes,{global,global_inviso_test_proc},[call,timestamp]), + ?l true=check_noderesults(Nodes, + fun({N,{ok,[1]}}) when N==ANode->true; + ({_,{ok,[0]}})->true; + (_)->false + end, + NodeResults2), + ?l {flags,Flags2}=rpc:call(ANode,erlang,trace_info,[GPid,flags]), + ?l 2=length(Flags2), + ?l true=lists:member(call,Flags2), + ?l true=lists:member(timestamp,Flags2), + true. +%% ------------------------------------------------------------------------------ + +deactivate_traceflags(Nodes) -> + ?l lists:foreach(fun(N)-> + P=rpc:call(N,erlang,whereis,[inviso_test_proc]), + {flags,Flags}=rpc:call(N,erlang,trace_info,[P,flags]), + true=lists:member(call,Flags), + true=lists:member(timestamp,Flags) + end, + Nodes), + ?l {ok,NodeResults}=inviso:ctf(Nodes,inviso_test_proc,[call,timestamp]), + ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults), + ?l lists:foreach(fun(N)-> + P=rpc:call(N,erlang,whereis,[inviso_test_proc]), + {flags,[]}=rpc:call(N,erlang,trace_info,[P,flags]) + end, + Nodes), + ?l GPid=global:whereis_name(global_inviso_test_proc), + ?l ANode=node(GPid), + ?l {flags,Flags2}=rpc:call(ANode,erlang,trace_info,[GPid,flags]), + ?l 2=length(Flags2), + ?l {ok,NodeResults2}=inviso:ctf(Nodes,{global,global_inviso_test_proc},[call,timestamp]), + ?l true=check_noderesults(Nodes, + fun({N,{ok,[1]}}) when N==ANode->true; + ({_,{ok,[0]}})->true; + (_)->false + end, + NodeResults2). +%% ------------------------------------------------------------------------------ + + +activate_meta_tracing(Nodes) -> + ?l {ok,NodeResults1}=inviso:tpm_localnames(), + ?l true=check_noderesults(Nodes,{{ok,1},{ok,1}},NodeResults1), + ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]), + {meta,P}=rpc:call(N,erlang,trace_info,[{erlang,register,2},meta]) + end, + Nodes), + ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]), + {meta,P}=rpc:call(N,erlang,trace_info,[{erlang,unregister,1},meta]) + end, + Nodes), + ?l {ok,NodeResults2}=inviso:tpm_globalnames(), + ?l true=check_noderesults(Nodes,{{ok,1},{ok,1}},NodeResults2), + ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]), + {meta,P}=rpc:call(N, + erlang, + trace_info, + [{global,handle_call,3},meta]) + end, + Nodes), + ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]), + {meta,P}=rpc:call(N, + erlang, + trace_info, + [{global,delete_global_name,2},meta]) + end, + Nodes), + + ?l lists:foreach(fun(N)->true=rpc:call(N, + ets, + insert, + [inviso_sideeffect_tab,{tpm_init_func1,0}]), + true=rpc:call(N, + ets, + insert, + [inviso_sideeffect_tab,{tpm_call_func1,0}]), + true=rpc:call(N, + ets, + insert, + [inviso_sideeffect_tab,{tpm_return_func1,0}]) + end, + Nodes), + ?l {ok,NodeResults3}= + inviso:init_tpm(lists, + module_info, + 0, + {?MODULE,tpm_init_func1}, + {?MODULE,tpm_call_func1}, + {?MODULE,tpm_return_func1}, + {?MODULE,tpm_remove_func1}), + ?l true=check_noderesults(Nodes,ok,NodeResults3), + ?l [{_,1}]=ets:lookup(inviso_sideeffect_tab,tpm_init_func1), + ?l {ok,NodeResults3a}= + inviso:init_tpm(lists, + module_info, + 0, + {?MODULE,tpm_init_func1}, + {?MODULE,tpm_call_func1}, + {?MODULE,tpm_return_func1}, + {?MODULE,tpm_remove_func1}), + ?l true=check_noderesults(Nodes,{error,already_initiated},NodeResults3a), +% %% Try more forbidden things. Wildcards not allowed in meta tracing! +% ?l {ok,NodeResults3b}=inviso:tpm(Nodes,lists,'_',0,[{'_',[],[{return_trace}]}]), +% io:format("The noderesults3b is:~w~n",[NodeResults3b]), +% ?l true=check_noderesults(Nodes,{error,bad_mfa},NodeResults3b), + ?l {ok,NodeResults3c}=inviso:tpm(Nodes,lists,module_info,0,[{'_',[],[{return_trace}]}]), + ?l true=check_noderesults(Nodes,{ok,1},NodeResults3c), + ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]), + {meta,P}=rpc:call(N,erlang,trace_info,[{lists,module_info,0},meta]) + end, + Nodes), + ?l lists:foreach(fun(N)->rpc:call(N,lists,module_info,[]) end,Nodes), + ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_call_func1],[{tpm_call_func1,1}],20), + ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_return_func1],[{tpm_return_func1,1}],20), + ?l lists:foreach(fun(N)->rpc:call(N,lists,module_info,[]) end,Nodes), + ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_call_func1],[{tpm_call_func1,2}],20), + ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_return_func1],[{tpm_return_func1,2}],20), + + ?l {ok,NodeResults4}= + inviso:init_tpm(math, + module_info, + 1, + {?MODULE,tpm_init_func2}, % Does not exist on purpose. + {?MODULE,tpm_call_func2}, % Does not exist on purpose. + {?MODULE,tpm_return_func2}, % Does not exist on purpose. + {?MODULE,tpm_remove_func2}), % Does not exist on purpose. + ?l true=check_noderesults(Nodes,ok,NodeResults4), + ?l {ok,NodeResults5}= + inviso:tpm_ms(math,module_info,1,ms1,[{'_',[],[{return_trace}]}]), + ?l true=check_noderesults(Nodes,{ok,1},NodeResults5), + ?l lists:foreach(fun(N)->{meta_match_spec,[{'_',[],[{return_trace}]}]}= + rpc:call(N,erlang,trace_info,[{math,module_info,1}, + meta_match_spec]) + end, + Nodes), + + ?l {ok,NodeResults6}=inviso:tpm_ms(math,module_info,1,ms2,[{[exports],[],[]}]), + ?l true=check_noderesults(Nodes,{ok,1},NodeResults6), + ?l lists:foreach(fun(N)->{meta_match_spec,[{[exports],[],[]},{'_',[],[{return_trace}]}]}= + rpc:call(N,erlang,trace_info,[{math,module_info,1}, + meta_match_spec]) + end, + Nodes), + ?l {ok,NodeResults7}=inviso:tpm_ms(math,module_info,1,ms3,[{[attributes],[],[]}]), + ?l true=check_noderesults(Nodes,{ok,1},NodeResults7), + ?l lists:foreach(fun(N)->{meta_match_spec,[{[attributes],[],[]}, + {[exports],[],[]}, + {'_',[],[{return_trace}]}]}= + rpc:call(N,erlang,trace_info,[{math,module_info,1}, + meta_match_spec]) + end, + Nodes), + ?l {ok,NodeResults8}=inviso:ctpm_ms(math,module_info,1,ms2), + ?l true=check_noderesults(Nodes,ok,NodeResults8), + ?l lists:foreach(fun(N)->{meta_match_spec,[{[attributes],[],[]}, + {'_',[],[{return_trace}]}]}= + rpc:call(N,erlang,trace_info,[{math,module_info,1}, + meta_match_spec]) + end, + Nodes), + ?l io:format("whereis:~w~n",[lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_rt_meta]) end,Nodes)]), + ?l {ok,NodeResults8}=inviso:ctpm_ms(math,module_info,1,ms3), + ?l io:format("whereis:~w~n",[lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_rt_meta]) end,Nodes)]), + ?l {ok,NodeResults8}=inviso:ctpm_ms(math,module_info,1,ms1), + ?l lists:foreach(fun(N)->{meta_match_spec,false}= + rpc:call(N,erlang,trace_info,[{math,module_info,1}, + meta_match_spec]) + end, + Nodes), + + %% Now try to do this with exception tracing instead. + %% Reset the side effect tables. + ?l lists:foreach(fun(N)->true=rpc:call(N, + ets, + insert, + [inviso_sideeffect_tab,{tpm_init_func1,0}]), + true=rpc:call(N, + ets, + insert, + [inviso_sideeffect_tab,{tpm_call_func1,0}]), + true=rpc:call(N, + ets, + insert, + [inviso_sideeffect_tab,{tpm_return_func1,0}]) + end, + Nodes), + ?l {ok,NodeResults9}= + inviso:init_tpm(?MODULE, + failing_function, + 1, + {?MODULE,tpm_init_func1}, + {?MODULE,tpm_call_func1}, + {?MODULE,tpm_return_func1}, + {?MODULE,tpm_remove_func1}), + ?l true=check_noderesults(Nodes,ok,NodeResults9), + ?l [{_,1}]=ets:lookup(inviso_sideeffect_tab,tpm_init_func1), + ?l {ok,NodeResults10}=inviso:tpm(Nodes,?MODULE,failing_function,1,[{'_',[],[{exception_trace}]}]), + ?l true=check_noderesults(Nodes,{ok,1},NodeResults10), + ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]), + {meta,P}=rpc:call(N,erlang,trace_info,[{?MODULE,failing_function,1},meta]) + end, + Nodes), + ?l lists:foreach(fun(N)->rpc:call(N,?MODULE,failing_function,[nofailure]) end,Nodes), + ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_call_func1],[{tpm_call_func1,1}],20), + ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_return_func1],[{tpm_return_func1,1}],20), + ?l lists:foreach(fun(N)->rpc:call(N,?MODULE,failing_function,[failure]) end,Nodes), + ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_call_func1],[{tpm_call_func1,2}],20), + ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_return_func1],[{tpm_return_func1,3}],20), + + ok. +%% ------------------------------------------------------------------------------ + +%% This function is for testing that appending the tracer to a trace action term +%% works. +activate_deactivate_meta_tracing_tracer(Nodes) -> + ?l {ok,NodeResults}= + inviso:tpm_tracer(Nodes,lists,module_info,0,[{'_',[],[{trace,[all],[call]}]}],void), + ?l true=check_noderesults(Nodes,{ok,1},NodeResults), + ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]), + {meta,P}=rpc:call(N,erlang,trace_info,[{lists,module_info,0},meta]), + {meta_match_spec,[{'_',[],[{trace,[all],Enable}]}]}= + rpc:call(N,erlang,trace_info,[{lists,module_info,0}, + meta_match_spec]), + true=list_search(Enable,fun({{tracer,P}}) when is_port(P)->true; + (_) -> false + end) + end, + Nodes), + ?l {ok,NodeResults2}= + inviso:ctpm(Nodes,lists,module_info,0), + ?l true=check_noderesults(Nodes,ok,NodeResults2), + ?l lists:foreach(fun(N)->{meta,false}= + rpc:call(N,erlang,trace_info,[{lists,module_info,0},meta]) + end, + Nodes), + ok. +%% ------------------------------------------------------------------------------ + +deactivate_meta_tracing(Nodes) -> + ?l lists:foreach(fun(N)->{meta,P}= + rpc:call(N,erlang,trace_info,[{erlang,register,2},meta]), + true=is_pid(P) + end, + Nodes), + ?l lists:foreach(fun(N)->{meta,P}= + rpc:call(N,erlang,trace_info,[{erlang,unregister,1},meta]), + true=is_pid(P) + end, + Nodes), + ?l {ok,NodeResults1}=inviso:ctpm_localnames(), + ?l lists:foreach(fun(N)->{meta,false}= + rpc:call(N,erlang,trace_info,[{erlang,register,2},meta]) end, + Nodes), + ?l lists:foreach(fun(N)->{meta,false}= + rpc:call(N,erlang,trace_info,[{erlang,unregister,1},meta]) + end, + Nodes), + ?l true=check_noderesults(Nodes,{ok,ok},NodeResults1), + + ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]), + {meta,P}=rpc:call(N, + erlang, + trace_info, + [{global,handle_call,3},meta]) + end, + Nodes), + ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]), + {meta,P}=rpc:call(N, + erlang, + trace_info, + [{global,delete_global_name,2},meta]) + end, + Nodes), + ?l {ok,NodeResults1b}=inviso:ctpm_globalnames(), + ?l true=check_noderesults(Nodes,{ok,ok},NodeResults1b), + ?l lists:foreach(fun(N)-> + {meta,false}=rpc:call(N, + erlang, + trace_info, + [{global,handle_call,3},meta]) + end, + Nodes), + ?l lists:foreach(fun(N)-> + {meta,false}=rpc:call(N, + erlang, + trace_info, + [{global,delete_global_name,2},meta]) + end, + Nodes), + + ?l lists:foreach(fun(N)->{meta,P}= + rpc:call(N,erlang,trace_info,[{lists,module_info,0},meta]), + true=is_pid(P) + end, + Nodes), + ?l {ok,NodeResults2}=inviso:ctpm(lists,module_info,0), + ?l true=check_noderesults(Nodes,ok,NodeResults2), + ?l lists:foreach(fun(N)->{meta,false}= + rpc:call(N,erlang,trace_info,[{lists,module_info,0},meta]) end, + Nodes), + ?l [{_,0}]=ets:lookup(inviso_sideeffect_tab,tpm_init_func1), + ?l {ok,NodeResults3}=inviso:ctpm(math,module_info,1), + ?l true=check_noderesults(Nodes,ok,NodeResults3), + ok. +%% ------------------------------------------------------------------------------ + +%% Functions acting as callbacks for testing the meta tracing mechanisms. +tpm_init_func1(_M,_F,_Arity,PublLD) -> + ets:update_counter(inviso_sideeffect_tab,tpm_init_func1,1), + {ok,PublLD,void}. +tpm_call_func1(_Pid,{call,_Args,_TS},PublLD) -> + ets:update_counter(inviso_sideeffect_tab,tpm_call_func1,1), + {ok,PublLD,void}. +tpm_return_func1(_Pid,{return_from,_ReturnVal,_TS},PublLD) -> + ets:update_counter(inviso_sideeffect_tab,tpm_return_func1,1), + {ok,PublLD,void}; +tpm_return_func1(_Pid,{exception_from,_ReturnVal,_TS},PublLD) -> + ets:update_counter(inviso_sideeffect_tab,tpm_return_func1,1), + ets:update_counter(inviso_sideeffect_tab,tpm_return_func1,1), + {ok,PublLD,void}. +tpm_remove_func1(_M,_F,_Arity,PublLD) -> + ets:update_counter(inviso_sideeffect_tab,tpm_init_func1,-1), + {ok,PublLD}. +%% ------------------------------------------------------------------------------ + + +%% Help function which traces on a function and makes function calls until there +%% are two files in the wrap-set. +fill_and_reach_two_wrapfiles(PrivDir,RegExp,Nodes) -> + ?l lists:foreach(fun(N)->spawn(N,?MODULE,test_proc_init,[]) end,Nodes), + ?l {ok,NodeResults1}=inviso:tpl(Nodes,?MODULE,test_function,0,[]), + ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults1), + ?l {ok,NodeResults2}=inviso:tf(Nodes,inviso_test_proc,[call]), + ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults2), + fill_and_reach_two_wrapfiles_2(PrivDir,RegExp,Nodes), + ?l {ok,NodeResults3}=inviso:ctf(Nodes,inviso_test_proc,[call]), + ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults3), + ?l {ok,NodeResults4}=inviso:ctpl(Nodes,?MODULE,test_function,0), + ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults4), + ok. + +fill_and_reach_two_wrapfiles_2(PrivDir,RegExp,[Node|Rest]) -> + ?l ok=rpc:call(Node,?MODULE,fill_and_reach_two_wrapfiles_3,[PrivDir,RegExp]), + fill_and_reach_two_wrapfiles_2(PrivDir,RegExp,Rest); +fill_and_reach_two_wrapfiles_2(_,_,[]) -> + ok. + +fill_and_reach_two_wrapfiles_3(Dir,RegExp) -> + ok=send_to_test_proc({apply,?MODULE,test_function,[]}, + fun reach_two_wraps_stopfun/1, + {Dir,RegExp++atom_to_list(node())}, + 100). + +%% Help function intended to be used as fun in a send_to_test_proc/4 call. +%% The function lists the content of Dir and looks for occurancies of String. +%% If two files containing the string String are found, 'done' is returned. +%% Otherwise 'continue'. +reach_two_wraps_stopfun({Dir,RegExp}) -> + case file:list_dir(Dir) of + {ok,FileNames} -> + case how_many_files_regexp(FileNames,RegExp,0) of + {ok,2} -> + done; + _ -> + continue + end; + {error,_Reason} -> + error + end. +%% ------------------------------------------------------------------------------ + +%% ------------------------------------------------------------------------------ +%% Help function for the overload tests. These functions are used as callbacks. +%% ------------------------------------------------------------------------------ + +overload1(_) -> + ets:update_counter(inviso_sideeffect_tab,ovl1,1), + ok. +%% This function is used when timeout occurs inside the runtime component. +%% That is it is time to check for overload. +overload2({timeout,overload2i_data}) -> + ets:update_counter(inviso_sideeffect_tab,ovl2,1), + ok. +overload2i() -> + ets:insert(inviso_sideeffect_tab,{ovl2,0}), + {ok,overload2i_data}. +overload2r(overload2i_data) -> + ets:delete(inviso_sideeffect_tab,ovl2). + +%% This function is used when timeout occurs inside the runtime component. +%% That is it is time to check for overload. +overload3({timeout,overload3i_data}) -> + ets:update_counter(inviso_sideeffect_tab,ovl3,1), + ok; +overload3(_) -> % Must handle garbage too. + ignore. +overload3i() -> + ets:insert(inviso_sideeffect_tab,{ovl3,0}), + {ok,overload3i_data}. +overload3r(overload3i_data) -> + ets:insert(inviso_sideeffect_tab,{ovl3r,done}), + ets:delete(inviso_sideeffect_tab,ovl3). + +overload4(_) -> + case ets:lookup(inviso_sideeffect_tab,ovl4_suspend) of + [] -> % We are supposed to be running. + ets:update_counter(inviso_sideeffect_tab,ovl4,1), + ok; + [_] -> + {suspend,test} + end. + +%% This function is used when overload check is done by icomming message. +overload5({msg,{test_of_loadcheck,overload5i_data}}) -> + ets:update_counter(inviso_sideeffect_tab,ovl5,1), + ok; +overload5(_) -> + ignore. +overload5i() -> + ets:insert(inviso_sideeffect_tab,{ovl5,0}), + {ok,overload5i_data}. +overload5r(overload5i_data) -> + ets:delete(inviso_sideeffect_tab,ovl5), + ets:insert(inviso_sideeffect_tab,{ovl5r,done}); +overload5r(X) -> + erlang:display({'***',overload5r,X}). + +overload6(_) -> + ets:update_counter(inviso_sideeffect_tab,ovl6,1), + ok. +%% ------------------------------------------------------------------------------ + +%% ------------------------------------------------------------------------------ +%% Help function for the subscription tests. These function implements a collector +%% process which will subscribe to inviso_events from the control component. +%% ------------------------------------------------------------------------------ + +%% Function which can be used to check if an inviso_event has arrived. The function +%% takes a fun which tests the messages. +check_msg_collector([],_,_) -> + true; +check_msg_collector(_,_,0) -> + false; +check_msg_collector(Nodes,Fun,T) -> + Ref=make_ref(), + inviso_collector_proc ! {fetch_message,self(),Ref,Fun}, + receive + {inviso,Ref,{true,Node}} -> + check_msg_collector(lists:delete(Node,Nodes),Fun,T-1); + {inviso,Ref,false} -> + timer:sleep(100), + check_msg_collector(Nodes,Fun,T-1) + end. + +%% Spawn on this function to get a subscriber. +inviso_msg_collector() -> + register(inviso_collector_proc,self()), + inviso_msg_collector_loop([]). + +inviso_msg_collector_loop(Msgs) -> + receive + {fetch_message,From,Ref,Fun} -> + {NewMsgs,Reply}=inviso_msg_collector_selector(Msgs,Fun,[]), + From ! {inviso,Ref,Reply}, + inviso_msg_collector_loop(NewMsgs); + Msg -> + inviso_msg_collector_loop([Msg|Msgs]) + end. + +inviso_msg_collector_selector([M|Rest],Fun,Accum) -> + case Fun(M) of + {true,X} -> + {Rest++Accum,{true,X}}; + _ -> + inviso_msg_collector_selector(Rest,Fun,[M|Accum]) + end; +inviso_msg_collector_selector([],_,Accum) -> + {Accum,false}. +%% ------------------------------------------------------------------------------ + + +%% ============================================================================== +%% Help functions +%% ============================================================================== + +list_search([E|Rest],Fun) -> + case Fun(E) of + true -> + true; + false -> + list_search(Rest,Fun) + end; +list_search([],_Fun) -> + false. +%% ------------------------------------------------------------------------------ + +%% Help function checking that there is a Result for each node in Nodes. +%% Returns 'true' if successful. +check_noderesults(Nodes,Fun,[{Node,Result}|Rest]) when is_function(Fun) -> + case Fun({Node,Result}) of + true -> + case lists:member(Node,Nodes) of + true -> + check_noderesults(lists:delete(Node,Nodes),Fun,Rest); + false -> % Not good. + unknown_node_in_returnvalue + end; + _ -> + illegal_result + end; +check_noderesults(Nodes,Result,[{Node,Result}|Rest]) -> + case lists:member(Node,Nodes) of + true -> + check_noderesults(lists:delete(Node,Nodes),Result,Rest); + false -> % Not good. + unknown_node_in_returnvalue + end; +check_noderesults([],_,[]) -> + true; +check_noderesults(X,Y,Z) -> + io:format("Bad arguments to check noderesults:~w~n~w~n~w~n",[X,Y,Z]), + false. +%% ------------------------------------------------------------------------------ + +%% Help function doing rpc on all nodes in Nodes calling M:F. Returns 'true' if +%% successful. +check_on_nodes([Node|Rest],M,F,Args,Result) when Node==node() -> + if + is_function(Result) -> + ?l true=Result(apply(M,F,Args)); + true -> + ?l Result=apply(M,F,Args) + end, + check_on_nodes(Rest,M,F,Args,Result); +check_on_nodes([Node|Rest],M,F,Args,Result) -> + if + is_function(Result) -> + ?l true=Result(rpc:call(Node,M,F,Args)); + true -> + ?l Result=rpc:call(Node,M,F,Args) + end, + check_on_nodes(Rest,M,F,Args,Result); +check_on_nodes([],_,_,_,_) -> + true. +%% ------------------------------------------------------------------------------ + +%% Help function which given a list of files searches through it and returns +%% how many satisfies the RegExp. +%% Returns {ok,N}. +how_many_files_regexp([],_,N) -> + {ok,N}; +how_many_files_regexp([FName|Rest],RegExp,N) -> + case regexp:first_match(FName,RegExp) of + {match,1,_} -> + how_many_files_regexp(Rest,RegExp,N+1); + nomatch -> + how_many_files_regexp(Rest,RegExp,N); + {error,Reason} -> + test_server:fail(Reason) + end. +%% ------------------------------------------------------------------------------ + +%% Help function killing a bunch of registered processes. +process_killer([RegName|Rest]) -> + case whereis(RegName) of + undefined -> + case global:whereis_name(RegName) of + undefined -> + process_killer(Rest); + P when is_pid(P) -> + if + node()==node(P) -> + exit(P,kill); + true -> + true + end, + process_killer(Rest) + end; + P when is_pid(P) -> + exit(P,kill), + process_killer(Rest) + end; +process_killer([]) -> + true. +%% ------------------------------------------------------------------------------ + +%% Help function which waits for a function call to become Result. This is useful +%% if what we are waiting for can happend independantly of indications we have +%% access to. +poll(_,_,_,_,0) -> + error; +poll(M,F,Args,Result,Times) -> + try apply(M,F,Args) of + What when is_function(Result) -> + case Result(What) of + true -> + ok; + _ -> + timer:sleep(100), + poll(M,F,Args,Result,Times-1) + end; + Result -> + ok; + _ -> + timer:sleep(100), + poll(M,F,Args,Result,Times-1) + catch + error:Reason -> + io:format("Apply in suite-function poll/5 failed, ~w~n",[Reason]), + timer:sleep(100), + poll(M,F,Args,Result,Times-1) + end. +%% ------------------------------------------------------------------------------ + +insert_remotenode_config(Name,Node,Config) -> + [{remotenode,{Name,Node}}|Config]. +%% ------------------------------------------------------------------------------ + +insert_timetraphandle_config(Handle,Config) -> + [{timetraphandle,Handle}|Config]. +%% ------------------------------------------------------------------------------ + +get_remotenode_config(Name, [{remotenode, {Name, Node}}| _Cs]) -> + Node; +get_remotenode_config(Name, [_ | Cs]) -> + get_remotenode_config(Name, Cs); +get_remotenode_config(Name, []) -> + exit({no_remotenode, Name}). + +%% ------------------------------------------------------------------------------ + +get_timetraphandle_config(Config) -> + {value,{_,Handle}}=lists:keysearch(timetraphandle,1,Config), + Handle. +%% ------------------------------------------------------------------------------ + +get_remotenodes_config([{remotenode,{_Name,Node}}|Config]) -> + [Node|get_remotenodes_config(Config)]; +get_remotenodes_config([_|Config]) -> + get_remotenodes_config(Config); +get_remotenodes_config([]) -> + []. +%% ------------------------------------------------------------------------------ + +remove_remotenode_config(Name, [{remotenode, {Name, _}} | Cs]) -> + Cs; +remove_remotenode_config(Name, [C | Cs]) -> + [C | remove_remotenode_config(Name, Cs)]; +remove_remotenode_config(_Name, []) -> + []. + +%% ------------------------------------------------------------------------------ + +remove_timetraphandle_config(Config) -> + lists:keydelete(timetraphandle,1,Config). +%% ------------------------------------------------------------------------------ + +%% This function can be meta traced in order to check that exception_trace works. +%% Must be exported. +failing_function(nofailure) -> + true; +failing_function(failure) -> + exit(failure). +%% ------------------------------------------------------------------------------ + +%% ============================================================================== +%% Code for a test process which can be started. +%% ============================================================================== + +test_proc_init() -> + register(inviso_test_proc,self()), + test_proc_loop(). + +test_proc_loop() -> + receive + {apply,M,F,Args} -> + apply(M,F,Args), + test_proc_loop(); + X -> + io:format("Got ~w~n",[X]), + test_proc_loop() + end. + +global_test_proc_init() -> + global:register_name(global_inviso_test_proc,self()), + test_proc_loop(). +%% ------------------------------------------------------------------------------ + +send_to_test_proc(_,_,_,0) -> + error; +send_to_test_proc(Msg,Fun,FunArg,N) -> + inviso_test_proc ! Msg, + case Fun(FunArg) of + done -> + ok; + error -> + test_server:fail(send_to_test_proc); + _ -> + send_to_test_proc(Msg,Fun,FunArg,N-1) + end. +%% ------------------------------------------------------------------------------ + + +%% This function is here to be traced on by the inviso_test_proc. Must be exported. +test_function() -> + 1+1. +%% ------------------------------------------------------------------------------ + + +%% ============================================================================== +%% Code for a test side effect table process. +%% ============================================================================== + +%% The side effect logger is a process owning a public ETS table. The idea is that +%% various callback functions can write in the table when called. In that way +%% correct calling of the call-backs can be verified. +start_side_effect_logger(Node) -> + ?l true=is_pid(spawn(Node,?MODULE,side_effect_logger_proc,[])), + ?l ok=poll(rpc,call,[Node,ets,lookup,[inviso_sideeffect_tab,foo]],[],20). + +%% This one must be exported. +side_effect_logger_proc() -> + register(inviso_tab_proc,self()), % So we can kill it later. + ets:new(inviso_sideeffect_tab,[public,named_table]), + side_effect_logger_proc_2(). + +side_effect_logger_proc_2() -> + receive + _X -> % This process is not expecting anything! + side_effect_logger_proc_2() + end. +%% ------------------------------------------------------------------------------ diff --git a/lib/runtime_tools/test/inviso_testmodule1_foo.erl b/lib/runtime_tools/test/inviso_testmodule1_foo.erl new file mode 100644 index 0000000000..a7a22cad39 --- /dev/null +++ b/lib/runtime_tools/test/inviso_testmodule1_foo.erl @@ -0,0 +1,9 @@ +-module(inviso_testmodule1_foo). + +-compile(export_all). + +%% The purpose of this module is simply to have a module that is +%% guaranteed not loaded. + +foo() -> + true. diff --git a/lib/runtime_tools/test/runtime_tools.cover b/lib/runtime_tools/test/runtime_tools.cover new file mode 100644 index 0000000000..2d62ebe6ac --- /dev/null +++ b/lib/runtime_tools/test/runtime_tools.cover @@ -0,0 +1 @@ +{exclude,[observer_backend]}. diff --git a/lib/runtime_tools/test/runtime_tools.spec b/lib/runtime_tools/test/runtime_tools.spec new file mode 100644 index 0000000000..a60a533ce2 --- /dev/null +++ b/lib/runtime_tools/test/runtime_tools.spec @@ -0,0 +1 @@ +{topcase, {dir, "../runtime_tools_test"}}. diff --git a/lib/runtime_tools/test/runtime_tools_SUITE.erl b/lib/runtime_tools/test/runtime_tools_SUITE.erl new file mode 100644 index 0000000000..84e255e126 --- /dev/null +++ b/lib/runtime_tools/test/runtime_tools_SUITE.erl @@ -0,0 +1,50 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010. 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(runtime_tools_SUITE). +-include("test_server.hrl"). + +%% Test server specific exports +-export([all/1]). +-export([init_per_testcase/2, end_per_testcase/2]). + +%% Test cases +-export([app_file/1]). + +%% Default timetrap timeout (set in init_per_testcase) +-define(default_timeout, ?t:minutes(1)). + +init_per_testcase(_Case, Config) -> + Dog = test_server:timetrap(?default_timeout), + [{watchdog, Dog} | Config]. + +end_per_testcase(_Case, Config) -> + Dog = ?config(watchdog, Config), + ?t:timetrap_cancel(Dog), + ok. + +all(suite) -> + [app_file]. + +app_file(suite) -> + []; +app_file(doc) -> + ["Testing .app file"]; +app_file(Config) when is_list(Config) -> + ?line ok = ?t:app_test(runtime_tools), + ok. |