aboutsummaryrefslogtreecommitdiffstats
path: root/lib/runtime_tools/test/inviso_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/runtime_tools/test/inviso_SUITE.erl')
-rw-r--r--lib/runtime_tools/test/inviso_SUITE.erl2840
1 files changed, 2840 insertions, 0 deletions
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, [email protected]
+%% Lennart �hman, [email protected]
+%% -----------------------------------------------------------------------------
+
+-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.
+%% ------------------------------------------------------------------------------