diff options
Diffstat (limited to 'lib/inviso/src')
-rw-r--r-- | lib/inviso/src/inviso.erl | 78 | ||||
-rw-r--r-- | lib/inviso/src/inviso_c.erl | 50 | ||||
-rw-r--r-- | lib/inviso/src/inviso_lfm.erl | 14 | ||||
-rw-r--r-- | lib/inviso/src/inviso_lfm_tpfreader.erl | 12 | ||||
-rw-r--r-- | lib/inviso/src/inviso_tool.erl | 6579 | ||||
-rw-r--r-- | lib/inviso/src/inviso_tool_lib.erl | 18 | ||||
-rw-r--r-- | lib/inviso/src/inviso_tool_sh.erl | 3480 |
7 files changed, 5090 insertions, 5141 deletions
diff --git a/lib/inviso/src/inviso.erl b/lib/inviso/src/inviso.erl index de42926ffe..0eda06a5c2 100644 --- a/lib/inviso/src/inviso.erl +++ b/lib/inviso/src/inviso.erl @@ -129,7 +129,7 @@ start(Options) -> add_node(Reference) ->
gen_server:call(?CONTROLLER,{add_nodes,[node()],[],Reference,any_ref},?CALL_TIMEOUT).
-add_node(Reference,Options) when list(Options) ->
+add_node(Reference,Options) when is_list(Options) -> gen_server:call(?CONTROLLER,{add_nodes,[node()],Options,Reference,any_ref},?CALL_TIMEOUT).
%% -----------------------------------------------------------------------------
@@ -142,7 +142,7 @@ add_node(Reference,Options) when list(Options) -> add_node_if_ref(Reference) ->
gen_server:call(?CONTROLLER,{add_nodes,[node()],[],Reference,if_ref},?CALL_TIMEOUT).
-add_node_if_ref(Reference,Options) when list(Options) ->
+add_node_if_ref(Reference,Options) when is_list(Options) -> gen_server:call(?CONTROLLER,{add_nodes,[node()],Options,Reference,if_ref},?CALL_TIMEOUT).
%% -----------------------------------------------------------------------------
@@ -156,10 +156,10 @@ add_node_if_ref(Reference,Options) when list(Options) -> %% system. By speicifying node() as the node where the runtime component shall
%% be started. The return value will then follow the rules of non distributed
%% returnvalues and not have a node indicator.
-add_nodes(Nodes,Reference) when list(Nodes) ->
+add_nodes(Nodes,Reference) when is_list(Nodes) -> gen_server:call(?CONTROLLER,{add_nodes,Nodes,[],Reference,any_ref},?CALL_TIMEOUT).
-add_nodes(Nodes,Reference,Options) when list(Nodes),list(Options) ->
+add_nodes(Nodes,Reference,Options) when is_list(Nodes),is_list(Options) -> gen_server:call(?CONTROLLER,{add_nodes,Nodes,Options,Reference,any_ref},?CALL_TIMEOUT).
%% -----------------------------------------------------------------------------
@@ -168,10 +168,10 @@ add_nodes(Nodes,Reference,Options) when list(Nodes),list(Options) -> %%
%% As add_nodes/2,/3 but will only connect to an already existing runtime component
%% if its reference is the same as the one given as argument.
-add_nodes_if_ref(Nodes,Reference) when list(Nodes) ->
+add_nodes_if_ref(Nodes,Reference) when is_list(Nodes) -> gen_server:call(?CONTROLLER,{add_nodes,Nodes,[],Reference,if_ref},?CALL_TIMEOUT).
-add_nodes_if_ref(Nodes,Reference,Options) when list(Nodes),list(Options) ->
+add_nodes_if_ref(Nodes,Reference,Options) when is_list(Nodes),is_list(Options) -> gen_server:call(?CONTROLLER,{add_nodes,Nodes,Options,Reference,if_ref},?CALL_TIMEOUT).
%% -----------------------------------------------------------------------------
@@ -182,9 +182,9 @@ add_nodes_if_ref(Nodes,Reference,Options) when list(Nodes),list(Options) -> %%
%% Change options on all or specified Nodes. This may result in for instance
%% reinitialization of overloadcheck.
-change_options(Options) when list(Options) ->
+change_options(Options) when is_list(Options) -> gen_server:call(?CONTROLLER,{change_options,all,Options},?CALL_TIMEOUT).
-change_options(Nodes,Options) when list(Nodes),list(Options) ->
+change_options(Nodes,Options) when is_list(Nodes),is_list(Options) -> gen_server:call(?CONTROLLER,{change_options,Nodes,Options},?CALL_TIMEOUT).
%% -----------------------------------------------------------------------------
@@ -234,7 +234,7 @@ change_options(Nodes,Options) when list(Nodes),list(Options) -> init_tracing(TracerDataList) ->
gen_server:call(?CONTROLLER,{init_tracing,TracerDataList},?CALL_TIMEOUT).
-init_tracing(Nodes,TracerData) when list(Nodes) ->
+init_tracing(Nodes,TracerData) when is_list(Nodes) -> gen_server:call(?CONTROLLER,{init_tracing,Nodes,TracerData},?CALL_TIMEOUT).
%% -----------------------------------------------------------------------------
@@ -270,10 +270,10 @@ stop_tracing(Nodes) when is_list(Nodes) -> clear() ->
gen_server:call(?CONTROLLER,{clear,all,[]},?CALL_TIMEOUT).
-clear(Nodes) when list(Nodes) ->
+clear(Nodes) when is_list(Nodes) -> gen_server:call(?CONTROLLER,{clear,Nodes,[]},?CALL_TIMEOUT).
-clear(Nodes,Options) when list(Nodes),list(Options) ->
+clear(Nodes,Options) when is_list(Nodes),is_list(Options) -> gen_server:call(?CONTROLLER,{clear,Nodes,Options},?CALL_TIMEOUT).
%% -----------------------------------------------------------------------------
@@ -355,9 +355,9 @@ stop_all() -> tp(Nodes,Module,Function,Arity,MatchSpec,Opts) ->
trace_pattern(Nodes,[{Module,Function,Arity,MatchSpec,Opts}],[global]).
-tp(Nodes,Module,Function,Arity,MatchSpec) when list(Nodes) ->
+tp(Nodes,Module,Function,Arity,MatchSpec) when is_list(Nodes) -> trace_pattern(Nodes,[{Module,Function,Arity,MatchSpec,[]}],[global]);
-tp(Module,Function,Arity,MatchSpec,Opts) when atom(Module) ->
+tp(Module,Function,Arity,MatchSpec,Opts) when is_atom(Module) -> trace_pattern(all,[{Module,Function,Arity,MatchSpec,Opts}],[global]).
tp(Module,Function,Arity,MatchSpec) ->
@@ -384,9 +384,9 @@ tp(PatternList) -> tpl(Nodes,Module,Function,Arity,MatchSpec,Opts) ->
trace_pattern(Nodes,[{Module,Function,Arity,MatchSpec,Opts}],[local]).
-tpl(Nodes,Module,Function,Arity,MatchSpec) when list(Nodes) ->
+tpl(Nodes,Module,Function,Arity,MatchSpec) when is_list(Nodes) -> trace_pattern(Nodes,[{Module,Function,Arity,MatchSpec,[]}],[local]);
-tpl(Module,Function,Arity,MatchSpec,Opts) when atom(Module) ->
+tpl(Module,Function,Arity,MatchSpec,Opts) when is_atom(Module) -> trace_pattern(all,[{Module,Function,Arity,MatchSpec,Opts}],[local]).
tpl(Module,Function,Arity,MatchSpec) ->
@@ -417,12 +417,12 @@ ctp(Nodes,Module,Function,Arity) -> ctp(Module,Function,Arity) ->
trace_pattern(all,[{Module,Function,Arity,false,[only_loaded]}],[global]).
-ctp(Nodes,PatternList) when list(PatternList) ->
+ctp(Nodes,PatternList) when is_list(PatternList) -> trace_pattern(Nodes,
lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
[global]).
-ctp(PatternList) when list(PatternList) ->
+ctp(PatternList) when is_list(PatternList) -> trace_pattern(all,
lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
[global]).
@@ -445,12 +445,12 @@ ctpl(Nodes,Module,Function,Arity) -> ctpl(Module,Function,Arity) ->
trace_pattern(all,[{Module,Function,Arity,false,[only_loaded]}],[local]).
-ctpl(Nodes,PatternList) when list(PatternList) ->
+ctpl(Nodes,PatternList) when is_list(PatternList) -> trace_pattern(Nodes,
lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
[local]).
-ctpl(PatternList) when list(PatternList) ->
+ctpl(PatternList) when is_list(PatternList) -> trace_pattern(all,
lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
[local]).
@@ -485,19 +485,19 @@ trace_pattern(Nodes,Patterns,FlagList) -> %% specifying a certain pid at all nodes. Or an empty TraceConfList for all
%% nodes.
%% When calling several nodes, the nodes are called in parallel.
-tf(Nodes,PidSpec,FlagList) when list(Nodes),list(FlagList) ->
+tf(Nodes,PidSpec,FlagList) when is_list(Nodes),is_list(FlagList) -> trace_flags(Nodes,[{PidSpec, FlagList}],true).
-tf(Nodes,TraceConfList) when list(Nodes),list(TraceConfList) ->
+tf(Nodes,TraceConfList) when is_list(Nodes),is_list(TraceConfList) -> trace_flags(Nodes,TraceConfList,true);
-tf(PidSpec,FlagList) when list(FlagList) ->
+tf(PidSpec,FlagList) when is_list(FlagList) -> trace_flags(all,[{PidSpec,FlagList}],true).
-tf(ArgList) when list(ArgList) -> % This one has triple functionality!
+tf(ArgList) when is_list(ArgList) -> % This one has triple functionality! case ArgList of
- [{_Process,Flags}|_] when list(Flags),atom(hd(Flags))-> % A call to all nodes.
+ [{_Process,Flags}|_] when is_list(Flags),is_atom(hd(Flags))-> % A call to all nodes. trace_flags(all,ArgList,true);
- [{_Node,TraceConfList}|_] when list(TraceConfList),tuple(hd(TraceConfList)) ->
+ [{_Node,TraceConfList}|_] when is_list(TraceConfList),is_tuple(hd(TraceConfList)) -> trace_flags(ArgList,true);
[{_Node,_TraceConfList,_How}|_] ->
trace_flags(ArgList);
@@ -517,15 +517,15 @@ tf(ArgList) when list(ArgList) -> % This one has triple functionality %% The functions without a Nodes argument means all nodes, in a non-distributed
%% environment it means the local node.
%% When calling several nodes, the nodes are called in parallel.
-ctf(Nodes,PidSpec,FlagList) when list(Nodes),list(FlagList) ->
+ctf(Nodes,PidSpec,FlagList) when is_list(Nodes),is_list(FlagList) -> trace_flags(Nodes,[{PidSpec,FlagList}],false).
-ctf(Nodes,TraceConfList) when list(Nodes),list(TraceConfList) ->
+ctf(Nodes,TraceConfList) when is_list(Nodes),is_list(TraceConfList) -> trace_flags(Nodes,TraceConfList,false);
-ctf(PidSpec,FlagList) when list(FlagList) ->
+ctf(PidSpec,FlagList) when is_list(FlagList) -> trace_flags(all,[{PidSpec,FlagList}],false).
-ctf(TraceConfList) when list(TraceConfList) ->
+ctf(TraceConfList) when is_list(TraceConfList) -> trace_flags(all,TraceConfList,false).
%% -----------------------------------------------------------------------------
@@ -668,10 +668,10 @@ init_tpm(Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> tpm(Mod,Func,Arity,MS) ->
tpm(all,Mod,Func,Arity,MS).
-tpm(Nodes,Mod,Func,Arity,MS) when integer(Arity) ->
+tpm(Nodes,Mod,Func,Arity,MS) when is_integer(Arity) -> gen_server:call(?CONTROLLER,
{meta_pattern,Nodes,{tpm,[Mod,Func,Arity,MS]}});
-tpm(Mod,Func,Arity,MS,CallFunc) when integer(Arity) ->
+tpm(Mod,Func,Arity,MS,CallFunc) when is_integer(Arity) -> tpm(all,Mod,Func,Arity,MS,CallFunc).
tpm(Nodes,Mod,Func,Arity,MS,CallFunc) ->
@@ -695,11 +695,11 @@ tpm(Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> tpm_tracer(Mod,Func,Arity,MS) ->
tpm_tracer(all,Mod,Func,Arity,MS).
-tpm_tracer(Nodes,Mod,Func,Arity,MS) when integer(Arity) ->
+tpm_tracer(Nodes,Mod,Func,Arity,MS) when is_integer(Arity) -> gen_server:call(?CONTROLLER,
{meta_pattern,Nodes,{tpm_tracer,[Mod,Func,Arity,MS]}},
?CALL_TIMEOUT);
-tpm_tracer(Mod,Func,Arity,MS,CallFunc) when integer(Arity) ->
+tpm_tracer(Mod,Func,Arity,MS,CallFunc) when is_integer(Arity) -> tpm_tracer(all,Mod,Func,Arity,MS,CallFunc).
tpm_tracer(Nodes,Mod,Func,Arity,MS,CallFunc) ->
@@ -878,7 +878,7 @@ cancel_suspension() -> %% Status=running|{suspended,SReason}
%%
%% Get Status form all or specified runtime components.
-get_status(Nodes) when list(Nodes) ->
+get_status(Nodes) when is_list(Nodes) -> gen_server:call(?CONTROLLER,{get_status,Nodes},?CALL_TIMEOUT).
get_status() ->
@@ -918,7 +918,7 @@ get_tracerdata(Nodes) when is_list(Nodes) -> list_logs() ->
gen_server:call(?CONTROLLER,list_logs,?CALL_TIMEOUT).
-list_logs(TracerDataOrNodesList) when list(TracerDataOrNodesList) ->
+list_logs(TracerDataOrNodesList) when is_list(TracerDataOrNodesList) -> gen_server:call(?CONTROLLER,{list_logs,TracerDataOrNodesList},?CALL_TIMEOUT).
%% ------------------------------------------------------------------------------
@@ -960,17 +960,17 @@ list_logs(TracerDataOrNodesList) when list(TracerDataOrNodesList) -> %% Note that the client process using this function will wait until all files
%% are moved. The job can be cancelled, causing any already copied files to be
%% removed, by simply terminating the waiting client process.
-fetch_log(DestDir,Prefix) when list(DestDir),list(Prefix) ->
+fetch_log(DestDir,Prefix) when is_list(DestDir),is_list(Prefix) -> gen_server:call(?CONTROLLER,{fetch_log,node(),all,DestDir,Prefix},infinity).
-fetch_log(ToNode,DestDir,Prefix) when atom(ToNode),list(DestDir),list(Prefix) ->
+fetch_log(ToNode,DestDir,Prefix) when is_atom(ToNode),is_list(DestDir),is_list(Prefix) -> gen_server:call(?CONTROLLER,{fetch_log,ToNode,all,DestDir,Prefix},infinity);
-fetch_log(LogSpecList,DestDir,Prefix) when list(LogSpecList),list(DestDir),list(Prefix) ->
+fetch_log(LogSpecList,DestDir,Prefix) when is_list(LogSpecList),is_list(DestDir),is_list(Prefix) -> gen_server:call(?CONTROLLER,{fetch_log,node(),LogSpecList,DestDir,Prefix},infinity).
fetch_log(ToNode,LogSpecList,DestDir,Prefix)
- when atom(ToNode),list(LogSpecList),list(DestDir),list(Prefix) ->
+ when is_atom(ToNode),is_list(LogSpecList),is_list(DestDir),is_list(Prefix) -> gen_server:call(?CONTROLLER,{fetch_log,ToNode,LogSpecList,DestDir,Prefix},infinity).
%% ------------------------------------------------------------------------------
diff --git a/lib/inviso/src/inviso_c.erl b/lib/inviso/src/inviso_c.erl index ecbd5b0778..b1597a7f35 100644 --- a/lib/inviso/src/inviso_c.erl +++ b/lib/inviso/src/inviso_c.erl @@ -100,7 +100,7 @@ init({_Parent,Options}) -> end. %% ------------------------------------------------------------------------------ -handle_call({subscribe,Pid},_From,LD) when pid(Pid) -> +handle_call({subscribe,Pid},_From,LD) when is_pid(Pid) -> MRef=erlang:monitor(process,Pid), {reply,ok,LD#state{subscribers=[{Pid,MRef}|LD#state.subscribers]}}; handle_call({subscribe,Faulty},_From,LD) -> @@ -131,7 +131,7 @@ handle_call({change_options,Nodes,Opts},_From,LD) -> end; handle_call({init_tracing,TracerDataList},_From,LD) -> {reply,adapt_reply(LD,do_init_tracing(TracerDataList,LD)),LD}; -handle_call({init_tracing,Nodes,TracerData},_From,LD) when list(Nodes) -> +handle_call({init_tracing,Nodes,TracerData},_From,LD) when is_list(Nodes) -> TracerDataList= lists:map(fun(N)->{N,TracerData} end,started_trace_nodes(Nodes,LD)), {reply,adapt_reply(LD,do_init_tracing(TracerDataList,LD)),LD}; @@ -173,7 +173,7 @@ handle_call({fetch_log,ToNode,Spec,Dest,Prefix},From,LD) -> end; handle_call({delete_log,NodesOrNodeSpecs},_From,LD) -> {reply,adapt_reply(LD,do_delete_log(NodesOrNodeSpecs,LD)),LD}; -handle_call({delete_log,Nodes,Specs},_From,LD) when list(Nodes) -> +handle_call({delete_log,Nodes,Specs},_From,LD) when is_list(Nodes) -> Reply=do_delete_log(lists:map(fun(N)->{N,Specs} end,Nodes),LD), {reply,adapt_reply(LD,Reply),LD}; handle_call({delete_log,FaultyNodes,_Specs},_From,LD) -> @@ -283,7 +283,7 @@ do_change_option(Nodes,Options,LD) -> do_change_option_2([Node|Tail],Options,LD,Replies) -> case get_node_rec(Node,LD) of - Rec when record(Rec,node) -> + Rec when is_record(Rec,node) -> Answer=?RUNTIME:change_options(Rec#node.pid,Options), do_change_option_2(Tail,Options,LD,[{Node,Answer}|Replies]); Error -> @@ -333,7 +333,7 @@ do_init_tracing_2(What,_LD,_) -> %% Returns {ok,Reply} or {error,Reason}. distribute_tp(all,Patterns,FlagList,LD) -> distribute_tp(started_trace_nodes(all,LD),Patterns,FlagList,LD); -distribute_tp(Nodes,Patterns,FlagList,LD) when list(Nodes) -> +distribute_tp(Nodes,Patterns,FlagList,LD) when is_list(Nodes) -> RTpids=lists:map(fun(N)->case get_node_rec(N,LD) of #node{pid=Pid} -> {Pid,N}; @@ -354,7 +354,7 @@ distribute_tp(Faulty,_,_,_) -> %% Returns {ok,Reply} or {error,Reason}. distribute_tf(all,Args,How,LD) -> distribute_tf(started_trace_nodes(all,LD),Args,How,LD); -distribute_tf(Nodes,Args,How,LD) when list(Nodes) -> +distribute_tf(Nodes,Args,How,LD) when is_list(Nodes) -> RTpids=lists:map(fun(Node)-> case get_node_rec(Node,LD) of #node{pid=Pid} -> @@ -369,7 +369,7 @@ distribute_tf(Faulty,_,_,_) -> {error,{badarg,Faulty}}. %% As above but specific args for each node. -distribute_tf(NodeArgs,How,LD) when list(NodeArgs) -> +distribute_tf(NodeArgs,How,LD) when is_list(NodeArgs) -> RTpidArgs=lists:map(fun({Node,Args})-> case get_node_rec(Node,LD) of #node{pid=Pid} -> @@ -384,7 +384,7 @@ distribute_tf(Faulty,_,_) -> {error,{badarg,Faulty}}. %% As above but both specific args for each node and How (set or remove flag). -distribute_tf(NodeArgHows,LD) when list(NodeArgHows) -> +distribute_tf(NodeArgHows,LD) when is_list(NodeArgHows) -> RTpidArgHows= lists:map(fun({Node,Args,How}) -> case get_node_rec(Node,LD) of @@ -405,7 +405,7 @@ distribute_tf(Faulty,_) -> %% Returns {ok,Reply} or {error,Reason}. distribute_metapattern(all,Args,LD) -> distribute_metapattern(started_trace_nodes(all,LD),Args,LD); -distribute_metapattern(Nodes,Args,LD) when list(Nodes) -> +distribute_metapattern(Nodes,Args,LD) when is_list(Nodes) -> RTpids=lists:map(fun(N)->case get_node_rec(N,LD) of #node{pid=Pid} -> {Pid,N}; @@ -480,7 +480,7 @@ do_cancel_suspension_2(Faulty,_,_) -> %% Return {ok,Reply} or {error,Reason}. do_stop_tracing(all,LD) -> do_stop_tracing(started_trace_nodes(all,LD),LD); -do_stop_tracing(Nodes,LD) when list(Nodes) -> +do_stop_tracing(Nodes,LD) when is_list(Nodes) -> RTpids=lists:map(fun(N)->case get_node_rec(N,LD) of #node{pid=Pid} -> {Pid,N}; @@ -580,7 +580,7 @@ do_list_logs_2(Other,_LD,_Replies) -> %% proper strings. do_fetch_log(ToNode,all,Dest,Prefix,From,LD) -> do_fetch_log(ToNode,started_trace_nodes(all,LD),Dest,Prefix,From,LD); -do_fetch_log(ToNode,Specs,Dest,Prefix,From,LD) when list(Dest),list(Prefix) -> +do_fetch_log(ToNode,Specs,Dest,Prefix,From,LD) when is_list(Dest),is_list(Prefix) -> CollectPid=spawn_link(ToNode,?MODULE,log_rec_init,[self(),Dest,Prefix,From]), do_fetch_log_2(Specs,LD,CollectPid,[],[]); do_fetch_log(_ToNode,_Specs,Dest,Prefix,From,_LD) -> @@ -639,7 +639,7 @@ do_delete_log(NodeSpecs,LD) -> LD,[]); false -> if - list(NodeSpecs),list(hd(NodeSpecs)) -> % A list of files. + is_list(NodeSpecs),is_list(hd(NodeSpecs)) -> % A list of files. do_delete_log_2(lists:map(fun(N)->{N,NodeSpecs} end, started_trace_nodes(all,LD)), LD,[]); @@ -785,9 +785,9 @@ check_options(Options, Context) -> check_options_2([],_Context,Result) -> {ok,Result}; -check_options_2([{subscribe,Pid}|OptionsTail],start,Result) when pid(Pid) -> +check_options_2([{subscribe,Pid}|OptionsTail],start,Result) when is_pid(Pid) -> check_options_2(OptionsTail,start,[{subscribe,Pid}|Result]); -check_options_2([{unsubscribe,Pid}|OptionsTail],start,Result) when pid(Pid) -> +check_options_2([{unsubscribe,Pid}|OptionsTail],start,Result) when is_pid(Pid) -> check_options_2(OptionsTail,start,[{unsubscribe,Pid}|Result]); check_options_2([{dependency,How}|OptionsTail],Context,Result) -> check_options_2(OptionsTail,Context,[{dependency,How}|Result]); @@ -819,12 +819,12 @@ initiate_state(Options) -> LD1#state{distributed=true} end. -initiate_state_2([{subscribe,Proc}|Tail],LD) when pid(Proc);atom(Proc)-> +initiate_state_2([{subscribe,Proc}|Tail],LD) when is_pid(Proc);is_atom(Proc)-> MRef=erlang:monitor(process,Proc), initiate_state_2(Tail,LD#state{subscribers=[{Proc,MRef}|LD#state.subscribers]}); -initiate_state_2([Opt|Tail],LD) when tuple(Opt),size(Opt)>=1 -> +initiate_state_2([Opt|Tail],LD) when is_tuple(Opt),size(Opt)>=1 -> initiate_state_2(Tail,initiate_state_3(element(1,Opt),Opt,LD)); -initiate_state_2([Opt|Tail],LD) when atom(Opt) -> +initiate_state_2([Opt|Tail],LD) when is_atom(Opt) -> initiate_state_2(Tail,initiate_state_3(Opt,Opt,LD)); initiate_state_2([_|Tail],LD) -> initiate_state_2(Tail,LD); @@ -853,9 +853,9 @@ initiate_state_is_rt_option(_) -> false. %% or more values associated with the Parameter, or just an atom. merge_options([], Options) -> Options; -merge_options([T|DefaultTail],Options) when tuple(T),size(T)>=1 -> +merge_options([T|DefaultTail],Options) when is_tuple(T),size(T)>=1 -> merge_options(DefaultTail,merge_options_2(element(1,T),T,Options)); -merge_options([Param|DefaultTail],Options) when atom(Param) -> +merge_options([Param|DefaultTail],Options) when is_atom(Param) -> merge_options(DefaultTail,merge_options_2(Param,Param,Options)); merge_options([_|DefaultTail],Options) -> % Strange, bad default option! merge_options(DefaultTail,Options). @@ -868,7 +868,7 @@ merge_options_2(Param,Opt,Options) -> [Opt|Options] end. -merge_options_find(Param,[T|_]) when tuple(T),element(1,T)==Param -> +merge_options_find(Param,[T|_]) when is_tuple(T),element(1,T)==Param -> true; merge_options_find(Param,[Param|_]) -> true; @@ -883,7 +883,7 @@ merge_options_find(_,[]) -> %% It also checks the formatting of the tracerdata since runtime components %% does not accept too badly formatted tracerdata. %% Returns {ok,NewTraceData} or {error,Reason}. -check_modify_tracerdata(TracerData,LoopData) when list(TracerData) -> +check_modify_tracerdata(TracerData,LoopData) when is_list(TracerData) -> case lists:keysearch(trace,1,TracerData) of {value,{_,TraceTD}} -> % Examine the trace part. case check_modify_tracerdata(TraceTD,LoopData) of @@ -908,7 +908,7 @@ check_modify_tracerdata({relayer,Collector},LoopData) when is_atom(Collector) -> end; check_modify_tracerdata({Type,Data},_LoopData) when Type==ip;Type==file -> {ok,{Type,Data}}; -check_modify_tracerdata({Handler,Data},_LoopData) when function(Handler) -> +check_modify_tracerdata({Handler,Data},_LoopData) when is_function(Handler) -> {ok,{Handler,Data}}; check_modify_tracerdata(Data,_LoopData) -> {error,{bad_tracerdata,Data}}. @@ -999,7 +999,7 @@ set_node_rec_2(Rec,[NodeRec|Tail]) -> %% Help function finding a node record for Node in a list of #node or in loopdata. %% Returns the #node in question or {error,not_an_added_node}. -get_node_rec(Node,NodeList) when list(NodeList) -> +get_node_rec(Node,NodeList) when is_list(NodeList) -> get_node_rec_2(Node,NodeList); get_node_rec(Node,#state{nodes=NodeList}) -> get_node_rec_2(Node,NodeList). @@ -1016,7 +1016,7 @@ get_node_rec_2(Node,[_NodeRec|Tail]) -> %% structure. Returns a new list of #node or a new loopdata structure. delete_node_rec(Node,LD=#state{nodes=NodeList}) -> LD#state{nodes=delete_node_rec_2(Node,NodeList)}; -delete_node_rec(Node,NodeList) when list(NodeList) -> +delete_node_rec(Node,NodeList) when is_list(NodeList) -> delete_node_rec_2(Node,NodeList). delete_node_rec_2(_,[]) -> @@ -1059,7 +1059,7 @@ log_rec_init(Parent,Dest,Prefix,From={ClientPid,_}) -> Fetchers), CMRef=erlang:monitor(process,ClientPid), % Monitor the client. case log_rec_loop(Dest,Prefix,RTs,InitialReplies,CMRef) of - Reply when list(Reply) -> % It is an ok value. + Reply when is_list(Reply) -> % It is an ok value. gen_server:reply(From,{ok,Reply}); {error,Reason} -> gen_server:reply(From,{error,Reason}); diff --git a/lib/inviso/src/inviso_lfm.erl b/lib/inviso/src/inviso_lfm.erl index 362176c776..085048518c 100644 --- a/lib/inviso/src/inviso_lfm.erl +++ b/lib/inviso/src/inviso_lfm.erl @@ -87,13 +87,13 @@ %% close the outfile.
%%
%% Using merge/2 assumes you want to use default handlers writing to a file.
-merge(Files,OutputFile) when list(OutputFile) ->
+merge(Files,OutputFile) when is_list(OutputFile) -> merge(Files,fun outfile_opener/1,fun outfile_writer/4,fun outfile_closer/1,OutputFile,off).
-merge(Files,WorkHandlerFun,HandlerData) when function(WorkHandlerFun) ->
+merge(Files,WorkHandlerFun,HandlerData) when is_function(WorkHandlerFun) -> merge(Files,void,WorkHandlerFun,void,HandlerData,off);
-merge(Files,OutputFile,Dbg) when list(OutputFile) ->
+merge(Files,OutputFile,Dbg) when is_list(OutputFile) -> merge(Files,fun outfile_opener/1,fun outfile_writer/4,fun outfile_closer/1,OutputFile,Dbg).
-merge(Files,WorkHandlerFun,HandlerData,Dbg) when function(WorkHandlerFun) ->
+merge(Files,WorkHandlerFun,HandlerData,Dbg) when is_function(WorkHandlerFun) -> merge(Files,void,WorkHandlerFun,void,HandlerData,Dbg).
merge(Files,BeginHandlerFun,WorkHandlerFun,EndHandlerFun,HandlerData) ->
merge(Files,BeginHandlerFun,WorkHandlerFun,EndHandlerFun,HandlerData,off).
@@ -123,7 +123,7 @@ init_receiver(From,Files,BeginHandlerFun,WorkHandlerFun,EndHandlerFun,HandlerDat {ok,Readers} ->
process_flag(trap_exit,true),
if
- function(BeginHandlerFun) ->
+ is_function(BeginHandlerFun) -> case catch BeginHandlerFun(HandlerData) of
{ok,NewHandlerData} ->
init_receiver_2(From,WorkHandlerFun,EndHandlerFun,
@@ -145,7 +145,7 @@ init_receiver_2(From,WorkHandlerFun,EndHandlerFun,HandlerData,Dbg,Readers) -> {Reply,NewHandlerData}=
loop(From,WorkHandlerFun,HandlerData,NewReaders,EntryStruct,Dbg,0),
if
- function(EndHandlerFun) ->
+ is_function(EndHandlerFun) -> case EndHandlerFun(NewHandlerData) of
ok ->
From ! {reply,self(),Reply};
@@ -207,7 +207,7 @@ find_oldest_entry(EntryStruct) -> case list_all_entries(EntryStruct) of
[] -> % The we are done!
done;
- EntryList when list(EntryList) -> % Find smallest timestamp in here then.
+ EntryList when is_list(EntryList) -> % Find smallest timestamp in here then. {Pid,Node,PidMappings,_TS,Term}=
lists:foldl(fun({P,N,PMap,TS1,T},{_P,_N,_PMap,TS0,_T}) when TS1<TS0 ->
{P,N,PMap,TS1,T};
diff --git a/lib/inviso/src/inviso_lfm_tpfreader.erl b/lib/inviso/src/inviso_lfm_tpfreader.erl index d0db4b6d02..6de4d11fe0 100644 --- a/lib/inviso/src/inviso_lfm_tpfreader.erl +++ b/lib/inviso/src/inviso_lfm_tpfreader.erl @@ -60,9 +60,9 @@ %% File=string()
%% Spawn on this function to start a reader process for trace-port generated
%% logfiles, possibly with inviso-generated ti-files.
-init(RecPid,LogFiles=[Tuple|_]) when tuple(Tuple) -> % Only one LogFiles.
+init(RecPid,LogFiles=[Tuple|_]) when is_tuple(Tuple) -> % Only one LogFiles. init(RecPid,[LogFiles]);
-init(RecPid,FileStruct) when list(FileStruct) ->
+init(RecPid,FileStruct) when is_list(FileStruct) -> logfiles_loop(RecPid,FileStruct).
%% -----------------------------------------------------------------------------
@@ -135,7 +135,7 @@ read_traceport_file(FileName,FD) -> case file:read(FD,5) of % Trace-port file entries start with 5 bytes.
{ok,<<0,Size:32>>} -> % Each entry in a traceport file begins.
case file:read(FD,Size) of
- {ok,Bin} when binary(Bin),size(Bin)=:=Size ->
+ {ok,Bin} when is_binary(Bin),size(Bin)=:=Size -> try binary_to_term(Bin) of
Term -> % Bin was a properly formatted term!
{ok,Term}
@@ -244,7 +244,7 @@ find_timestamp_in_term(_) -> % Don't know if there is a timestamp %% (1) plain straight raw binary files.
handle_ti_file(FileStruct) ->
case lists:keysearch(ti_log,1,FileStruct) of
- {value,{_,[FileName]}} when list(FileName) -> % There is one ti-file in this set.
+ {value,{_,[FileName]}} when is_list(FileName) -> % There is one ti-file in this set. case file:open(FileName,[read,raw,binary]) of
{ok,FD} ->
TIdAlias=ets:new(list_to_atom("inviso_ti_atab_"++pid_to_list(self())),
@@ -308,9 +308,9 @@ handle_ti_file_2(FD,TIdAlias,TIdUnalias) -> handle_logfiles(FileStruct) ->
handle_logfiles_2(lists:keysearch(trace_log,1,FileStruct)).
-handle_logfiles_2({value,{_,[FileName]}}) when list(FileName)-> % One single plain file.
+handle_logfiles_2({value,{_,[FileName]}}) when is_list(FileName)-> % One single plain file. [FileName];
-handle_logfiles_2({value,{_,Files}}) when list(Files) -> % A wrap-set.
+handle_logfiles_2({value,{_,Files}}) when is_list(Files) -> % A wrap-set. handle_logfile_sort_wrapset(Files);
handle_logfiles_2(_) ->
[]. % Pretend there were no files otherwise.
diff --git a/lib/inviso/src/inviso_tool.erl b/lib/inviso/src/inviso_tool.erl index 7126ba4387..7d3cfb9da0 100644 --- a/lib/inviso/src/inviso_tool.erl +++ b/lib/inviso/src/inviso_tool.erl @@ -1,3324 +1,3255 @@ -% ``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 via the world wide web 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.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id$
-%%
-%% Description:
-%% The inviso_tool implementation. A tool that uses inviso.
-%%
-%% Authors:
-%% Lennart �hman, [email protected]
-%% -----------------------------------------------------------------------------
-
--module(inviso_tool).
-
-
-%% This is the inviso tool, which is a tool using the inviso trace application.
-%% It is developed to make tracing using trace cases possible in an environment
-%% of distributed Erlang nodes.
-%% A current restriction is that the Erlang nodes are supposed to have the same
-%% code. This since inviso tool can at this point not handle subsets of nodes.
-%% Instead all participating Erlang nodes are treated the same.
-%%
-%% The main functionality of the inviso tool are:
-%%
-%% (1) Handles start and stop of tracing at participating nodes.
-%% (2) Interprets trace-case files at a distributed network level.
-%% (The inviso runtime component is responsible for interpreting
-%% trace cases at a local level, if run in an autostart).
-%% (3) Keeps a command history log from which:
-%% (a) Sequences easily can be repeated.
-%% (b) Autostart configuration files can be created (understood by the
-%% default inviso autostart mechanism).
-%% (4) Performs reactivation in case tracing is suspended (manually or by
-%% an overload mechanism).
-%% (5) Can reconnect crashed nodes and by using the history bringing them
-%% up to speed.
-
-%% Distributed Erlang
-%% ------------------
-%% Inviso is built to run in a distributed environment.
-%% The inviso tool can also be used in a non distributed environment.
-
-%% Short description
-%% -----------------
-%% Start-up of the inviso tool
-%% During the start-up of the tool, the tool starts runtime components at
-%% all participating nodes. A runtime component can already be running at
-%% a particular node and will then simply be adopted.
-%%
-%% Session
-%% A session is said to start when tracing is initiated, and ends when
-%% made to stop by the user. When a session is stopped, tracing is stopped
-%% at all participating nodes. Note that participating nodes may come and
-%% go though the time-frame of a session. That means that if a node is
-%% reconnected it may resume its tracing in the current session through
-%% a 'restart_session'. A runtime component that is already tracing at the
-%% time start-session will simply be part of the session without its
-%% ingoing tracing being changed.
-%%
-%% Reactivation
-%% A node that is suspended can be reactivated to resume tracing. Note that
-%% tracing has in this situation never been stopped at the node in question.
-%% The inviso tool resumes the node and applies the history to it.
-%%
-%% Reconnect
-%% A node that is "down" from the inviso tool's perspective can be
-%% reconnected. During reconnection the tool restarts the runtime component
-%% at that node but does not (re)initiate tracing. The latter is called
-%% restart_session and must be done explicitly, unless the node in question
-%% is in fact already tracing. If the node is already tracing (due to an autostart
-%% for instance), it automatically becomes part of the ongoing session (if
-%% there is an ongoing session).
-%%
-%% Restart Session
-%% A node that has been down and has been reconnected can be made to
-%% initialize and resume its tracing. This is done by starting the session
-%% at the node in question and redoing the current history.
-
-%% Trace files within a session
-%% Since it is possible to init-tracing (from an inviso perspective) several
-%% times within the same session, a session may leave several trace log files
-%% behind. This must be resolved by the tracer data generator function
-%% (user supplied) by marking filenames in a chronological order but still
-%% making them possible to identify as part of the same session
-
-
-
-%% -----------------------------------------------------------------------------
-%% API exports.
-%% -----------------------------------------------------------------------------
-
--export([start/0,start/1,stop/0,stop/1]).
--export([reconnect_nodes/0,reconnect_nodes/1,
- start_session/0,start_session/1,
- reinitiate_session/0,reinitiate_session/1,
- restore_session/0,restore_session/1,restore_session/2,
- stop_session/0,
- reset_nodes/0,reset_nodes/1,
- atc/3,sync_atc/3,sync_atc/4,
- sync_rtc/2,sync_rtc/3,
- dtc/2,sync_dtc/2,sync_dtc/3,
- inviso/2]).
--export([reactivate/0,reactivate/1,
- save_history/1,
- get_autostart_data/1,get_autostart_data/2,
- get_activities/0,get_node_status/0,get_node_status/1,get_session_data/0]).
--export([flush/0,flush/1]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Debug exports.
-%% -----------------------------------------------------------------------------
-
--export([get_loopdata/0]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% OTP exports and call backs.
-%% -----------------------------------------------------------------------------
-
--export([init/1,handle_call/3,handle_cast/2,handle_info/2,terminate/2]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Internal exports.
-%% -----------------------------------------------------------------------------
-
--export([tc_executer/4,reactivator_executer/6]).
--export([std_options_generator/1]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Constants.
-%% -----------------------------------------------------------------------------
-
-%% Defines the inviso function calls that shall be possible to do through the
-%% inviso API in this tool.
--define(INVISO_CMDS,
- [{tp,5},{tp,4},{tp,1},{tpl,5},{tpl,4},{tpl,1},
- {ctp,1},{ctp,2},{ctp,3},{ctpl,1},{ctpl,2},{ctpl,3},
- {tf,2},{tf,1},{ctf,2},{ctf,1},{ctf_all,0},
- {init_tpm,4},{init_tpm,7},
- {tpm,4},{tpm,5},{tpm,8},
- {tpm_tracer,4},{tpm_tracer,5},{init_tpm,8},
- {tpm_ms,5},{tpm_ms_tracer,5},
- {ctpm_ms,4},{ctpm,3},
- {tpm_localnames,0},{ctpm_localnames,0},
- {tpm_globalnames,0},{ctpm_globalnames,0},
- {ctp_all,0},
- {suspend,1},{cancel_suspension,0}]).
-%% -----------------------------------------------------------------------------
-
-%% These inviso functions shall be included in the command history log. Others
-%% are not relevant to be redone during a recactivation, a restart session or
-%% exported to an autostart file.
--define(INVISO_CMD_HISTORY,
- [{tp,5},{tp,4},{tp,1},{tpl,5},{tpl,4},{tpl,1},
- {ctp,1},{ctp,2},{ctp,3},{ctpl,1},{ctpl,2},{ctpl,3},
- {tf,2},{tf,1},{ctf,2},{ctf,1},{ctf_all,0},
- {init_tpm,4},{init_tpm,7},
- {tpm,4},{tpm,5},{tpm,8},
- {tpm_tracer,4},{tpm_tracer,5},{init_tpm,8},
- {tpm_ms,5},{tpm_ms_tracer,5},
- {ctpm_ms,4},{ctpm,3},
- {tpm_localnames,0},{ctpm_localnames,0},
- {tpm_globalnames,0},{ctpm_globalnames,0},
- {ctp_all,0}]).
-%% -----------------------------------------------------------------------------
-
-%% Since many function calls to inviso may take long time, especially if they
-%% involve difficult and many trace patterns to set, the default gen_server:call
-%% time out can not be used. We just do not want to get stuck for ever if some
-%% error occurs.
--define(CALL_TIMEOUT,60000).
-
-%% Default max time to wait for a trace case called synchronously to return.
--define(SYNC_TC_TIMEOUT,10000).
-
-%% Runtime components shall terminate when the tool terminates.
--define(DEFAULT_DEPENDENCY,{dependency,0}).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Record definitions.
-%% -----------------------------------------------------------------------------
-
-%% The loopdata record.
--record(ld,{
- dir=".", % Working dir of the tool.
- nodes=down, % The nodesD database, defaults to non-distr.
- c_node, % Location of inviso_c.
- c_pid, % The inviso control component.
- regexp_node, % Node for regexp expansions.
- tc_dict, % Trace case definition db.
- chl, % Command history log.
- session_state=passive, % passive | tracing
- tdg={inviso_tool_lib,std_tdg,[]}, % Tracer data generator func.
- tracer_data, % Current session nr and TDGargs.
- reactivators=[], % Pids of now running reactivators.
- tc_def_file, % Trace case definition file.
- optg={?MODULE,std_options_generator,[]}, % Generates options to add_nodes/3.
- initial_tcs=[], % Initial trace cases.
- started_initial_tcs=[], % Cases that must be stopped when stop_tracing.
- history_dir, % File path for history file.
- keep_nodes=[], % Nodes that shall not be cleared when stopping.
- debug=false % Internal debug mode
- }).
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% API
-%% =============================================================================
-
-%% start()={ok,Pid} | {error,{already_started,pid()}}
-%% start(Config)
-%% Config=[{Opt,Value},...], list of tuple options.
-%% Opt=dir|nodes|c_node|regexp_node|tdg|tc_def_file|optg|initial_tcs|
-%% history_dir|keep_nodes
-%% Starts the inviso_tool process. Options in Config are the same as those
-%% which are kept in the #ld structure.
-start() ->
- start([]).
-start(Config) ->
- gen_server:start({local,?MODULE},?MODULE,Config,[]).
-%% -----------------------------------------------------------------------------
-
-%% stop(UntouchedNodes)=
-%% stop()={ok,NodeResults} | NodeResult | {error,Reason}
-%% UntouchedNodes=list(), nodes where any trace patterns shall not be removed.
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok | {error,Reason} | patterns_untouched
-%% Stops the inviso tool and the inviso control component. Runtime components are
-%% stopped by them selves depending on their dependcy of the control component.
-%% All runtime components that are not marked as to be kept will have their
-%% trace patterns cleared before the inviso control component is shutdown.
-%% The NodeResults indicates which nodes were successfullt handled.
-stop() ->
- stop([]).
-stop(UntouchedNodes) ->
- gen_server:call(?MODULE,{stop,UntouchedNodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% reconnect_nodes()=NodeResult; function for the nod-distributed case.
-%% reconnect_nodes(Nodes)={ok,NodesResults}
-%% NodesResults=[{Node,NodeResult},...]
-%% NodeResult={ok,{State,Status}} | {error,NReason}
-%% State=tracing | inactive
-%% Status=running | suspended
-%% NReason=unknown_node | already_connected | down
-%% (Re)starts the inviso runtime components at Nodes. Depending on its state
-%% (new,idle or tracing) and if the tool is running a session or not, it becomes
-%% part of the tool's ongoing session. If the newly reconnected node is not
-%% tracing but the tool runs a session, the node must be reinitiated to become
-%% tracing.
-reconnect_nodes() ->
- gen_server:call(?MODULE,{reconnect_nodes,local_runtime},?CALL_TIMEOUT).
-reconnect_nodes(Node) when atom(Node) ->
- reconnect_nodes([Node]);
-reconnect_nodes(Nodes) when list(Nodes) ->
- gen_server:call(?MODULE,{reconnect_nodes,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% start_session()={ok,{SessionNr,InvisoReturn}} | {error,Reason}
-%% start_session(MoreTDGargs)=
-%% MoreTDGargs=list(), prepended to the fixed list of args used when calling the
-%% tracer data generator function.
-%% SessionNr=integer(), trace sessions are numbered by the tool.
-%% InvisoReturn=If successful inviso call, the returnvalue from inviso.
-%% Note that individual nodes may be unsuccessful. See inviso:init_tracing/1
-%% Initiates tracing at all participating nodes.
-start_session() ->
- start_session([]).
-start_session(MoreTDGargs) ->
- gen_server:call(?MODULE,{start_session,MoreTDGargs},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% reinitiate_session(Nodes)={ok,InvisoReturn} | {error,Reason}
-%% InvisoReturn=If successful inviso call, the returnvalue from inviso:init_tracing/1.
-%% Note that individual nodes may be unsuccessful. Mentioned nodes not part
-%% of the tool or not in state inactive will be marked as failing by the
-%% tool in the InvisoReturn.
-%% To reinitate a node means to (inviso) init tracing at it according to saved
-%% tracer data generator arguments for the current session and then redo the current
-%% history to bring it up to speed. Note that the tool must be running a session
-%% for reinitiate to work.
-reinitiate_session() ->
- gen_server:call(?MODULE,{reinitiate_session,local_runtime},?CALL_TIMEOUT).
-reinitiate_session(Nodes) ->
- gen_server:call(?MODULE,{reinitiate_session,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% restore_session()=
-%% restore_session(MoreTDGargs)=
-%% restore_session(FileName)=
-%% restore_session(FileName,MoreTDGargs)={ok,{SessionNr,InvisoReturn}} | {error,Reason}
-%% The two first clauses will start a new session using the last history. This
-%% implies that there must have been a session running prior.
-%% The two last clauses starts a session and reads a history file and executes the
-%% tracecases in it at all inactive nodes.
-%% In both cases the reused or read history becomes the current histoy, just if the
-%% session had been initiated manually. The tool may not
-%% have a session ongoing, and nodes already tracing (nodes which were adopted)
-%% are not effected. Just like when starting a session manually.
-restore_session() ->
- restore_session([]).
-restore_session([]) -> % This cant be a filename.
- gen_server:call(?MODULE,{restore_session,[]},?CALL_TIMEOUT);
-restore_session(FileNameOrMoreTDGargs) ->
- case is_string(FileNameOrMoreTDGargs) of
- true -> % Interpret it as a filename.
- restore_session(FileNameOrMoreTDGargs,[]);
- false -> % The we want to use last session history!
- gen_server:call(?MODULE,{restore_session,FileNameOrMoreTDGargs},?CALL_TIMEOUT)
- end.
-restore_session(FileName,MoreTDGargs) ->
- gen_server:call(?MODULE,{restore_session,{FileName,MoreTDGargs}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% stop_session()={ok,{SessionNr,Result}} | {error,Reason}
-%% SessionNr=integer()
-%% Result=[{Node,NodeResult},...] | NonDistributedNodeResult
-%% NodeResult=ok | {error,Reason}
-%% NonDistributedNodeResult=[ok] | []
-%% Stops inviso tracing at all participating nodes. The inviso runtime components
-%% will go to state idle. It is now time to fetch the logfiles. Will most often
-%% succeed. Will only return an error if the entire inviso call returned an
-%% error. Not if an individual node failed stop tracing successfully.
-%% Any running trace case, including reactivator processes will be terminated.
-stop_session() ->
- gen_server:call(?MODULE,stop_session,?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% reset_nodes()=NodeResult | {error,Reason}
-%% reset_nodes(Nodes)={ok,NodeResults} | {error,Reason}
-%% NodeResults and NodeResult as returned by inviso:clear/1 and /0.
-%% Clear nodes from trace flags, trace patterns and meta trace patterns. The tool
-%% must not be having a running session.
-reset_nodes() ->
- gen_server:call(?MODULE,{reset_nodes,local_runtime},?CALL_TIMEOUT).
-reset_nodes(Nodes) ->
- gen_server:call(?MODULE,{reset_nodes,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% atc(TC,Id,Vars)=ok | {error,Reason}
-%% TC=atom(), name of the trace case.
-%% Id=term(), given name of this usage of TC.
-%% Vars=list(), list of variable bindings [{Var,Value},...], Var=atom(),Value=term().
-%% Function activating a trace case. The trace case must be defined in the
-%% trace case dictionary. The 'ok' return value is only a signal that the
-%% trace case has started successfully. It may then run for as long as it is
-%% programmed to run. An erroneous return value does not necessarily mean that
-%% the trace case has not been executed. It rather means that is undetermined
-%% what happend.
-atc(TC,Id,Vars) ->
- gen_server:call(?MODULE,{atc,{TC,Id,Vars}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% sync_atc(TC,Id,Vars)=Result | {error,Reason}
-%% sync_atc(TC,Id,Vars,TimeOut)=
-%% Result=term(), what ever is returned be the last expression in the trace case.
-%% TimeOut=interger() | infinity, the max wait time for the trace case to finnish.
-%% As atc/3 but waits for the trace case to finish.
-sync_atc(TC,Id,Vars) ->
- gen_server:call(?MODULE,{sync_atc,{TC,Id,Vars,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT).
-sync_atc(TC,Id,Vars,TimeOut) ->
- gen_server:call(?MODULE,{sync_atc,{TC,Id,Vars,TimeOut}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% sync_rtc(TC,Vars)=Result | {error,Reason}
-%% sync_rtc(TC,Vars,TimeOut)=
-%% Result=term(), what ever is returned be the last expression in the trace case.
-%% TimeOut=interger() | infinity, the max wait time for the trace case to finnish.
-%% As sync_atc/3 but the trace case is not marked as activated. It is mearly placed
-%% in the history. Hence with sync_rtc a trace case can be "activated" multiple time.
-sync_rtc(TC,Vars) ->
- gen_server:call(?MODULE,{sync_rtc,{TC,Vars,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT).
-sync_rtc(TC,Vars,TimeOut) ->
- gen_server:call(?MODULE,{sync_rtc,{TC,Vars,TimeOut}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% dtc(TC,Id)=ok | {error,Reason}
-%% Deactivates a previosly activated trace case. This function can only be used
-%% on trace cases that has a deactivation defined in the trace case dictionary.
-%% There is of course really no difference between a file containing an activation
-%% compared to a deactivation. But to be able cancelling activations out from the
-%% history log, a defined deactivation is essential.
-%% As with activation, the returned 'ok' simply indicates the start of the trace
-%% case.
-dtc(TC,Id) ->
- gen_server:call(?MODULE,{dtc,{TC,Id}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% sync_dtc(TC,Id)=Result | {error,Reason}
-%% sync_dtc(TC,Id,TimeOut)=
-%% Synchronous deactivation of trace case. See dtc/2 and sync_atc/3 for
-%% parameters.
-sync_dtc(TC,Id) ->
- gen_server:call(?MODULE,{sync_dtc,{TC,Id,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT).
-sync_dtc(TC,Id,TimeOut) ->
- gen_server:call(?MODULE,{sync_dtc,{TC,Id,TimeOut}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% inviso(Cmd,Args)=Result
-%% Cmd=atom(), the (inviso) function name that shall be called.
-%% Args=list(), the arguments to Cmd.
-%% Result=term(), the result from the inviso function call.
-%% This function executes a Cmd in the inviso tool context. The inviso call will
-%% be logged in history log and thereby repeated in case of a reactivation.
-%% Note that this function is intended for use with inviso function API without
-%% specifying any nodes, since the function call is supposed to be carried out on
-%% all nodes.
-%% When these functions are written to an autostart config file by the tool there
-%% is supposed to be a translation to inviso_rt functions.
-inviso(Cmd,Args) ->
- gen_server:call(?MODULE,{inviso,{Cmd,Args}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% reactivate()=ok | {error,Reason}
-%% reactivate(Node)=ok | {error,Reason}
-%% Moves a runtime component from suspended to the state running. This can be
-%% done for both tracing and inactive nodes. The later is necessary since you
-%% may have stopped tracing with a node suspended.
-%% In case the node is tracing, commands in the command history log are redone at
-%% the node in questions.
-%% Note that this function returns 'ok' before the node is running. This because the
-%% the reactivated history is done by a separate process and there is no guarantee
-%% when it will be ready. The reactivated node will not be marked as running in
-%% the tool until done reactivating.
-%% Further it is important to understand that if there are "ongoing" tracecases
-%% (i.e tracecase scripts that are currently executing) and this node was running
-%% at the time that tracecase script started to execute, the list of nodes bound
-%% to the Nodes variable in that script executer includes this node. Making it
-%% no longer suspended makes it start executing inviso commands from where ever
-%% such are called. Hence the reactivation may be interferred by that tracecase.
-reactivate() -> % Non-distributed API.
- reactivate(node()).
-reactivate(Node) ->
- gen_server:call(?MODULE,{reactivate,Node},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% save_history(FileName)={ok,AbsFileName} | {error,Reason}
-%% Saves the currently collected command history log to a file. The file will
-%% be a binary-file. If FileName is an absolute path, it will be saved to that
-%% file. Otherwise the history dir will be used. If no history dir was specified
-%% the tool dir will be used, prepended to FileName.
-save_history(FileName) ->
- gen_server:call(?MODULE,{save_history,FileName},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_autostart_data(Nodes,Dependency)={ok,{AutoStartData,NodeResults} |
-%% {ok,{AutoStartData,NodeResult}} | {error,Reason}
-%% Dependency=inviso dependency parameter which will be used for every
-%% autostarted runtime component (included in Options).
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={ok,{Options,{tdg,{M,F,CompleteTDGargs}}}} | {error,Reason}
-%% Options=add_nodes options to the inviso runtime component.
-%% M,F=atom(), the module and function for tracerdata generation.
-%% CompleteTDGargs=list(), all arguments as they are given to the tracer
-%% data generator function.
-%% AutostartData=[CaseSpec,...]
-%% CaseSpec={file,{FileName,Bindings}} | {mfa,{M,F,Args}}
-%% FileName=string(), pointing out the trace case file. Note that this
-%% is the same as the path used by the tool.
-%% Bindings=Var bindings used according to the history for the
-%% invocation.
-%% M,F=atom(), the function that shall be called (normally some inviso).
-%% Args=list(), the actual arguments. Note that this may contain things
-%% which can not be written to file (ports, pids,...).
-%% Function returning information on how to autostart a node to make it trace
-%% according to the current history. The inviso_tool does not know how to write
-%% the necessary files at the nodes in question. That must be done by the user
-%% of the tool, guided by the return value from this function.
-%% Note that there will be two types of trace case files. Regular trace case
-%% files and binaries returned from this function. The latter contains the
-%% inviso commands which have been executed. Note that the order amongst the
-%% trace cases and binaries is of importance (otherwise they will be redone in
-%% an incorrect order).
-get_autostart_data(Dependency) ->
- gen_server:call(?MODULE,{get_autostart_data,Dependency},?CALL_TIMEOUT).
-get_autostart_data(Nodes,Dependency) ->
- gen_server:call(?MODULE,{get_autostart_data,{Nodes,Dependency}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_activities()={ok,Ongoing} | {error,Reason}
-%% Ongoing=list(); [ [TraceCases] [,Reactivators] ]
-%% TraceCases={tracecases,TraceCaseList}
-%% TraceCaseList=[{{TCname,Id},Phase},...]
-%% Phase=activating | deactivating
-%% Reactivators={reactivating_nodes,ReactivatingNodes}
-%% ReactivatingNodes=[Node,...]
-%% Returns a list of assynchronous tracecases and nodes doing reactivation at
-%% this momement. This can be useful to implement "home brewn" synchronization,
-%% waiting for the runtime components to reach a certain state.
-get_activities() ->
- gen_server:call(?MODULE,get_activities,?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_status(Node)={ok,StateStatus} | {error,Reason}
-%% StateStatus={State,Status} | reactivating | down
-%% State=tracing | inactive | trace_failure
-%% Status=running | suspended
-get_node_status() ->
- get_node_status(local_runtime).
-get_node_status(Node) ->
- gen_server:call(?MODULE,{get_node_status,Node},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_session_data()={ok,{Status,SessionNr,TDGargs}} | {error,Reason}
-%% Status=tracing | not_tracing, info about current/last session.
-%% SessionNr=integer()
-%% TDGargs=list(), list of the arguments that will be given to the tracer data
-%% generator function (not including the leading Nodes list).
-%% Returns data about the current or last session.
-get_session_data() ->
- gen_server:call(?MODULE,get_session_data,?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% flush()={ok,NodeResults} | NodeResult | {error,Reason}
-%% flush(Nodes)={ok,NodesResults} | {error,Reason}
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok | {error,Reason}
-%% Makes runtime components flush their trace ports.
-flush() ->
- gen_server:call(?MODULE,flush,?CALL_TIMEOUT).
-flush(Nodes) ->
- gen_server:call(?MODULE,{flush,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_loopdata()=#ld
-%% Debug API returning the internal loopdata structure. See #ld above for details.
-get_loopdata() ->
- gen_server:call(?MODULE,get_loopdata,?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Internal APIs.
-%% -----------------------------------------------------------------------------
-
-%% tc_executer_reply(To,Reply)=nothing significant
-%% To=pid()
-%% Reply=term()
-%% Internal API used by a trace case executer process to signal its completion.
-tc_executer_reply(To,Reply) ->
- gen_server:cast(To,{tc_executer_reply,Reply}).
-%% -----------------------------------------------------------------------------
-
-%% Internal API used by a reactivator process indicating it is done with the
-%% history log it has got so far.
-%% Timeout set to infinity since the tool may be busy, then the reactivator just
-%% have to wait. If the tool crashes the reactivator will be go down too automatically.
-reactivator_reply(TPid,Counter) ->
- gen_server:call(TPid,{reactivator_reply,{Counter,self()}},infinity).
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% gen_server implementation.
-%% =============================================================================
-
-init(Config) ->
- case fetch_configuration(Config) of % From conf-file and Config.
- {ok,LD} when record(LD,ld) ->
- case start_inviso_at_c_node(LD) of
- {ok,CPid} ->
- LD2=start_runtime_components(LD),
- LD3=read_trace_case_definitions(LD2),
- process_flag(trap_exit,true),
- start_subscribe_inviso_events(LD3#ld.c_node),
- {ok,LD3#ld{c_pid=CPid}};
- {error,Reason} -> % Most likely already running.
- {stop,{error,Reason}}
- end;
- {error,Reason} ->
- {stop,{error,{start_up,Reason}}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function starting the inviso control component at node c_node, or "here"
-%% if it is not a distributed network.
-start_inviso_at_c_node(#ld{c_node=undefined}) -> % Non distributed case.
- case inviso:start() of
- {ok,Pid} ->
- {ok,Pid};
- {error,Reason} ->
- {error,Reason}
- end;
-start_inviso_at_c_node(#ld{c_node=CNode}) ->
- case rpc:call(CNode,inviso,start,[]) of
- {ok,Pid} ->
- {ok,Pid};
- {error,{already_started,_}} -> % A control component already started.
- {error,{inviso_control_already_running,CNode}};
- {error,Reason} ->
- {error,Reason};
- {badrpc,Reason} ->
- {error,{inviso_control_node_error,Reason}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function starting the runtime components at all particapting nodes.
-%% It also updates the nodes structure in the #ld to indicate which nodes where
-%% successfully started. Returns a new #ld.
-%% Note that a runtime component may actually be running at one or several nodes.
-%% This is supposed to be the result of an (wanted) autostart. Meaning that the
-%% inviso tool can not handle the situation if a runtime component is not doing
-%% what it is supposed to do. In case a runtime component is already running it
-%% will be adopted and therefore marked as running.
-start_runtime_components(LD=#ld{c_node=undefined}) ->
- start_runtime_components_2(local_runtime,undefined,LD);
-start_runtime_components(LD=#ld{c_node=CNode,nodes=NodesD}) ->
- start_runtime_components_2(get_all_nodenames_nodes(NodesD),CNode,LD).
-start_runtime_components(Nodes,LD=#ld{c_node=CNode}) ->
- start_runtime_components_2(Nodes,CNode,LD).
-
-start_runtime_components_2(local_runtime,CNode,LD=#ld{optg=OptG}) ->
- Opts=start_runtime_components_mk_opts(local_runtime,OptG),
- case inviso:add_node(mk_rt_tag(),Opts) of
- {ok,NAnsw} -> % Should be more clever really!
- NewNodesD=update_added_nodes(CNode,{ok,NAnsw},LD#ld.nodes),
- LD#ld{nodes=NewNodesD};
- {error,_Reason} ->
- LD
- end;
-start_runtime_components_2([Node|Rest],CNode,LD=#ld{optg=OptG}) ->
- Opts=start_runtime_components_mk_opts(Node,OptG),
- case rpc:call(CNode,inviso,add_nodes,[[Node],mk_rt_tag(),Opts]) of
- {ok,NodeResults} ->
- NewNodesD=update_added_nodes(CNode,NodeResults,LD#ld.nodes),
- start_runtime_components_2(Rest,CNode,LD#ld{nodes=NewNodesD});
- {error,_Reason} ->
- start_runtime_components_2(Rest,CNode,LD);
- {badrpc,_Reason} ->
- start_runtime_components_2(Rest,CNode,LD)
- end;
-start_runtime_components_2([],_,LD) ->
- LD.
-
-start_runtime_components_mk_opts(Node,{M,F,Args}) ->
- case catch apply(M,F,[Node|Args]) of
- {ok,Opts} when list(Opts) ->
- start_runtime_component_mk_opts_add_dependency(Opts);
- _ ->
- [?DEFAULT_DEPENDENCY]
- end.
-
-%% The options generator is not supposed to generate the dependency. Hence this
-%% function adds and if necessary removes an incorrectly added dependency tag.
-start_runtime_component_mk_opts_add_dependency(Opts) ->
- case lists:keysearch(dependency,1,Opts) of
- {value,_} -> % Not allowed!!!
- [?DEFAULT_DEPENDENCY|lists:keydelete(dependecy,1,Opts)];
- false ->
- [?DEFAULT_DEPENDENCY|Opts]
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function subscribing to inviso events from the inviso controller. This
-%% will make it possible to follow runtime components going down.
-start_subscribe_inviso_events(undefined) ->
- inviso:subscribe();
-start_subscribe_inviso_events(CNode) ->
- rpc:call(CNode,inviso,subscribe,[self()]). % Don't want the rpc-proc to subscribe!
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% gen_server handle call back functions.
-%% -----------------------------------------------------------------------------
-
-handle_call({stop,UntouchedNodes},_From,LD=#ld{nodes=NodesD,c_node=CNode,keep_nodes=KeepNodes})
- when is_list(UntouchedNodes) ->
- {stop,
- normal,
- remove_all_trace_patterns(CNode,
- UntouchedNodes++KeepNodes,
- get_available_nodes(NodesD)),
- LD};
-handle_call({stop,BadArg},_From,LD) ->
- {reply,{error,{badarg,BadArg}},LD};
-
-handle_call({reconnect_nodes,Nodes},_From,LD) ->
- case h_reconnect_nodes(Nodes,LD) of
- {ok,{Nodes2,NodesErr,NewLD}} ->
- if
- Nodes==local_runtime ->
- {reply,
- build_reconnect_nodes_reply(Nodes,Nodes2,NodesErr,NewLD#ld.nodes),
- NewLD};
- list(Nodes) ->
- {reply,
- {ok,build_reconnect_nodes_reply(Nodes,Nodes2,NodesErr,NewLD#ld.nodes)},
- NewLD}
- end;
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
-
-handle_call({start_session,MoreTDGargs},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- false -> % No session running.
- if
- list(MoreTDGargs) ->
- DateTime=calendar:universal_time(),
- {M,F,Args}=LD#ld.tdg,
- TDGargs=inviso_tool_lib:mk_tdg_args(DateTime,MoreTDGargs++Args),
- case h_start_session(M,F,TDGargs,LD) of
- {ok,{SessionNr,ReturnVal,NewLD}} -> % No nodes to initiate.
- NewLD2=add_initial_tcs_to_history(NewLD#ld.initial_tcs,
- NewLD#ld{chl=mk_chl(LD#ld.chl)}),
- {reply,
- {ok,{SessionNr,ReturnVal}},
- NewLD2#ld{session_state=tracing_sessionstate()}};
- {ok,{SessionNr,ReturnVal,Nodes2,NewLD}} ->
- NewLD2=do_initial_tcs(NewLD#ld.initial_tcs,
- Nodes2,
- NewLD#ld{chl=mk_chl(LD#ld.chl)}),
- {reply,
- {ok,{SessionNr,ReturnVal}},
- NewLD2#ld{session_state=tracing_sessionstate()}};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true -> % Faulty TDGargs.
- {reply,{error,{badarg,MoreTDGargs}},LD}
- end;
- true ->
- {reply,{error,session_already_started},LD}
- end;
-
-handle_call({reinitiate_session,Nodes},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- true -> % The tool must be tracing.
- {M,F,_Args}=LD#ld.tdg,
- TDGargs=get_latest_tdgargs_tracer_data(LD#ld.tracer_data),
- case h_reinitiate_session(Nodes,M,F,TDGargs,LD) of
- {ok,{NodesErr,ReturnVal,NewLD}} ->
- {reply,
- {ok,build_reinitiate_session_reply(Nodes,NodesErr,ReturnVal)},
- NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- false -> % Must have a running session!
- {reply,{error,no_session},LD}
- end;
-
-handle_call({restore_session,{FileName,MoreTDGargs}},_From,LD=#ld{chl=OldCHL})
- when list(MoreTDGargs) ->
- case is_tracing(LD#ld.session_state) of
- false ->
- case catch make_absolute_path(FileName,LD#ld.dir) of
- AbsFileName when list(AbsFileName) ->
- case file:read_file(AbsFileName) of
- {ok,Bin} ->
- if
- list(MoreTDGargs) ->
- case catch replace_history_chl(OldCHL,
- binary_to_term(Bin)) of
- {ok,CHL} -> % The file was well formatted.
- case h_restore_session(MoreTDGargs,
- LD#ld{chl=CHL}) of
- {ok,{SessionNr,ReturnVal,NewLD}} ->
- {reply,
- {ok,{SessionNr,ReturnVal}},
- NewLD#ld{session_state=
- tracing_sessionstate()}};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- Error -> % Badly formatted file.
- {reply,
- {error,{bad_file,{AbsFileName,Error}}},
- LD}
- end;
- true ->
- {reply,{error,{badarg,MoreTDGargs}},LD}
- end;
- {error,Reason} ->
- {reply,{error,{read_file,Reason}},LD}
- end;
- Error ->
- {reply,{error,{bad_filename,{FileName,Error}}},LD}
- end;
- true ->
- {reply,{error,session_already_started},LD}
- end;
-%% This is doing restore session on the current history.
-handle_call({restore_session,MoreTDGargs},_From,LD=#ld{chl=CHL}) ->
- case is_tracing(LD#ld.session_state) of
- false ->
- case history_exists_chl(CHL) of
- true -> % There is a history to redo.
- if
- list(MoreTDGargs) ->
- case h_restore_session(MoreTDGargs,LD) of
- {ok,{SessionNr,ReturnVal,NewLD}} ->
- {reply,
- {ok,{SessionNr,ReturnVal}},
- NewLD#ld{session_state=tracing_sessionstate()}};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,MoreTDGargs}},LD}
- end;
- false ->
- {reply,{error,no_history},LD}
- end;
- true ->
- {reply,{error,session_already_started},LD}
- end;
-
-%% To stop tracing means stop_tracing through the inviso API. But we must also
-%% remove any help processes executing inviso commands (trace case executers
-%% and reactivators).
-%% Note that to be really sure we should actually wait for EXIT-signals from those
-%% processes before returning a successful returnvalue to the caller. In theory
-%% those processes could issue an inviso call effecting a new trace session started
-%% with init_tracing shortly after the call to stop_tracing. But too complicated! :-)
-%% Further, stop-tracing is done on all nodes in our nodes structure. Regardless
-%% if the node is tracing or not
-handle_call(stop_session,_From,LD=#ld{session_state=SState,chl=CHL,reactivators=ReAct}) ->
- case is_tracing(SState) of
- true ->
- NewCHL=stop_all_tc_executer_chl(CHL), % Stop any running trace case proc.
- NewReAct=stop_all_reactivators(ReAct), % Stop any running reactivators.
- case h_stop_session(LD) of
- {ok,{SessionNr,Result}} ->
- NewNodesD=set_inactive_nodes(Result,LD#ld.nodes),
- {reply,
- {ok,{SessionNr,Result}},
- LD#ld{session_state=passive_sessionstate(),
- nodes=NewNodesD,
- chl=NewCHL,
- reactivators=NewReAct,
- started_initial_tcs=[]}};
- {error,Reason} -> % Now we're really in deep shit :-)
- {reply,{error,{unrecoverable,Reason}},LD}
- end;
- false ->
- {reply,{error,no_session},LD}
- end;
-
-handle_call({reset_nodes,Nodes},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- false -> % We can not be in a session.
- {reply,h_reset_nodes(Nodes,LD#ld.c_node),LD};
- true ->
- {reply,{error,session_active},LD}
- end;
-
-%% Calling a trace-case, or "turning it on".
-handle_call({atc,{TC,Id,Vars}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of % Check that we are tracing now.
- true ->
- case h_atc(TC,Id,Vars,LD) of
- {ok,NewLD} -> % Trace case executed.
- {reply,ok,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- false -> % Can't activate if not tracing.
- {reply,{error,no_session},LD}
- end;
-
-handle_call({sync_atc,{TC,Id,Vars,TimeOut}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- true ->
- if
- integer(TimeOut);TimeOut==infinity ->
- case h_sync_atc(TC,Id,Vars,TimeOut,LD) of
- {ok,NewLD,Result} ->
- {reply,Result,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,TimeOut}},LD}
- end;
- false ->
- {reply,{error,no_session},LD}
- end;
-
-handle_call({sync_rtc,{TC,Vars,TimeOut}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- true ->
- if
- integer(TimeOut);TimeOut==infinity ->
- case h_sync_rtc(TC,Vars,TimeOut,LD) of
- {ok,NewLD,Result} ->
- {reply,Result,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,TimeOut}},LD}
- end;
- false ->
- {reply,{error,no_session},LD}
- end;
-
-
-handle_call({dtc,{TC,Id}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of % Check that we are tracing now.
- true ->
- case h_dtc(TC,Id,LD) of
- {ok,NewLD} ->
- {reply,ok,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- false -> % Can't activate if not tracing.
- {reply,{error,no_session},LD}
- end;
-
-handle_call({sync_dtc,{TC,Id,TimeOut}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of % Check that we are tracing now.
- true ->
- if
- integer(TimeOut);TimeOut==infinity ->
- case h_sync_dtc(TC,Id,TimeOut,LD) of
- {ok,NewLD,Result} ->
- {reply,Result,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,TimeOut}},LD}
- end;
- false -> % Can't activate if not tracing.
- {reply,{error,no_session},LD}
- end;
-
-handle_call({inviso,{Cmd,Args}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- true ->
- if
- list(Args) ->
- case h_inviso(Cmd,Args,LD) of
- {ok,{Reply,NewLD}} ->
- {reply,Reply,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,Args}},LD}
- end;
- false -> % Can't do if not tracing.
- {reply,{error,no_session},LD}
- end;
-
-handle_call({reactivate,Node},_From,LD=#ld{nodes=NodesD,c_node=CNode}) ->
- case get_state_nodes(Node,NodesD) of
- {trace_failure,_} ->
- {reply,{error,trace_failure},LD};
- {State,suspended} -> % The node is infact suspended.
- case h_reactivate(Node,CNode) of
- ok ->
- case {State,is_tracing(LD#ld.session_state)} of
- {tracing,true} -> % Only then shall we redo cmds.
- {reply,ok,redo_cmd_history(Node,LD)};
- _ -> % All other just no longer suspended.
- {reply,ok,LD#ld{nodes=set_running_nodes(Node,NodesD)}}
- end;
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- reactivating ->
- {reply,{error,reactivating},LD};
- {_,running} ->
- {reply,{error,already_running},LD};
- down ->
- {reply,{error,not_available},LD};
- false ->
- {reply,{error,unknown_node},LD}
- end;
-
-handle_call({save_history,FileName},_From,LD=#ld{chl=CHL,dir=Dir,history_dir=HDir}) ->
- case lists:keysort(2,get_loglist_chl(CHL)) of
- [] -> % Empty history or no history.
- {reply,{error,no_history},LD};
- Log ->
- case h_save_history(HDir,Dir,FileName,Log) of
- {ok,AbsFileName} ->
- {reply,{ok,AbsFileName},LD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end
- end;
-
-
-handle_call({get_autostart_data,{Nodes,Dependency}},_From,LD=#ld{chl=CHL}) ->
- case build_autostart_data(lists:keysort(2,get_loglist_chl(CHL)),LD#ld.tc_dict) of
- {ok,ASD} ->
- TDGargs=get_latest_tdgargs_tracer_data(LD#ld.tracer_data),
- {M,F,_}=LD#ld.tdg,
- OptsG=LD#ld.optg, % Addnodes options generator.
- {reply,
- h_get_autostart_data(Nodes,LD#ld.c_node,Dependency,ASD,M,F,TDGargs,OptsG),
- LD};
- {error,Reason} -> % Bad datatypes in command args.
- {reply,{error,Reason},LD}
- end;
-
-handle_call({get_autostart_data,Dependency},From,LD=#ld{c_node=undefined}) ->
- handle_call({get_autostart_data,{local_runtime,Dependency}},From,LD);
-handle_call({get_autostart_data,Dependency},From,LD=#ld{nodes=NodesD}) ->
- Nodes=get_all_nodenames_nodes(NodesD),
- handle_call({get_autostart_data,{local_runtime,{Nodes,Dependency}}},From,LD);
-
-handle_call(get_activities,_From,LD=#ld{chl=CHL,reactivators=Reactivators}) ->
- TraceCases=get_ongoing_chl(CHL),
- RNodes=get_all_nodes_reactivators(Reactivators),
- ReturnList1=
- if
- TraceCases==[] ->
- [];
- true ->
- [{tracecases,TraceCases}]
- end,
- ReturnList2=
- if
- RNodes==[] ->
- ReturnList1;
- true ->
- [{reactivating_nodes,RNodes}|ReturnList1]
- end,
- {reply,{ok,ReturnList2},LD};
-
-handle_call({get_node_status,Node},_Node,LD) ->
- case get_state_nodes(Node,LD#ld.nodes) of
- false ->
- {reply,{error,unknown_node},LD};
- StateStatus ->
- {reply,{ok,StateStatus},LD}
- end;
-
-handle_call(get_session_data,_From,LD=#ld{session_state=SState,tracer_data=TD}) ->
- case get_latest_session_nr_tracer_data(TD) of
- undefined ->
- {reply,{error,no_session},LD};
- SessionNr ->
- TDGargs=get_latest_tdgargs_tracer_data(TD),
- case is_tracing(SState) of
- true ->
- {reply,{ok,{tracing,SessionNr,TDGargs}},LD};
- false ->
- {reply,{ok,{not_tracing,SessionNr,TDGargs}},LD}
- end
- end;
-
-handle_call(flush,_From,LD=#ld{c_node=CNode,nodes=NodesD}) ->
- Nodes=get_tracing_nodes(NodesD),
- {reply,h_flush(CNode,Nodes),LD};
-handle_call({flush,Nodes},_From,LD=#ld{c_node=CNode}) ->
- {reply,h_flush(CNode,Nodes),LD};
-
-handle_call(get_loopdata,_From,LD) ->
- {reply,LD,LD};
-
-%% Internal handle_call callbacks.
-
-handle_call({reactivator_reply,{Counter,RPid}},_From,LD=#ld{chl=CHL}) ->
- HighestUsedCounter=get_highest_used_counter_chl(CHL),
- if
- HighestUsedCounter>Counter -> % There are now more log entries.
- NewUnsortedLog=get_loglist_chl(CHL),
- {reply,{more,NewUnsortedLog},LD};
- true -> % No Counter is youngest log entry.
- NodesD=LD#ld.nodes,
- Node=get_node_reactivators(RPid,LD#ld.reactivators),
- {reply,
- done,
- LD#ld{nodes=set_running_nodes(Node,NodesD),
- reactivators=del_reactivators(RPid,LD#ld.reactivators)}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Handling a notification from a trace case execution process. Receiving this
-%% indicated that this phase of the trace case is finnished.
-handle_cast({tc_executer_reply,{Phase,ProcH,Result}},LD) ->
- case Phase of
- activating -> % The trace case is running now.
- {ok,NewLD}=h_tc_activation_done(ProcH,Result,LD),
- {noreply,NewLD};
- stopping ->
- {ok,NewLD}=h_tc_stopping_done(ProcH,Result,LD),
- {noreply,NewLD};
- _ ->
- {noreply,LD}
- end;
-handle_cast(_,LD) ->
- {noreply,LD}.
-%% -----------------------------------------------------------------------------
-
-%% This is the case when a runtime component goes down. We stop all running
-%% reactivators for this node. Note that there can also be tracecases ongoing
-%% where this node is part of the Nodes variable. But there is not much we can
-%% do about that. Other then informing the user that it is unwise to reconnect
-%% this node before those tracecases have stopped being ongoing.
-handle_info({inviso_event,_CNode,_Time,{disconnected,Node,_}},LD) ->
- {noreply,LD#ld{nodes=set_down_nodes(Node,LD#ld.nodes),
- reactivators=stop_node_reactivators(Node,LD#ld.reactivators)}};
-
-%% This is the case when a runtime component gets suspended. Much of the same
-%% problem as described above applies.
-handle_info({inviso_event,_CNode,_Time,{state_change,Node,{_,{suspended,_}}}},LD) ->
- {noreply,LD#ld{nodes=set_suspended_nodes(Node,LD#ld.nodes),
- reactivators=stop_node_reactivators(Node,LD#ld.reactivators)}};
-
-handle_info(_,LD) ->
- {noreply,LD}.
-%% -----------------------------------------------------------------------------
-
-%% Called when the tool server stops. First clause, termination is initiated by
-%% our self and therefore controlled another way. In the second case we are
-%% stopping for some external reason, and we must then do more here in terminate/2.
-terminate(normal,#ld{c_node=CNode}) -> % This is when we are stopping our self.
- stop_inviso_at_c_node(CNode);
-terminate(_,#ld{c_node=CNode,nodes=NodesD,keep_nodes=KeepNodes}) ->
- remove_all_trace_patterns(CNode,KeepNodes,get_all_nodenames_nodes(NodesD)),
- stop_inviso_at_c_node(CNode).
-%% -----------------------------------------------------------------------------
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%% =============================================================================
-%% Handler first level help functions.
-%% =============================================================================
-
-%% -----------------------------------------------------------------------------
-%% reconnect_nodes
-%% -----------------------------------------------------------------------------
-
-%% Help function reconnecting the nodes in Nodes. Listed nodes must be part of
-%% the set of nodes handled by the tool. It is not possible to reconnect a node
-%% that is not marked as down. This partly because we otherwise risk losing the
-%% trace_failure state (which can not be rediscovered).
-h_reconnect_nodes(local_runtime,LD=#ld{nodes=NodesD}) -> % Non-distributed.
- case get_state_nodes(local_runtime,NodesD) of
- down ->
- {ok,{local_runtime,[],start_runtime_components(local_runtime,LD)}};
- _ -> % Allready connected!
- {ok,{[],{error,already_connected},LD}}
- end;
-h_reconnect_nodes(Nodes,LD=#ld{nodes=NodesD}) when list(Nodes) ->
- {Nodes2,NodesErr}=
- lists:foldl(fun(N,{Nodes2,NodesErr})->
- case get_state_nodes(N,NodesD) of
- down -> % Yes this node can be reconnected.
- {[N|Nodes2],NodesErr};
- false -> % Not part of the node-set!
- {Nodes2,[{N,{error,unknown_node}}|NodesErr]};
- _ -> % Allready connected!
- {Nodes2,[{N,{error,already_connected}}|NodesErr]}
- end
- end,
- {[],[]},
- Nodes),
- LD2=start_runtime_components(Nodes2,LD), % Inpect the #ld.nodes for result.
- {ok,{Nodes2,NodesErr,LD2}};
-h_reconnect_nodes(Nodes,_LD) ->
- {error,{badarg,Nodes}}.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% start_session
-%% -----------------------------------------------------------------------------
-
-%% Help function starting the tracing at all nodes. Note that the tracer data
-%% is calculated using a user defined function. This is how for instance the
-%% file names (of the log files) are determined.
-%% Before the nodes are initiated their (possibly remaining) trace patterns are
-%% cleared, both local and global.
-h_start_session(M,F,TDGargs,LD=#ld{c_node=CNode,nodes=NodesD,tracer_data=TDs}) ->
- case get_inactive_running_nodes(NodesD) of
- [] -> % There are no nodes to initiate!
- h_start_session_nonodes(TDGargs,LD,[]);
- Nodes -> % List of nodes or 'local_runtime'.
- case h_start_session_ctp_all(CNode,Nodes) of
- {ok,Errors,[]} -> % Now no nodes to initiate!
- h_start_session_nonodes(TDGargs,LD,Errors);
- {ok,Errors,Nodes2} -> % Now these nodes are fresh.
- case call_tracer_data_generator(CNode,M,F,TDGargs,Nodes2) of
- {ok,TracerList} -> % Generated our tracerdata.
- case h_start_session_2(CNode,TracerList,Errors) of
- {ok,ReturnValue} -> % Some nodes are initialized now.
- {NewNodesD,Nodes3}=
- set_tracing_running_nodes(CNode,ReturnValue,NodesD),
- {SessionNr,NewTDs}=insert_td_tracer_data(TDGargs,TDs),
- {ok,{SessionNr,
- ReturnValue,
- Nodes3, % The nodes that shall get initial tracases.
- LD#ld{nodes=NewNodesD,tracer_data=NewTDs}}};
- {error,Reason} ->
- {error,Reason}
- end;
- {error,Reason} -> % Faulty tracer data generator func.
- {error,{bad_tdg,Reason}}
- end;
- {error,Reason} -> % Error clearing patterns.
- {error,Reason}
- end
- end.
-
-h_start_session_nonodes(TDGargs,LD=#ld{c_node=CNode,tracer_data=TDs},Errors) ->
- {SessionNr,NewTDs}=insert_td_tracer_data(TDGargs,TDs),
- if
- CNode==undefined ->
- {ok,{SessionNr,[],LD#ld{tracer_data=NewTDs}}};
- true ->
- {ok,{SessionNr,{ok,Errors},LD#ld{tracer_data=NewTDs}}}
- end.
-
-%% Help function clearing all trace patterns on all nodes.
-h_start_session_ctp_all(CNode,Nodes) ->
- case remove_all_trace_patterns(CNode,[],Nodes) of
- ok -> % Non-distributed case1.
- {ok,[],local_runtime};
- {error,Reason} -> % Non-distributed case2 and general failure.
- {error,Reason};
- {ok,NodeResults} ->
- h_start_session_ctp_all_2(NodeResults,[],[])
- end.
-
-h_start_session_ctp_all_2([{Node,{error,Reason}}|Rest],Errors,Nodes) ->
- h_start_session_ctp_all_2(Rest,[{Node,{error,Reason}}|Errors],Nodes);
-h_start_session_ctp_all_2([{Node,_OkOrPatternsUntouched}|Rest],Errors,Nodes) ->
- h_start_session_ctp_all_2(Rest,Errors,[Node|Nodes]);
-h_start_session_ctp_all_2([],Errors,Nodes) ->
- {ok,Errors,Nodes}.
-
-%% Help function doing the actual init_tracing.
-h_start_session_2(undefined,TracerData,_Errors) -> % Non distributed case.
- case inviso:init_tracing(TracerData) of
- {ok,LogResult} when list(LogResult) ->
- {ok,{ok,LogResult}};
- {error,already_initated} -> % Perhaps adopted!?
- {ok,{error,already_initiated}}; % Not necessarily wrong.
- {error,Reason} ->
- {error,Reason}
- end;
-h_start_session_2(CNode,TracerList,Errors) ->
- case rpc:call(CNode,inviso,init_tracing,[TracerList]) of
- {ok,NodeResults} ->
- {ok,{ok,Errors++NodeResults}};
- {error,Reason} ->
- {error,Reason};
- {badrpc,Reason} ->
- {error,{inviso_control_node_error,Reason}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function starting all initial trace cases. They are actually handled
-%% the same way as user started trace cases. We actually only start initial
-%% tracecases at Nodes (if Nodes is a list of nodes). This because we may have
-%% adopted some nodes some already tracing nodes, and such are supposed to have
-%% the correct patterns and flags set.
-do_initial_tcs([{TC,Vars}|Rest],Nodes,LD) ->
- Id=make_ref(), % Trace case ID.
- case h_atc(TC,Id,Vars,LD,Nodes) of % Start using regular start methods.
- {ok,NewLD} -> % Trace case was successfully started.
- NewInitialTcs=add_initial_tcs(TC,Id,NewLD#ld.started_initial_tcs),
- do_initial_tcs(Rest,Nodes,NewLD#ld{started_initial_tcs=NewInitialTcs});
- {error,_Reason} ->
- do_initial_tcs(Rest,Nodes,LD)
- end;
-do_initial_tcs([_|Rest],Nodes,LD) ->
- do_initial_tcs(Rest,Nodes,LD);
-do_initial_tcs([],_Nodes,LD) ->
- LD.
-%% -----------------------------------------------------------------------------
-
-%% This help functio is used instead of do_initial_tcs/3 if there actually are no
-%% nodes to do the trace cases on. The reason we must have this function is that
-%% the tracecases must still be entered into the history with bindings and all.
-%% But we let them be marked as 'running' immediately (no need for the activator
-%% process).
-add_initial_tcs_to_history([{TC,Vars}|Rest],LD=#ld{tc_dict=TCdict,chl=CHL}) ->
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} ->
- case check_bindings(Vars,TraceCase) of
- {ok,Bindings} ->
- Id=make_ref(), % Trace case ID.
- FakeProcH=make_ref(), % Need something to enter as activator.
- NewCHL=set_activating_chl(TC,Id,CHL,Bindings,FakeProcH),
- NewCHL2=set_running_chl(FakeProcH,TC,Id,void,NewCHL), % Result=void.
- NewInitialTcs=add_initial_tcs(TC,Id,LD#ld.started_initial_tcs),
- add_initial_tcs_to_history(Rest,LD#ld{chl=NewCHL2,
- started_initial_tcs=NewInitialTcs});
- {error,_Reason} -> % Not much we can do about that.
- add_initial_tcs_to_history(Rest,LD)
- end;
- false ->
- add_initial_tcs_to_history(Rest,LD)
- end;
-add_initial_tcs_to_history([],LD) ->
- LD.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% reinitiate_session
-%% -----------------------------------------------------------------------------
-
-%% Function doing the reinitiation. That means first do init_tracing at the nodes
-%% in question. Then redo the command history to bring them up to speed.
-%% But first the runtime component is cleared of all trace patterns.
-h_reinitiate_session(Nodes,M,F,TDGargs,LD=#ld{c_node=CNode,nodes=NodesD}) ->
- case h_reinitiate_session_2(Nodes,NodesD,CNode) of
- {ok,{[],NodesErr}} -> % No nodes to reinitiate.
- {ok,{NodesErr,{ok,[]},LD}};
- {ok,{Nodes2,NodesErr}} -> % List of nodes or local_runtime.
- case call_tracer_data_generator(CNode,M,F,TDGargs,Nodes2) of
- {ok,TracerList} ->
- case h_start_session_2(CNode,TracerList,[]) of % Borrow from start_session.
- {ok,ReturnValue} -> % Ok, now we must redo cmd history.
- {NewNodesD,_Nodes}=
- set_tracing_running_nodes(CNode,ReturnValue,NodesD),
- NewLD=h_reinitiate_session_chl(Nodes2,LD#ld{nodes=NewNodesD}),
- {ok,{NodesErr,ReturnValue,NewLD}};
- {error,Reason} ->
- {error,Reason}
- end;
- {error,Reason} ->
- {error,{bad_tdg,Reason}}
- end;
- {error,Reason} ->
- {error,Reason}
- end.
-
-%% Help function finding out which nodes in Nodes actually can be reinitiated.
-%% A node must be up, inactive and not suspended in order for this to work. All the
-%% rest is just a matter of how detailed error return values we want to generate.
-h_reinitiate_session_2(local_runtime,NodesD,undefined) -> % Non distributed case.
- case get_state_nodes(local_runtime,NodesD) of
- {inactive,running} -> % Only ok case.
- case inviso:ctp_all() of
- ok ->
- {ok,{local_runtime,[]}};
- {error,Reason} -> % This is strange.
- {error,Reason}
- end;
- {_,suspended} ->
- {ok,{[],{error,suspended}}};
- down ->
- {ok,{[],{error,down}}};
- _ ->
- {ok,{[],{error,already_in_session}}}
- end;
-h_reinitiate_session_2(Nodes,NodesD,CNode) when list(Nodes) ->
- {ok,lists:foldl(fun(N,{Nodes2,NodesErr})->
- case get_state_nodes(N,NodesD) of
- {inactive,running} -> % Only ok case.
- case rpc:call(CNode,inviso,ctp_all,[[N]]) of
- {ok,[{N,ok}]} ->
- {[N|Nodes2],NodesErr};
- {ok,[{N,{error,Reason}}]} ->
- {Nodes2,[{N,{error,Reason}}|NodesErr]};
- {error,Reason} ->
- {Nodes2,[{N,{error,Reason}}|NodesErr]};
- {badrpc,Reason} ->
- {Nodes2,[{N,{error,{badrpc,Reason}}}|NodesErr]}
- end;
- {_,suspended} ->
- {Nodes2,[{N,{error,suspended}}|NodesErr]};
- down ->
- {Nodes2,[{N,{error,down}}|NodesErr]};
- false ->
- {Nodes2,[{N,{error,unknown_node}}|NodesErr]};
- _ ->
- {Nodes2,[{N,{error,already_in_session}}|NodesErr]}
- end
- end,
- {[],[]},
- Nodes)};
-h_reinitiate_session_2(Nodes,_NodesD,_CNode) ->
- {error,{badarg7,Nodes}}.
-
-%% Help function redoing the command history log at all nodes that actually
-%% started to trace. Note that we do not modify the return value which will be
-%% given to the caller just because we decide not to redo commands. The user
-%% must conclude him self from the inviso return value that commands were not
-%% redone at a particular node.
-h_reinitiate_session_chl(local_runtime,LD) ->
- h_reinitiate_session_chl([local_runtime],LD);
-h_reinitiate_session_chl([Node|Rest],LD=#ld{nodes=NodesD}) ->
- case get_state_nodes(Node,NodesD) of
- {tracing,running} -> % Only case when we shall redo!
- h_reinitiate_session_chl(Rest,redo_cmd_history(Node,LD));
- _ -> % No redo of chl in other cases.
- h_reinitiate_session_chl(Rest,LD)
- end;
-h_reinitiate_session_chl([],LD) ->
- LD.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% restore_session
-%% -----------------------------------------------------------------------------
-
-%% Help function starting a session (init tracing) and redoes the history
-%% found in CHL.
-h_restore_session(MoreTDGargs,LD) ->
- DateTime=calendar:universal_time(),
- {M,F,Args}=LD#ld.tdg,
- TDGargs=inviso_tool_lib:mk_tdg_args(DateTime,MoreTDGargs++Args),
- case h_start_session(M,F,TDGargs,LD) of
- {ok,{SessionNr,ReturnVal,NewLD}} -> % There were no available nodes.
- {ok,{SessionNr,ReturnVal,NewLD}};
- {ok,{SessionNr,ReturnVal,Nodes2,NewLD}} ->
- NewLD2=h_reinitiate_session_chl(Nodes2,NewLD),
- {ok,{SessionNr,ReturnVal,NewLD2}};
- {error,Reason} -> % Risk of out of control.
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% stop_session
-%% -----------------------------------------------------------------------------
-
-%% Help function stopping tracing at tracing nodes.
-h_stop_session(#ld{c_node=CNode,nodes=NodesD,tracer_data=TDs}) ->
- case h_stop_session_2(CNode,NodesD) of
- {ok,Result} ->
- {ok,{get_latest_session_nr_tracer_data(TDs),Result}};
- {error,Reason} ->
- {error,Reason}
- end.
-
-h_stop_session_2(undefined,NodesD) -> % The non distributed case.
- case get_tracing_nodes(NodesD) of
- {up,{inactive,_}} -> % Already not tracing!
- {ok,[]};
- {up,_} ->
- case inviso:stop_tracing() of
- {ok,_State} ->
- {ok,[ok]};
- {error,no_response} ->
- {ok,[]};
- {error,Reason} ->
- {error,Reason}
- end;
- down ->
- {ok,[]}
- end;
-h_stop_session_2(CNode,NodesD) ->
- Nodes=get_tracing_nodes(NodesD),
- case rpc:call(CNode,inviso,stop_tracing,[Nodes]) of
- {ok,NodeResults} ->
- {ok,lists:map(fun({N,{ok,_}})->{N,ok};
- (NodeError)->NodeError
- end,
- NodeResults)};
- {error,Reason} ->
- {error,Reason};
- {badrpc,Reason} ->
- {error,{inviso_control_node_error,Reason}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function removing any trace flags, trace patterns and meta trace patterns
-%% at Nodes. This will cause the nodes to become "fresh".
-h_reset_nodes(local_runtime,_CNode) ->
- inviso:clear([keep_log_files]);
-h_reset_nodes(Nodes,CNode) ->
- case inviso_tool_lib:inviso_cmd(CNode,clear,[Nodes,[keep_log_files]]) of
- {ok,NodeResults} ->
- {ok,NodeResults};
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% atc
-%% -----------------------------------------------------------------------------
-
-%% Function handling ativating a trace case. Trace cases that do not have a
-%% particular on/off handling (but just on in some scense) are handled here too.
-%% The trace case is entered into the Command History Log.
-%% Note that the trace case can not be executed at this node but must be
-%% executed where the inviso control component is.
-%% Further it is possible to either activated the tracecase for all running and
-%% tracing nodes, or just for a specified list of nodes.
-%% TC=tracecase_name(),
-%% Id=term(), identifiying this usage so we can turn it off later.
-%% Vars=list(), list of variable-value bindnings.
-h_atc(TC,Id,Vars,LD) ->
- h_atc(TC,Id,Vars,LD,void). % For all running-tracing nodes.
-
-h_atc(TC,Id,Vars,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL},Nodes) ->
- case find_id_chl(TC,Id,CHL) of
- activating -> % Already started.
- {error,activating};
- stopping -> % Not yet stopped.
- {error,deactivating};
- false ->
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} -> % Such a trace case exists.
- case check_bindings(Vars,TraceCase) of
- {ok,Bindings} -> % Necessary vars exists in Vars.
- if
- list(Nodes) -> % Nodes predefined.
- h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes);
- true -> % Use all tracing and running nodes.
- Nodes1=get_nodenames_running_nodes(LD#ld.nodes),
- h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes1)
- end;
- {error,Reason} -> % Variable def missing.
- {error,Reason}
- end;
- false ->
- {error,unknown_tracecase}
- end;
- {ok,_Bindings} -> % Already activated and running.
- {error,already_started}
- end.
-
-h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes) ->
- case exec_trace_case_on(CNode,TraceCase,Bindings,Nodes) of
- {ok,ProcH} -> % Trace cases have no return values.
- NewCHL=set_activating_chl(TC,Id,CHL,Bindings,ProcH),
- {ok,LD#ld{chl=NewCHL}};
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% sync_atc
-%% -----------------------------------------------------------------------------
-
-h_sync_atc(TC,Id,Vars,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
- case find_id_chl(TC,Id,CHL) of
- activating -> % Already started.
- {error,activating};
- stopping -> % Not yet stopped.
- {error,deactivating};
- false ->
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} -> % Such a trace case exists.
- case check_bindings(Vars,TraceCase) of
- {ok,Bindings} -> % Necessary vars exists in Vars.
- {ok,TcFName}=get_tc_activate_fname(TraceCase),
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings),
- RpcNode=get_rpc_nodename(CNode),
- case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of
- {ok,Value} ->
- FakeProcH=make_ref(),
- NewCHL1=set_activating_chl(TC,Id,CHL,Bindings,FakeProcH),
- NewCHL2=set_running_chl(FakeProcH,TC,Id,Value,NewCHL1),
- {ok,LD#ld{chl=NewCHL2},Value};
- {error,Reason} ->
- {error,{faulty_tracecase,{TcFName,Reason}}};
- {badrpc,Reason} ->
- {error,{badrpc,Reason}}
- end;
- {error,Reason} -> % Variable def missing.
- {error,Reason}
- end;
- false ->
- {error,unknown_tracecase}
- end;
- {ok,_Bindings} -> % Already activated and running.
- {error,already_started}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% rtc
-%% -----------------------------------------------------------------------------
-
-%% Function handling running a trace case without marking it as activated. It
-%% is in the history mearly indicated as activated
-h_sync_rtc(TC,Vars,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} -> % Such a trace case exists.
- case check_bindings(Vars,TraceCase) of
- {ok,Bindings} -> % Necessary vars exists in Vars.
- {ok,TcFName}=get_tc_activate_fname(TraceCase),
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings),
- RpcNode=get_rpc_nodename(CNode),
- case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of
- {ok,Value} ->
- {ok,LD#ld{chl=add_rtc_chl(TC,Bindings2,CHL)},Value};
- {error,Reason} ->
- {error,{faulty_tracecase,{TcFName,Reason}}};
- {badrpc,Reason} ->
- {error,{badrpc,Reason}}
- end;
- {error,Reason} -> % Variable def missing.
- {error,Reason}
- end;
- false ->
- {error,unknown_tracecase}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% dtc
-%% -----------------------------------------------------------------------------
-
-%% Function handling turning a trace case off. The trace case must be registered
-%% as having an off mechanism. If it has an off mechanism and was previously entered
-%% into the Command History Log and is done with its activation phase, it will be
-%% executed and removed from the CHL.
-h_dtc(TC,Id,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
- case find_id_chl(TC,Id,CHL) of
- {ok,Bindings} -> % Yes, we have turned it on before.
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} ->
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- case exec_trace_case_off(CNode,TraceCase,Bindings,Nodes) of
- {ok,ProcH} ->
- NewCHL=set_stopping_chl(TC,Id,CHL,ProcH),
- {ok,LD#ld{chl=NewCHL}};
- {error,Reason} ->
- {error,Reason}
- end;
- false -> % Strange, Id ok but no such trace case.
- {error,unknown_tracecase}
- end;
- false -> % Not previously turned on.
- {error,unknown_id};
- activating ->
- {error,activating};
- stopping ->
- {error,already_deactivating}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% sync_dtc
-%% -----------------------------------------------------------------------------
-
-h_sync_dtc(TC,Id,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
- case find_id_chl(TC,Id,CHL) of
- {ok,Bindings} -> % Yes, we have turned it on before.
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} ->
- case get_tc_deactivate_fname(TraceCase) of
- {ok,TcFName} ->
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings),
- RpcNode=get_rpc_nodename(CNode),
- case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of
- {ok,Value} ->
- FakeProcH=make_ref(),
- NewCHL1=set_stopping_chl(TC,Id,CHL,FakeProcH),
- NewCHL2=nullify_chl(FakeProcH,TC,Id,NewCHL1),
- {ok,LD#ld{chl=NewCHL2},Value};
- {error,Reason} -> % Script fault.
- {error,{faulty_tracecase,{TcFName,Reason}}};
- {badrpc,Reason} ->
- {error,{badrpc,Reason}}
- end;
- false ->
- {error,no_deactivation}
- end;
- false -> % Strange, Id ok but no such trace case.
- {error,unknown_tracecase}
- end;
- false -> % Not previously turned on.
- {error,unknown_id};
- activating ->
- {error,activating};
- stopping ->
- {error,already_deactivating}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% inviso
-%% -----------------------------------------------------------------------------
-
-%% Function executing one inviso command. The returnvalue from the inviso
-%% function call will be the return value to the client. The command is
-%% entered into the history command log.
-%% Note that the inviso call may have to be done at another node, dictated
-%% by the c_node field. Further, if the module name is not an atom it is
-%% most likely a regexp, which must be expanded at the regexp_node. Note
-%% this is only relevant for tp and tpl.
-h_inviso(Cmd,Args,LD=#ld{c_node=CNode,regexp_node=RegExpNode,chl=CHL}) ->
- Arity=length(Args),
- case check_proper_inviso_call(Cmd,Arity) of
- {true,RegExpFlag} -> % Yes it is an inviso call.
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- case h_inviso_2(Cmd,Args,CNode,RegExpNode,RegExpFlag,Nodes) of
- {ok,Result} ->
- case check_inviso_call_to_history(Cmd,Arity) of
- true -> % This function shall be added to chl.
- {ok,{Result,LD#ld{chl=add_inviso_call_chl(Cmd,Args,CHL)}}};
- false -> % Do not add it.
- {ok,{Result,LD}}
- end;
- {error,Reason} ->
- {error,Reason}
- end;
- false -> % Not an inviso function.
- {error,invalid_function_name}
- end.
-
-h_inviso_2(Cmd,Args,undefined,_,_,_) -> % A non distributed system.
- case catch apply(inviso,Cmd,Args) of % Regexp expansion only relevant when
- {'EXIT',Reason} -> % distributed, here let inviso_rt expand.
- {error,{'EXIT',Reason}};
- Result ->
- {ok,Result}
- end;
-h_inviso_2(Cmd,Args,CNode,RegExpNode,RegExpFlag,Nodes) ->
- case expand_module_regexps(Args,RegExpNode,Nodes,RegExpFlag) of
- {ok,NewArgs} ->
- case catch inviso_tool_lib:inviso_cmd(CNode,Cmd,[Nodes|NewArgs]) of
- {'EXIT',Reason} ->
- {error,{'EXIT',Reason}};
- {error,{badrpc,Reason}} -> % Includes runtime failure.
- {error,{badrpc,Reason}};
- Result ->
- {ok,Result}
- end;
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% reactivate
-%% -----------------------------------------------------------------------------
-
-h_reactivate(_Node,undefined) -> % The non-distributed case.
- case inviso:cancel_suspension() of
- ok ->
- ok;
- {error,Reason} ->
- {error,Reason}
- end;
-h_reactivate(Node,CNode) ->
- case inviso_tool_lib:inviso_cmd(CNode,cancel_suspension,[[Node]]) of
- {ok,[{Node,ok}]} ->
- ok;
- {ok,[{Node,{error,Reason}}]} ->
- {error,Reason};
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% save_history
-%% -----------------------------------------------------------------------------
-
-h_save_history(HDir,Dir,FileName,SortedLog) ->
- Dir0=
- if
- list(HDir) -> % There is a history dir specified.
- HDir; % Use it then.
- true ->
- Dir % Else use the tool dir.
- end,
- case catch make_absolute_path(FileName,Dir0) of
- AbsFileName when list(AbsFileName) ->
- Log2=build_saved_history_data(SortedLog), % Remove stopped tracecases.
- case file:write_file(AbsFileName,term_to_binary(Log2)) of
- ok ->
- {ok,AbsFileName};
- {error,Reason} ->
- {error,{write_file,Reason}}
- end;
- {'EXIT',_Reason} ->
- {error,{bad_filename,FileName}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% get_autostart_data
-%% -----------------------------------------------------------------------------
-
-%% Help function building the structures used when exporting autostart information
-%% from the tool. Note that we remove the tool-dependency and insert the one
-%% specify in the get_autostart_data call.
-h_get_autostart_data(local_runtime,_,Dependency,ASD,M,F,TDGargs,OptsG) ->
- CompleteTDGargs=call_tracer_data_generator_mkargs(local_runtime,TDGargs),
- Opts0=start_runtime_components_mk_opts(local_runtime,OptsG),
- Opts=[Dependency|lists:keydelete(dependency,1,Opts0)],
- {ok,{ASD,{ok,{Opts,{tdg,{M,F,CompleteTDGargs}}}}}};
-
-h_get_autostart_data(Nodes,CNode,Dependency,ASD,M,F,TDGargs,OptsG) when list(Nodes) ->
- {ok,{ASD,h_get_autostart_data_2(Nodes,CNode,Dependency,M,F,TDGargs,OptsG)}};
-h_get_autostart_data(Nodes,_CNode,_Dependency,_ASD,_M,_F,_TDGargs,_OptsG) ->
- {error,{badarg,Nodes}}.
-
-h_get_autostart_data_2([Node|Rest],CNode,Dependency,M,F,TDGargs,OptsG) ->
- CompleteTDGargs=call_tracer_data_generator_mkargs(Node,TDGargs),
- Opts0=start_runtime_components_mk_opts(Node,OptsG),
- Opts=[Dependency|lists:keydelete(dependency,1,Opts0)],
- [{Node,{ok,{Opts,{tdg,{M,F,CompleteTDGargs}}}}}|
- h_get_autostart_data_2(Rest,CNode,Dependency,M,F,TDGargs,OptsG)];
-h_get_autostart_data_2([],_CNode,_Dependency,_M,_F,_TDGargs,_OptsG) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% flush
-%% -----------------------------------------------------------------------------
-
-h_flush(undefined,_Nodes) ->
- inviso:flush();
-h_flush(CNode,Nodes) ->
- inviso_tool_lib:inviso_cmd(CNode,flush,[Nodes]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% tc_executer_reply
-%% -----------------------------------------------------------------------------
-
-%% Function handling that a trace case has completed its activation phase and
-%% shall now be marked in the Command History Log as running.
-h_tc_activation_done(ProcH,Result,LD=#ld{chl=CHL}) ->
- case find_tc_executer_chl(ProcH,CHL) of
- {activating,{TC,Id}} ->
- case Result of
- {ok,Value} -> % The trace case is successful activated.
- {ok,LD#ld{chl=set_running_chl(ProcH,TC,Id,Value,CHL)}};
- {error,_} -> % Then pretend it never happend :-)
- {ok,LD#ld{chl=del_tc_chl(ProcH,TC,Id,CHL)}} % Remove it.
- end;
- _ -> % Where did this come from?
- {ok,LD} % Well just ignore it then.
- end.
-%% -----------------------------------------------------------------------------
-
-%% Function handling that a trace case has completed its stopping phase and
-%% shall now be nulled in the Command History Log (meaning that it will not
-%% be repeated in the event of a reactivation).
-h_tc_stopping_done(ProcH,Result,LD=#ld{chl=CHL}) ->
- case find_tc_executer_chl(ProcH,CHL) of
- {stopping,{TC,Id}} ->
- case Result of
- {ok,_Result} -> % _Result is returned from the tracecase.
- {ok,LD#ld{chl=nullify_chl(ProcH,TC,Id,CHL)}};
- {error,_} -> % This is difficult, is it still active?
- {ok,LD#ld{chl=nullify_chl(ProcH,TC,Id,CHL)}}
- end;
- _ -> % Strange.
- {ok,LD}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Terminate.
-%% -----------------------------------------------------------------------------
-
-%% Help function stopping the inviso control component. Does not return
-%% anything significant.
-stop_inviso_at_c_node(undefined) -> % Non distributed case.
- inviso:stop();
-stop_inviso_at_c_node(CNode) ->
- rpc:call(CNode,inviso,stop,[]).
-%% -----------------------------------------------------------------------------
-
-%% Help function that removes all trace patterns from the nodes that are not
-%% marked as such were patterns shall be left after stopping of inviso.
-%% Returns {ok,NodeResult} or {error,Reason}. In the non-distributed case
-%% 'ok' is returned incase of success, ot 'patterns_untouched'.
-remove_all_trace_patterns(undefined,KeepNodes,_Nodes) ->
- case KeepNodes of
- undefined -> % No, remove patterns from localruntime.
- inviso:ctp_all();
- _ ->
- patterns_untouched
- end;
-remove_all_trace_patterns(CNode,KeepNodes,Nodes) ->
- Nodes2=lists:filter(fun(N)->not(lists:member(N,KeepNodes)) end,Nodes),
- case inviso_tool_lib:inviso_cmd(CNode,ctp_all,[Nodes2]) of
- {ok,NodeResults} ->
- F=fun(N) ->
- case lists:member(N,KeepNodes) of
- true ->
- {N,patterns_untouched};
- false ->
- case lists:keysearch(N,1,NodeResults) of
- {value,Result} ->
- Result; % {Node,ok}
- false -> % Extremely strange.
- {N,{error,general_error}}
- end
- end
- end,
- {ok,lists:map(F,Nodes)};
- {error,{badrpc,Reason}} ->
- {error,{inviso_control_node_error,Reason}};
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% =============================================================================
-%% Second level help functions.
-%% =============================================================================
-
-%% Help function building a reply to a reconnection call based on which nodes
-%% where asked to be reconnected and which of those are actually now working.
-%% We actually make an effort to serve the return value in the same order as the
-%% nodes were mentioned in the original call (Nodes).
-build_reconnect_nodes_reply(local_runtime,local_runtime,_NodesErr,NodesD) ->
- case get_state_nodes(local_runtime,NodesD) of
- down ->
- {error,down};
- {State,Status} ->
- {ok,{State,Status}}
- end;
-build_reconnect_nodes_reply(local_runtime,_,NodesErr,_NodesD) ->
- NodesErr;
-build_reconnect_nodes_reply([Node|Rest],Nodes2,NodesErr,NodesD) ->
- case lists:member(Node,Nodes2) of
- true -> % Ok, look in the #ld.nodes.
- case get_state_nodes(Node,NodesD) of
- down -> % Somekind of failure, still down.
- [{Node,{error,down}}|
- build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)];
- {State,Status} -> % {State,Status}
- [{Node,{ok,{State,Status}}}|
- build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)]
- end;
- false -> % Error already from the beginning.
- {value,{_,Error}}=lists:keysearch(Node,1,NodesErr),
- [{Node,Error}|build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)]
- end;
-build_reconnect_nodes_reply([],_,_,_) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Help function building a return value to reinitiate_session. Nodes contains
-%% all involved nodes. If the node occurrs in NodesErr, we choose the error in
-%% NodesErr. Otherwise the returnvalue in ReturnVal is used.
-build_reinitiate_session_reply(Nodes,NodesErr,{ok,NodesResults}) ->
- {ok,build_reinitiate_session_reply_2(Nodes,NodesErr,NodesResults)};
-build_reinitiate_session_reply(local_runtime,[],NodeResult) ->
- NodeResult;
-build_reinitiate_session_reply(local_runtime,NodesErr,_NodeResult) ->
- NodesErr.
-build_reinitiate_session_reply_2([Node|Rest],NodesErr,NodeResults) ->
- case lists:keysearch(Node,1,NodesErr) of
- {value,{_,Error}} ->
- [{Node,Error}|build_reinitiate_session_reply_2(Rest,NodesErr,NodeResults)];
- false ->
- case lists:keysearch(Node,1,NodeResults) of
- {value,Value} ->
- [Value|build_reinitiate_session_reply_2(Rest,NodesErr,NodeResults)]
- end
- end;
-build_reinitiate_session_reply_2([],_NodesErr,_NodeResults) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Help function returning a history log where stop and stopping entries have
-%% been removed. Further all tracecase log entries must be set to running since
-%% there can not be such a thing as an activating tracecase stored away in a
-%% saved historyfile!
-%% We must also take away any #Ref.
-build_saved_history_data(SortedLog) ->
- CleanedLog=
- lists:filter(fun({_,_,Stop,_}) when Stop==stop;Stop==stopping -> false;
- (_) -> true
- end,
- SortedLog),
- lists:map(fun({{TC,Id},C,activating,B}) -> {{TC,Id},C,running,B};
- ({{TC,Id},C,S,B}) -> {{TC,Id},C,S,B};
- ({{M,F,Args,_Ref},C}) -> {{M,F,Args},C};
- ({{TC,_Ref},C,B}) -> {TC,C,B} % An rtc.
- end,
- CleanedLog).
-%% -----------------------------------------------------------------------------
-
-%% This help function builds the AutoStartData structure which is returned from
-%% get_austostart_data. An AutoStartData structure is a list of trace-files and
-%% inviso commands. The order is significant since it is the idea that doing
-%% the trace case files and inviso commands in that order will bring a node to
-%% a certain state in a trace perspective.
-%% Returns {ok,AutoStartData} or {error,Reason}
-build_autostart_data(SortedLog,TCdict) ->
- build_autostart_data_2(SortedLog,TCdict,[]).
-
-build_autostart_data_2([{_,_C,Stop,_B}|Rest],TCdict,Accum) when Stop==stop;Stop==stopping->
- build_autostart_data_2(Rest,TCdict,Accum); % Simply skip deactivated/deativating.
-build_autostart_data_2([{{TCname,_},_C,activating,Bindings}|Rest],TCdict,Accum) ->
- build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum);
-build_autostart_data_2([{{TCname,_},_C,running,Bindings}|Rest],TCdict,Accum) ->
- build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum);
-build_autostart_data_2([{{TCname,_Ref},_C,Bindings}|Rest],TCdict,Accum) ->
- build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum);
-build_autostart_data_2([{{M,F,Args,_Ref},_C}|Rest],TCdict,Accum) ->
- build_autostart_data_2(Rest,TCdict,[{mfa,{M,F,Args}}|Accum]);
-build_autostart_data_2([],_TCdict,Accum) ->
- {ok,lists:reverse(Accum)}.
-
-%% Help function placing the filename in the AutoStartData structure.
-build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum) ->
- {ok,TC}=get_tracecase_tc_dict(TCname,TCdict),
- {ok,FName}=get_tc_activate_fname(TC),
- build_autostart_data_2(Rest,TCdict,[{file,{FName,Bindings}}|Accum]).
-%% -----------------------------------------------------------------------------
-
-%% Help function generating tracerdata to init inviso tracing. The generation
-%% is done by the TracerDataGenerator, TDG, function.
-%% Individual tracerdata is generated for each node in Nodes.
-%% Returns {ok,TracerData} or {error,Reason}.
-call_tracer_data_generator(undefined,M,F,TDGargs,_Nodes) -> % Non distributed.
- case catch call_tracer_data_generator_3(M,F,TDGargs,local_runtime) of
- {'EXIT',Reason} ->
- {error,{'EXIT',Reason}};
- TracerData ->
- {ok,TracerData}
- end;
-call_tracer_data_generator(_CNode,M,F,TDGargs,Nodes) ->
- case catch call_tracer_data_generator_2(M,F,TDGargs,Nodes) of
- {'EXIT',Reason} ->
- {error,{'EXIT',Reason}};
- TracerList ->
- {ok,TracerList}
- end.
-
-call_tracer_data_generator_2(M,F,TDGargs,[Node|Rest]) ->
- [{Node,call_tracer_data_generator_3(M,F,TDGargs,Node)}|
- call_tracer_data_generator_2(M,F,TDGargs,Rest)];
-call_tracer_data_generator_2(_,_,_,[]) ->
- [].
-
-call_tracer_data_generator_3(M,F,TDGargs,Node) ->
- apply(M,F,call_tracer_data_generator_mkargs(Node,TDGargs)).
-
-%% This function creates the arguments that the tracer data generator function
-%% accepts (in an apply call). The reason for making it a sepparate function is
-%% that the arguments are constructed in more situations than just when actually
-%% doing the apply. By having a function it will become obvious where to change
-%% should the arguments change.
-call_tracer_data_generator_mkargs(Node,TDGargs) ->
- inviso_tool_lib:mk_complete_tdg_args(Node,TDGargs).
-%% -----------------------------------------------------------------------------
-
-%% This function acts as standard options generator function. That is returning
-%% the options argument to inviso:add_node/3. Note that this function must not
-%% return the dependency part of that option.
-std_options_generator(_Node) ->
- []. % No particular options(!)
-%% -----------------------------------------------------------------------------
-
-
-%% Help function checking that Vars contains a binding for every variable
-%% listed in the VarNames field in TraceCase. Note that the special variable 'Nodes'
-%% is disregarded, since it is always added by the inviso_tool.
-%% Returns {ok,Bindings} or {error,Reason}. Where Bindings is a bindngs structure
-%% according to file:eval functionality.
-check_bindings(Vars,TraceCase) ->
- case catch check_bindings_2(Vars,
- get_tc_varnames(TraceCase),
- erl_eval:new_bindings()) of
- {'EXIT',_Reason} ->
- {error,variable_error};
- {error,Reason} -> % Missing a bindning.
- {error,Reason};
- {ok,Bindings} ->
- {ok,Bindings}
- end.
-
-check_bindings_2(Vars,['Nodes'|Rest],Bindings) ->
- check_bindings_2(Vars,Rest,Bindings); % Disregard Nodes since it is automatic.
-check_bindings_2(Vars,[VarName|Rest],Bindings) ->
- case lists:keysearch(VarName,1,Vars) of
- {value,{_,Val}} ->
- check_bindings_2(Vars,Rest,erl_eval:add_binding(VarName,Val,Bindings));
- false -> % Mandatory variable missing.
- {error,{missing_variable,VarName}} % Quite here then.
- end;
-check_bindings_2(_,[],Bindings) ->
- {ok,Bindings}.
-%% -----------------------------------------------------------------------------
-
-%% This help function checks that the command the user tries to do is amongst
-%% the inviso API. It at the same time returns what kind of command it is.
-%% {true,RegExpFlag} or 'false' where RegExpFlag indicates if this command
-%% needs to have its argument modified by module regexp expansion or not.
-check_proper_inviso_call(Cmd,Arity) ->
- case lists:member({Cmd,Arity},?INVISO_CMDS) of
- true -> % It is part of inviso API.
- {true,check_proper_inviso_call_regexp(Cmd,Arity)};
- false ->
- false
- end.
-
-%% Returns {Type,Arity,PlaceOfModuleSpec} or 'false'.
-check_proper_inviso_call_regexp(tp,5) -> {tp,5,1};
-check_proper_inviso_call_regexp(tp,4) -> {tp,4,1};
-check_proper_inviso_call_regexp(tp,1) -> {tp,1,1};
-check_proper_inviso_call_regexp(tpl,5) -> {tp,5,1};
-check_proper_inviso_call_regexp(tpl,4) -> {tp,4,1};
-check_proper_inviso_call_regexp(tpl,1) -> {tp,1,1};
-check_proper_inviso_call_regexp(ctp,3) -> {ctp,3,1};
-check_proper_inviso_call_regexp(ctp,1) -> {ctp,1,1};
-check_proper_inviso_call_regexp(ctpl,3) -> {ctp,3,1};
-check_proper_inviso_call_regexp(ctpl,1) -> {ctp,1,1};
-check_proper_inviso_call_regexp(_,_) -> % No regexp expansion.
- false.
-%% -----------------------------------------------------------------------------
-
-%% Help function checking if this inviso command shall be added to the command
-%% history log. Returns true or false.
-check_inviso_call_to_history(Cmd,Arity) ->
- case lists:member({Cmd,Arity},?INVISO_CMD_HISTORY) of
- true ->
- true;
- false ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function traversing the arguments and expanding module names stated
-%% as regular expressions. This means that the resulting arguments may be longer
-%% than the orginal ones.
-%% When we run this function it has been determined that we are a distributed
-%% system.
-%% Also note that if there are no regexps in Args, no regexpansion will be
-%% made and RegExpNode may be 'undefined' (as it is if not set at start-up).
-%% If RegExpNode is unavailable the nodes found in Nodes will be used until
-%% one that works is found.
-expand_module_regexps(Args,_RegExpNode,_Nodes,false) ->
- {ok,Args};
-expand_module_regexps([PatternList],RegExpNode,Nodes,{tp,1,1}) ->
- case catch expand_module_regexps_tp(PatternList,RegExpNode,Nodes) of
- NewPatternList when list(NewPatternList) ->
- {ok,[NewPatternList]};
- {error,Reason} ->
- {error,Reason}
- end;
-expand_module_regexps([PatternList],RegExpNode,Nodes,{ctp,1,1}) ->
- case catch expand_module_regexps_ctp(PatternList,RegExpNode,Nodes) of
- NewPatternList when list(NewPatternList) ->
- {ok,[NewPatternList]};
- {error,Reason} ->
- {error,Reason}
- end;
-expand_module_regexps([M,F,Arity,MS,Opts],RegExpNode,Nodes,{tp,5,1}) ->
- expand_module_regexps([[{M,F,Arity,MS,Opts}]],RegExpNode,Nodes,{tp,1,1});
-expand_module_regexps([M,F,Arity,MS],RegExpNode,Nodes,{tp,4,1}) ->
- expand_module_regexps([[{M,F,Arity,MS,[]}]],RegExpNode,Nodes,{tp,1,1});
-expand_module_regexps([M,F,Arity],RegExpNode,Nodes,{ctp,3,1}) ->
- expand_module_regexps([[{M,F,Arity}]],RegExpNode,Nodes,{ctp,1,1}).
-
-
-expand_module_regexps_tp([E={M,_,_,_,_}|Rest],RegExpNode,Nodes) when atom(M) ->
- [E|expand_module_regexps_tp(Rest,RegExpNode,Nodes)];
-expand_module_regexps_tp([{M,F,Arity,MS,Opts}|Rest],RegExpNode,Nodes) when list(M);tuple(M) ->
- case inviso_tool_lib:expand_module_names([RegExpNode],
- M,
- [{expand_only_at,RegExpNode}]) of
- {singlenode_expansion,Modules} ->
- expand_module_regexps_tp_2(Modules,F,Arity,MS,Opts,Rest,RegExpNode,Nodes);
- {error,{faulty_node,RegExpNode}} -> % RegExpNode probably down.
- case Nodes of
- [NewRegExpNode|RestNodes] -> % Ok, just choose a node.
- expand_module_regexps_tp([{M,F,Arity,MS,Opts}|Rest],NewRegExpNode,RestNodes);
- [] -> % No more nodes to choose from.
- throw({error,no_available_regexpnode})
- end;
- {error,_Reason} ->
- expand_module_regexps_tp(Rest,RegExpNode,Nodes)
- end;
-expand_module_regexps_tp([_|Rest],RegExpNode,Nodes) ->
- expand_module_regexps_tp(Rest,RegExpNode,Nodes); % Skip faulty module specification.
-expand_module_regexps_tp([],_RegExpNodes,_Nodes) ->
- [].
-
-expand_module_regexps_tp_2([M|MRest],F,Arity,MS,Opts,Rest,RegExpNode,Nodes) ->
- [{M,F,Arity,MS,Opts}|
- expand_module_regexps_tp_2(MRest,F,Arity,MS,Opts,Rest,RegExpNode,Nodes)];
-expand_module_regexps_tp_2([],_,_,_,_,Rest,RegExpNode,Nodes) ->
- expand_module_regexps_tp(Rest,RegExpNode,Nodes).
-
-expand_module_regexps_ctp([E={M,_,_}|Rest],RegExpNode,Nodes) when atom(M) ->
- [E|expand_module_regexps_ctp(Rest,RegExpNode,Nodes)];
-expand_module_regexps_ctp([{M,F,Arity}|Rest],RegExpNode,Nodes) when list(M);tuple(M) ->
- case inviso_tool_lib:expand_module_names([RegExpNode],
- M,
- [{expand_only_at,RegExpNode}]) of
- {singlenode_expansion,badrpc} -> % RegExpNode probably down.
- case Nodes of
- [NewRegExpNode|RestNodes] -> % Ok, just choose a node.
- expand_module_regexps_ctp([{M,F,Arity}|Rest],NewRegExpNode,RestNodes);
- [] -> % No more nodes to choose from.
- throw({error,no_available_regexpnode})
- end;
- {singlenode_expansion,Modules} ->
- expand_module_regexps_ctp_2(Modules,F,Arity,Rest,RegExpNode,Nodes);
- {error,_Reason} ->
- expand_module_regexps_ctp(Rest,RegExpNode,Nodes)
- end;
-expand_module_regexps_ctp([_|Rest],RegExpNode,Nodes) ->
- expand_module_regexps_tp(Rest,RegExpNode,Nodes); % Skip faulty module specification.
-expand_module_regexps_ctp([],_RegExpNodes,_Nodes) ->
- [].
-
-expand_module_regexps_ctp_2([M|MRest],F,Arity,Rest,RegExpNode,Nodes) ->
- [{M,F,Arity}|expand_module_regexps_ctp_2(MRest,F,Arity,Rest,RegExpNode,Nodes)];
-expand_module_regexps_ctp_2([],_,_,Rest,RegExpNode,Nodes) ->
- expand_module_regexps_ctp(Rest,RegExpNode,Nodes).
-%% -----------------------------------------------------------------------------
-
-
-
-%% Help function running the activation of a trace case. Note that this must
-%% be done at the inviso control component's Erlang node *and* that it must be
-%% done in its own process since there is no telling for how long a trace case
-%% may run.
-%% Returns {ok,ActivationHandler}.
-exec_trace_case_on(CNode,TraceCase,Bindings,Nodes) ->
- {ok,TcFName}=get_tc_activate_fname(TraceCase),
- {ok,exec_trace_case_2(CNode,
- TcFName,
- erl_eval:add_binding('Nodes',Nodes,Bindings),
- activating)}.
-
-%% Help function running the deactivation of a trace case.
-exec_trace_case_off(CNode,TraceCase,Bindings,Nodes) ->
- case get_tc_deactivate_fname(TraceCase) of
- {ok,TcFName} -> % There is a deactivation.
- {ok,exec_trace_case_2(CNode,
- TcFName,
- erl_eval:add_binding('Nodes',Nodes,Bindings),
- stopping)};
- false ->
- {error,no_deactivation}
- end.
-
-exec_trace_case_2(CNode,TcFName,Bindings,Phase) ->
- if
- CNode==undefined -> % The non distributed case.
- spawn_link(?MODULE,tc_executer,[TcFName,Bindings,Phase,self()]);
- true ->
- spawn_link(CNode,?MODULE,tc_executer,[TcFName,Bindings,Phase,self()])
- end.
-
-%% This function is run in its own process and is responsible for executing
-%% the trace case.
-tc_executer(TcFName,Bindings,Phase,Parent) ->
- case catch file:script(TcFName,Bindings) of
- {ok,Value} ->
- tc_executer_reply(Parent,{Phase,self(),{ok,Value}});
- {'EXIT',Reason} ->
- tc_executer_reply(Parent,{Phase,self(),{error,{'EXIT',Reason}}});
- Error ->
- tc_executer_reply(Parent,{Phase,self(),Error})
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function which starts a reactivator process redoing command history at
-%% Node. It also updates the loopdata to indicate that Node is now in state
-%% reactivating. It is a good idea to only handle one node per reactivator process.
-%% This because if the node terminates and comes back up, the reactivator must be
-%% stopped.
-redo_cmd_history(Node,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL,nodes=NodesD}) ->
- P=start_reactivator(Node,CNode,TCdict,CHL),
- LD#ld{nodes=set_reactivating_nodes(Node,NodesD),
- reactivators=add_reactivators(Node,P,LD#ld.reactivators)}.
-
-%% Help function starting a reactivator process replaying the command history log.
-%% Returns a pid of the reactivator process.
-start_reactivator(Node,CNode,TCdict,CHL) ->
- UnsortedLog=get_loglist_chl(CHL), % Must fetch here, later on wrong node.
- if
- CNode==undefined -> % The non-distributed case.
- spawn_link(?MODULE,
- reactivator_executer,
- [Node,TCdict,UnsortedLog,self(),0,[]]);
- true ->
- spawn_link(CNode,
- ?MODULE,
- reactivator_executer,
- [Node,TCdict,UnsortedLog,self(),0,[]])
- end.
-
-%% The strategy is to traverse the CHL ETS table in Counter order, redoing the
-%% commands one by one. We wait until one command is finished until we do the
-%% next. Commands marked as nullified are not performed. In fact when a command
-%% is nullified only the stop will be found in the CHL. Its activation will be
-%% removed.
-reactivator_executer(Node,TCdict,UnsortedLog,TPid,StartCounter,DoneCases) ->
- SortedLog=lists:keysort(2,UnsortedLog), % Sort on Counter, oldest first.
- Log=reactivator_skip_log_entries(SortedLog,StartCounter),
- case reactivator_executer_2(Node,TCdict,TPid,StartCounter,DoneCases,Log) of
- done ->
- true; % Simply terminate the reactivator then.
- {more,{NewStartCounter,NewDoneCases,NewUnsortedLog}} ->
- reactivator_executer(Node,TCdict,NewUnsortedLog,TPid,NewStartCounter,NewDoneCases)
- end.
-
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{TCname,Id},NextC,running,Bindings}|Rest]) ->
- reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{TCname,_Ref},NextC,Bindings}|Rest]) ->
- reactivator_executer_rtc(Node,TCdict,TPid,DoneCases,Rest,TCname,NextC,Bindings,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{TCname,Id},NextC,activating,Bindings}|Rest]) ->
- reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{M,F,Args,_Ref},NextC}|Rest]) ->
- reactivator_executer_cmd(Node,M,F,Args),
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{_TCname,_Id},NextC,stopping,_Bindings}|Rest]) ->
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{TCname,Id,_Ref},NextC,stop,Bindings}|Rest]) ->
- case lists:member({TCname,Id},DoneCases) of
- true -> % We have activated it, must stop then.
- case get_tracecase_tc_dict(TCname,TCdict) of
- {ok,{_,_,_,_,FNameOff}} ->
- reactivator_executer_tc(Node,Bindings,FNameOff),
- NewDoneCases=lists:delete({TCname,Id},DoneCases),
- reactivator_executer_2(Node,TCdict,TPid,NextC,NewDoneCases,Rest);
- {ok,_} -> % No stop-filename, strange!
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
- false -> % Even stranger, does not exist!?
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
- end;
- false -> % Never activated in the first place.
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
- end;
-%% Done all log entries found this lap. See if there are more entries by now.
-reactivator_executer_2(_Node,_TCdict,TPid,Counter,DoneCases,[]) ->
- case reactivator_reply(TPid,Counter) of % Ask the tool process for more entries.
- done -> % No more entries in the CHL.
- done;
- {more,NewUnsortedLog} -> % Repeat the procedure
- {more,{Counter+1,DoneCases,NewUnsortedLog}} % with log entries from Counter+1.
- end.
-
-%% This help function activates a tracecase.
-reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest) ->
- case get_tracecase_tc_dict(TCname,TCdict) of
- {ok,{_,_,_,FNameOn}} -> % A case with just on functionality.
- reactivator_executer_tc(Node,Bindings,FNameOn),
- reactivator_executer_2(Node,TCdict,TPid,NextC,[{TCname,Id}|DoneCases],Rest);
- {ok,{_,_,_,FNameOn,_}} ->
- reactivator_executer_tc(Node,Bindings,FNameOn),
- reactivator_executer_2(Node,TCdict,TPid,NextC,[{TCname,Id}|DoneCases],Rest);
- false -> % Strange, does not exist anylonger!?
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
- end.
-
-%% Help function executing a trace case in the reactivators context. Does not
-%% return anything significant.
-reactivator_executer_tc(Node,Bindings,FileName) ->
- catch file:eval(FileName,erl_eval:add_binding('Nodes',[Node],Bindings)).
-
-%% Help function handling trace case that are simply executed - rtc.
-reactivator_executer_rtc(Node,TCdict,TPid,DoneCases,Rest,TCname,NextC,Bindings,Rest) ->
- case get_tracecase_tc_dict(TCname,TCdict) of
- {ok,{_,_,_,FNameOn}} -> % A case with just on functionality.
- reactivator_executer_tc(Node,Bindings,FNameOn),
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
- {ok,{_,_,_,FNameOn,_}} ->
- reactivator_executer_tc(Node,Bindings,FNameOn),
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
- false -> % Strange, does not exist anylonger!?
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
- end.
-
-reactivator_executer_cmd(nonode@nohost,M,F,Args) ->
- catch apply(M,F,Args); % Non-distributed.
-reactivator_executer_cmd(Node,M,F,Args) ->
- catch apply(M,F,[[Node]|Args]).
-
-%% Help function returning a list of log entries missing the first entries
-%% having a counter less or equal to C1.
-reactivator_skip_log_entries([{_,C,_,_}|Rest],C1) when C<C1 ->
- reactivator_skip_log_entries(Rest,C1);
-reactivator_skip_log_entries([{_,C}|Rest],C1) when C<C1 ->
- reactivator_skip_log_entries(Rest,C1);
-reactivator_skip_log_entries(Log,_) ->
- Log.
-%% -----------------------------------------------------------------------------
-
-%% Help function returning the node name to use in an rpc call.
-get_rpc_nodename(undefined) ->
- node();
-get_rpc_nodename(CNode) ->
- CNode.
-%% -----------------------------------------------------------------------------
-
-mk_rt_tag() ->
- inviso_tool.
-%% -----------------------------------------------------------------------------
-
-is_string([C|Rest]) when C>=32, C=<255 ->
- is_string(Rest);
-is_string([]) ->
- true;
-is_string(_) ->
- false.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Functions for handling the configuration file.
-%% -----------------------------------------------------------------------------
-
-%% The inviso tool is configured via start arguments and/or a configuration file.
-%% Start arguments will override any definitions in a configuration file.
-%% The configuration file is pointed out by either a start argument or the
-%% inviso application parameter 'inviso_tool_config_file'.
-
-%% Help function building the internal configuration structure. Configurations
-%% in the start argument will override parameters found in a configuration file.
-fetch_configuration(Config) ->
- case fetch_config_filename(Config) of
- {ok,FName} -> % We are supposed to use a conf-file.
- case read_config_file(FName) of
- {ok,LD} -> % Managed to open a file.
- NewLD=read_config_list(LD,Config),
- {ok,NewLD};
- {error,_Reason} -> % Problem finding/opening file.
- LD=read_config_list(#ld{},Config),
- {ok,LD}
- end;
- false -> % No filename specified.
- LD=read_config_list(#ld{},Config),
- {ok,LD}
- end.
-
-%% Help function determining the name of the file which shall be consulted as
-%% the main configuration file.
-%% Returns {ok,FileName} or 'false'. The latter if no name could be determined.
-fetch_config_filename(Config) ->
- case catch lists:keysearch(config_file,1,Config) of
- {value,{_,FName}} when list(FName) ->
- {ok,FName};
- _ -> % No filename in the start argument.
- fetch_config_filename_2()
- end.
-
-fetch_config_filename_2() ->
- case application:get_env(inviso_tool_config_file) of
- {ok,FName} when list(FName) ->
- {ok,FName};
- _ -> % Application parameter not specified.
- false % Means no config file will be used.
- end.
-
-%% Help function reading the configuration file. Returns a #conf or {error,Reason}.
-read_config_file(FName) ->
- case catch file:consult(FName) of
- {ok,Terms} ->
- {ok,read_config_list(#ld{},Terms)};
- {error,Reason} ->
- {error,{file_consult,Reason}};
- {'EXIT',Reason} ->
- {error,{failure,Reason}}
- end.
-
-%% Help function traversing the Terms list entering known tag-values into #ld.
-read_config_list(LD,Terms) ->
- LD1=read_config_list_2(LD,Terms,nodes),
- LD2=read_config_list_2(LD1,Terms,c_node),
- LD3=read_config_list_2(LD2,Terms,regexp_node),
- LD4=read_config_list_2(LD3,Terms,tc_def_file),
- LD6=read_config_list_2(LD4,Terms,tdg),
- LD8=read_config_list_2(LD6,Terms,debug),
- LD10=read_config_list_2(LD8,Terms,initial_tcs),
- LD11=read_config_list_2(LD10,Terms,dir),
- _LD12=read_config_list_2(LD11,Terms,optg).
-
-read_config_list_2(LD,Terms,Tag) ->
- case catch lists:keysearch(Tag,1,Terms) of
- {value,{_,Value}} ->
- update_ld_record(LD,Tag,Value);
- _ ->
- LD % Tag not found in Terms (or error!)
- end.
-%% -----------------------------------------------------------------------------
-
-%% Function updating a named field in a record. Returns a new record. Note that
-%% this function must be maintained due the fact that field names are removed
-%% at compile time.
-update_ld_record(LD,nodes,Value) when record(LD,ld) ->
- case mk_nodes(Value) of
- {ok,NodesD} ->
- LD#ld{nodes=NodesD};
- error ->
- LD
- end;
-update_ld_record(LD,Tag,Value) when record(LD,ld) ->
- Index=
- case Tag of
- c_node -> % atom()
- #ld.c_node;
- regexp_node -> % atom()
- #ld.regexp_node;
- tc_def_file -> % string()
- #ld.tc_def_file;
- initial_tcs -> % [{TCname,VarList},...]
- #ld.initial_tcs;
- history_dir -> % string()
- #ld.history_dir;
- debug -> % true | false
- #ld.debug;
- dir -> % string()
- #ld.dir;
- optg -> % {Mod,Func,Args}
- #ld.optg;
- tdg -> % {Mod,Func,Args}
- #ld.tdg;
- keep_nodes -> % [Nodes,...]
- #ld.keep_nodes
- end,
- setelement(Index,LD,Value). % Cheeting!
-%% -----------------------------------------------------------------------------
-
-
-%% Help function which, if it exists, consults the trace definition file. The
-%% idea behind the trace definition file is to point out which trace cases there
-%% are, where to find them and how to turn them on and off.
-%% Trace case definitions are:
-%% {TCname,Type,VariableNameList,ActivatioFileName} |
-%% {TCname,Type,VariableNameList,ActivationFileName,DeactivationFileName}
-%% TCname=atom()
-%% Type=on | on_off
-%% VariableNameList=[atom(),...]
-%% ActivationFileName=DeactivationFileName=string()
-read_trace_case_definitions(LD) ->
- case LD#ld.tc_def_file of
- TCfileName when list(TCfileName) ->
- case catch file:consult(TCfileName) of
- {ok,Terms} ->
- Dir=LD#ld.dir, % The working directory of the tool.
- TCdict=read_trace_case_definitions_2(Terms,Dir,mk_tc_dict()),
- LD#ld{tc_dict=TCdict};
- _ ->
- LD
- end;
- _ ->
- LD
- end.
-
-read_trace_case_definitions_2([{TCname,on,VarNames,FName}|Rest],Dir,TCdict) ->
- FileName=make_absolute_path(FName,Dir),
- read_trace_case_definitions_2(Rest,
- Dir,
- insert_tracecase_tc_dict(TCname,
- on,
- VarNames,
- FileName,
- TCdict));
-read_trace_case_definitions_2([{TCname,on_off,VarNames,FNameOn,FNameOff}|Rest],Dir,TCdict) ->
- FileNameOn=make_absolute_path(FNameOn,Dir),
- FileNameOff=make_absolute_path(FNameOff,Dir),
- read_trace_case_definitions_2(Rest,
- Dir,
- insert_tracecase_tc_dict(TCname,
- on_off,
- VarNames,
- FileNameOn,
- FileNameOff,
- TCdict));
-read_trace_case_definitions_2([_|Rest],Dir,TCdict) ->
- read_trace_case_definitions_2(Rest,Dir,TCdict);
-read_trace_case_definitions_2([],_Dir,TCdict) ->
- TCdict.
-
-%% Help function returning an absolute path to FName if FName is not already
-%% absolute. Dir is the working dir of the tool and supposed to be absolute.
-make_absolute_path(FName,Dir) ->
- case filename:pathtype(FName) of
- absolute -> % Then do nothing, allready absolute.
- FName;
- _ ->
- filename:join(Dir,FName)
- end.
-%% -----------------------------------------------------------------------------
-
-get_status(undefined,_Node) ->
- inviso:get_status();
-get_status(CNode,Nodes) ->
- inviso_tool_lib:inviso_cmd(CNode,get_status,[Nodes]).
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Internal data structure functions.
-%% =============================================================================
-
-%% -----------------------------------------------------------------------------
-%% The nodes database structure.
-%% -----------------------------------------------------------------------------
-
-%% The purpose of the nodes database structure is to keep track of what runtime
-%% nodes we have, and their current status.
-%% Implementation:
-%% [{NodeName,AvailableStatus},...] or AvailableStatus in the
-%% non-distributed case.
-%% AvailableStatus={up,Status1} | down
-%% Status1={State,Status} | reactivating
-%% State=tracing | inactive | trace_failure
-%% Status=running | suspended
-%% reactivating=the node is now being brought up to date.
-%% inactive=not tracing, can be initiated and then reactivated.
-%% The following states can occure.
-%% {inactive,running}
-%% Mainly when we start the tool, before a session has been started.
-%% {tracing,running}
-%% When a trace session is on-going.
-%% {trace_failure,running}
-%% If init_tracing failed for some reason.
-%% {tracing,suspended}
-%% reactivating
-%% The node is tracing (has always been) but was suspended. It is now
-%% no longer suspended and the tool is redong commands.
-%% {inactive,suspended}
-%% We can end up here if a session is stopped with this node suspended.
-
-%% Returns a nodes database structure filled with the nodes Nodes.
-mk_nodes(Nodes) when list(Nodes) ->
- {ok,lists:map(fun(N) when atom(N)->{N,down} end,Nodes)};
-mk_nodes(local_runtime) -> % The non-distributed case.
- down;
-mk_nodes(_Nodes) ->
- error.
-%% -----------------------------------------------------------------------------
-
-%% Updates the nodes database structure for each node that has been added.
-%% This is the case when we start the tool or reactivate a node. Note that a node
-%% may have become adopted instead of started.
-%% Returns a new nodes database structure.
-update_added_nodes(CNode,[{Node,NodeResult}|Rest],NodesD) ->
- case update_added_nodes_3(NodeResult) of
- already_added -> % Already added to the control component.
- case get_status(CNode,[Node]) of % Examine if it is tracing or not.
- {ok,[{Node,NodeResult2}]} ->
- Result=mk_nodes_state_from_status(NodeResult2),
- update_added_nodes_2(CNode,Node,Result,NodesD,Rest);
- {error,_Reason} -> % Strange, mark it as down now.
- update_added_nodes_2(CNode,Node,down,NodesD,Rest)
- end;
- Result ->
- update_added_nodes_2(CNode,Node,Result,NodesD,Rest)
- end;
-update_added_nodes(_CNode,[],NodesD) ->
- NodesD;
-update_added_nodes(_CNode,NodeResult,_NodesD) -> % Non distributed case.
- case update_added_nodes_3(NodeResult) of
- already_added -> % Already added, most likely autostart.
- mk_nodes_state_from_status(inviso:get_status());
- Result ->
- Result % Simply replace NodesD.
- end.
-
-update_added_nodes_2(CNode,Node,Result,NodesD,Rest) ->
- case lists:keysearch(Node,1,NodesD) of
- {value,_} -> % Node already exists, replace!
- update_added_nodes(CNode,Rest,lists:keyreplace(Node,1,NodesD,{Node,Result}));
- false -> % Strange, unknown node!
- update_added_nodes(CNode,Rest,NodesD)
- end.
-
-update_added_nodes_3({ok,{adopted,tracing,running,_Tag}}) ->
- {up,{tracing,running}};
-update_added_nodes_3({ok,{adopted,tracing,{suspended,_SReason},_Tag}}) ->
- {up,{tracing,suspended}};
-update_added_nodes_3({ok,{adopted,_,running,_Tag}}) ->
- {up,{inactive,running}};
-update_added_nodes_3({ok,{adopted,_,{suspended,_SReason},_Tag}}) ->
- {up,{inactive,suspended}};
-update_added_nodes_3({ok,new}) ->
- {up,{inactive,running}};
-update_added_nodes_3({ok,already_added}) ->
- already_added; % This is an error value!
-update_added_nodes_3({error,_Reason}) ->
- down.
-%% -----------------------------------------------------------------------------
-
-%% Function marking all nodes that, according to the returnvalue from init_tracing,
-%% now are successfully initiated as tracing and running. Note that nodes that
-%% does not fully respond 'ok' when init_tracing are marked as 'trace_failure'.
-%% Also note that we assume that the nodes must be running to have made it this far.
-%% A node can of course have become suspended in the process, but that node will
-%% be marked as suspended later when that inviso event message arrives to the tool.
-%% Returns {NewNodesD,Nodes} where Nodes are the nodes that actually got initiated
-%% as a result of the init_tracing call (judged from the LogResults).
-set_tracing_running_nodes(undefined,{ok,LogResults},_AvailableStatus) -> % Non-distr. case.
- case set_tracing_running_nodes_checkresult(LogResults) of
- ok ->
- {{up,{tracing,running}},local_runtime};
- error ->
- {down,[]}
- end;
-set_tracing_running_nodes(undefined,{error,already_initiated},_) -> % Non-distributed case.
- {mk_nodes_state_from_status(inviso:get_status()),[]}; % Ask it for its status.
-set_tracing_running_nodes(undefined,{error,_Reason},_) -> % Non-distributed case.
- {down,[]}; % This is questionable!
-set_tracing_running_nodes(CNode,{ok,NodeResults},NodesD) ->
- set_tracing_running_nodes_2(CNode,NodeResults,NodesD,[]).
-
-set_tracing_running_nodes_2(CNode,[{Node,{ok,LogResults}}|Rest],NodesD,Nodes) ->
- case set_tracing_running_nodes_checkresult(LogResults) of
- ok -> % The result is good.
- case lists:keysearch(Node,1,NodesD) of
- {value,_} ->
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,{up,{tracing,running}}}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,[Node|Nodes]);
- false -> % Strange.
- set_tracing_running_nodes_2(CNode,Rest,NodesD,Nodes)
- end;
- error -> % This node is not tracing correctly.
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,down}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes)
- end;
-set_tracing_running_nodes_2(CNode,[{Node,{error,already_initiated}}|Rest],NodesD,Nodes) ->
- case get_status(CNode,[Node]) of % Then we must ask what it is doing now.
- {ok,[{Node,NodeResult}]} ->
- Result=mk_nodes_state_from_status(NodeResult),
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,Result}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes);
- {error,_Reason} -> % Strange, mark it as down.
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,down}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes)
- end;
-set_tracing_running_nodes_2(CNode,[{Node,{error,_Reason}}|Rest],NodesD,Nodes) ->
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,{up,{trace_failure,running}}}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes);
-set_tracing_running_nodes_2(_CNode,[],NodesD,Nodes) ->
- {NodesD,Nodes}. % New NodesD and nodes successfully initiated.
-
-%% Help function checking if a returnvalue from inviso:init_tracing really
-%% means that tracing has started as requested.
-set_tracing_running_nodes_checkresult(_LogResults) -> ok. % Should really be better!
-%% -----------------------------------------------------------------------------
-
-%% Function updating Node in the NodesD structure and sets it to 'down'.
-%% Returns a new nodes structure.
-set_down_nodes(Node,[{Node,_}|Rest]) ->
- [{Node,down}|Rest];
-set_down_nodes(Node,[NodeStruct|Rest]) ->
- [NodeStruct|set_down_nodes(Node,Rest)];
-set_down_nodes(_,[]) ->
- [];
-set_down_nodes(_,_) -> % Non-distributed case.
- down. % One can argue if this can happend.
-%% -----------------------------------------------------------------------------
-
-%% Function updating Node in NodesD to now be suspended. Note that if the node is
-%% reactivating it must be moved to state tracing because that is what is doing.
-set_suspended_nodes(Node,[{Node,{up,reactivating}}|Rest]) ->
- [{Node,{up,{tracing,suspended}}}|Rest];
-set_suspended_nodes(Node,[{Node,{up,{State,_}}}|Rest]) ->
- [{Node,{up,{State,suspended}}}|Rest];
-set_suspended_nodes(Node,[NodesData|Rest]) ->
- [NodesData|set_suspended_nodes(Node,Rest)];
-set_suspended_nodes(_Node,[]) -> % Hmm, strange why did we end up here?
- [];
-set_suspended_nodes(_,{up,reactivating}) -> % Non-distributed case.
- {up,{tracing,suspended}};
-set_suspended_nodes(_,{up,{State,_}}) ->
- {up,{State,suspended}}.
-%% -----------------------------------------------------------------------------
-
-%% This function is called when reactivation is completed. Hence it moves the
-%% node to no longer suspended. Note this can mean that the node is either
-%% tracing or inactive. Reactivation is not allowed for a node have trace_failure.
-set_running_nodes(Node,NodesD) when list(NodesD) ->
- case lists:keysearch(Node,1,NodesD) of
- {value,{_,AvailableStatus}} ->
- lists:keyreplace(Node,1,NodesD,{Node,set_running_nodes_2(AvailableStatus)});
- false -> % Very strange!
- NodesD
- end;
-set_running_nodes(_,NodesD) -> % The non-distributed case.
- set_running_nodes_2(NodesD).
-
-set_running_nodes_2({up,reactivating}) ->
- {up,{tracing,running}};
-set_running_nodes_2({up,{State,suspended}}) ->
- {up,{State,running}}.
-%% -----------------------------------------------------------------------------
-
-%% Function marking node as now reactivating. That means it is not suspended
-%% any longer (and tracing), but still not part of the set of nodes which shall
-%% get all commands. Returns a new NodesD.
-set_reactivating_nodes(Node,[{Node,_}|Rest]) ->
- [{Node,{up,reactivating}}|Rest];
-set_reactivating_nodes(Node,[NodesData|Rest]) ->
- [NodesData|set_reactivating_nodes(Node,Rest)];
-set_reactivating_nodes(_,[]) ->
- [];
-set_reactivating_nodes(_,{up,_}) -> % The non-distributed case.
- {up,reactivating}.
-%% -----------------------------------------------------------------------------
-
-%% Function called when stop-tracing is done. That is all nodes in Nodes shall
-%% be inactive now. Note that an inactive node can still be suspended.
-%% Returns a new NodesD.
-set_inactive_nodes(_,{up,reactivating}) -> % Non-distributed case.
- {up,{inactive,running}};
-set_inactive_nodes(_,{up,{_,Status}}) -> % Tracing or trace_failure.
- {up,{inactive,Status}};
-set_inactive_nodes(_,down) ->
- down;
-set_inactive_nodes([{Node,ok}|Rest],NodesD) ->
- case lists:keysearch(Node,1,NodesD) of
- {value,{_,{up,reactivating}}} ->
- set_inactive_nodes(Rest,lists:keyreplace(Node,1,NodesD,{Node,{up,{inactive,running}}}));
- {value,{_,{up,{_,Status}}}} -> % Tracing or trace_failure.
- set_inactive_nodes(Rest,lists:keyreplace(Node,1,NodesD,{Node,{up,{inactive,Status}}}));
- _ -> % This should not happend.
- set_inactive_nodes(Rest,NodesD)
- end;
-set_inactive_nodes([{_Node,_Error}|Rest],NodesD) ->
- set_inactive_nodes(Rest,NodesD);
-set_inactive_nodes([],NodesD) ->
- NodesD.
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of all node names. Note that it can only be used in the
-%% distributed case.
-get_all_nodenames_nodes(NodesD) ->
- lists:map(fun({Node,_})->Node end,NodesD).
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of all nodes that are up, tracing and running (not suspended),
-%% or 'void' in the non-distributed case. This is the list of nodes that shall get
-%% inviso commands.
-get_nodenames_running_nodes([{Node,{up,{tracing,running}}}|Rest]) ->
- [Node|get_nodenames_running_nodes(Rest)];
-get_nodenames_running_nodes([{_Node,_}|Rest]) ->
- get_nodenames_running_nodes(Rest);
-get_nodenames_running_nodes([]) ->
- [];
-get_nodenames_running_nodes(_) ->
- void. % When non distributed, N/A.
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of nodes that can be made to initiate tracing.
-get_inactive_running_nodes({up,{inactive,running}}) ->
- local_runtime;
-get_inactive_running_nodes(NonDistributed) when not(is_list(NonDistributed)) ->
- [];
-get_inactive_running_nodes([{Node,{up,{inactive,running}}}|Rest]) ->
- [Node|get_inactive_running_nodes(Rest)];
-get_inactive_running_nodes([{_Node,_}|Rest]) ->
- get_inactive_running_nodes(Rest);
-get_inactive_running_nodes([]) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of nodes that are currently tracing (not necessarily running).
-%% In the non-distributed case the status of the runtime component will be
-%% returned.
-%% Note that nodes showing trace_failure will be included since we like to stop
-%% tracing at those nodes too.
-get_tracing_nodes([{Node,{up,{tracing,_}}}|Rest]) ->
- [Node|get_tracing_nodes(Rest)];
-get_tracing_nodes([{Node,{up,{trace_failure,_}}}|Rest]) ->
- [Node|get_tracing_nodes(Rest)];
-get_tracing_nodes([{Node,{up,reactivating}}|Rest]) ->
- [Node|get_tracing_nodes(Rest)];
-get_tracing_nodes([_|Rest]) ->
- get_tracing_nodes(Rest);
-get_tracing_nodes([]) ->
- [];
-get_tracing_nodes(AvailableStatus) ->
- AvailableStatus.
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of all nodes that are currently up.
-get_available_nodes(down) ->
- undefined;
-get_available_nodes([{_Node,down}|Rest]) ->
- get_available_nodes(Rest);
-get_available_nodes([{Node,_}|Rest]) ->
- [Node|get_available_nodes(Rest)];
-get_available_nodes([]) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Function returning the "state" of Node. Mainly used to check if the node is
-%% suspended or not.
-%% Returns {State,Status} | reactivating | down
-%% where
-get_state_nodes(Node,NodesD) when list(NodesD) ->
- case lists:keysearch(Node,1,NodesD) of
- {value,{_,AvailableStatus}} ->
- get_state_nodes_2(AvailableStatus);
- false ->
- false
- end;
-get_state_nodes(_,NodesD) -> % Non distributed case.
- get_state_nodes_2(NodesD).
-
-get_state_nodes_2({up,{trace_failure,Status}}) ->
- {trace_failure,Status};
-get_state_nodes_2({up,{State,suspended}}) -> % {tracing|inactive,suspended}
- {State,suspended};
-get_state_nodes_2({up,reactivating}) ->
- reactivating;
-get_state_nodes_2({up,{State,running}}) ->
- {State,running};
-get_state_nodes_2(down) ->
- down.
-%% -----------------------------------------------------------------------------
-
-%% Help function in the case we need to consult the state/status of a runtime
-%% component. Returns a nodesD value that can be added to the nodes database.
-mk_nodes_state_from_status({ok,{tracing,running}}) ->
- {up,{tracing,running}};
-mk_nodes_state_from_status({ok,{tracing,{suspended,_SReason}}}) ->
- {up,{tracing,suspended}};
-mk_nodes_state_from_status({ok,{_,running}}) ->
- {up,{inactive,running}};
-mk_nodes_state_from_status({ok,{_,{suspended,_SReason}}}) ->
- {up,{inactive,suspended}};
-mk_nodes_state_from_status({error,_Reason}) ->
- down.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% The session_state.
-%% -----------------------------------------------------------------------------
-
-%% The session state reflects if the inviso_tool is tracing or not.
-%% This means that if the tool is tracing a reconnected node can be made to
-%% restart_session.
-
-%% Returns the correct value indicating that we are tracing now.
-tracing_sessionstate() ->
- tracing.
-%% -----------------------------------------------------------------------------
-
-%% Returns true or false depending on if we are tracing now or not.
-is_tracing(tracing) ->
- true;
-is_tracing(_) ->
- false.
-%% -----------------------------------------------------------------------------
-
-%% Returns the correct value indicating that the tool is not tracing.
-passive_sessionstate() ->
- idle.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% The tracer_data datastructure.
-%% -----------------------------------------------------------------------------
-
-%% The tracer_data structure collects the tracer data arguments used to init tracing
-%% by this inviso tool. The args are saved per session. Each session has
-%% a number.
-%% Implementation:
-%% Sessions=[{SessionNr,TDGargs},...]
-%% SessionNr=integer()
-%% TDGargs=list(), args given to the tracer data generator
-%% minus the first argument which is the Node name.
-
-%% Function taking tracerdata args structure inserting yet another session.
-%% Returns {SessionNr,NewTDs}.
-insert_td_tracer_data(TDGargs,TDs=[{SNr,_}|_]) ->
- {SNr+1,[{SNr+1,TDGargs}|TDs]};
-insert_td_tracer_data(TDGargs,undefined) ->
- {1,[{1,TDGargs}]}.
-%% -----------------------------------------------------------------------------
-
-%% Returns the latest session nr.
-get_latest_session_nr_tracer_data(undefined) ->
- undefined;
-get_latest_session_nr_tracer_data([{SessionNr,_}|_]) ->
- SessionNr.
-%% -----------------------------------------------------------------------------
-
-%% Returns the tracer data arguments used when creating the trace data for the
-%% latest session.
-get_latest_tdgargs_tracer_data(undefined) ->
- undefined;
-get_latest_tdgargs_tracer_data([{_,TDGargs}|_]) ->
- TDGargs.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% The tc_dict or trace case dictionary datastructure.
-%% -----------------------------------------------------------------------------
-
-%% The tc_dict stores information about all available trace cases.
-%% Implementation:
-%% [{TCname,Type,VarNames,FNameOn [,FNameOff]},...]
-%% TCname=atom()
-%% Type=on | on_off
-%% VarNames=[atom(),...]
-%% FNameOn=FNameOff=string()
-
-%% Returns the empty trace case dictionary.
-mk_tc_dict() ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Function inserting a new trace case into the trace case dictionary.
-insert_tracecase_tc_dict(TCname,on,VarNames,FNameOn,TCdict) ->
- [{TCname,on,VarNames,FNameOn}|TCdict].
-insert_tracecase_tc_dict(TCname,on_off,VarNames,FNameOn,FNameOff,TCdict) ->
- [{TCname,on_off,VarNames,FNameOn,FNameOff}|TCdict].
-%% -----------------------------------------------------------------------------
-
-%% Function finding a trace case definition in the tc_dict structure.
-%% Returns {ok,{TCname,Type,VarNAmes,FNameOn [,FNameOff]}} or 'false'.
-get_tracecase_tc_dict(TCname,[Tuple|_]) when element(1,Tuple)==TCname ->
- {ok,Tuple};
-get_tracecase_tc_dict(TCname,[_|Rest]) ->
- get_tracecase_tc_dict(TCname,Rest);
-get_tracecase_tc_dict(_,[]) ->
- false;
-get_tracecase_tc_dict(_,_) -> % There are no trace cases!
- false.
-%% -----------------------------------------------------------------------------
-
-%% Function working on the trace case definition returned by get_tracecase_tc_dict/2
-%% function.
-%% Returning {ok,ActivationFileName}.
-get_tc_activate_fname({_TCname,_Type,_VarNames,FNameOn}) ->
- {ok,FNameOn};
-get_tc_activate_fname({_TCname,_Type,_VarNames,FNameOn,_FNameOff}) ->
- {ok,FNameOn}.
-
-get_tc_deactivate_fname({_TCname,_Type,_VarNames,_FNameOn,FNameOff}) ->
- {ok,FNameOff};
-get_tc_deactivate_fname(_) -> % Not a case with off function.
- false.
-
-get_tc_varnames({_TCname,_Type,VarNames,_FNameOn}) ->
- VarNames;
-get_tc_varnames({_TCname,_Type,VarNames,_FNameOn,_FNameOff}) ->
- VarNames.
-
-%% -----------------------------------------------------------------------------
-
-
-%% The Command History Log (CHL) stores commands to make it possible to
-%% reactivate suspended nodes, reconnect restarted nodes, and to make
-%% autostart files.
-%% Each time tracing is initiated (that is started) the CHL is cleared since
-%% it would not make scense to repeat commands from an earlier tracing at
-%% reactivation for instance.
-
-%% Implementation: {NextCounter,OnGoingList,ETStable}
-%% NextCounter=integer(), next command number - to be able to sort them in order.
-%% OnGoingList=[{ProcH,{TCname,ID}},...]
-%% ID=term(), instance id for this execution of this trace case.
-%% ETStable=tid() -> {{TCname,Id},Counter,State1,Bindings}
-%% ETStable=tid() -> {{TCname,Id},Counter,running,Bindings,Result} |
-%% {{TCname,Id,#Ref},Counter,stop,Bindings} |
-%% {{TCname,#Ref},Counter,Bindings} % An rtc
-%% {{M,F,Args,#Ref},Counter}
-%% Counter=integer(), the order-counter for this logged entry.
-%% State1=activating | stopping
-%% Where:
-%% activating: the activation file for the tracecase is running.
-%% running : activation is completed.
-%% stopping : set on the previously running ETS entry when deactivation
-%% file is currently executing.
-%% stop : entered with own Counter into the ETS table when
-%% deactivation file is executing. Remains after too.
-%% Result=term(), the result returned from the tr-case or inviso call.
-
-
-%% Returning an initial empty CHL.
-mk_chl(undefined) ->
- {1,[],ets:new(inviso_tool_chl,[set,protected])};
-mk_chl({_,_,TId}) ->
- ets:delete(TId),
- mk_chl(undefined).
-
-%% Help function returning 'true' if there is a current history.
-history_exists_chl(undefined) ->
- false;
-history_exists_chl({_,_,_}) ->
- true.
-
-%% Function looking up the state of this trace case.
-find_id_chl(TCname,Id,{_NextCounter,_OnGoingList,TId}) ->
- case ets:lookup(TId,{TCname,Id}) of
- [{_,_,running,Bindings,_Result}] -> % The trace case is tracing.
- {ok,Bindings};
- [{_,_,State,_}] -> % activating or stopping.
- State;
- [] ->
- false
- end.
-
-%% Function finding the Trace case associated with a process handle
-%% doing this trace case's activation or stopping.
-find_tc_executer_chl(ProcH,{_,OnGoingList,TId}) ->
- case lists:keysearch(ProcH,1,OnGoingList) of
- {value,{_,{TCname,Id}}} ->
- [{_,_,State,_}]=ets:lookup(TId,{TCname,Id}),
- {State,{TCname,Id}}; % Should be activating or stopping.
- false ->
- false
- end.
-
-%% Adds a Trace case to the CHL. This is done when it is turned on. Or when it
-%% is called for trace cases that do not have on/off functionality.
-set_activating_chl(TCname,Id,{Counter,OnGoingList,TId},Bindings,ProcH) ->
- ets:insert(TId,{{TCname,Id},Counter,activating,Bindings}),
- {Counter+1,[{ProcH,{TCname,Id}}|OnGoingList],TId}.
-
-%% Function marking a trace case as now running. That is the activation
-%% phase is completed. It is normaly completed when the process executing
-%% the trace case signals that it is done.
-set_running_chl(ProcH,TCname,Id,Result,{NextCounter,OnGoingList,TId}) ->
- [{_,Counter,_,Bindings}]=ets:lookup(TId,{TCname,Id}),
- ets:insert(TId,{{TCname,Id},Counter,running,Bindings,Result}),
- NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList),
- {NextCounter,NewOnGoingList,TId}.
-
-%% Function marking trace case TCname with identifier Id as now in its stopping
-%% state. Where ProcH is the handler to the process running the stopping
-%% trace case.
-set_stopping_chl(TCname,Id,{NextCounter,OnGoingList,TId},ProcH)->
- [{_,Counter,_,Bindings,_}]=ets:lookup(TId,{TCname,Id}),
- ets:insert(TId,{{TCname,Id},Counter,stopping,Bindings}),
- ets:insert(TId,{{TCname,Id,make_ref()},NextCounter,stop,Bindings}),
- {NextCounter+1,[{ProcH,{TCname,Id}}|OnGoingList],TId}.
-
-%% Function removing a TCname-Id from the CHL. This is mostly used
-%% if activating the trace case failed for some reason. We do not then
-%% expect the user to stop the trace case. Hence it must be removed now.
-%% A reactivation process may have noticed the activating-entry and started
-%% to activate it. But since the general state reached after an unsuccessful
-%% activation can not easily be determined, we don't try to do much about it.
-del_tc_chl(ProcH,TCname,Id,{NextCounter,OnGoingList,TId}) ->
- ets:delete(TId,{TCname,Id}),
- NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList),
- {NextCounter,NewOnGoingList,TId}.
-
-%% Function removing the entry TCname+Id from the CHL. This makes it
-%% possible to activate a tracecase with this id again. The entry was
-%% previously marked as stopping.
-nullify_chl(ProcH,TCname,Id,{NextCounter,OnGoingList,TId}) ->
- ets:delete(TId,{TCname,Id}),
- NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList),
- {NextCounter+1,NewOnGoingList,TId}.
-
-%% Function stopping all processes saved as being now running tc executers.
-%% This is useful as cleanup during stop tracing for instance.
-%% Returns a new CHL which is not in all parts correct. Entries in the
-%% ETS table are for instance not properly state-changed. But the CHL will
-%% from now on only be used to create command files and similar.
-stop_all_tc_executer_chl({NextCounter,[{ProcH,_}|Rest],TId}) ->
- exit(ProcH,kill),
- stop_all_tc_executer_chl({NextCounter,Rest,TId});
-stop_all_tc_executer_chl({NextCounter,[],TId}) ->
- {NextCounter,[],TId}.
-
-%% Function adding a "plain" inviso call to the CHL.
-add_inviso_call_chl(Cmd,Args,{NextCounter,OnGoingList,TId}) ->
- ets:insert(TId,{{inviso,Cmd,Args,make_ref()},NextCounter}),
- {NextCounter+1,OnGoingList,TId}.
-
-%% Function adding a run trace case entry to the chl.
-add_rtc_chl(TCname,Bindings,{NextCounter,OnGoingList,TId}) ->
- ets:insert(TId,{{TCname,make_ref()},NextCounter,Bindings}),
- {NextCounter+1,OnGoingList,TId}.
-%% Returns the highest used counter number in the command history log.
-get_highest_used_counter_chl({NextCounter,_,_}) ->
- NextCounter-1.
-
-%% Help function returning a list of {{TCname,Id},Phase} for all ongoing
-%% assynchronous tracecases.
-get_ongoing_chl(undefined) ->
- [];
-get_ongoing_chl({_,OngoingList,TId}) ->
- get_ongoing_chl_2(OngoingList,TId).
-
-get_ongoing_chl_2([{_ProcH,{TCname,Id}}|Rest],TId) ->
- case ets:lookup(TId,{TCname,Id}) of
- [{_,_C,activating,_B}] ->
- [{{TCname,Id},activating}|get_ongoing_chl_2(Rest,TId)];
- [{_,_C,stopping,_B}] ->
- [{{TCname,Id},deactivating}|get_ongoing_chl_2(Rest,TId)]
- end;
-get_ongoing_chl_2([],_) ->
- [].
-
-%% Function returning a list of log entries. Note that the list is unsorted
-%% in respect to Counter.
-get_loglist_chl({_,_,TId}) ->
- L=ets:tab2list(TId),
- lists:map(fun({{TC,Id},C,S,B,_Result}) -> {{TC,Id},C,S,B}; % running
- (Tuple={{_TC,_Id},_C,_S,_B}) -> Tuple; % activating | stopping
- (Tuple={{_TC,_Id,_Ref},_C,_S,_B}) -> Tuple; % stop
- (Tuple={{_M,_F,_Args,_Ref},_C}) -> Tuple;
- (Tuple={{_TC,_Ref},_C,_B}) -> Tuple
- end,
- L);
-get_loglist_chl(_) -> % The history is not initiated, ever!
- [].
-
-%% Function returning a list of log entries, but only those which are not
-%% cancelled out by deactivations.
-% get_loglist_active_chl({_,_,TId}) ->
-% L=ets:tab2list(TId),
-% lists:zf(fun({{TC,Id},C,S,B,_Result}) -> {true,{{TC,Id},C,S,B}}; % running
-% (Tuple={{_TC,_Id},_C,_S,_B}) -> Tuple; % activating | stopping
-% (Tuple={{_TC,_Id,_Ref},_C,_S,_B}) -> Tuple; % stop
-% (Tuple={{_M,_F,_Args,_Ref},_C}) -> Tuple
-% end,
-% L);
-% get_loglist_chl(_) -> % The history is not initiated, ever!
-% [].
-
-
-%% This helpfunction recreates a history from a saved history list. This function
-%% is supposed to crash if the log is not well formatted. Note that we must restore
-%% the counter in order for the counter to work if new commands are added to the
-%% history.
-replace_history_chl(OldCHL,SortedLog) ->
- {_,Ongoing,TId}=mk_chl(OldCHL),
- {NewTId,Counter}=replace_history_chl_2(TId,SortedLog,0),
- {ok,{Counter+1,Ongoing,NewTId}}.
-
-replace_history_chl_2(TId,[{{TC,Id},C,running,B}|Rest],_Counter) ->
- ets:insert(TId,{{TC,Id},C,running,B,undefined}),
- replace_history_chl_2(TId,Rest,C);
-replace_history_chl_2(TId,[{{M,F,Args},C}|Rest],_Counter) ->
- ets:insert(TId,{{M,F,Args,make_ref()},C}),
- replace_history_chl_2(TId,Rest,C);
-replace_history_chl_2(TId,[{TC,C,B}|Rest],_Counter) ->
- ets:insert(TId,{{TC,make_ref()},C,B}),
- replace_history_chl_2(TId,Rest,C);
-replace_history_chl_2(TId,[],Counter) ->
- {TId,Counter}.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Reactivators data structure.
-%% -----------------------------------------------------------------------------
-
-%% Function adding a new node-reactivatorpid pair to the reactivators structure.
-%% In this way we know which reactivators to remove if Node terminates, or when
-%% a node is fully updated when a reactivator is done.
-add_reactivators(Node,Pid,Reactivators) ->
- [{Node,Pid}|Reactivators].
-
-%% Function removing a reactivator entry from the reactivators structure.
-del_reactivators(RPid,[{_Node,RPid}|Rest]) ->
- Rest;
-del_reactivators(RPid,[Element|Rest]) ->
- [Element|del_reactivators(RPid,Rest)];
-del_reactivators(_,[]) -> % This should not happend.
- [].
-
-get_node_reactivators(RPid,Reactivators) ->
- case lists:keysearch(RPid,2,Reactivators) of
- {value,{Node,_}} ->
- Node;
- false -> % This should not happend.
- false
- end.
-
-%% Returns a list of list all nodes that are currently reactivating.
-get_all_nodes_reactivators([{Nodes,_Pid}|Rest]) ->
- [Nodes|get_all_nodes_reactivators(Rest)];
-get_all_nodes_reactivators([]) ->
- [].
-
-%% Function stopping all running reactivator processes. Returns a new empty
-%% reactivators structure. Note that this function does not set the state of
-%% Nodes. It must most often be set to running.
-stop_all_reactivators([{_Nodes,Pid}|Rest]) ->
- exit(Pid,kill),
- stop_all_reactivators(Rest);
-stop_all_reactivators([]) ->
- []. % Returns an empty reactivators.
-
-%% Help function stopping the reactivator (if any) that reactivates Node.
-%% Returns a new list of reactivators structure.
-stop_node_reactivators(Node,[{Node,Pid}|Rest]) ->
- exit(Pid,kill),
- Rest;
-stop_node_reactivators(Node,[NodePid|Rest]) ->
- [NodePid|stop_node_reactivators(Node,Rest)];
-stop_node_reactivators(_,[]) ->
- [].
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Started initial trace cases data structure.
-%% -----------------------------------------------------------------------------
-
-%% This datastructure keeps information about ongoing trace cases started
-%% automatically at init_tracing. These must be automatically stopped when calling
-%% stop_tracing.
-
-add_initial_tcs(TCname,Id,StartedInitialTcs) ->
- [{TCname,Id}|StartedInitialTcs].
-%% -----------------------------------------------------------------------------
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% Description: +%% The inviso_tool implementation. A tool that uses inviso. +%% +%% Authors: +%% Lennart Öhman, [email protected] +%% ----------------------------------------------------------------------------- + +-module(inviso_tool). + + +%% This is the inviso tool, which is a tool using the inviso trace application. +%% It is developed to make tracing using trace cases possible in an environment +%% of distributed Erlang nodes. +%% A current restriction is that the Erlang nodes are supposed to have the same +%% code. This since inviso tool can at this point not handle subsets of nodes. +%% Instead all participating Erlang nodes are treated the same. +%% +%% The main functionality of the inviso tool are: +%% +%% (1) Handles start and stop of tracing at participating nodes. +%% (2) Interprets trace-case files at a distributed network level. +%% (The inviso runtime component is responsible for interpreting +%% trace cases at a local level, if run in an autostart). +%% (3) Keeps a command history log from which: +%% (a) Sequences easily can be repeated. +%% (b) Autostart configuration files can be created (understood by the +%% default inviso autostart mechanism). +%% (4) Performs reactivation in case tracing is suspended (manually or by +%% an overload mechanism). +%% (5) Can reconnect crashed nodes and by using the history bringing them +%% up to speed. + +%% Distributed Erlang +%% ------------------ +%% Inviso is built to run in a distributed environment. +%% The inviso tool can also be used in a non distributed environment. + +%% Short description +%% ----------------- +%% Start-up of the inviso tool +%% During the start-up of the tool, the tool starts runtime components at +%% all participating nodes. A runtime component can already be running at +%% a particular node and will then simply be adopted. +%% +%% Session +%% A session is said to start when tracing is initiated, and ends when +%% made to stop by the user. When a session is stopped, tracing is stopped +%% at all participating nodes. Note that participating nodes may come and +%% go though the time-frame of a session. That means that if a node is +%% reconnected it may resume its tracing in the current session through +%% a 'restart_session'. A runtime component that is already tracing at the +%% time start-session will simply be part of the session without its +%% ingoing tracing being changed. +%% +%% Reactivation +%% A node that is suspended can be reactivated to resume tracing. Note that +%% tracing has in this situation never been stopped at the node in question. +%% The inviso tool resumes the node and applies the history to it. +%% +%% Reconnect +%% A node that is "down" from the inviso tool's perspective can be +%% reconnected. During reconnection the tool restarts the runtime component +%% at that node but does not (re)initiate tracing. The latter is called +%% restart_session and must be done explicitly, unless the node in question +%% is in fact already tracing. If the node is already tracing (due to an autostart +%% for instance), it automatically becomes part of the ongoing session (if +%% there is an ongoing session). +%% +%% Restart Session +%% A node that has been down and has been reconnected can be made to +%% initialize and resume its tracing. This is done by starting the session +%% at the node in question and redoing the current history. + +%% Trace files within a session +%% Since it is possible to init-tracing (from an inviso perspective) several +%% times within the same session, a session may leave several trace log files +%% behind. This must be resolved by the tracer data generator function +%% (user supplied) by marking filenames in a chronological order but still +%% making them possible to identify as part of the same session + + + +%% ----------------------------------------------------------------------------- +%% API exports. +%% ----------------------------------------------------------------------------- + +-export([start/0,start/1,stop/0,stop/1]). +-export([reconnect_nodes/0,reconnect_nodes/1, + start_session/0,start_session/1, + reinitiate_session/0,reinitiate_session/1, + restore_session/0,restore_session/1,restore_session/2, + stop_session/0, + reset_nodes/0,reset_nodes/1, + atc/3,sync_atc/3,sync_atc/4, + sync_rtc/2,sync_rtc/3, + dtc/2,sync_dtc/2,sync_dtc/3, + inviso/2]). +-export([reactivate/0,reactivate/1, + save_history/1, + get_autostart_data/1,get_autostart_data/2, + get_activities/0,get_node_status/0,get_node_status/1,get_session_data/0]). +-export([flush/0,flush/1]). +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% Debug exports. +%% ----------------------------------------------------------------------------- + +-export([get_loopdata/0]). +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% OTP exports and call backs. +%% ----------------------------------------------------------------------------- + +-export([init/1,handle_call/3,handle_cast/2,handle_info/2,terminate/2]). +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% Internal exports. +%% ----------------------------------------------------------------------------- + +-export([tc_executer/4,reactivator_executer/6]). +-export([std_options_generator/1]). +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% Constants. +%% ----------------------------------------------------------------------------- + +%% Defines the inviso function calls that shall be possible to do through the +%% inviso API in this tool. +-define(INVISO_CMDS, + [{tp,5},{tp,4},{tp,1},{tpl,5},{tpl,4},{tpl,1}, + {ctp,1},{ctp,2},{ctp,3},{ctpl,1},{ctpl,2},{ctpl,3}, + {tf,2},{tf,1},{ctf,2},{ctf,1},{ctf_all,0}, + {init_tpm,4},{init_tpm,7}, + {tpm,4},{tpm,5},{tpm,8}, + {tpm_tracer,4},{tpm_tracer,5},{init_tpm,8}, + {tpm_ms,5},{tpm_ms_tracer,5}, + {ctpm_ms,4},{ctpm,3}, + {tpm_localnames,0},{ctpm_localnames,0}, + {tpm_globalnames,0},{ctpm_globalnames,0}, + {ctp_all,0}, + {suspend,1},{cancel_suspension,0}]). +%% ----------------------------------------------------------------------------- + +%% These inviso functions shall be included in the command history log. Others +%% are not relevant to be redone during a recactivation, a restart session or +%% exported to an autostart file. +-define(INVISO_CMD_HISTORY, + [{tp,5},{tp,4},{tp,1},{tpl,5},{tpl,4},{tpl,1}, + {ctp,1},{ctp,2},{ctp,3},{ctpl,1},{ctpl,2},{ctpl,3}, + {tf,2},{tf,1},{ctf,2},{ctf,1},{ctf_all,0}, + {init_tpm,4},{init_tpm,7}, + {tpm,4},{tpm,5},{tpm,8}, + {tpm_tracer,4},{tpm_tracer,5},{init_tpm,8}, + {tpm_ms,5},{tpm_ms_tracer,5}, + {ctpm_ms,4},{ctpm,3}, + {tpm_localnames,0},{ctpm_localnames,0}, + {tpm_globalnames,0},{ctpm_globalnames,0}, + {ctp_all,0}]). +%% ----------------------------------------------------------------------------- + +%% Since many function calls to inviso may take long time, especially if they +%% involve difficult and many trace patterns to set, the default gen_server:call +%% time out can not be used. We just do not want to get stuck for ever if some +%% error occurs. +-define(CALL_TIMEOUT,60000). + +%% Default max time to wait for a trace case called synchronously to return. +-define(SYNC_TC_TIMEOUT,10000). + +%% Runtime components shall terminate when the tool terminates. +-define(DEFAULT_DEPENDENCY,{dependency,0}). +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% Record definitions. +%% ----------------------------------------------------------------------------- + +%% The loopdata record. +-record(ld,{ + dir=".", % Working dir of the tool. + nodes=down, % The nodesD database, defaults to non-distr. + c_node, % Location of inviso_c. + c_pid, % The inviso control component. + regexp_node, % Node for regexp expansions. + tc_dict, % Trace case definition db. + chl, % Command history log. + session_state=passive, % passive | tracing + tdg={inviso_tool_lib,std_tdg,[]}, % Tracer data generator func. + tracer_data, % Current session nr and TDGargs. + reactivators=[], % Pids of now running reactivators. + tc_def_file, % Trace case definition file. + optg={?MODULE,std_options_generator,[]}, % Generates options to add_nodes/3. + initial_tcs=[], % Initial trace cases. + started_initial_tcs=[], % Cases that must be stopped when stop_tracing. + history_dir, % File path for history file. + keep_nodes=[], % Nodes that shall not be cleared when stopping. + debug=false % Internal debug mode + }). +%% ----------------------------------------------------------------------------- + + +%% ============================================================================= +%% API +%% ============================================================================= + +%% start()={ok,Pid} | {error,{already_started,pid()}} +%% start(Config) +%% Config=[{Opt,Value},...], list of tuple options. +%% Opt=dir|nodes|c_node|regexp_node|tdg|tc_def_file|optg|initial_tcs| +%% history_dir|keep_nodes +%% Starts the inviso_tool process. Options in Config are the same as those +%% which are kept in the #ld structure. +start() -> + start([]). +start(Config) -> + gen_server:start({local,?MODULE},?MODULE,Config,[]). +%% ----------------------------------------------------------------------------- + +%% stop(UntouchedNodes)= +%% stop()={ok,NodeResults} | NodeResult | {error,Reason} +%% UntouchedNodes=list(), nodes where any trace patterns shall not be removed. +%% NodeResults=[{Node,NodeResult},...] +%% NodeResult=ok | {error,Reason} | patterns_untouched +%% Stops the inviso tool and the inviso control component. Runtime components are +%% stopped by them selves depending on their dependcy of the control component. +%% All runtime components that are not marked as to be kept will have their +%% trace patterns cleared before the inviso control component is shutdown. +%% The NodeResults indicates which nodes were successfullt handled. +stop() -> + stop([]). +stop(UntouchedNodes) -> + gen_server:call(?MODULE,{stop,UntouchedNodes},?CALL_TIMEOUT). +%% ----------------------------------------------------------------------------- + +%% reconnect_nodes()=NodeResult; function for the nod-distributed case. +%% reconnect_nodes(Nodes)={ok,NodesResults} +%% NodesResults=[{Node,NodeResult},...] +%% NodeResult={ok,{State,Status}} | {error,NReason} +%% State=tracing | inactive +%% Status=running | suspended +%% NReason=unknown_node | already_connected | down +%% (Re)starts the inviso runtime components at Nodes. Depending on its state +%% (new,idle or tracing) and if the tool is running a session or not, it becomes +%% part of the tool's ongoing session. If the newly reconnected node is not +%% tracing but the tool runs a session, the node must be reinitiated to become +%% tracing. +reconnect_nodes() -> + gen_server:call(?MODULE,{reconnect_nodes,local_runtime},?CALL_TIMEOUT). +reconnect_nodes(Node) when is_atom(Node) -> + reconnect_nodes([Node]); +reconnect_nodes(Nodes) when is_list(Nodes) -> + gen_server:call(?MODULE,{reconnect_nodes,Nodes},?CALL_TIMEOUT). +%% ----------------------------------------------------------------------------- + +%% start_session()={ok,{SessionNr,InvisoReturn}} | {error,Reason} +%% start_session(MoreTDGargs)= +%% MoreTDGargs=list(), prepended to the fixed list of args used when calling the +%% tracer data generator function. +%% SessionNr=integer(), trace sessions are numbered by the tool. +%% InvisoReturn=If successful inviso call, the returnvalue from inviso. +%% Note that individual nodes may be unsuccessful. See inviso:init_tracing/1 +%% Initiates tracing at all participating nodes. +start_session() -> + start_session([]). +start_session(MoreTDGargs) -> + gen_server:call(?MODULE,{start_session,MoreTDGargs},?CALL_TIMEOUT). +%% ----------------------------------------------------------------------------- + +%% reinitiate_session(Nodes)={ok,InvisoReturn} | {error,Reason} +%% InvisoReturn=If successful inviso call, the returnvalue from inviso:init_tracing/1. +%% Note that individual nodes may be unsuccessful. Mentioned nodes not part +%% of the tool or not in state inactive will be marked as failing by the +%% tool in the InvisoReturn. +%% To reinitate a node means to (inviso) init tracing at it according to saved +%% tracer data generator arguments for the current session and then redo the current +%% history to bring it up to speed. Note that the tool must be running a session +%% for reinitiate to work. +reinitiate_session() -> + gen_server:call(?MODULE,{reinitiate_session,local_runtime},?CALL_TIMEOUT). +reinitiate_session(Nodes) -> + gen_server:call(?MODULE,{reinitiate_session,Nodes},?CALL_TIMEOUT). +%% ----------------------------------------------------------------------------- + +%% restore_session()= +%% restore_session(MoreTDGargs)= +%% restore_session(FileName)= +%% restore_session(FileName,MoreTDGargs)={ok,{SessionNr,InvisoReturn}} | {error,Reason} +%% The two first clauses will start a new session using the last history. This +%% implies that there must have been a session running prior. +%% The two last clauses starts a session and reads a history file and executes the +%% tracecases in it at all inactive nodes. +%% In both cases the reused or read history becomes the current histoy, just if the +%% session had been initiated manually. The tool may not +%% have a session ongoing, and nodes already tracing (nodes which were adopted) +%% are not effected. Just like when starting a session manually. +restore_session() -> + restore_session([]). +restore_session([]) -> % This cant be a filename. + gen_server:call(?MODULE,{restore_session,[]},?CALL_TIMEOUT); +restore_session(FileNameOrMoreTDGargs) -> + case is_string(FileNameOrMoreTDGargs) of + true -> % Interpret it as a filename. + restore_session(FileNameOrMoreTDGargs,[]); + false -> % The we want to use last session history! + gen_server:call(?MODULE,{restore_session,FileNameOrMoreTDGargs},?CALL_TIMEOUT) + end. +restore_session(FileName,MoreTDGargs) -> + gen_server:call(?MODULE,{restore_session,{FileName,MoreTDGargs}},?CALL_TIMEOUT). +%% ----------------------------------------------------------------------------- + +%% stop_session()={ok,{SessionNr,Result}} | {error,Reason} +%% SessionNr=integer() +%% Result=[{Node,NodeResult},...] | NonDistributedNodeResult +%% NodeResult=ok | {error,Reason} +%% NonDistributedNodeResult=[ok] | [] +%% Stops inviso tracing at all participating nodes. The inviso runtime components +%% will go to state idle. It is now time to fetch the logfiles. Will most often +%% succeed. Will only return an error if the entire inviso call returned an +%% error. Not if an individual node failed stop tracing successfully. +%% Any running trace case, including reactivator processes will be terminated. +stop_session() -> + gen_server:call(?MODULE,stop_session,?CALL_TIMEOUT). +%% ----------------------------------------------------------------------------- + +%% reset_nodes()=NodeResult | {error,Reason} +%% reset_nodes(Nodes)={ok,NodeResults} | {error,Reason} +%% NodeResults and NodeResult as returned by inviso:clear/1 and /0. +%% Clear nodes from trace flags, trace patterns and meta trace patterns. The tool +%% must not be having a running session. +reset_nodes() -> + gen_server:call(?MODULE,{reset_nodes,local_runtime},?CALL_TIMEOUT). +reset_nodes(Nodes) -> + gen_server:call(?MODULE,{reset_nodes,Nodes},?CALL_TIMEOUT). +%% ----------------------------------------------------------------------------- + +%% atc(TC,Id,Vars)=ok | {error,Reason} +%% TC=atom(), name of the trace case. +%% Id=term(), given name of this usage of TC. +%% Vars=list(), list of variable bindings [{Var,Value},...], Var=atom(),Value=term(). +%% Function activating a trace case. The trace case must be defined in the +%% trace case dictionary. The 'ok' return value is only a signal that the +%% trace case has started successfully. It may then run for as long as it is +%% programmed to run. An erroneous return value does not necessarily mean that +%% the trace case has not been executed. It rather means that is undetermined +%% what happend. +atc(TC,Id,Vars) -> + gen_server:call(?MODULE,{atc,{TC,Id,Vars}},?CALL_TIMEOUT). +%% ----------------------------------------------------------------------------- + +%% sync_atc(TC,Id,Vars)=Result | {error,Reason} +%% sync_atc(TC,Id,Vars,TimeOut)= +%% Result=term(), what ever is returned be the last expression in the trace case. +%% TimeOut=interger() | infinity, the max wait time for the trace case to finnish. +%% As atc/3 but waits for the trace case to finish. +sync_atc(TC,Id,Vars) -> + gen_server:call(?MODULE,{sync_atc,{TC,Id,Vars,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT). +sync_atc(TC,Id,Vars,TimeOut) -> + gen_server:call(?MODULE,{sync_atc,{TC,Id,Vars,TimeOut}},?CALL_TIMEOUT). +%% ----------------------------------------------------------------------------- + +%% sync_rtc(TC,Vars)=Result | {error,Reason} +%% sync_rtc(TC,Vars,TimeOut)= +%% Result=term(), what ever is returned be the last expression in the trace case. +%% TimeOut=interger() | infinity, the max wait time for the trace case to finnish. +%% As sync_atc/3 but the trace case is not marked as activated. It is mearly placed +%% in the history. Hence with sync_rtc a trace case can be "activated" multiple time. +sync_rtc(TC,Vars) -> + gen_server:call(?MODULE,{sync_rtc,{TC,Vars,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT). +sync_rtc(TC,Vars,TimeOut) -> + gen_server:call(?MODULE,{sync_rtc,{TC,Vars,TimeOut}},?CALL_TIMEOUT). +%% ----------------------------------------------------------------------------- + +%% dtc(TC,Id)=ok | {error,Reason} +%% Deactivates a previosly activated trace case. This function can only be used +%% on trace cases that has a deactivation defined in the trace case dictionary. +%% There is of course really no difference between a file containing an activation +%% compared to a deactivation. But to be able cancelling activations out from the +%% history log, a defined deactivation is essential. +%% As with activation, the returned 'ok' simply indicates the start of the trace +%% case. +dtc(TC,Id) -> + gen_server:call(?MODULE,{dtc,{TC,Id}},?CALL_TIMEOUT). +%% ----------------------------------------------------------------------------- + +%% sync_dtc(TC,Id)=Result | {error,Reason} +%% sync_dtc(TC,Id,TimeOut)= +%% Synchronous deactivation of trace case. See dtc/2 and sync_atc/3 for +%% parameters. +sync_dtc(TC,Id) -> + gen_server:call(?MODULE,{sync_dtc,{TC,Id,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT). +sync_dtc(TC,Id,TimeOut) -> + gen_server:call(?MODULE,{sync_dtc,{TC,Id,TimeOut}},?CALL_TIMEOUT). +%% ----------------------------------------------------------------------------- + +%% inviso(Cmd,Args)=Result +%% Cmd=atom(), the (inviso) function name that shall be called. +%% Args=list(), the arguments to Cmd. +%% Result=term(), the result from the inviso function call. +%% This function executes a Cmd in the inviso tool context. The inviso call will +%% be logged in history log and thereby repeated in case of a reactivation. +%% Note that this function is intended for use with inviso function API without +%% specifying any nodes, since the function call is supposed to be carried out on +%% all nodes. +%% When these functions are written to an autostart config file by the tool there +%% is supposed to be a translation to inviso_rt functions. +inviso(Cmd,Args) -> + gen_server:call(?MODULE,{inviso,{Cmd,Args}},?CALL_TIMEOUT). +%% ----------------------------------------------------------------------------- + +%% reactivate()=ok | {error,Reason} +%% reactivate(Node)=ok | {error,Reason} +%% Moves a runtime component from suspended to the state running. This can be +%% done for both tracing and inactive nodes. The later is necessary since you +%% may have stopped tracing with a node suspended. +%% In case the node is tracing, commands in the command history log are redone at +%% the node in questions. +%% Note that this function returns 'ok' before the node is running. This because the +%% the reactivated history is done by a separate process and there is no guarantee +%% when it will be ready. The reactivated node will not be marked as running in +%% the tool until done reactivating. +%% Further it is important to understand that if there are "ongoing" tracecases +%% (i.e tracecase scripts that are currently executing) and this node was running +%% at the time that tracecase script started to execute, the list of nodes bound +%% to the Nodes variable in that script executer includes this node. Making it +%% no longer suspended makes it start executing inviso commands from where ever +%% such are called. Hence the reactivation may be interferred by that tracecase. +reactivate() -> % Non-distributed API. + reactivate(node()). +reactivate(Node) -> + gen_server:call(?MODULE,{reactivate,Node},?CALL_TIMEOUT). +%% ----------------------------------------------------------------------------- + +%% save_history(FileName)={ok,AbsFileName} | {error,Reason} +%% Saves the currently collected command history log to a file. The file will +%% be a binary-file. If FileName is an absolute path, it will be saved to that +%% file. Otherwise the history dir will be used. If no history dir was specified +%% the tool dir will be used, prepended to FileName. +save_history(FileName) -> + gen_server:call(?MODULE,{save_history,FileName},?CALL_TIMEOUT). +%% ----------------------------------------------------------------------------- + +%% get_autostart_data(Nodes,Dependency)={ok,{AutoStartData,NodeResults} | +%% {ok,{AutoStartData,NodeResult}} | {error,Reason} +%% Dependency=inviso dependency parameter which will be used for every +%% autostarted runtime component (included in Options). +%% NodeResults=[{Node,NodeResult},...] +%% NodeResult={ok,{Options,{tdg,{M,F,CompleteTDGargs}}}} | {error,Reason} +%% Options=add_nodes options to the inviso runtime component. +%% M,F=atom(), the module and function for tracerdata generation. +%% CompleteTDGargs=list(), all arguments as they are given to the tracer +%% data generator function. +%% AutostartData=[CaseSpec,...] +%% CaseSpec={file,{FileName,Bindings}} | {mfa,{M,F,Args}} +%% FileName=string(), pointing out the trace case file. Note that this +%% is the same as the path used by the tool. +%% Bindings=Var bindings used according to the history for the +%% invocation. +%% M,F=atom(), the function that shall be called (normally some inviso). +%% Args=list(), the actual arguments. Note that this may contain things +%% which can not be written to file (ports, pids,...). +%% Function returning information on how to autostart a node to make it trace +%% according to the current history. The inviso_tool does not know how to write +%% the necessary files at the nodes in question. That must be done by the user +%% of the tool, guided by the return value from this function. +%% Note that there will be two types of trace case files. Regular trace case +%% files and binaries returned from this function. The latter contains the +%% inviso commands which have been executed. Note that the order amongst the +%% trace cases and binaries is of importance (otherwise they will be redone in +%% an incorrect order). +get_autostart_data(Dependency) -> + gen_server:call(?MODULE,{get_autostart_data,Dependency},?CALL_TIMEOUT). +get_autostart_data(Nodes,Dependency) -> + gen_server:call(?MODULE,{get_autostart_data,{Nodes,Dependency}},?CALL_TIMEOUT). +%% ----------------------------------------------------------------------------- + +%% get_activities()={ok,Ongoing} | {error,Reason} +%% Ongoing=list(); [ [TraceCases] [,Reactivators] ] +%% TraceCases={tracecases,TraceCaseList} +%% TraceCaseList=[{{TCname,Id},Phase},...] +%% Phase=activating | deactivating +%% Reactivators={reactivating_nodes,ReactivatingNodes} +%% ReactivatingNodes=[Node,...] +%% Returns a list of assynchronous tracecases and nodes doing reactivation at +%% this momement. This can be useful to implement "home brewn" synchronization, +%% waiting for the runtime components to reach a certain state. +get_activities() -> + gen_server:call(?MODULE,get_activities,?CALL_TIMEOUT). +%% ----------------------------------------------------------------------------- + +%% get_status(Node)={ok,StateStatus} | {error,Reason} +%% StateStatus={State,Status} | reactivating | down +%% State=tracing | inactive | trace_failure +%% Status=running | suspended +get_node_status() -> + get_node_status(local_runtime). +get_node_status(Node) -> + gen_server:call(?MODULE,{get_node_status,Node},?CALL_TIMEOUT). +%% ----------------------------------------------------------------------------- + +%% get_session_data()={ok,{Status,SessionNr,TDGargs}} | {error,Reason} +%% Status=tracing | not_tracing, info about current/last session. +%% SessionNr=integer() +%% TDGargs=list(), list of the arguments that will be given to the tracer data +%% generator function (not including the leading Nodes list). +%% Returns data about the current or last session. +get_session_data() -> + gen_server:call(?MODULE,get_session_data,?CALL_TIMEOUT). +%% ----------------------------------------------------------------------------- + +%% flush()={ok,NodeResults} | NodeResult | {error,Reason} +%% flush(Nodes)={ok,NodesResults} | {error,Reason} +%% NodeResults=[{Node,NodeResult},...] +%% NodeResult=ok | {error,Reason} +%% Makes runtime components flush their trace ports. +flush() -> + gen_server:call(?MODULE,flush,?CALL_TIMEOUT). +flush(Nodes) -> + gen_server:call(?MODULE,{flush,Nodes},?CALL_TIMEOUT). +%% ----------------------------------------------------------------------------- + +%% get_loopdata()=#ld +%% Debug API returning the internal loopdata structure. See #ld above for details. +get_loopdata() -> + gen_server:call(?MODULE,get_loopdata,?CALL_TIMEOUT). +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% Internal APIs. +%% ----------------------------------------------------------------------------- + +%% tc_executer_reply(To,Reply)=nothing significant +%% To=pid() +%% Reply=term() +%% Internal API used by a trace case executer process to signal its completion. +tc_executer_reply(To,Reply) -> + gen_server:cast(To,{tc_executer_reply,Reply}). +%% ----------------------------------------------------------------------------- + +%% Internal API used by a reactivator process indicating it is done with the +%% history log it has got so far. +%% Timeout set to infinity since the tool may be busy, then the reactivator just +%% have to wait. If the tool crashes the reactivator will be go down too automatically. +reactivator_reply(TPid,Counter) -> + gen_server:call(TPid,{reactivator_reply,{Counter,self()}},infinity). +%% ----------------------------------------------------------------------------- + + +%% ============================================================================= +%% gen_server implementation. +%% ============================================================================= + +init(Config) -> + case fetch_configuration(Config) of % From conf-file and Config. + {ok,LD} when is_record(LD,ld) -> + case start_inviso_at_c_node(LD) of + {ok,CPid} -> + LD2=start_runtime_components(LD), + LD3=read_trace_case_definitions(LD2), + process_flag(trap_exit,true), + start_subscribe_inviso_events(LD3#ld.c_node), + {ok,LD3#ld{c_pid=CPid}}; + {error,Reason} -> % Most likely already running. + {stop,{error,Reason}} + end; + {error,Reason} -> + {stop,{error,{start_up,Reason}}} + end. +%% ----------------------------------------------------------------------------- + +%% Help function starting the inviso control component at node c_node, or "here" +%% if it is not a distributed network. +start_inviso_at_c_node(#ld{c_node=undefined}) -> % Non distributed case. + case inviso:start() of + {ok,Pid} -> + {ok,Pid}; + {error,Reason} -> + {error,Reason} + end; +start_inviso_at_c_node(#ld{c_node=CNode}) -> + case rpc:call(CNode,inviso,start,[]) of + {ok,Pid} -> + {ok,Pid}; + {error,{already_started,_}} -> % A control component already started. + {error,{inviso_control_already_running,CNode}}; + {error,Reason} -> + {error,Reason}; + {badrpc,Reason} -> + {error,{inviso_control_node_error,Reason}} + end. +%% ----------------------------------------------------------------------------- + +%% Help function starting the runtime components at all particapting nodes. +%% It also updates the nodes structure in the #ld to indicate which nodes where +%% successfully started. Returns a new #ld. +%% Note that a runtime component may actually be running at one or several nodes. +%% This is supposed to be the result of an (wanted) autostart. Meaning that the +%% inviso tool can not handle the situation if a runtime component is not doing +%% what it is supposed to do. In case a runtime component is already running it +%% will be adopted and therefore marked as running. +start_runtime_components(LD=#ld{c_node=undefined}) -> + start_runtime_components_2(local_runtime,undefined,LD); +start_runtime_components(LD=#ld{c_node=CNode,nodes=NodesD}) -> + start_runtime_components_2(get_all_nodenames_nodes(NodesD),CNode,LD). +start_runtime_components(Nodes,LD=#ld{c_node=CNode}) -> + start_runtime_components_2(Nodes,CNode,LD). + +start_runtime_components_2(local_runtime,CNode,LD=#ld{optg=OptG}) -> + Opts=start_runtime_components_mk_opts(local_runtime,OptG), + case inviso:add_node(mk_rt_tag(),Opts) of + {ok,NAnsw} -> % Should be more clever really! + NewNodesD=update_added_nodes(CNode,{ok,NAnsw},LD#ld.nodes), + LD#ld{nodes=NewNodesD}; + {error,_Reason} -> + LD + end; +start_runtime_components_2([Node|Rest],CNode,LD=#ld{optg=OptG}) -> + Opts=start_runtime_components_mk_opts(Node,OptG), + case rpc:call(CNode,inviso,add_nodes,[[Node],mk_rt_tag(),Opts]) of + {ok,NodeResults} -> + NewNodesD=update_added_nodes(CNode,NodeResults,LD#ld.nodes), + start_runtime_components_2(Rest,CNode,LD#ld{nodes=NewNodesD}); + {error,_Reason} -> + start_runtime_components_2(Rest,CNode,LD); + {badrpc,_Reason} -> + start_runtime_components_2(Rest,CNode,LD) + end; +start_runtime_components_2([],_,LD) -> + LD. + +start_runtime_components_mk_opts(Node,{M,F,Args}) -> + case catch apply(M,F,[Node|Args]) of + {ok,Opts} when is_list(Opts) -> + start_runtime_component_mk_opts_add_dependency(Opts); + _ -> + [?DEFAULT_DEPENDENCY] + end. + +%% The options generator is not supposed to generate the dependency. Hence this +%% function adds and if necessary removes an incorrectly added dependency tag. +start_runtime_component_mk_opts_add_dependency(Opts) -> + case lists:keysearch(dependency,1,Opts) of + {value,_} -> % Not allowed!!! + [?DEFAULT_DEPENDENCY|lists:keydelete(dependecy,1,Opts)]; + false -> + [?DEFAULT_DEPENDENCY|Opts] + end. +%% ----------------------------------------------------------------------------- + +%% Help function subscribing to inviso events from the inviso controller. This +%% will make it possible to follow runtime components going down. +start_subscribe_inviso_events(undefined) -> + inviso:subscribe(); +start_subscribe_inviso_events(CNode) -> + rpc:call(CNode,inviso,subscribe,[self()]). % Don't want the rpc-proc to subscribe! +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% gen_server handle call back functions. +%% ----------------------------------------------------------------------------- + +handle_call({stop,UntouchedNodes},_From,LD=#ld{nodes=NodesD,c_node=CNode,keep_nodes=KeepNodes}) + when is_list(UntouchedNodes) -> + {stop, + normal, + remove_all_trace_patterns(CNode, + UntouchedNodes++KeepNodes, + get_available_nodes(NodesD)), + LD}; +handle_call({stop,BadArg},_From,LD) -> + {reply,{error,{badarg,BadArg}},LD}; + +handle_call({reconnect_nodes,Nodes},_From,LD) -> + case h_reconnect_nodes(Nodes,LD) of + {ok,{Nodes2,NodesErr,NewLD}} -> + if + Nodes==local_runtime -> + {reply, + build_reconnect_nodes_reply(Nodes,Nodes2,NodesErr,NewLD#ld.nodes), + NewLD}; + is_list(Nodes) -> + {reply, + {ok,build_reconnect_nodes_reply(Nodes,Nodes2,NodesErr,NewLD#ld.nodes)}, + NewLD} + end; + {error,Reason} -> + {reply,{error,Reason},LD} + end; + +handle_call({start_session,MoreTDGargs},_From,LD=#ld{session_state=SState}) -> + case is_tracing(SState) of + false -> % No session running. + if + is_list(MoreTDGargs) -> + DateTime=calendar:universal_time(), + {M,F,Args}=LD#ld.tdg, + TDGargs=inviso_tool_lib:mk_tdg_args(DateTime,MoreTDGargs++Args), + case h_start_session(M,F,TDGargs,LD) of + {ok,{SessionNr,ReturnVal,NewLD}} -> % No nodes to initiate. + NewLD2=add_initial_tcs_to_history(NewLD#ld.initial_tcs, + NewLD#ld{chl=mk_chl(LD#ld.chl)}), + {reply, + {ok,{SessionNr,ReturnVal}}, + NewLD2#ld{session_state=tracing_sessionstate()}}; + {ok,{SessionNr,ReturnVal,Nodes2,NewLD}} -> + NewLD2=do_initial_tcs(NewLD#ld.initial_tcs, + Nodes2, + NewLD#ld{chl=mk_chl(LD#ld.chl)}), + {reply, + {ok,{SessionNr,ReturnVal}}, + NewLD2#ld{session_state=tracing_sessionstate()}}; + {error,Reason} -> + {reply,{error,Reason},LD} + end; + true -> % Faulty TDGargs. + {reply,{error,{badarg,MoreTDGargs}},LD} + end; + true -> + {reply,{error,session_already_started},LD} + end; + +handle_call({reinitiate_session,Nodes},_From,LD=#ld{session_state=SState}) -> + case is_tracing(SState) of + true -> % The tool must be tracing. + {M,F,_Args}=LD#ld.tdg, + TDGargs=get_latest_tdgargs_tracer_data(LD#ld.tracer_data), + case h_reinitiate_session(Nodes,M,F,TDGargs,LD) of + {ok,{NodesErr,ReturnVal,NewLD}} -> + {reply, + {ok,build_reinitiate_session_reply(Nodes,NodesErr,ReturnVal)}, + NewLD}; + {error,Reason} -> + {reply,{error,Reason},LD} + end; + false -> % Must have a running session! + {reply,{error,no_session},LD} + end; + +handle_call({restore_session,{FileName,MoreTDGargs}},_From,LD=#ld{chl=OldCHL}) + when is_list(MoreTDGargs) -> + case is_tracing(LD#ld.session_state) of + false -> + case catch make_absolute_path(FileName,LD#ld.dir) of + AbsFileName when is_list(AbsFileName) -> + case file:read_file(AbsFileName) of + {ok,Bin} -> + if + is_list(MoreTDGargs) -> + case catch replace_history_chl(OldCHL, + binary_to_term(Bin)) of + {ok,CHL} -> % The file was well formatted. + case h_restore_session(MoreTDGargs, + LD#ld{chl=CHL}) of + {ok,{SessionNr,ReturnVal,NewLD}} -> + {reply, + {ok,{SessionNr,ReturnVal}}, + NewLD#ld{session_state= + tracing_sessionstate()}}; + {error,Reason} -> + {reply,{error,Reason},LD} + end; + Error -> % Badly formatted file. + {reply, + {error,{bad_file,{AbsFileName,Error}}}, + LD} + end; + true -> + {reply,{error,{badarg,MoreTDGargs}},LD} + end; + {error,Reason} -> + {reply,{error,{read_file,Reason}},LD} + end; + Error -> + {reply,{error,{bad_filename,{FileName,Error}}},LD} + end; + true -> + {reply,{error,session_already_started},LD} + end; +%% This is doing restore session on the current history. +handle_call({restore_session,MoreTDGargs},_From,LD=#ld{chl=CHL}) -> + case is_tracing(LD#ld.session_state) of + false -> + case history_exists_chl(CHL) of + true -> % There is a history to redo. + if + is_list(MoreTDGargs) -> + case h_restore_session(MoreTDGargs,LD) of + {ok,{SessionNr,ReturnVal,NewLD}} -> + {reply, + {ok,{SessionNr,ReturnVal}}, + NewLD#ld{session_state=tracing_sessionstate()}}; + {error,Reason} -> + {reply,{error,Reason},LD} + end; + true -> + {reply,{error,{badarg,MoreTDGargs}},LD} + end; + false -> + {reply,{error,no_history},LD} + end; + true -> + {reply,{error,session_already_started},LD} + end; + +%% To stop tracing means stop_tracing through the inviso API. But we must also +%% remove any help processes executing inviso commands (trace case executers +%% and reactivators). +%% Note that to be really sure we should actually wait for EXIT-signals from those +%% processes before returning a successful returnvalue to the caller. In theory +%% those processes could issue an inviso call effecting a new trace session started +%% with init_tracing shortly after the call to stop_tracing. But too complicated! :-) +%% Further, stop-tracing is done on all nodes in our nodes structure. Regardless +%% if the node is tracing or not +handle_call(stop_session,_From,LD=#ld{session_state=SState,chl=CHL,reactivators=ReAct}) -> + case is_tracing(SState) of + true -> + NewCHL=stop_all_tc_executer_chl(CHL), % Stop any running trace case proc. + NewReAct=stop_all_reactivators(ReAct), % Stop any running reactivators. + case h_stop_session(LD) of + {ok,{SessionNr,Result}} -> + NewNodesD=set_inactive_nodes(Result,LD#ld.nodes), + {reply, + {ok,{SessionNr,Result}}, + LD#ld{session_state=passive_sessionstate(), + nodes=NewNodesD, + chl=NewCHL, + reactivators=NewReAct, + started_initial_tcs=[]}}; + {error,Reason} -> % Now we're really in deep shit :-) + {reply,{error,{unrecoverable,Reason}},LD} + end; + false -> + {reply,{error,no_session},LD} + end; + +handle_call({reset_nodes,Nodes},_From,LD=#ld{session_state=SState}) -> + case is_tracing(SState) of + false -> % We can not be in a session. + {reply,h_reset_nodes(Nodes,LD#ld.c_node),LD}; + true -> + {reply,{error,session_active},LD} + end; + +%% Calling a trace-case, or "turning it on". +handle_call({atc,{TC,Id,Vars}},_From,LD=#ld{session_state=SState}) -> + case is_tracing(SState) of % Check that we are tracing now. + true -> + case h_atc(TC,Id,Vars,LD) of + {ok,NewLD} -> % Trace case executed. + {reply,ok,NewLD}; + {error,Reason} -> + {reply,{error,Reason},LD} + end; + false -> % Can't activate if not tracing. + {reply,{error,no_session},LD} + end; + +handle_call({sync_atc,{TC,Id,Vars,TimeOut}},_From,LD=#ld{session_state=SState}) -> + case is_tracing(SState) of + true -> + if + is_integer(TimeOut);TimeOut==infinity -> + case h_sync_atc(TC,Id,Vars,TimeOut,LD) of + {ok,NewLD,Result} -> + {reply,Result,NewLD}; + {error,Reason} -> + {reply,{error,Reason},LD} + end; + true -> + {reply,{error,{badarg,TimeOut}},LD} + end; + false -> + {reply,{error,no_session},LD} + end; + +handle_call({sync_rtc,{TC,Vars,TimeOut}},_From,LD=#ld{session_state=SState}) -> + case is_tracing(SState) of + true -> + if + is_integer(TimeOut);TimeOut==infinity -> + case h_sync_rtc(TC,Vars,TimeOut,LD) of + {ok,NewLD,Result} -> + {reply,Result,NewLD}; + {error,Reason} -> + {reply,{error,Reason},LD} + end; + true -> + {reply,{error,{badarg,TimeOut}},LD} + end; + false -> + {reply,{error,no_session},LD} + end; + + +handle_call({dtc,{TC,Id}},_From,LD=#ld{session_state=SState}) -> + case is_tracing(SState) of % Check that we are tracing now. + true -> + case h_dtc(TC,Id,LD) of + {ok,NewLD} -> + {reply,ok,NewLD}; + {error,Reason} -> + {reply,{error,Reason},LD} + end; + false -> % Can't activate if not tracing. + {reply,{error,no_session},LD} + end; + +handle_call({sync_dtc,{TC,Id,TimeOut}},_From,LD=#ld{session_state=SState}) -> + case is_tracing(SState) of % Check that we are tracing now. + true -> + if + is_integer(TimeOut);TimeOut==infinity -> + case h_sync_dtc(TC,Id,TimeOut,LD) of + {ok,NewLD,Result} -> + {reply,Result,NewLD}; + {error,Reason} -> + {reply,{error,Reason},LD} + end; + true -> + {reply,{error,{badarg,TimeOut}},LD} + end; + false -> % Can't activate if not tracing. + {reply,{error,no_session},LD} + end; + +handle_call({inviso,{Cmd,Args}},_From,LD=#ld{session_state=SState}) -> + case is_tracing(SState) of + true -> + if + is_list(Args) -> + case h_inviso(Cmd,Args,LD) of + {ok,{Reply,NewLD}} -> + {reply,Reply,NewLD}; + {error,Reason} -> + {reply,{error,Reason},LD} + end; + true -> + {reply,{error,{badarg,Args}},LD} + end; + false -> % Can't do if not tracing. + {reply,{error,no_session},LD} + end; + +handle_call({reactivate,Node},_From,LD=#ld{nodes=NodesD,c_node=CNode}) -> + case get_state_nodes(Node,NodesD) of + {trace_failure,_} -> + {reply,{error,trace_failure},LD}; + {State,suspended} -> % The node is infact suspended. + case h_reactivate(Node,CNode) of + ok -> + case {State,is_tracing(LD#ld.session_state)} of + {tracing,true} -> % Only then shall we redo cmds. + {reply,ok,redo_cmd_history(Node,LD)}; + _ -> % All other just no longer suspended. + {reply,ok,LD#ld{nodes=set_running_nodes(Node,NodesD)}} + end; + {error,Reason} -> + {reply,{error,Reason},LD} + end; + reactivating -> + {reply,{error,reactivating},LD}; + {_,running} -> + {reply,{error,already_running},LD}; + down -> + {reply,{error,not_available},LD}; + false -> + {reply,{error,unknown_node},LD} + end; + +handle_call({save_history,FileName},_From,LD=#ld{chl=CHL,dir=Dir,history_dir=HDir}) -> + case lists:keysort(2,get_loglist_chl(CHL)) of + [] -> % Empty history or no history. + {reply,{error,no_history},LD}; + Log -> + case h_save_history(HDir,Dir,FileName,Log) of + {ok,AbsFileName} -> + {reply,{ok,AbsFileName},LD}; + {error,Reason} -> + {reply,{error,Reason},LD} + end + end; + +handle_call({get_autostart_data,{Nodes,Dependency}},_From,LD=#ld{chl=CHL}) -> + {ok,ASD} = build_autostart_data(lists:keysort(2,get_loglist_chl(CHL)),LD#ld.tc_dict), + TDGargs=get_latest_tdgargs_tracer_data(LD#ld.tracer_data), + {M,F,_}=LD#ld.tdg, + OptsG=LD#ld.optg, % Addnodes options generator. + {reply, + h_get_autostart_data(Nodes,LD#ld.c_node,Dependency,ASD,M,F,TDGargs,OptsG), + LD}; + +handle_call({get_autostart_data,Dependency},From,LD=#ld{c_node=undefined}) -> + handle_call({get_autostart_data,{local_runtime,Dependency}},From,LD); +handle_call({get_autostart_data,Dependency},From,LD=#ld{nodes=NodesD}) -> + Nodes=get_all_nodenames_nodes(NodesD), + handle_call({get_autostart_data,{local_runtime,{Nodes,Dependency}}},From,LD); + +handle_call(get_activities,_From,LD=#ld{chl=CHL,reactivators=Reactivators}) -> + TraceCases=get_ongoing_chl(CHL), + RNodes=get_all_nodes_reactivators(Reactivators), + ReturnList1= + if + TraceCases==[] -> + []; + true -> + [{tracecases,TraceCases}] + end, + ReturnList2= + if + RNodes==[] -> + ReturnList1; + true -> + [{reactivating_nodes,RNodes}|ReturnList1] + end, + {reply,{ok,ReturnList2},LD}; + +handle_call({get_node_status,Node},_Node,LD) -> + case get_state_nodes(Node,LD#ld.nodes) of + false -> + {reply,{error,unknown_node},LD}; + StateStatus -> + {reply,{ok,StateStatus},LD} + end; + +handle_call(get_session_data,_From,LD=#ld{session_state=SState,tracer_data=TD}) -> + case get_latest_session_nr_tracer_data(TD) of + undefined -> + {reply,{error,no_session},LD}; + SessionNr -> + TDGargs=get_latest_tdgargs_tracer_data(TD), + case is_tracing(SState) of + true -> + {reply,{ok,{tracing,SessionNr,TDGargs}},LD}; + false -> + {reply,{ok,{not_tracing,SessionNr,TDGargs}},LD} + end + end; + +handle_call(flush,_From,LD=#ld{c_node=CNode,nodes=NodesD}) -> + Nodes=get_tracing_nodes(NodesD), + {reply,h_flush(CNode,Nodes),LD}; +handle_call({flush,Nodes},_From,LD=#ld{c_node=CNode}) -> + {reply,h_flush(CNode,Nodes),LD}; + +handle_call(get_loopdata,_From,LD) -> + {reply,LD,LD}; + +%% Internal handle_call callbacks. + +handle_call({reactivator_reply,{Counter,RPid}},_From,LD=#ld{chl=CHL}) -> + HighestUsedCounter=get_highest_used_counter_chl(CHL), + if + HighestUsedCounter>Counter -> % There are now more log entries. + NewUnsortedLog=get_loglist_chl(CHL), + {reply,{more,NewUnsortedLog},LD}; + true -> % No Counter is youngest log entry. + NodesD=LD#ld.nodes, + Node=get_node_reactivators(RPid,LD#ld.reactivators), + {reply, + done, + LD#ld{nodes=set_running_nodes(Node,NodesD), + reactivators=del_reactivators(RPid,LD#ld.reactivators)}} + end. +%% ----------------------------------------------------------------------------- + +%% Handling a notification from a trace case execution process. Receiving this +%% indicated that this phase of the trace case is finnished. +handle_cast({tc_executer_reply,{Phase,ProcH,Result}},LD) -> + case Phase of + activating -> % The trace case is running now. + {ok,NewLD}=h_tc_activation_done(ProcH,Result,LD), + {noreply,NewLD}; + stopping -> + {ok,NewLD}=h_tc_stopping_done(ProcH,Result,LD), + {noreply,NewLD}; + _ -> + {noreply,LD} + end; +handle_cast(_,LD) -> + {noreply,LD}. +%% ----------------------------------------------------------------------------- + +%% This is the case when a runtime component goes down. We stop all running +%% reactivators for this node. Note that there can also be tracecases ongoing +%% where this node is part of the Nodes variable. But there is not much we can +%% do about that. Other then informing the user that it is unwise to reconnect +%% this node before those tracecases have stopped being ongoing. +handle_info({inviso_event,_CNode,_Time,{disconnected,Node,_}},LD) -> + {noreply,LD#ld{nodes=set_down_nodes(Node,LD#ld.nodes), + reactivators=stop_node_reactivators(Node,LD#ld.reactivators)}}; + +%% This is the case when a runtime component gets suspended. Much of the same +%% problem as described above applies. +handle_info({inviso_event,_CNode,_Time,{state_change,Node,{_,{suspended,_}}}},LD) -> + {noreply,LD#ld{nodes=set_suspended_nodes(Node,LD#ld.nodes), + reactivators=stop_node_reactivators(Node,LD#ld.reactivators)}}; + +handle_info(_,LD) -> + {noreply,LD}. +%% ----------------------------------------------------------------------------- + +%% Called when the tool server stops. First clause, termination is initiated by +%% our self and therefore controlled another way. In the second case we are +%% stopping for some external reason, and we must then do more here in terminate/2. +terminate(normal,#ld{c_node=CNode}) -> % This is when we are stopping our self. + stop_inviso_at_c_node(CNode); +terminate(_,#ld{c_node=CNode,nodes=NodesD,keep_nodes=KeepNodes}) -> + remove_all_trace_patterns(CNode,KeepNodes,get_all_nodenames_nodes(NodesD)), + stop_inviso_at_c_node(CNode). +%% ----------------------------------------------------------------------------- + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% ============================================================================= +%% Handler first level help functions. +%% ============================================================================= + +%% ----------------------------------------------------------------------------- +%% reconnect_nodes +%% ----------------------------------------------------------------------------- + +%% Help function reconnecting the nodes in Nodes. Listed nodes must be part of +%% the set of nodes handled by the tool. It is not possible to reconnect a node +%% that is not marked as down. This partly because we otherwise risk losing the +%% trace_failure state (which can not be rediscovered). +h_reconnect_nodes(local_runtime,LD=#ld{nodes=NodesD}) -> % Non-distributed. + case get_state_nodes(local_runtime,NodesD) of + down -> + {ok,{local_runtime,[],start_runtime_components(local_runtime,LD)}}; + _ -> % Allready connected! + {ok,{[],{error,already_connected},LD}} + end; +h_reconnect_nodes(Nodes,LD=#ld{nodes=NodesD}) when is_list(Nodes) -> + {Nodes2,NodesErr}= + lists:foldl(fun(N,{Nodes2,NodesErr})-> + case get_state_nodes(N,NodesD) of + down -> % Yes this node can be reconnected. + {[N|Nodes2],NodesErr}; + false -> % Not part of the node-set! + {Nodes2,[{N,{error,unknown_node}}|NodesErr]}; + _ -> % Allready connected! + {Nodes2,[{N,{error,already_connected}}|NodesErr]} + end + end, + {[],[]}, + Nodes), + LD2=start_runtime_components(Nodes2,LD), % Inpect the #ld.nodes for result. + {ok,{Nodes2,NodesErr,LD2}}; +h_reconnect_nodes(Nodes,_LD) -> + {error,{badarg,Nodes}}. +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% start_session +%% ----------------------------------------------------------------------------- + +%% Help function starting the tracing at all nodes. Note that the tracer data +%% is calculated using a user defined function. This is how for instance the +%% file names (of the log files) are determined. +%% Before the nodes are initiated their (possibly remaining) trace patterns are +%% cleared, both local and global. +h_start_session(M,F,TDGargs,LD=#ld{c_node=CNode,nodes=NodesD,tracer_data=TDs}) -> + case get_inactive_running_nodes(NodesD) of + [] -> % There are no nodes to initiate! + h_start_session_nonodes(TDGargs,LD,[]); + Nodes -> % List of nodes or 'local_runtime'. + case h_start_session_ctp_all(CNode,Nodes) of + {ok,Errors,[]} -> % Now no nodes to initiate! + h_start_session_nonodes(TDGargs,LD,Errors); + {ok,Errors,Nodes2} -> % Now these nodes are fresh. + case call_tracer_data_generator(CNode,M,F,TDGargs,Nodes2) of + {ok,TracerList} -> % Generated our tracerdata. + case h_start_session_2(CNode,TracerList,Errors) of + {ok,ReturnValue} -> % Some nodes are initialized now. + {NewNodesD,Nodes3}= + set_tracing_running_nodes(CNode,ReturnValue,NodesD), + {SessionNr,NewTDs}=insert_td_tracer_data(TDGargs,TDs), + {ok,{SessionNr, + ReturnValue, + Nodes3, % The nodes that shall get initial tracases. + LD#ld{nodes=NewNodesD,tracer_data=NewTDs}}}; + {error,Reason} -> + {error,Reason} + end; + {error,Reason} -> % Faulty tracer data generator func. + {error,{bad_tdg,Reason}} + end; + {error,Reason} -> % Error clearing patterns. + {error,Reason} + end + end. + +h_start_session_nonodes(TDGargs,LD=#ld{c_node=CNode,tracer_data=TDs},Errors) -> + {SessionNr,NewTDs}=insert_td_tracer_data(TDGargs,TDs), + if + CNode==undefined -> + {ok,{SessionNr,[],LD#ld{tracer_data=NewTDs}}}; + true -> + {ok,{SessionNr,{ok,Errors},LD#ld{tracer_data=NewTDs}}} + end. + +%% Help function clearing all trace patterns on all nodes. +h_start_session_ctp_all(CNode,Nodes) -> + case remove_all_trace_patterns(CNode,[],Nodes) of + ok -> % Non-distributed case1. + {ok,[],local_runtime}; + {error,Reason} -> % Non-distributed case2 and general failure. + {error,Reason}; + {ok,NodeResults} -> + h_start_session_ctp_all_2(NodeResults,[],[]) + end. + +h_start_session_ctp_all_2([{Node,{error,Reason}}|Rest],Errors,Nodes) -> + h_start_session_ctp_all_2(Rest,[{Node,{error,Reason}}|Errors],Nodes); +h_start_session_ctp_all_2([{Node,_OkOrPatternsUntouched}|Rest],Errors,Nodes) -> + h_start_session_ctp_all_2(Rest,Errors,[Node|Nodes]); +h_start_session_ctp_all_2([],Errors,Nodes) -> + {ok,Errors,Nodes}. + +%% Help function doing the actual init_tracing. +h_start_session_2(undefined,TracerData,_Errors) -> % Non distributed case. + case inviso:init_tracing(TracerData) of + {ok,LogResult} when is_list(LogResult) -> + {ok,{ok,LogResult}}; + {error,already_initated} -> % Perhaps adopted!? + {ok,{error,already_initiated}}; % Not necessarily wrong. + {error,Reason} -> + {error,Reason} + end; +h_start_session_2(CNode,TracerList,Errors) -> + case rpc:call(CNode,inviso,init_tracing,[TracerList]) of + {ok,NodeResults} -> + {ok,{ok,Errors++NodeResults}}; + {error,Reason} -> + {error,Reason}; + {badrpc,Reason} -> + {error,{inviso_control_node_error,Reason}} + end. +%% ----------------------------------------------------------------------------- + +%% Help function starting all initial trace cases. They are actually handled +%% the same way as user started trace cases. We actually only start initial +%% tracecases at Nodes (if Nodes is a list of nodes). This because we may have +%% adopted some nodes some already tracing nodes, and such are supposed to have +%% the correct patterns and flags set. +do_initial_tcs([{TC,Vars}|Rest],Nodes,LD) -> + Id=make_ref(), % Trace case ID. + case h_atc(TC,Id,Vars,LD,Nodes) of % Start using regular start methods. + {ok,NewLD} -> % Trace case was successfully started. + NewInitialTcs=add_initial_tcs(TC,Id,NewLD#ld.started_initial_tcs), + do_initial_tcs(Rest,Nodes,NewLD#ld{started_initial_tcs=NewInitialTcs}); + {error,_Reason} -> + do_initial_tcs(Rest,Nodes,LD) + end; +do_initial_tcs([_|Rest],Nodes,LD) -> + do_initial_tcs(Rest,Nodes,LD); +do_initial_tcs([],_Nodes,LD) -> + LD. +%% ----------------------------------------------------------------------------- + +%% This help functio is used instead of do_initial_tcs/3 if there actually are no +%% nodes to do the trace cases on. The reason we must have this function is that +%% the tracecases must still be entered into the history with bindings and all. +%% But we let them be marked as 'running' immediately (no need for the activator +%% process). +add_initial_tcs_to_history([{TC,Vars}|Rest],LD=#ld{tc_dict=TCdict,chl=CHL}) -> + case get_tracecase_tc_dict(TC,TCdict) of + {ok,TraceCase} -> + case check_bindings(Vars,TraceCase) of + {ok,Bindings} -> + Id=make_ref(), % Trace case ID. + FakeProcH=make_ref(), % Need something to enter as activator. + NewCHL=set_activating_chl(TC,Id,CHL,Bindings,FakeProcH), + NewCHL2=set_running_chl(FakeProcH,TC,Id,void,NewCHL), % Result=void. + NewInitialTcs=add_initial_tcs(TC,Id,LD#ld.started_initial_tcs), + add_initial_tcs_to_history(Rest,LD#ld{chl=NewCHL2, + started_initial_tcs=NewInitialTcs}); + {error,_Reason} -> % Not much we can do about that. + add_initial_tcs_to_history(Rest,LD) + end; + false -> + add_initial_tcs_to_history(Rest,LD) + end; +add_initial_tcs_to_history([],LD) -> + LD. +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% reinitiate_session +%% ----------------------------------------------------------------------------- + +%% Function doing the reinitiation. That means first do init_tracing at the nodes +%% in question. Then redo the command history to bring them up to speed. +%% But first the runtime component is cleared of all trace patterns. +h_reinitiate_session(Nodes,M,F,TDGargs,LD=#ld{c_node=CNode,nodes=NodesD}) -> + case h_reinitiate_session_2(Nodes,NodesD,CNode) of + {ok,{[],NodesErr}} -> % No nodes to reinitiate. + {ok,{NodesErr,{ok,[]},LD}}; + {ok,{Nodes2,NodesErr}} -> % List of nodes or local_runtime. + case call_tracer_data_generator(CNode,M,F,TDGargs,Nodes2) of + {ok,TracerList} -> + case h_start_session_2(CNode,TracerList,[]) of % Borrow from start_session. + {ok,ReturnValue} -> % Ok, now we must redo cmd history. + {NewNodesD,_Nodes}= + set_tracing_running_nodes(CNode,ReturnValue,NodesD), + NewLD=h_reinitiate_session_chl(Nodes2,LD#ld{nodes=NewNodesD}), + {ok,{NodesErr,ReturnValue,NewLD}}; + {error,Reason} -> + {error,Reason} + end; + {error,Reason} -> + {error,{bad_tdg,Reason}} + end; + {error,Reason} -> + {error,Reason} + end. + +%% Help function finding out which nodes in Nodes actually can be reinitiated. +%% A node must be up, inactive and not suspended in order for this to work. All the +%% rest is just a matter of how detailed error return values we want to generate. +h_reinitiate_session_2(local_runtime,NodesD,undefined) -> % Non distributed case. + case get_state_nodes(local_runtime,NodesD) of + {inactive,running} -> % Only ok case. + case inviso:ctp_all() of + ok -> + {ok,{local_runtime,[]}}; + {error,Reason} -> % This is strange. + {error,Reason} + end; + {_,suspended} -> + {ok,{[],{error,suspended}}}; + down -> + {ok,{[],{error,down}}}; + _ -> + {ok,{[],{error,already_in_session}}} + end; +h_reinitiate_session_2(Nodes,NodesD,CNode) when is_list(Nodes) -> + {ok,lists:foldl(fun(N,{Nodes2,NodesErr})-> + case get_state_nodes(N,NodesD) of + {inactive,running} -> % Only ok case. + case rpc:call(CNode,inviso,ctp_all,[[N]]) of + {ok,[{N,ok}]} -> + {[N|Nodes2],NodesErr}; + {ok,[{N,{error,Reason}}]} -> + {Nodes2,[{N,{error,Reason}}|NodesErr]}; + {error,Reason} -> + {Nodes2,[{N,{error,Reason}}|NodesErr]}; + {badrpc,Reason} -> + {Nodes2,[{N,{error,{badrpc,Reason}}}|NodesErr]} + end; + {_,suspended} -> + {Nodes2,[{N,{error,suspended}}|NodesErr]}; + down -> + {Nodes2,[{N,{error,down}}|NodesErr]}; + false -> + {Nodes2,[{N,{error,unknown_node}}|NodesErr]}; + _ -> + {Nodes2,[{N,{error,already_in_session}}|NodesErr]} + end + end, + {[],[]}, + Nodes)}; +h_reinitiate_session_2(Nodes,_NodesD,_CNode) -> + {error,{badarg7,Nodes}}. + +%% Help function redoing the command history log at all nodes that actually +%% started to trace. Note that we do not modify the return value which will be +%% given to the caller just because we decide not to redo commands. The user +%% must conclude him self from the inviso return value that commands were not +%% redone at a particular node. +h_reinitiate_session_chl(local_runtime,LD) -> + h_reinitiate_session_chl([local_runtime],LD); +h_reinitiate_session_chl([Node|Rest],LD=#ld{nodes=NodesD}) -> + case get_state_nodes(Node,NodesD) of + {tracing,running} -> % Only case when we shall redo! + h_reinitiate_session_chl(Rest,redo_cmd_history(Node,LD)); + _ -> % No redo of chl in other cases. + h_reinitiate_session_chl(Rest,LD) + end; +h_reinitiate_session_chl([],LD) -> + LD. +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% restore_session +%% ----------------------------------------------------------------------------- + +%% Help function starting a session (init tracing) and redoes the history +%% found in CHL. +h_restore_session(MoreTDGargs,LD) -> + DateTime=calendar:universal_time(), + {M,F,Args}=LD#ld.tdg, + TDGargs=inviso_tool_lib:mk_tdg_args(DateTime,MoreTDGargs++Args), + case h_start_session(M,F,TDGargs,LD) of + {ok,{SessionNr,ReturnVal,NewLD}} -> % There were no available nodes. + {ok,{SessionNr,ReturnVal,NewLD}}; + {ok,{SessionNr,ReturnVal,Nodes2,NewLD}} -> + NewLD2=h_reinitiate_session_chl(Nodes2,NewLD), + {ok,{SessionNr,ReturnVal,NewLD2}}; + {error,Reason} -> % Risk of out of control. + {error,Reason} + end. +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% stop_session +%% ----------------------------------------------------------------------------- + +%% Help function stopping tracing at tracing nodes. +h_stop_session(#ld{c_node=CNode,nodes=NodesD,tracer_data=TDs}) -> + case h_stop_session_2(CNode,NodesD) of + {ok,Result} -> + {ok,{get_latest_session_nr_tracer_data(TDs),Result}}; + {error,Reason} -> + {error,Reason} + end. + +h_stop_session_2(undefined,NodesD) -> % The non distributed case. + case get_tracing_nodes(NodesD) of + {up,{inactive,_}} -> % Already not tracing! + {ok,[]}; + {up,_} -> + case inviso:stop_tracing() of + {ok,_State} -> + {ok,[ok]}; + {error,no_response} -> + {ok,[]}; + {error,Reason} -> + {error,Reason} + end; + down -> + {ok,[]} + end; +h_stop_session_2(CNode,NodesD) -> + Nodes=get_tracing_nodes(NodesD), + case rpc:call(CNode,inviso,stop_tracing,[Nodes]) of + {ok,NodeResults} -> + {ok,lists:map(fun({N,{ok,_}})->{N,ok}; + (NodeError)->NodeError + end, + NodeResults)}; + {error,Reason} -> + {error,Reason}; + {badrpc,Reason} -> + {error,{inviso_control_node_error,Reason}} + end. +%% ----------------------------------------------------------------------------- + +%% Help function removing any trace flags, trace patterns and meta trace patterns +%% at Nodes. This will cause the nodes to become "fresh". +h_reset_nodes(local_runtime,_CNode) -> + inviso:clear([keep_log_files]); +h_reset_nodes(Nodes,CNode) -> + case inviso_tool_lib:inviso_cmd(CNode,clear,[Nodes,[keep_log_files]]) of + {ok,NodeResults} -> + {ok,NodeResults}; + {error,Reason} -> + {error,Reason} + end. +%% ----------------------------------------------------------------------------- + + +%% ----------------------------------------------------------------------------- +%% atc +%% ----------------------------------------------------------------------------- + +%% Function handling ativating a trace case. Trace cases that do not have a +%% particular on/off handling (but just on in some scense) are handled here too. +%% The trace case is entered into the Command History Log. +%% Note that the trace case can not be executed at this node but must be +%% executed where the inviso control component is. +%% Further it is possible to either activated the tracecase for all running and +%% tracing nodes, or just for a specified list of nodes. +%% TC=tracecase_name(), +%% Id=term(), identifiying this usage so we can turn it off later. +%% Vars=list(), list of variable-value bindnings. +h_atc(TC,Id,Vars,LD) -> + h_atc(TC,Id,Vars,LD,void). % For all running-tracing nodes. + +h_atc(TC,Id,Vars,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL},Nodes) -> + case find_id_chl(TC,Id,CHL) of + activating -> % Already started. + {error,activating}; + stopping -> % Not yet stopped. + {error,deactivating}; + false -> + case get_tracecase_tc_dict(TC,TCdict) of + {ok,TraceCase} -> % Such a trace case exists. + case check_bindings(Vars,TraceCase) of + {ok,Bindings} -> % Necessary vars exists in Vars. + if + is_list(Nodes) -> % Nodes predefined. + h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes); + true -> % Use all tracing and running nodes. + Nodes1=get_nodenames_running_nodes(LD#ld.nodes), + h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes1) + end; + {error,Reason} -> % Variable def missing. + {error,Reason} + end; + false -> + {error,unknown_tracecase} + end; + {ok,_Bindings} -> % Already activated and running. + {error,already_started} + end. + +h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes) -> + {ok,ProcH} = exec_trace_case_on(CNode,TraceCase,Bindings,Nodes), + %% Trace cases have no return values. + NewCHL=set_activating_chl(TC,Id,CHL,Bindings,ProcH), + {ok,LD#ld{chl=NewCHL}}. +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% sync_atc +%% ----------------------------------------------------------------------------- + +h_sync_atc(TC,Id,Vars,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) -> + case find_id_chl(TC,Id,CHL) of + activating -> % Already started. + {error,activating}; + stopping -> % Not yet stopped. + {error,deactivating}; + false -> + case get_tracecase_tc_dict(TC,TCdict) of + {ok,TraceCase} -> % Such a trace case exists. + case check_bindings(Vars,TraceCase) of + {ok,Bindings} -> % Necessary vars exists in Vars. + {ok,TcFName}=get_tc_activate_fname(TraceCase), + Nodes=get_nodenames_running_nodes(LD#ld.nodes), + Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings), + RpcNode=get_rpc_nodename(CNode), + case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of + {ok,Value} -> + FakeProcH=make_ref(), + NewCHL1=set_activating_chl(TC,Id,CHL,Bindings,FakeProcH), + NewCHL2=set_running_chl(FakeProcH,TC,Id,Value,NewCHL1), + {ok,LD#ld{chl=NewCHL2},Value}; + {error,Reason} -> + {error,{faulty_tracecase,{TcFName,Reason}}}; + {badrpc,Reason} -> + {error,{badrpc,Reason}} + end; + {error,Reason} -> % Variable def missing. + {error,Reason} + end; + false -> + {error,unknown_tracecase} + end; + {ok,_Bindings} -> % Already activated and running. + {error,already_started} + end. +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% rtc +%% ----------------------------------------------------------------------------- + +%% Function handling running a trace case without marking it as activated. It +%% is in the history mearly indicated as activated +h_sync_rtc(TC,Vars,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) -> + case get_tracecase_tc_dict(TC,TCdict) of + {ok,TraceCase} -> % Such a trace case exists. + case check_bindings(Vars,TraceCase) of + {ok,Bindings} -> % Necessary vars exists in Vars. + {ok,TcFName}=get_tc_activate_fname(TraceCase), + Nodes=get_nodenames_running_nodes(LD#ld.nodes), + Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings), + RpcNode=get_rpc_nodename(CNode), + case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of + {ok,Value} -> + {ok,LD#ld{chl=add_rtc_chl(TC,Bindings2,CHL)},Value}; + {error,Reason} -> + {error,{faulty_tracecase,{TcFName,Reason}}}; + {badrpc,Reason} -> + {error,{badrpc,Reason}} + end; + {error,Reason} -> % Variable def missing. + {error,Reason} + end; + false -> + {error,unknown_tracecase} + end. +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% dtc +%% ----------------------------------------------------------------------------- + +%% Function handling turning a trace case off. The trace case must be registered +%% as having an off mechanism. If it has an off mechanism and was previously entered +%% into the Command History Log and is done with its activation phase, it will be +%% executed and removed from the CHL. +h_dtc(TC,Id,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) -> + case find_id_chl(TC,Id,CHL) of + {ok,Bindings} -> % Yes, we have turned it on before. + case get_tracecase_tc_dict(TC,TCdict) of + {ok,TraceCase} -> + Nodes=get_nodenames_running_nodes(LD#ld.nodes), + case exec_trace_case_off(CNode,TraceCase,Bindings,Nodes) of + {ok,ProcH} -> + NewCHL=set_stopping_chl(TC,Id,CHL,ProcH), + {ok,LD#ld{chl=NewCHL}}; + {error,Reason} -> + {error,Reason} + end; + false -> % Strange, Id ok but no such trace case. + {error,unknown_tracecase} + end; + false -> % Not previously turned on. + {error,unknown_id}; + activating -> + {error,activating}; + stopping -> + {error,already_deactivating} + end. +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% sync_dtc +%% ----------------------------------------------------------------------------- + +h_sync_dtc(TC,Id,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) -> + case find_id_chl(TC,Id,CHL) of + {ok,Bindings} -> % Yes, we have turned it on before. + case get_tracecase_tc_dict(TC,TCdict) of + {ok,TraceCase} -> + case get_tc_deactivate_fname(TraceCase) of + {ok,TcFName} -> + Nodes=get_nodenames_running_nodes(LD#ld.nodes), + Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings), + RpcNode=get_rpc_nodename(CNode), + case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of + {ok,Value} -> + FakeProcH=make_ref(), + NewCHL1=set_stopping_chl(TC,Id,CHL,FakeProcH), + NewCHL2=nullify_chl(FakeProcH,TC,Id,NewCHL1), + {ok,LD#ld{chl=NewCHL2},Value}; + {error,Reason} -> % Script fault. + {error,{faulty_tracecase,{TcFName,Reason}}}; + {badrpc,Reason} -> + {error,{badrpc,Reason}} + end; + false -> + {error,no_deactivation} + end; + false -> % Strange, Id ok but no such trace case. + {error,unknown_tracecase} + end; + false -> % Not previously turned on. + {error,unknown_id}; + activating -> + {error,activating}; + stopping -> + {error,already_deactivating} + end. +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% inviso +%% ----------------------------------------------------------------------------- + +%% Function executing one inviso command. The returnvalue from the inviso +%% function call will be the return value to the client. The command is +%% entered into the history command log. +%% Note that the inviso call may have to be done at another node, dictated +%% by the c_node field. Further, if the module name is not an atom it is +%% most likely a regexp, which must be expanded at the regexp_node. Note +%% this is only relevant for tp and tpl. +h_inviso(Cmd,Args,LD=#ld{c_node=CNode,regexp_node=RegExpNode,chl=CHL}) -> + Arity=length(Args), + case check_proper_inviso_call(Cmd,Arity) of + {true,RegExpFlag} -> % Yes it is an inviso call. + Nodes=get_nodenames_running_nodes(LD#ld.nodes), + case h_inviso_2(Cmd,Args,CNode,RegExpNode,RegExpFlag,Nodes) of + {ok,Result} -> + case check_inviso_call_to_history(Cmd,Arity) of + true -> % This function shall be added to chl. + {ok,{Result,LD#ld{chl=add_inviso_call_chl(Cmd,Args,CHL)}}}; + false -> % Do not add it. + {ok,{Result,LD}} + end; + {error,Reason} -> + {error,Reason} + end; + false -> % Not an inviso function. + {error,invalid_function_name} + end. + +h_inviso_2(Cmd,Args,undefined,_,_,_) -> % A non distributed system. + case catch apply(inviso,Cmd,Args) of % Regexp expansion only relevant when + {'EXIT',Reason} -> % distributed, here let inviso_rt expand. + {error,{'EXIT',Reason}}; + Result -> + {ok,Result} + end; +h_inviso_2(Cmd,Args,CNode,RegExpNode,RegExpFlag,Nodes) -> + case expand_module_regexps(Args,RegExpNode,Nodes,RegExpFlag) of + {ok,NewArgs} -> + case catch inviso_tool_lib:inviso_cmd(CNode,Cmd,[Nodes|NewArgs]) of + {'EXIT',Reason} -> + {error,{'EXIT',Reason}}; + {error,{badrpc,Reason}} -> % Includes runtime failure. + {error,{badrpc,Reason}}; + Result -> + {ok,Result} + end; + {error,Reason} -> + {error,Reason} + end. +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% reactivate +%% ----------------------------------------------------------------------------- + +h_reactivate(_Node,undefined) -> % The non-distributed case. + case inviso:cancel_suspension() of + ok -> + ok; + {error,Reason} -> + {error,Reason} + end; +h_reactivate(Node,CNode) -> + case inviso_tool_lib:inviso_cmd(CNode,cancel_suspension,[[Node]]) of + {ok,[{Node,ok}]} -> + ok; + {ok,[{Node,{error,Reason}}]} -> + {error,Reason}; + {error,Reason} -> + {error,Reason} + end. +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% save_history +%% ----------------------------------------------------------------------------- + +h_save_history(HDir,Dir,FileName,SortedLog) -> + Dir0= + if + is_list(HDir) -> % There is a history dir specified. + HDir; % Use it then. + true -> + Dir % Else use the tool dir. + end, + case catch make_absolute_path(FileName,Dir0) of + AbsFileName when is_list(AbsFileName) -> + Log2=build_saved_history_data(SortedLog), % Remove stopped tracecases. + case file:write_file(AbsFileName,term_to_binary(Log2)) of + ok -> + {ok,AbsFileName}; + {error,Reason} -> + {error,{write_file,Reason}} + end; + {'EXIT',_Reason} -> + {error,{bad_filename,FileName}} + end. +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% get_autostart_data +%% ----------------------------------------------------------------------------- + +%% Help function building the structures used when exporting autostart information +%% from the tool. Note that we remove the tool-dependency and insert the one +%% specify in the get_autostart_data call. +h_get_autostart_data(local_runtime,_,Dependency,ASD,M,F,TDGargs,OptsG) -> + CompleteTDGargs=call_tracer_data_generator_mkargs(local_runtime,TDGargs), + Opts0=start_runtime_components_mk_opts(local_runtime,OptsG), + Opts=[Dependency|lists:keydelete(dependency,1,Opts0)], + {ok,{ASD,{ok,{Opts,{tdg,{M,F,CompleteTDGargs}}}}}}; + +h_get_autostart_data(Nodes,CNode,Dependency,ASD,M,F,TDGargs,OptsG) when is_list(Nodes) -> + {ok,{ASD,h_get_autostart_data_2(Nodes,CNode,Dependency,M,F,TDGargs,OptsG)}}; +h_get_autostart_data(Nodes,_CNode,_Dependency,_ASD,_M,_F,_TDGargs,_OptsG) -> + {error,{badarg,Nodes}}. + +h_get_autostart_data_2([Node|Rest],CNode,Dependency,M,F,TDGargs,OptsG) -> + CompleteTDGargs=call_tracer_data_generator_mkargs(Node,TDGargs), + Opts0=start_runtime_components_mk_opts(Node,OptsG), + Opts=[Dependency|lists:keydelete(dependency,1,Opts0)], + [{Node,{ok,{Opts,{tdg,{M,F,CompleteTDGargs}}}}}| + h_get_autostart_data_2(Rest,CNode,Dependency,M,F,TDGargs,OptsG)]; +h_get_autostart_data_2([],_CNode,_Dependency,_M,_F,_TDGargs,_OptsG) -> + []. +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% flush +%% ----------------------------------------------------------------------------- + +h_flush(undefined,_Nodes) -> + inviso:flush(); +h_flush(CNode,Nodes) -> + inviso_tool_lib:inviso_cmd(CNode,flush,[Nodes]). +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% tc_executer_reply +%% ----------------------------------------------------------------------------- + +%% Function handling that a trace case has completed its activation phase and +%% shall now be marked in the Command History Log as running. +h_tc_activation_done(ProcH,Result,LD=#ld{chl=CHL}) -> + case find_tc_executer_chl(ProcH,CHL) of + {activating,{TC,Id}} -> + case Result of + {ok,Value} -> % The trace case is successful activated. + {ok,LD#ld{chl=set_running_chl(ProcH,TC,Id,Value,CHL)}}; + {error,_} -> % Then pretend it never happend :-) + {ok,LD#ld{chl=del_tc_chl(ProcH,TC,Id,CHL)}} % Remove it. + end; + _ -> % Where did this come from? + {ok,LD} % Well just ignore it then. + end. +%% ----------------------------------------------------------------------------- + +%% Function handling that a trace case has completed its stopping phase and +%% shall now be nulled in the Command History Log (meaning that it will not +%% be repeated in the event of a reactivation). +h_tc_stopping_done(ProcH,Result,LD=#ld{chl=CHL}) -> + case find_tc_executer_chl(ProcH,CHL) of + {stopping,{TC,Id}} -> + case Result of + {ok,_Result} -> % _Result is returned from the tracecase. + {ok,LD#ld{chl=nullify_chl(ProcH,TC,Id,CHL)}}; + {error,_} -> % This is difficult, is it still active? + {ok,LD#ld{chl=nullify_chl(ProcH,TC,Id,CHL)}} + end; + _ -> % Strange. + {ok,LD} + end. +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% Terminate. +%% ----------------------------------------------------------------------------- + +%% Help function stopping the inviso control component. Does not return +%% anything significant. +stop_inviso_at_c_node(undefined) -> % Non distributed case. + inviso:stop(); +stop_inviso_at_c_node(CNode) -> + rpc:call(CNode,inviso,stop,[]). +%% ----------------------------------------------------------------------------- + +%% Help function that removes all trace patterns from the nodes that are not +%% marked as such were patterns shall be left after stopping of inviso. +%% Returns {ok,NodeResult} or {error,Reason}. In the non-distributed case +%% 'ok' is returned incase of success, ot 'patterns_untouched'. +remove_all_trace_patterns(undefined,KeepNodes,_Nodes) -> + case KeepNodes of + undefined -> % No, remove patterns from localruntime. + inviso:ctp_all(); + _ -> + patterns_untouched + end; +remove_all_trace_patterns(CNode,KeepNodes,Nodes) -> + Nodes2=lists:filter(fun(N)->not(lists:member(N,KeepNodes)) end,Nodes), + case inviso_tool_lib:inviso_cmd(CNode,ctp_all,[Nodes2]) of + {ok,NodeResults} -> + F=fun(N) -> + case lists:member(N,KeepNodes) of + true -> + {N,patterns_untouched}; + false -> + case lists:keysearch(N,1,NodeResults) of + {value,Result} -> + Result; % {Node,ok} + false -> % Extremely strange. + {N,{error,general_error}} + end + end + end, + {ok,lists:map(F,Nodes)}; + {error,{badrpc,Reason}} -> + {error,{inviso_control_node_error,Reason}}; + {error,Reason} -> + {error,Reason} + end. +%% ----------------------------------------------------------------------------- + +%% ============================================================================= +%% Second level help functions. +%% ============================================================================= + +%% Help function building a reply to a reconnection call based on which nodes +%% where asked to be reconnected and which of those are actually now working. +%% We actually make an effort to serve the return value in the same order as the +%% nodes were mentioned in the original call (Nodes). +build_reconnect_nodes_reply(local_runtime,local_runtime,_NodesErr,NodesD) -> + case get_state_nodes(local_runtime,NodesD) of + down -> + {error,down}; + {State,Status} -> + {ok,{State,Status}} + end; +build_reconnect_nodes_reply(local_runtime,_,NodesErr,_NodesD) -> + NodesErr; +build_reconnect_nodes_reply([Node|Rest],Nodes2,NodesErr,NodesD) -> + case lists:member(Node,Nodes2) of + true -> % Ok, look in the #ld.nodes. + case get_state_nodes(Node,NodesD) of + down -> % Somekind of failure, still down. + [{Node,{error,down}}| + build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)]; + {State,Status} -> % {State,Status} + [{Node,{ok,{State,Status}}}| + build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)] + end; + false -> % Error already from the beginning. + {value,{_,Error}}=lists:keysearch(Node,1,NodesErr), + [{Node,Error}|build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)] + end; +build_reconnect_nodes_reply([],_,_,_) -> + []. +%% ----------------------------------------------------------------------------- + +%% Help function building a return value to reinitiate_session. Nodes contains +%% all involved nodes. If the node occurrs in NodesErr, we choose the error in +%% NodesErr. Otherwise the returnvalue in ReturnVal is used. +build_reinitiate_session_reply(Nodes,NodesErr,{ok,NodesResults}) -> + {ok,build_reinitiate_session_reply_2(Nodes,NodesErr,NodesResults)}; +build_reinitiate_session_reply(local_runtime,[],NodeResult) -> + NodeResult; +build_reinitiate_session_reply(local_runtime,NodesErr,_NodeResult) -> + NodesErr. +build_reinitiate_session_reply_2([Node|Rest],NodesErr,NodeResults) -> + case lists:keysearch(Node,1,NodesErr) of + {value,{_,Error}} -> + [{Node,Error}|build_reinitiate_session_reply_2(Rest,NodesErr,NodeResults)]; + false -> + case lists:keysearch(Node,1,NodeResults) of + {value,Value} -> + [Value|build_reinitiate_session_reply_2(Rest,NodesErr,NodeResults)] + end + end; +build_reinitiate_session_reply_2([],_NodesErr,_NodeResults) -> + []. +%% ----------------------------------------------------------------------------- + +%% Help function returning a history log where stop and stopping entries have +%% been removed. Further all tracecase log entries must be set to running since +%% there can not be such a thing as an activating tracecase stored away in a +%% saved historyfile! +%% We must also take away any #Ref. +build_saved_history_data(SortedLog) -> + CleanedLog= + lists:filter(fun({_,_,Stop,_}) when Stop==stop;Stop==stopping -> false; + (_) -> true + end, + SortedLog), + lists:map(fun({{TC,Id},C,activating,B}) -> {{TC,Id},C,running,B}; + ({{TC,Id},C,S,B}) -> {{TC,Id},C,S,B}; + ({{M,F,Args,_Ref},C}) -> {{M,F,Args},C}; + ({{TC,_Ref},C,B}) -> {TC,C,B} % An rtc. + end, + CleanedLog). +%% ----------------------------------------------------------------------------- + +%% This help function builds the AutoStartData structure which is returned from +%% get_austostart_data. An AutoStartData structure is a list of trace-files and +%% inviso commands. The order is significant since it is the idea that doing +%% the trace case files and inviso commands in that order will bring a node to +%% a certain state in a trace perspective. +%% Returns {ok,AutoStartData} or {error,Reason} +build_autostart_data(SortedLog,TCdict) -> + build_autostart_data_2(SortedLog,TCdict,[]). + +build_autostart_data_2([{_,_C,Stop,_B}|Rest],TCdict,Accum) when Stop==stop;Stop==stopping-> + build_autostart_data_2(Rest,TCdict,Accum); % Simply skip deactivated/deativating. +build_autostart_data_2([{{TCname,_},_C,activating,Bindings}|Rest],TCdict,Accum) -> + build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum); +build_autostart_data_2([{{TCname,_},_C,running,Bindings}|Rest],TCdict,Accum) -> + build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum); +build_autostart_data_2([{{TCname,_Ref},_C,Bindings}|Rest],TCdict,Accum) -> + build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum); +build_autostart_data_2([{{M,F,Args,_Ref},_C}|Rest],TCdict,Accum) -> + build_autostart_data_2(Rest,TCdict,[{mfa,{M,F,Args}}|Accum]); +build_autostart_data_2([],_TCdict,Accum) -> + {ok,lists:reverse(Accum)}. + +%% Help function placing the filename in the AutoStartData structure. +build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum) -> + {ok,TC}=get_tracecase_tc_dict(TCname,TCdict), + {ok,FName}=get_tc_activate_fname(TC), + build_autostart_data_2(Rest,TCdict,[{file,{FName,Bindings}}|Accum]). +%% ----------------------------------------------------------------------------- + +%% Help function generating tracerdata to init inviso tracing. The generation +%% is done by the TracerDataGenerator, TDG, function. +%% Individual tracerdata is generated for each node in Nodes. +%% Returns {ok,TracerData} or {error,Reason}. +call_tracer_data_generator(undefined,M,F,TDGargs,_Nodes) -> % Non distributed. + case catch call_tracer_data_generator_3(M,F,TDGargs,local_runtime) of + {'EXIT',Reason} -> + {error,{'EXIT',Reason}}; + TracerData -> + {ok,TracerData} + end; +call_tracer_data_generator(_CNode,M,F,TDGargs,Nodes) -> + case catch call_tracer_data_generator_2(M,F,TDGargs,Nodes) of + {'EXIT',Reason} -> + {error,{'EXIT',Reason}}; + TracerList -> + {ok,TracerList} + end. + +call_tracer_data_generator_2(M,F,TDGargs,[Node|Rest]) -> + [{Node,call_tracer_data_generator_3(M,F,TDGargs,Node)}| + call_tracer_data_generator_2(M,F,TDGargs,Rest)]; +call_tracer_data_generator_2(_,_,_,[]) -> + []. + +call_tracer_data_generator_3(M,F,TDGargs,Node) -> + apply(M,F,call_tracer_data_generator_mkargs(Node,TDGargs)). + +%% This function creates the arguments that the tracer data generator function +%% accepts (in an apply call). The reason for making it a sepparate function is +%% that the arguments are constructed in more situations than just when actually +%% doing the apply. By having a function it will become obvious where to change +%% should the arguments change. +call_tracer_data_generator_mkargs(Node,TDGargs) -> + inviso_tool_lib:mk_complete_tdg_args(Node,TDGargs). +%% ----------------------------------------------------------------------------- + +%% This function acts as standard options generator function. That is returning +%% the options argument to inviso:add_node/3. Note that this function must not +%% return the dependency part of that option. +std_options_generator(_Node) -> + []. % No particular options(!) +%% ----------------------------------------------------------------------------- + + +%% Help function checking that Vars contains a binding for every variable +%% listed in the VarNames field in TraceCase. Note that the special variable 'Nodes' +%% is disregarded, since it is always added by the inviso_tool. +%% Returns {ok,Bindings} or {error,Reason}. Where Bindings is a bindngs structure +%% according to file:eval functionality. +check_bindings(Vars,TraceCase) -> + case catch check_bindings_2(Vars, + get_tc_varnames(TraceCase), + erl_eval:new_bindings()) of + {'EXIT',_Reason} -> + {error,variable_error}; + {error,Reason} -> % Missing a bindning. + {error,Reason}; + {ok,Bindings} -> + {ok,Bindings} + end. + +check_bindings_2(Vars,['Nodes'|Rest],Bindings) -> + check_bindings_2(Vars,Rest,Bindings); % Disregard Nodes since it is automatic. +check_bindings_2(Vars,[VarName|Rest],Bindings) -> + case lists:keysearch(VarName,1,Vars) of + {value,{_,Val}} -> + check_bindings_2(Vars,Rest,erl_eval:add_binding(VarName,Val,Bindings)); + false -> % Mandatory variable missing. + {error,{missing_variable,VarName}} % Quite here then. + end; +check_bindings_2(_,[],Bindings) -> + {ok,Bindings}. +%% ----------------------------------------------------------------------------- + +%% This help function checks that the command the user tries to do is amongst +%% the inviso API. It at the same time returns what kind of command it is. +%% {true,RegExpFlag} or 'false' where RegExpFlag indicates if this command +%% needs to have its argument modified by module regexp expansion or not. +check_proper_inviso_call(Cmd,Arity) -> + case lists:member({Cmd,Arity},?INVISO_CMDS) of + true -> % It is part of inviso API. + {true,check_proper_inviso_call_regexp(Cmd,Arity)}; + false -> + false + end. + +%% Returns {Type,Arity,PlaceOfModuleSpec} or 'false'. +check_proper_inviso_call_regexp(tp,5) -> {tp,5,1}; +check_proper_inviso_call_regexp(tp,4) -> {tp,4,1}; +check_proper_inviso_call_regexp(tp,1) -> {tp,1,1}; +check_proper_inviso_call_regexp(tpl,5) -> {tp,5,1}; +check_proper_inviso_call_regexp(tpl,4) -> {tp,4,1}; +check_proper_inviso_call_regexp(tpl,1) -> {tp,1,1}; +check_proper_inviso_call_regexp(ctp,3) -> {ctp,3,1}; +check_proper_inviso_call_regexp(ctp,1) -> {ctp,1,1}; +check_proper_inviso_call_regexp(ctpl,3) -> {ctp,3,1}; +check_proper_inviso_call_regexp(ctpl,1) -> {ctp,1,1}; +check_proper_inviso_call_regexp(_,_) -> % No regexp expansion. + false. +%% ----------------------------------------------------------------------------- + +%% Help function checking if this inviso command shall be added to the command +%% history log. Returns true or false. +check_inviso_call_to_history(Cmd,Arity) -> + case lists:member({Cmd,Arity},?INVISO_CMD_HISTORY) of + true -> + true; + false -> + false + end. +%% ----------------------------------------------------------------------------- + +%% Help function traversing the arguments and expanding module names stated +%% as regular expressions. This means that the resulting arguments may be longer +%% than the orginal ones. +%% When we run this function it has been determined that we are a distributed +%% system. +%% Also note that if there are no regexps in Args, no regexpansion will be +%% made and RegExpNode may be 'undefined' (as it is if not set at start-up). +%% If RegExpNode is unavailable the nodes found in Nodes will be used until +%% one that works is found. +expand_module_regexps(Args,_RegExpNode,_Nodes,false) -> + {ok,Args}; +expand_module_regexps([PatternList],RegExpNode,Nodes,{tp,1,1}) -> + case catch expand_module_regexps_tp(PatternList,RegExpNode,Nodes) of + NewPatternList when is_list(NewPatternList) -> + {ok,[NewPatternList]}; + {error,Reason} -> + {error,Reason} + end; +expand_module_regexps([PatternList],RegExpNode,Nodes,{ctp,1,1}) -> + case catch expand_module_regexps_ctp(PatternList,RegExpNode,Nodes) of + NewPatternList when is_list(NewPatternList) -> + {ok,[NewPatternList]}; + {error,Reason} -> + {error,Reason} + end; +expand_module_regexps([M,F,Arity,MS,Opts],RegExpNode,Nodes,{tp,5,1}) -> + expand_module_regexps([[{M,F,Arity,MS,Opts}]],RegExpNode,Nodes,{tp,1,1}); +expand_module_regexps([M,F,Arity,MS],RegExpNode,Nodes,{tp,4,1}) -> + expand_module_regexps([[{M,F,Arity,MS,[]}]],RegExpNode,Nodes,{tp,1,1}); +expand_module_regexps([M,F,Arity],RegExpNode,Nodes,{ctp,3,1}) -> + expand_module_regexps([[{M,F,Arity}]],RegExpNode,Nodes,{ctp,1,1}). + + +expand_module_regexps_tp([E={M,_,_,_,_}|Rest],RegExpNode,Nodes) when is_atom(M) -> + [E|expand_module_regexps_tp(Rest,RegExpNode,Nodes)]; +expand_module_regexps_tp([{M,F,Arity,MS,Opts}|Rest],RegExpNode,Nodes) when is_list(M);is_tuple(M) -> + case inviso_tool_lib:expand_module_names([RegExpNode], + M, + [{expand_only_at,RegExpNode}]) of + {singlenode_expansion,Modules} -> + expand_module_regexps_tp_2(Modules,F,Arity,MS,Opts,Rest,RegExpNode,Nodes); + {error,{faulty_node,RegExpNode}} -> % RegExpNode probably down. + case Nodes of + [NewRegExpNode|RestNodes] -> % Ok, just choose a node. + expand_module_regexps_tp([{M,F,Arity,MS,Opts}|Rest],NewRegExpNode,RestNodes); + [] -> % No more nodes to choose from. + throw({error,no_available_regexpnode}) + end; + {error,_Reason} -> + expand_module_regexps_tp(Rest,RegExpNode,Nodes) + end; +expand_module_regexps_tp([_|Rest],RegExpNode,Nodes) -> + expand_module_regexps_tp(Rest,RegExpNode,Nodes); % Skip faulty module specification. +expand_module_regexps_tp([],_RegExpNodes,_Nodes) -> + []. + +expand_module_regexps_tp_2([M|MRest],F,Arity,MS,Opts,Rest,RegExpNode,Nodes) -> + [{M,F,Arity,MS,Opts}| + expand_module_regexps_tp_2(MRest,F,Arity,MS,Opts,Rest,RegExpNode,Nodes)]; +expand_module_regexps_tp_2([],_,_,_,_,Rest,RegExpNode,Nodes) -> + expand_module_regexps_tp(Rest,RegExpNode,Nodes). + +expand_module_regexps_ctp([E={M,_,_}|Rest],RegExpNode,Nodes) when is_atom(M) -> + [E|expand_module_regexps_ctp(Rest,RegExpNode,Nodes)]; +expand_module_regexps_ctp([{M,F,Arity}|Rest],RegExpNode,Nodes) when is_list(M);is_tuple(M) -> + case inviso_tool_lib:expand_module_names([RegExpNode], + M, + [{expand_only_at,RegExpNode}]) of + {singlenode_expansion,Modules} -> + expand_module_regexps_ctp_2(Modules,F,Arity,Rest,RegExpNode,Nodes); + {error,_Reason} -> + expand_module_regexps_ctp(Rest,RegExpNode,Nodes) + end; +expand_module_regexps_ctp([_|Rest],RegExpNode,Nodes) -> + expand_module_regexps_tp(Rest,RegExpNode,Nodes); % Skip faulty module specification. +expand_module_regexps_ctp([],_RegExpNodes,_Nodes) -> + []. + +expand_module_regexps_ctp_2([M|MRest],F,Arity,Rest,RegExpNode,Nodes) -> + [{M,F,Arity}|expand_module_regexps_ctp_2(MRest,F,Arity,Rest,RegExpNode,Nodes)]; +expand_module_regexps_ctp_2([],_,_,Rest,RegExpNode,Nodes) -> + expand_module_regexps_ctp(Rest,RegExpNode,Nodes). +%% ----------------------------------------------------------------------------- + + + +%% Help function running the activation of a trace case. Note that this must +%% be done at the inviso control component's Erlang node *and* that it must be +%% done in its own process since there is no telling for how long a trace case +%% may run. +%% Returns {ok,ActivationHandler}. +exec_trace_case_on(CNode,TraceCase,Bindings,Nodes) -> + {ok,TcFName}=get_tc_activate_fname(TraceCase), + {ok,exec_trace_case_2(CNode, + TcFName, + erl_eval:add_binding('Nodes',Nodes,Bindings), + activating)}. + +%% Help function running the deactivation of a trace case. +exec_trace_case_off(CNode,TraceCase,Bindings,Nodes) -> + case get_tc_deactivate_fname(TraceCase) of + {ok,TcFName} -> % There is a deactivation. + {ok,exec_trace_case_2(CNode, + TcFName, + erl_eval:add_binding('Nodes',Nodes,Bindings), + stopping)}; + false -> + {error,no_deactivation} + end. + +exec_trace_case_2(CNode,TcFName,Bindings,Phase) -> + if + CNode==undefined -> % The non distributed case. + spawn_link(?MODULE,tc_executer,[TcFName,Bindings,Phase,self()]); + true -> + spawn_link(CNode,?MODULE,tc_executer,[TcFName,Bindings,Phase,self()]) + end. + +%% This function is run in its own process and is responsible for executing +%% the trace case. +tc_executer(TcFName,Bindings,Phase,Parent) -> + case catch file:script(TcFName,Bindings) of + {ok,Value} -> + tc_executer_reply(Parent,{Phase,self(),{ok,Value}}); + {'EXIT',Reason} -> + tc_executer_reply(Parent,{Phase,self(),{error,{'EXIT',Reason}}}); + Error -> + tc_executer_reply(Parent,{Phase,self(),Error}) + end. +%% ----------------------------------------------------------------------------- + +%% Help function which starts a reactivator process redoing command history at +%% Node. It also updates the loopdata to indicate that Node is now in state +%% reactivating. It is a good idea to only handle one node per reactivator process. +%% This because if the node terminates and comes back up, the reactivator must be +%% stopped. +redo_cmd_history(Node,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL,nodes=NodesD}) -> + P=start_reactivator(Node,CNode,TCdict,CHL), + LD#ld{nodes=set_reactivating_nodes(Node,NodesD), + reactivators=add_reactivators(Node,P,LD#ld.reactivators)}. + +%% Help function starting a reactivator process replaying the command history log. +%% Returns a pid of the reactivator process. +start_reactivator(Node,CNode,TCdict,CHL) -> + UnsortedLog=get_loglist_chl(CHL), % Must fetch here, later on wrong node. + if + CNode==undefined -> % The non-distributed case. + spawn_link(?MODULE, + reactivator_executer, + [Node,TCdict,UnsortedLog,self(),0,[]]); + true -> + spawn_link(CNode, + ?MODULE, + reactivator_executer, + [Node,TCdict,UnsortedLog,self(),0,[]]) + end. + +%% The strategy is to traverse the CHL ETS table in Counter order, redoing the +%% commands one by one. We wait until one command is finished until we do the +%% next. Commands marked as nullified are not performed. In fact when a command +%% is nullified only the stop will be found in the CHL. Its activation will be +%% removed. +reactivator_executer(Node,TCdict,UnsortedLog,TPid,StartCounter,DoneCases) -> + SortedLog=lists:keysort(2,UnsortedLog), % Sort on Counter, oldest first. + Log=reactivator_skip_log_entries(SortedLog,StartCounter), + case reactivator_executer_2(Node,TCdict,TPid,StartCounter,DoneCases,Log) of + done -> + true; % Simply terminate the reactivator then. + {more,{NewStartCounter,NewDoneCases,NewUnsortedLog}} -> + reactivator_executer(Node,TCdict,NewUnsortedLog,TPid,NewStartCounter,NewDoneCases) + end. + +reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases, + [{{TCname,Id},NextC,running,Bindings}|Rest]) -> + reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest); +reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases, + [{{TCname,_Ref},NextC,Bindings}|Rest]) -> + reactivator_executer_rtc(Node,TCdict,TPid,DoneCases,Rest,TCname,NextC,Bindings,Rest); +reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases, + [{{TCname,Id},NextC,activating,Bindings}|Rest]) -> + reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest); +reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases, + [{{M,F,Args,_Ref},NextC}|Rest]) -> + reactivator_executer_cmd(Node,M,F,Args), + reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest); +reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases, + [{{_TCname,_Id},NextC,stopping,_Bindings}|Rest]) -> + reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest); +reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases, + [{{TCname,Id,_Ref},NextC,stop,Bindings}|Rest]) -> + case lists:member({TCname,Id},DoneCases) of + true -> % We have activated it, must stop then. + case get_tracecase_tc_dict(TCname,TCdict) of + {ok,{_,_,_,_,FNameOff}} -> + reactivator_executer_tc(Node,Bindings,FNameOff), + NewDoneCases=lists:delete({TCname,Id},DoneCases), + reactivator_executer_2(Node,TCdict,TPid,NextC,NewDoneCases,Rest); + {ok,_} -> % No stop-filename, strange! + reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest); + false -> % Even stranger, does not exist!? + reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest) + end; + false -> % Never activated in the first place. + reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest) + end; +%% Done all log entries found this lap. See if there are more entries by now. +reactivator_executer_2(_Node,_TCdict,TPid,Counter,DoneCases,[]) -> + case reactivator_reply(TPid,Counter) of % Ask the tool process for more entries. + done -> % No more entries in the CHL. + done; + {more,NewUnsortedLog} -> % Repeat the procedure + {more,{Counter+1,DoneCases,NewUnsortedLog}} % with log entries from Counter+1. + end. + +%% This help function activates a tracecase. +reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest) -> + case get_tracecase_tc_dict(TCname,TCdict) of + {ok,{_,_,_,FNameOn}} -> % A case with just on functionality. + reactivator_executer_tc(Node,Bindings,FNameOn), + reactivator_executer_2(Node,TCdict,TPid,NextC,[{TCname,Id}|DoneCases],Rest); + {ok,{_,_,_,FNameOn,_}} -> + reactivator_executer_tc(Node,Bindings,FNameOn), + reactivator_executer_2(Node,TCdict,TPid,NextC,[{TCname,Id}|DoneCases],Rest); + false -> % Strange, does not exist anylonger!? + reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest) + end. + +%% Help function executing a trace case in the reactivators context. Does not +%% return anything significant. +reactivator_executer_tc(Node,Bindings,FileName) -> + catch file:eval(FileName,erl_eval:add_binding('Nodes',[Node],Bindings)). + +%% Help function handling trace case that are simply executed - rtc. +reactivator_executer_rtc(Node,TCdict,TPid,DoneCases,Rest,TCname,NextC,Bindings,Rest) -> + case get_tracecase_tc_dict(TCname,TCdict) of + {ok,{_,_,_,FNameOn}} -> % A case with just on functionality. + reactivator_executer_tc(Node,Bindings,FNameOn), + reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest); + {ok,{_,_,_,FNameOn,_}} -> + reactivator_executer_tc(Node,Bindings,FNameOn), + reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest); + false -> % Strange, does not exist anylonger!? + reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest) + end. + +reactivator_executer_cmd(nonode@nohost,M,F,Args) -> + catch apply(M,F,Args); % Non-distributed. +reactivator_executer_cmd(Node,M,F,Args) -> + catch apply(M,F,[[Node]|Args]). + +%% Help function returning a list of log entries missing the first entries +%% having a counter less or equal to C1. +reactivator_skip_log_entries([{_,C,_,_}|Rest],C1) when C<C1 -> + reactivator_skip_log_entries(Rest,C1); +reactivator_skip_log_entries([{_,C}|Rest],C1) when C<C1 -> + reactivator_skip_log_entries(Rest,C1); +reactivator_skip_log_entries(Log,_) -> + Log. +%% ----------------------------------------------------------------------------- + +%% Help function returning the node name to use in an rpc call. +get_rpc_nodename(undefined) -> + node(); +get_rpc_nodename(CNode) -> + CNode. +%% ----------------------------------------------------------------------------- + +mk_rt_tag() -> + inviso_tool. +%% ----------------------------------------------------------------------------- + +is_string([C|Rest]) when C>=32, C=<255 -> + is_string(Rest); +is_string([]) -> + true; +is_string(_) -> + false. +%% ----------------------------------------------------------------------------- + + +%% ----------------------------------------------------------------------------- +%% Functions for handling the configuration file. +%% ----------------------------------------------------------------------------- + +%% The inviso tool is configured via start arguments and/or a configuration file. +%% Start arguments will override any definitions in a configuration file. +%% The configuration file is pointed out by either a start argument or the +%% inviso application parameter 'inviso_tool_config_file'. + +%% Help function building the internal configuration structure. Configurations +%% in the start argument will override parameters found in a configuration file. +fetch_configuration(Config) -> + case fetch_config_filename(Config) of + {ok,FName} -> % We are supposed to use a conf-file. + case read_config_file(FName) of + {ok,LD} -> % Managed to open a file. + NewLD=read_config_list(LD,Config), + {ok,NewLD}; + Error = {error,_Reason} -> % Problem finding/opening file. + Error + end; + false -> % No filename specified. + LD=read_config_list(#ld{},Config), + {ok,LD} + end. + +%% Help function determining the name of the file which shall be consulted as +%% the main configuration file. +%% Returns {ok,FileName} or 'false'. The latter if no name could be determined. +fetch_config_filename(Config) -> + case catch lists:keysearch(config_file,1,Config) of + {value,{_,FName}} when is_list(FName) -> + {ok,FName}; + _ -> % No filename in the start argument. + fetch_config_filename_2() + end. + +fetch_config_filename_2() -> + case application:get_env(inviso_tool_config_file) of + {ok,FName} when is_list(FName) -> + {ok,FName}; + _ -> % Application parameter not specified. + false % Means no config file will be used. + end. + +%% Help function reading the configuration file. Returns a #conf or {error,Reason}. +read_config_file(FName) -> + case catch file:consult(FName) of + {ok,Terms} -> + {ok,read_config_list(#ld{},Terms)}; + {error,Reason} -> + {error,{file_consult,Reason}}; + {'EXIT',Reason} -> + {error,{failure,Reason}} + end. + +%% Help function traversing the Terms list entering known tag-values into #ld. +read_config_list(LD,Terms) -> + LD#ld{ + nodes = case mk_nodes(proplists:get_value(nodes,Terms,LD#ld.nodes)) of + {ok,Nodes} -> Nodes; + _ -> LD#ld.nodes + end, + c_node = proplists:get_value(c_node,Terms,LD#ld.c_node), % atom8) + regexp_node = proplists:get_value(regexp_node,Terms,LD#ld.regexp_node), % atom() + tc_def_file = proplists:get_value(tc_def_file,Terms,LD#ld.tc_def_file), + tdg = proplists:get_value(tdg,Terms,LD#ld.tdg), + debug = proplists:get_value(debug,Terms,LD#ld.debug), + initial_tcs = proplists:get_value(initial_tcs,Terms,LD#ld.initial_tcs), + dir = proplists:get_value(dir,Terms,LD#ld.dir), + optg = proplists:get_value(optg,Terms,LD#ld.optg) + }. + +%% ----------------------------------------------------------------------------- + + +%% Help function which, if it exists, consults the trace definition file. The +%% idea behind the trace definition file is to point out which trace cases there +%% are, where to find them and how to turn them on and off. +%% Trace case definitions are: +%% {TCname,Type,VariableNameList,ActivatioFileName} | +%% {TCname,Type,VariableNameList,ActivationFileName,DeactivationFileName} +%% TCname=atom() +%% Type=on | on_off +%% VariableNameList=[atom(),...] +%% ActivationFileName=DeactivationFileName=string() +read_trace_case_definitions(LD) -> + case LD#ld.tc_def_file of + TCfileName when is_list(TCfileName) -> + case catch file:consult(TCfileName) of + {ok,Terms} -> + Dir=LD#ld.dir, % The working directory of the tool. + TCdict=read_trace_case_definitions_2(Terms,Dir,mk_tc_dict()), + LD#ld{tc_dict=TCdict}; + _ -> + LD + end; + _ -> + LD + end. + +read_trace_case_definitions_2([{TCname,on,VarNames,FName}|Rest],Dir,TCdict) -> + FileName=make_absolute_path(FName,Dir), + read_trace_case_definitions_2(Rest, + Dir, + insert_tracecase_tc_dict(TCname, + on, + VarNames, + FileName, + TCdict)); +read_trace_case_definitions_2([{TCname,on_off,VarNames,FNameOn,FNameOff}|Rest],Dir,TCdict) -> + FileNameOn=make_absolute_path(FNameOn,Dir), + FileNameOff=make_absolute_path(FNameOff,Dir), + read_trace_case_definitions_2(Rest, + Dir, + insert_tracecase_tc_dict(TCname, + on_off, + VarNames, + FileNameOn, + FileNameOff, + TCdict)); +read_trace_case_definitions_2([_|Rest],Dir,TCdict) -> + read_trace_case_definitions_2(Rest,Dir,TCdict); +read_trace_case_definitions_2([],_Dir,TCdict) -> + TCdict. + +%% Help function returning an absolute path to FName if FName is not already +%% absolute. Dir is the working dir of the tool and supposed to be absolute. +make_absolute_path(FName,Dir) -> + case filename:pathtype(FName) of + absolute -> % Then do nothing, allready absolute. + FName; + _ -> + filename:join(Dir,FName) + end. +%% ----------------------------------------------------------------------------- + +get_status(undefined,_Node) -> + inviso:get_status(); +get_status(CNode,Nodes) -> + inviso_tool_lib:inviso_cmd(CNode,get_status,[Nodes]). +%% ----------------------------------------------------------------------------- + + +%% ============================================================================= +%% Internal data structure functions. +%% ============================================================================= + +%% ----------------------------------------------------------------------------- +%% The nodes database structure. +%% ----------------------------------------------------------------------------- + +%% The purpose of the nodes database structure is to keep track of what runtime +%% nodes we have, and their current status. +%% Implementation: +%% [{NodeName,AvailableStatus},...] or AvailableStatus in the +%% non-distributed case. +%% AvailableStatus={up,Status1} | down +%% Status1={State,Status} | reactivating +%% State=tracing | inactive | trace_failure +%% Status=running | suspended +%% reactivating=the node is now being brought up to date. +%% inactive=not tracing, can be initiated and then reactivated. +%% The following states can occure. +%% {inactive,running} +%% Mainly when we start the tool, before a session has been started. +%% {tracing,running} +%% When a trace session is on-going. +%% {trace_failure,running} +%% If init_tracing failed for some reason. +%% {tracing,suspended} +%% reactivating +%% The node is tracing (has always been) but was suspended. It is now +%% no longer suspended and the tool is redong commands. +%% {inactive,suspended} +%% We can end up here if a session is stopped with this node suspended. + +%% Returns a nodes database structure filled with the nodes Nodes. +mk_nodes(Nodes) when is_list(Nodes) -> + {ok,lists:map(fun(N) when is_atom(N)->{N,down} end,Nodes)}; +mk_nodes(local_runtime) -> % The non-distributed case. + down; +mk_nodes(_Nodes) -> + error. +%% ----------------------------------------------------------------------------- + +%% Updates the nodes database structure for each node that has been added. +%% This is the case when we start the tool or reactivate a node. Note that a node +%% may have become adopted instead of started. +%% Returns a new nodes database structure. +update_added_nodes(CNode,[{Node,NodeResult}|Rest],NodesD) -> + case update_added_nodes_3(NodeResult) of + already_added -> % Already added to the control component. + case get_status(CNode,[Node]) of % Examine if it is tracing or not. + {ok,[{Node,NodeResult2}]} -> + Result=mk_nodes_state_from_status(NodeResult2), + update_added_nodes_2(CNode,Node,Result,NodesD,Rest); + {error,_Reason} -> % Strange, mark it as down now. + update_added_nodes_2(CNode,Node,down,NodesD,Rest) + end; + Result -> + update_added_nodes_2(CNode,Node,Result,NodesD,Rest) + end; +update_added_nodes(_CNode,[],NodesD) -> + NodesD; +update_added_nodes(_CNode,NodeResult,_NodesD) -> % Non distributed case. + case update_added_nodes_3(NodeResult) of + already_added -> % Already added, most likely autostart. + mk_nodes_state_from_status(inviso:get_status()); + Result -> + Result % Simply replace NodesD. + end. + +update_added_nodes_2(CNode,Node,Result,NodesD,Rest) -> + case lists:keysearch(Node,1,NodesD) of + {value,_} -> % Node already exists, replace! + update_added_nodes(CNode,Rest,lists:keyreplace(Node,1,NodesD,{Node,Result})); + false -> % Strange, unknown node! + update_added_nodes(CNode,Rest,NodesD) + end. + +update_added_nodes_3({ok,{adopted,tracing,running,_Tag}}) -> + {up,{tracing,running}}; +update_added_nodes_3({ok,{adopted,tracing,{suspended,_SReason},_Tag}}) -> + {up,{tracing,suspended}}; +update_added_nodes_3({ok,{adopted,_,running,_Tag}}) -> + {up,{inactive,running}}; +update_added_nodes_3({ok,{adopted,_,{suspended,_SReason},_Tag}}) -> + {up,{inactive,suspended}}; +update_added_nodes_3({ok,new}) -> + {up,{inactive,running}}; +update_added_nodes_3({ok,already_added}) -> + already_added; % This is an error value! +update_added_nodes_3({error,_Reason}) -> + down. +%% ----------------------------------------------------------------------------- + +%% Function marking all nodes that, according to the returnvalue from init_tracing, +%% now are successfully initiated as tracing and running. Note that nodes that +%% does not fully respond 'ok' when init_tracing are marked as 'trace_failure'. +%% Also note that we assume that the nodes must be running to have made it this far. +%% A node can of course have become suspended in the process, but that node will +%% be marked as suspended later when that inviso event message arrives to the tool. +%% Returns {NewNodesD,Nodes} where Nodes are the nodes that actually got initiated +%% as a result of the init_tracing call (judged from the LogResults). +set_tracing_running_nodes(undefined,{ok,_LogResults},_AvailableStatus) -> % Non-distr. case. + {{up,{tracing,running}},local_runtime}; +set_tracing_running_nodes(undefined,{error,already_initiated},_) -> % Non-distributed case. + {mk_nodes_state_from_status(inviso:get_status()),[]}; % Ask it for its status. +set_tracing_running_nodes(undefined,{error,_Reason},_) -> % Non-distributed case. + {down,[]}; % This is questionable! +set_tracing_running_nodes(CNode,{ok,NodeResults},NodesD) -> + set_tracing_running_nodes_2(CNode,NodeResults,NodesD,[]). + +set_tracing_running_nodes_2(CNode,[{Node,{ok,_LogResults}}|Rest],NodesD,Nodes) -> + case lists:keysearch(Node,1,NodesD) of + {value,_} -> + NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,{up,{tracing,running}}}), + set_tracing_running_nodes_2(CNode,Rest,NewNodesD,[Node|Nodes]); + false -> % Strange. + set_tracing_running_nodes_2(CNode,Rest,NodesD,Nodes) + end; +set_tracing_running_nodes_2(CNode,[{Node,{error,already_initiated}}|Rest],NodesD,Nodes) -> + case get_status(CNode,[Node]) of % Then we must ask what it is doing now. + {ok,[{Node,NodeResult}]} -> + Result=mk_nodes_state_from_status(NodeResult), + NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,Result}), + set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes); + {error,_Reason} -> % Strange, mark it as down. + NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,down}), + set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes) + end; +set_tracing_running_nodes_2(CNode,[{Node,{error,_Reason}}|Rest],NodesD,Nodes) -> + NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,{up,{trace_failure,running}}}), + set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes); +set_tracing_running_nodes_2(_CNode,[],NodesD,Nodes) -> + {NodesD,Nodes}. % New NodesD and nodes successfully initiated. + +%% ----------------------------------------------------------------------------- + +%% Function updating Node in the NodesD structure and sets it to 'down'. +%% Returns a new nodes structure. +set_down_nodes(Node,[{Node,_}|Rest]) -> + [{Node,down}|Rest]; +set_down_nodes(Node,[NodeStruct|Rest]) -> + [NodeStruct|set_down_nodes(Node,Rest)]; +set_down_nodes(_,[]) -> + []; +set_down_nodes(_,_) -> % Non-distributed case. + down. % One can argue if this can happend. +%% ----------------------------------------------------------------------------- + +%% Function updating Node in NodesD to now be suspended. Note that if the node is +%% reactivating it must be moved to state tracing because that is what is doing. +set_suspended_nodes(Node,[{Node,{up,reactivating}}|Rest]) -> + [{Node,{up,{tracing,suspended}}}|Rest]; +set_suspended_nodes(Node,[{Node,{up,{State,_}}}|Rest]) -> + [{Node,{up,{State,suspended}}}|Rest]; +set_suspended_nodes(Node,[NodesData|Rest]) -> + [NodesData|set_suspended_nodes(Node,Rest)]; +set_suspended_nodes(_Node,[]) -> % Hmm, strange why did we end up here? + []; +set_suspended_nodes(_,{up,reactivating}) -> % Non-distributed case. + {up,{tracing,suspended}}; +set_suspended_nodes(_,{up,{State,_}}) -> + {up,{State,suspended}}. +%% ----------------------------------------------------------------------------- + +%% This function is called when reactivation is completed. Hence it moves the +%% node to no longer suspended. Note this can mean that the node is either +%% tracing or inactive. Reactivation is not allowed for a node have trace_failure. +set_running_nodes(Node,NodesD) when is_list(NodesD) -> + case lists:keysearch(Node,1,NodesD) of + {value,{_,AvailableStatus}} -> + lists:keyreplace(Node,1,NodesD,{Node,set_running_nodes_2(AvailableStatus)}); + false -> % Very strange! + NodesD + end; +set_running_nodes(_,NodesD) -> % The non-distributed case. + set_running_nodes_2(NodesD). + +set_running_nodes_2({up,reactivating}) -> + {up,{tracing,running}}; +set_running_nodes_2({up,{State,suspended}}) -> + {up,{State,running}}. +%% ----------------------------------------------------------------------------- + +%% Function marking node as now reactivating. That means it is not suspended +%% any longer (and tracing), but still not part of the set of nodes which shall +%% get all commands. Returns a new NodesD. +set_reactivating_nodes(Node,[{Node,_}|Rest]) -> + [{Node,{up,reactivating}}|Rest]; +set_reactivating_nodes(Node,[NodesData|Rest]) -> + [NodesData|set_reactivating_nodes(Node,Rest)]; +set_reactivating_nodes(_,[]) -> + []; +set_reactivating_nodes(_,{up,_}) -> % The non-distributed case. + {up,reactivating}. +%% ----------------------------------------------------------------------------- + +%% Function called when stop-tracing is done. That is all nodes in Nodes shall +%% be inactive now. Note that an inactive node can still be suspended. +%% Returns a new NodesD. +set_inactive_nodes(_,{up,reactivating}) -> % Non-distributed case. + {up,{inactive,running}}; +set_inactive_nodes(_,{up,{_,Status}}) -> % Tracing or trace_failure. + {up,{inactive,Status}}; +set_inactive_nodes(_,down) -> + down; +set_inactive_nodes([{Node,ok}|Rest],NodesD) -> + case lists:keysearch(Node,1,NodesD) of + {value,{_,{up,reactivating}}} -> + set_inactive_nodes(Rest,lists:keyreplace(Node,1,NodesD,{Node,{up,{inactive,running}}})); + {value,{_,{up,{_,Status}}}} -> % Tracing or trace_failure. + set_inactive_nodes(Rest,lists:keyreplace(Node,1,NodesD,{Node,{up,{inactive,Status}}})); + _ -> % This should not happend. + set_inactive_nodes(Rest,NodesD) + end; +set_inactive_nodes([{_Node,_Error}|Rest],NodesD) -> + set_inactive_nodes(Rest,NodesD); +set_inactive_nodes([],NodesD) -> + NodesD. +%% ----------------------------------------------------------------------------- + +%% Returns a list of all node names. Note that it can only be used in the +%% distributed case. +get_all_nodenames_nodes(NodesD) -> + lists:map(fun({Node,_})->Node end,NodesD). +%% ----------------------------------------------------------------------------- + +%% Returns a list of all nodes that are up, tracing and running (not suspended), +%% or 'void' in the non-distributed case. This is the list of nodes that shall get +%% inviso commands. +get_nodenames_running_nodes([{Node,{up,{tracing,running}}}|Rest]) -> + [Node|get_nodenames_running_nodes(Rest)]; +get_nodenames_running_nodes([{_Node,_}|Rest]) -> + get_nodenames_running_nodes(Rest); +get_nodenames_running_nodes([]) -> + []; +get_nodenames_running_nodes(_) -> + void. % When non distributed, N/A. +%% ----------------------------------------------------------------------------- + +%% Returns a list of nodes that can be made to initiate tracing. +get_inactive_running_nodes({up,{inactive,running}}) -> + local_runtime; +get_inactive_running_nodes(NonDistributed) when not(is_list(NonDistributed)) -> + []; +get_inactive_running_nodes([{Node,{up,{inactive,running}}}|Rest]) -> + [Node|get_inactive_running_nodes(Rest)]; +get_inactive_running_nodes([{_Node,_}|Rest]) -> + get_inactive_running_nodes(Rest); +get_inactive_running_nodes([]) -> + []. +%% ----------------------------------------------------------------------------- + +%% Returns a list of nodes that are currently tracing (not necessarily running). +%% In the non-distributed case the status of the runtime component will be +%% returned. +%% Note that nodes showing trace_failure will be included since we like to stop +%% tracing at those nodes too. +get_tracing_nodes([{Node,{up,{tracing,_}}}|Rest]) -> + [Node|get_tracing_nodes(Rest)]; +get_tracing_nodes([{Node,{up,{trace_failure,_}}}|Rest]) -> + [Node|get_tracing_nodes(Rest)]; +get_tracing_nodes([{Node,{up,reactivating}}|Rest]) -> + [Node|get_tracing_nodes(Rest)]; +get_tracing_nodes([_|Rest]) -> + get_tracing_nodes(Rest); +get_tracing_nodes([]) -> + []; +get_tracing_nodes(AvailableStatus) -> + AvailableStatus. +%% ----------------------------------------------------------------------------- + +%% Returns a list of all nodes that are currently up. +get_available_nodes(down) -> + undefined; +get_available_nodes([{_Node,down}|Rest]) -> + get_available_nodes(Rest); +get_available_nodes([{Node,_}|Rest]) -> + [Node|get_available_nodes(Rest)]; +get_available_nodes([]) -> + []. +%% ----------------------------------------------------------------------------- + +%% Function returning the "state" of Node. Mainly used to check if the node is +%% suspended or not. +%% Returns {State,Status} | reactivating | down +%% where +get_state_nodes(Node,NodesD) when is_list(NodesD) -> + case lists:keysearch(Node,1,NodesD) of + {value,{_,AvailableStatus}} -> + get_state_nodes_2(AvailableStatus); + false -> + false + end; +get_state_nodes(_,NodesD) -> % Non distributed case. + get_state_nodes_2(NodesD). + +get_state_nodes_2({up,{trace_failure,Status}}) -> + {trace_failure,Status}; +get_state_nodes_2({up,{State,suspended}}) -> % {tracing|inactive,suspended} + {State,suspended}; +get_state_nodes_2({up,reactivating}) -> + reactivating; +get_state_nodes_2({up,{State,running}}) -> + {State,running}; +get_state_nodes_2(down) -> + down. +%% ----------------------------------------------------------------------------- + +%% Help function in the case we need to consult the state/status of a runtime +%% component. Returns a nodesD value that can be added to the nodes database. +mk_nodes_state_from_status({ok,{tracing,running}}) -> + {up,{tracing,running}}; +mk_nodes_state_from_status({ok,{tracing,{suspended,_SReason}}}) -> + {up,{tracing,suspended}}; +mk_nodes_state_from_status({ok,{_,running}}) -> + {up,{inactive,running}}; +mk_nodes_state_from_status({ok,{_,{suspended,_SReason}}}) -> + {up,{inactive,suspended}}; +mk_nodes_state_from_status({error,_Reason}) -> + down. +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% The session_state. +%% ----------------------------------------------------------------------------- + +%% The session state reflects if the inviso_tool is tracing or not. +%% This means that if the tool is tracing a reconnected node can be made to +%% restart_session. + +%% Returns the correct value indicating that we are tracing now. +tracing_sessionstate() -> + tracing. +%% ----------------------------------------------------------------------------- + +%% Returns true or false depending on if we are tracing now or not. +is_tracing(tracing) -> + true; +is_tracing(_) -> + false. +%% ----------------------------------------------------------------------------- + +%% Returns the correct value indicating that the tool is not tracing. +passive_sessionstate() -> + idle. +%% ----------------------------------------------------------------------------- + +%% ----------------------------------------------------------------------------- +%% The tracer_data datastructure. +%% ----------------------------------------------------------------------------- + +%% The tracer_data structure collects the tracer data arguments used to init tracing +%% by this inviso tool. The args are saved per session. Each session has +%% a number. +%% Implementation: +%% Sessions=[{SessionNr,TDGargs},...] +%% SessionNr=integer() +%% TDGargs=list(), args given to the tracer data generator +%% minus the first argument which is the Node name. + +%% Function taking tracerdata args structure inserting yet another session. +%% Returns {SessionNr,NewTDs}. +insert_td_tracer_data(TDGargs,TDs=[{SNr,_}|_]) -> + {SNr+1,[{SNr+1,TDGargs}|TDs]}; +insert_td_tracer_data(TDGargs,undefined) -> + {1,[{1,TDGargs}]}. +%% ----------------------------------------------------------------------------- + +%% Returns the latest session nr. +get_latest_session_nr_tracer_data(undefined) -> + undefined; +get_latest_session_nr_tracer_data([{SessionNr,_}|_]) -> + SessionNr. +%% ----------------------------------------------------------------------------- + +%% Returns the tracer data arguments used when creating the trace data for the +%% latest session. +get_latest_tdgargs_tracer_data(undefined) -> + undefined; +get_latest_tdgargs_tracer_data([{_,TDGargs}|_]) -> + TDGargs. +%% ----------------------------------------------------------------------------- + + +%% ----------------------------------------------------------------------------- +%% The tc_dict or trace case dictionary datastructure. +%% ----------------------------------------------------------------------------- + +%% The tc_dict stores information about all available trace cases. +%% Implementation: +%% [{TCname,Type,VarNames,FNameOn [,FNameOff]},...] +%% TCname=atom() +%% Type=on | on_off +%% VarNames=[atom(),...] +%% FNameOn=FNameOff=string() + +%% Returns the empty trace case dictionary. +mk_tc_dict() -> + []. +%% ----------------------------------------------------------------------------- + +%% Function inserting a new trace case into the trace case dictionary. +insert_tracecase_tc_dict(TCname,on,VarNames,FNameOn,TCdict) -> + [{TCname,on,VarNames,FNameOn}|TCdict]. +insert_tracecase_tc_dict(TCname,on_off,VarNames,FNameOn,FNameOff,TCdict) -> + [{TCname,on_off,VarNames,FNameOn,FNameOff}|TCdict]. +%% ----------------------------------------------------------------------------- + +%% Function finding a trace case definition in the tc_dict structure. +%% Returns {ok,{TCname,Type,VarNAmes,FNameOn [,FNameOff]}} or 'false'. +get_tracecase_tc_dict(TCname,[Tuple|_]) when element(1,Tuple)==TCname -> + {ok,Tuple}; +get_tracecase_tc_dict(TCname,[_|Rest]) -> + get_tracecase_tc_dict(TCname,Rest); +get_tracecase_tc_dict(_,[]) -> + false; +get_tracecase_tc_dict(_,_) -> % There are no trace cases! + false. +%% ----------------------------------------------------------------------------- + +%% Function working on the trace case definition returned by get_tracecase_tc_dict/2 +%% function. +%% Returning {ok,ActivationFileName}. +get_tc_activate_fname({_TCname,_Type,_VarNames,FNameOn}) -> + {ok,FNameOn}; +get_tc_activate_fname({_TCname,_Type,_VarNames,FNameOn,_FNameOff}) -> + {ok,FNameOn}. + +get_tc_deactivate_fname({_TCname,_Type,_VarNames,_FNameOn,FNameOff}) -> + {ok,FNameOff}; +get_tc_deactivate_fname(_) -> % Not a case with off function. + false. + +get_tc_varnames({_TCname,_Type,VarNames,_FNameOn}) -> + VarNames; +get_tc_varnames({_TCname,_Type,VarNames,_FNameOn,_FNameOff}) -> + VarNames. + +%% ----------------------------------------------------------------------------- + + +%% The Command History Log (CHL) stores commands to make it possible to +%% reactivate suspended nodes, reconnect restarted nodes, and to make +%% autostart files. +%% Each time tracing is initiated (that is started) the CHL is cleared since +%% it would not make scense to repeat commands from an earlier tracing at +%% reactivation for instance. + +%% Implementation: {NextCounter,OnGoingList,ETStable} +%% NextCounter=integer(), next command number - to be able to sort them in order. +%% OnGoingList=[{ProcH,{TCname,ID}},...] +%% ID=term(), instance id for this execution of this trace case. +%% ETStable=tid() -> {{TCname,Id},Counter,State1,Bindings} +%% ETStable=tid() -> {{TCname,Id},Counter,running,Bindings,Result} | +%% {{TCname,Id,#Ref},Counter,stop,Bindings} | +%% {{TCname,#Ref},Counter,Bindings} % An rtc +%% {{M,F,Args,#Ref},Counter} +%% Counter=integer(), the order-counter for this logged entry. +%% State1=activating | stopping +%% Where: +%% activating: the activation file for the tracecase is running. +%% running : activation is completed. +%% stopping : set on the previously running ETS entry when deactivation +%% file is currently executing. +%% stop : entered with own Counter into the ETS table when +%% deactivation file is executing. Remains after too. +%% Result=term(), the result returned from the tr-case or inviso call. + + +%% Returning an initial empty CHL. +mk_chl(undefined) -> + {1,[],ets:new(inviso_tool_chl,[set,protected])}; +mk_chl({_,_,TId}) -> + ets:delete(TId), + mk_chl(undefined). + +%% Help function returning 'true' if there is a current history. +history_exists_chl(undefined) -> + false; +history_exists_chl({_,_,_}) -> + true. + +%% Function looking up the state of this trace case. +find_id_chl(TCname,Id,{_NextCounter,_OnGoingList,TId}) -> + case ets:lookup(TId,{TCname,Id}) of + [{_,_,running,Bindings,_Result}] -> % The trace case is tracing. + {ok,Bindings}; + [{_,_,State,_}] -> % activating or stopping. + State; + [] -> + false + end. + +%% Function finding the Trace case associated with a process handle +%% doing this trace case's activation or stopping. +find_tc_executer_chl(ProcH,{_,OnGoingList,TId}) -> + case lists:keysearch(ProcH,1,OnGoingList) of + {value,{_,{TCname,Id}}} -> + [{_,_,State,_}]=ets:lookup(TId,{TCname,Id}), + {State,{TCname,Id}}; % Should be activating or stopping. + false -> + false + end. + +%% Adds a Trace case to the CHL. This is done when it is turned on. Or when it +%% is called for trace cases that do not have on/off functionality. +set_activating_chl(TCname,Id,{Counter,OnGoingList,TId},Bindings,ProcH) -> + ets:insert(TId,{{TCname,Id},Counter,activating,Bindings}), + {Counter+1,[{ProcH,{TCname,Id}}|OnGoingList],TId}. + +%% Function marking a trace case as now running. That is the activation +%% phase is completed. It is normaly completed when the process executing +%% the trace case signals that it is done. +set_running_chl(ProcH,TCname,Id,Result,{NextCounter,OnGoingList,TId}) -> + [{_,Counter,_,Bindings}]=ets:lookup(TId,{TCname,Id}), + ets:insert(TId,{{TCname,Id},Counter,running,Bindings,Result}), + NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList), + {NextCounter,NewOnGoingList,TId}. + +%% Function marking trace case TCname with identifier Id as now in its stopping +%% state. Where ProcH is the handler to the process running the stopping +%% trace case. +set_stopping_chl(TCname,Id,{NextCounter,OnGoingList,TId},ProcH)-> + [{_,Counter,_,Bindings,_}]=ets:lookup(TId,{TCname,Id}), + ets:insert(TId,{{TCname,Id},Counter,stopping,Bindings}), + ets:insert(TId,{{TCname,Id,make_ref()},NextCounter,stop,Bindings}), + {NextCounter+1,[{ProcH,{TCname,Id}}|OnGoingList],TId}. + +%% Function removing a TCname-Id from the CHL. This is mostly used +%% if activating the trace case failed for some reason. We do not then +%% expect the user to stop the trace case. Hence it must be removed now. +%% A reactivation process may have noticed the activating-entry and started +%% to activate it. But since the general state reached after an unsuccessful +%% activation can not easily be determined, we don't try to do much about it. +del_tc_chl(ProcH,TCname,Id,{NextCounter,OnGoingList,TId}) -> + ets:delete(TId,{TCname,Id}), + NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList), + {NextCounter,NewOnGoingList,TId}. + +%% Function removing the entry TCname+Id from the CHL. This makes it +%% possible to activate a tracecase with this id again. The entry was +%% previously marked as stopping. +nullify_chl(ProcH,TCname,Id,{NextCounter,OnGoingList,TId}) -> + ets:delete(TId,{TCname,Id}), + NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList), + {NextCounter+1,NewOnGoingList,TId}. + +%% Function stopping all processes saved as being now running tc executers. +%% This is useful as cleanup during stop tracing for instance. +%% Returns a new CHL which is not in all parts correct. Entries in the +%% ETS table are for instance not properly state-changed. But the CHL will +%% from now on only be used to create command files and similar. +stop_all_tc_executer_chl({NextCounter,[{ProcH,_}|Rest],TId}) -> + exit(ProcH,kill), + stop_all_tc_executer_chl({NextCounter,Rest,TId}); +stop_all_tc_executer_chl({NextCounter,[],TId}) -> + {NextCounter,[],TId}. + +%% Function adding a "plain" inviso call to the CHL. +add_inviso_call_chl(Cmd,Args,{NextCounter,OnGoingList,TId}) -> + ets:insert(TId,{{inviso,Cmd,Args,make_ref()},NextCounter}), + {NextCounter+1,OnGoingList,TId}. + +%% Function adding a run trace case entry to the chl. +add_rtc_chl(TCname,Bindings,{NextCounter,OnGoingList,TId}) -> + ets:insert(TId,{{TCname,make_ref()},NextCounter,Bindings}), + {NextCounter+1,OnGoingList,TId}. +%% Returns the highest used counter number in the command history log. +get_highest_used_counter_chl({NextCounter,_,_}) -> + NextCounter-1. + +%% Help function returning a list of {{TCname,Id},Phase} for all ongoing +%% assynchronous tracecases. +get_ongoing_chl(undefined) -> + []; +get_ongoing_chl({_,OngoingList,TId}) -> + get_ongoing_chl_2(OngoingList,TId). + +get_ongoing_chl_2([{_ProcH,{TCname,Id}}|Rest],TId) -> + case ets:lookup(TId,{TCname,Id}) of + [{_,_C,activating,_B}] -> + [{{TCname,Id},activating}|get_ongoing_chl_2(Rest,TId)]; + [{_,_C,stopping,_B}] -> + [{{TCname,Id},deactivating}|get_ongoing_chl_2(Rest,TId)] + end; +get_ongoing_chl_2([],_) -> + []. + +%% Function returning a list of log entries. Note that the list is unsorted +%% in respect to Counter. +get_loglist_chl({_,_,TId}) -> + L=ets:tab2list(TId), + lists:map(fun({{TC,Id},C,S,B,_Result}) -> {{TC,Id},C,S,B}; % running + (Tuple={{_TC,_Id},_C,_S,_B}) -> Tuple; % activating | stopping + (Tuple={{_TC,_Id,_Ref},_C,_S,_B}) -> Tuple; % stop + (Tuple={{_M,_F,_Args,_Ref},_C}) -> Tuple; + (Tuple={{_TC,_Ref},_C,_B}) -> Tuple + end, + L); +get_loglist_chl(_) -> % The history is not initiated, ever! + []. + +%% Function returning a list of log entries, but only those which are not +%% cancelled out by deactivations. +% get_loglist_active_chl({_,_,TId}) -> +% L=ets:tab2list(TId), +% lists:zf(fun({{TC,Id},C,S,B,_Result}) -> {true,{{TC,Id},C,S,B}}; % running +% (Tuple={{_TC,_Id},_C,_S,_B}) -> Tuple; % activating | stopping +% (Tuple={{_TC,_Id,_Ref},_C,_S,_B}) -> Tuple; % stop +% (Tuple={{_M,_F,_Args,_Ref},_C}) -> Tuple +% end, +% L); +% get_loglist_chl(_) -> % The history is not initiated, ever! +% []. + + +%% This helpfunction recreates a history from a saved history list. This function +%% is supposed to crash if the log is not well formatted. Note that we must restore +%% the counter in order for the counter to work if new commands are added to the +%% history. +replace_history_chl(OldCHL,SortedLog) -> + {_,Ongoing,TId}=mk_chl(OldCHL), + {NewTId,Counter}=replace_history_chl_2(TId,SortedLog,0), + {ok,{Counter+1,Ongoing,NewTId}}. + +replace_history_chl_2(TId,[{{TC,Id},C,running,B}|Rest],_Counter) -> + ets:insert(TId,{{TC,Id},C,running,B,undefined}), + replace_history_chl_2(TId,Rest,C); +replace_history_chl_2(TId,[{{M,F,Args},C}|Rest],_Counter) -> + ets:insert(TId,{{M,F,Args,make_ref()},C}), + replace_history_chl_2(TId,Rest,C); +replace_history_chl_2(TId,[{TC,C,B}|Rest],_Counter) -> + ets:insert(TId,{{TC,make_ref()},C,B}), + replace_history_chl_2(TId,Rest,C); +replace_history_chl_2(TId,[],Counter) -> + {TId,Counter}. +%% ----------------------------------------------------------------------------- + + +%% ----------------------------------------------------------------------------- +%% Reactivators data structure. +%% ----------------------------------------------------------------------------- + +%% Function adding a new node-reactivatorpid pair to the reactivators structure. +%% In this way we know which reactivators to remove if Node terminates, or when +%% a node is fully updated when a reactivator is done. +add_reactivators(Node,Pid,Reactivators) -> + [{Node,Pid}|Reactivators]. + +%% Function removing a reactivator entry from the reactivators structure. +del_reactivators(RPid,[{_Node,RPid}|Rest]) -> + Rest; +del_reactivators(RPid,[Element|Rest]) -> + [Element|del_reactivators(RPid,Rest)]; +del_reactivators(_,[]) -> % This should not happend. + []. + +get_node_reactivators(RPid,Reactivators) -> + case lists:keysearch(RPid,2,Reactivators) of + {value,{Node,_}} -> + Node; + false -> % This should not happend. + false + end. + +%% Returns a list of list all nodes that are currently reactivating. +get_all_nodes_reactivators([{Nodes,_Pid}|Rest]) -> + [Nodes|get_all_nodes_reactivators(Rest)]; +get_all_nodes_reactivators([]) -> + []. + +%% Function stopping all running reactivator processes. Returns a new empty +%% reactivators structure. Note that this function does not set the state of +%% Nodes. It must most often be set to running. +stop_all_reactivators([{_Nodes,Pid}|Rest]) -> + exit(Pid,kill), + stop_all_reactivators(Rest); +stop_all_reactivators([]) -> + []. % Returns an empty reactivators. + +%% Help function stopping the reactivator (if any) that reactivates Node. +%% Returns a new list of reactivators structure. +stop_node_reactivators(Node,[{Node,Pid}|Rest]) -> + exit(Pid,kill), + Rest; +stop_node_reactivators(Node,[NodePid|Rest]) -> + [NodePid|stop_node_reactivators(Node,Rest)]; +stop_node_reactivators(_,[]) -> + []. +%% ----------------------------------------------------------------------------- + + +%% ----------------------------------------------------------------------------- +%% Started initial trace cases data structure. +%% ----------------------------------------------------------------------------- + +%% This datastructure keeps information about ongoing trace cases started +%% automatically at init_tracing. These must be automatically stopped when calling +%% stop_tracing. + +add_initial_tcs(TCname,Id,StartedInitialTcs) -> + [{TCname,Id}|StartedInitialTcs]. +%% ----------------------------------------------------------------------------- + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/inviso/src/inviso_tool_lib.erl b/lib/inviso/src/inviso_tool_lib.erl index 20a8b509ae..7953acedd6 100644 --- a/lib/inviso/src/inviso_tool_lib.erl +++ b/lib/inviso/src/inviso_tool_lib.erl @@ -83,7 +83,7 @@ inviso_cmd(NodeName,Func,Args) -> %% In the non-distributed case the singlenode_expansion will be returned.
expand_module_names(_Nodes,Mod={_,'_'},_) ->
{error,{faulty_regexp_combination,Mod}};
-expand_module_names(Nodes,{DirStr,ModStr},Opts) when list(DirStr), list(ModStr) ->
+expand_module_names(Nodes,{DirStr,ModStr},Opts) when is_list(DirStr), is_list(ModStr) -> case expand_module_names_special_regexp(DirStr) of
{ok,NewDirStr} ->
case expand_module_names_special_regexp(ModStr) of
@@ -97,11 +97,11 @@ expand_module_names(Nodes,{DirStr,ModStr},Opts) when list(DirStr), list(ModStr) end;
expand_module_names(_,'_',_Opts) -> % If we want to trace all modules
wildcard; % we shall not expand it.
-expand_module_names(_Nodes,Mod,_Opts) when atom(Mod) ->
+expand_module_names(_Nodes,Mod,_Opts) when is_atom(Mod) -> module; % If it is an atom, no expansion.
expand_module_names(Nodes,"*",Opts) -> % Treat this as a reg.exp.
expand_module_names(Nodes,".*",Opts);
-expand_module_names(Nodes,ModStr,Opts) when list(ModStr) ->
+expand_module_names(Nodes,ModStr,Opts) when is_list(ModStr) -> case expand_module_names_special_regexp(ModStr) of
{ok,NewModStr} ->
expand_module_names_2(Nodes,NewModStr,Opts);
@@ -115,7 +115,7 @@ expand_module_names_2(Nodes,ModStr,Opts) -> case get_expand_regexp_at_opts(Opts) of
{ok,Node} -> % Expansion only at this node.
case inviso_rt_lib:expand_regexp([Node],ModStr,Opts) of
- [{Node,Modules}] when list(Modules) ->
+ [{Node,Modules}] when is_list(Modules) -> {singlenode_expansion,Modules};
[{Node,_}] -> % Most likely badrpc.
{error,{faulty_node,Node}}
@@ -130,7 +130,7 @@ expand_module_names_2(Nodes,DirStr,ModStr,Opts) -> case get_expand_regexp_at_opts(Opts) of
{ok,Node} -> % Expansion only at this node.
case inviso_rt_lib:expand_regexp([Node],DirStr,ModStr,Opts) of
- [{Node,Modules}] when list(Modules) ->
+ [{Node,Modules}] when is_list(Modules) -> {singlenode_expansion,Modules};
[{Node,_}] -> % Most likely badrpc.
{error,{faulty_node,Node}}
@@ -186,12 +186,12 @@ make_patterns(Catches,Opts,Dbg,NodeModsOrMods,F,A,MS) -> make_patterns_2(Catches,OwnArg,Dbg,NodeModsOrMods,F,A,MS)
end.
-make_patterns_2(Catches,OwnArg,Dbg,[{Node,Mods}|Rest],F,A,MS) when list(Mods) ->
+make_patterns_2(Catches,OwnArg,Dbg,[{Node,Mods}|Rest],F,A,MS) when is_list(Mods) -> TPs=make_patterns_3(Catches,OwnArg,Dbg,Mods,F,A,MS,[]),
[{Node,join_patterns(TPs)}|make_patterns_2(Catches,OwnArg,Dbg,Rest,F,A,MS)];
make_patterns_2(Catches,OwnArg,Dbg,[{_Node,_}|Rest],F,A,MS) -> % badrpc!?
make_patterns_2(Catches,OwnArg,Dbg,Rest,F,A,MS);
-make_patterns_2(Catches,OwnArg,Dbg,Modules,F,A,MS) when list(Modules) ->
+make_patterns_2(Catches,OwnArg,Dbg,Modules,F,A,MS) when is_list(Modules) -> TPs=make_patterns_3(Catches,OwnArg,Dbg,Modules,F,A,MS,[]),
join_patterns(TPs);
make_patterns_2(_,_,_,[],_,_,_) ->
@@ -330,7 +330,7 @@ get_datetime_from_tdg_args([DateTime|_]) -> %% Returns a list.
get_ownarg_opts(Opts) ->
case lists:keysearch(arg,1,Opts) of
- {value,{_,OwnArg}} when list(OwnArg) ->
+ {value,{_,OwnArg}} when is_list(OwnArg) -> OwnArg;
{value,{_,OwnArg}} ->
[OwnArg];
@@ -350,7 +350,7 @@ get_disable_safety_opts(Opts) -> get_expand_regexp_at_opts(Opts) ->
case lists:keysearch(expand_only_at,1,Opts) of
- {value,{_,Node}} when atom(Node) ->
+ {value,{_,Node}} when is_atom(Node) -> {ok,Node};
_ ->
false
diff --git a/lib/inviso/src/inviso_tool_sh.erl b/lib/inviso/src/inviso_tool_sh.erl index fe876b955a..b02f498c5b 100644 --- a/lib/inviso/src/inviso_tool_sh.erl +++ b/lib/inviso/src/inviso_tool_sh.erl @@ -1,1731 +1,1749 @@ -%%%------------------------------------------------------------------------------
-%%% File : inviso_tool_sh.erl
-%%% Author : Lennart �hman <[email protected]>
-%%% Description :
-%%%
-%%% Created : 24 Oct 2005 by Lennart �hman
-%%%------------------------------------------------------------------------------
--module(inviso_tool_sh).
-
-%% Inviso Session Handler.
-%% This is the code for the session handler process. Its purpose is that we have
-%% one session handler process for each trace session started through the
-%% start_session inviso tool API. The session handler process is responsible for:
-%%
-%% -Knowing the state/status of all participating runtime components.
-%% -Keeping storage of all tracerdata all our participants have used. This means
-%% also to find out the tracerdata of runtime components connecting by them
-%% selves.
-%%
-%% STORAGE STRATEGY
-%% ----------------
-%% The local information storage can be changed by two things. Either by executing
-%% commands issued through our APIs. Or by receiving trace_event from the control
-%% component. When we execute commands, a corresponding event will also follow.
-%% Meaning that in those situations we are informed twice.
-%% A simple strategy could be to wait for the event even when doing the changes
-%% to the runtime components our self (through commands). But that may result in
-%% a small time frame where someone might do yet another command and failing
-%% because the local information storage is not uptodate as it would have been
-%% expected to be. Therefore we always update the local storage when making changes
-%% to a runtime component our selves. There will eventually be a double update
-%% through an incoming event. But the storage must coop with that, preventing
-%% inconsitancies to happend. An example of a strategy is that the tracerdata table
-%% is a bag, not allowing for double entries of the same kind. Therefore a double
-%% update is harmless there.
-
-%% ------------------------------------------------------------------------------
-%% Module wide constants.
-%% ------------------------------------------------------------------------------
--define(LOCAL_RUNTIME,local_runtime). % Used as node name when non-disitrbuted.
--define(TRACING,tracing). % A state defined by the control component.
--define(RUNNING,running). % A status according to control componet.
-
--define(COPY_LOG_FROM,copy_log_from). % Common fileystem option.
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% API exports.
-%% ------------------------------------------------------------------------------
--export([start_link/5,start_link/8]).
--export([cancel_session/1,stop_session/3]).
--export([reactivate/1,reactivate/2]).
--export([tpl/5,tpl/6,tpl/7,
- tf/2,tf/3,
- tpm_localnames/2,init_tpm/6,init_tpm/9,tpm/6,tpm/7,tpm/10,
- tpm_ms/7,ctpm_ms/6,ctpm/5
- ]).
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Internal exports.
-%% ------------------------------------------------------------------------------
--export([init/1,handle_call/3,handle_info/2,terminate/2]).
-
--export([get_loopdata/1]).
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Includes.
-%% ------------------------------------------------------------------------------
--include_lib("kernel/include/file.hrl"). % Necessary for file module.
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% Exported API functions.
-%% ==============================================================================
-
-%% start_link(From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,NodesIn,NodesNotIn) =
-%% {ok,Pid} | {error,Reason}
-%% From= pid(), the initial client expecting the reply.
-%% NodeParams=[{Node,TracerData},{Node,TracerData,Opts}...]
-%% CtrlNode=atom() | 'void', the node where the trace control component is.
-%% CtrlPid=pid(), the pid of the trace control component.
-%% SafetyCatches=
-%% Dir=string(), where to place fetched logs and the merged log.
-%% Dbg=debug structure.
-%% NodesIn=[Node,...], list of nodes already in another session.
-%% NodesNotIn=[Node,...], list of nodes not in another session.
-%%
-%% Starts a session-handler. It keeps track of the the state and status of all
-%% participating runtime components. Note that there is a non-distributed case too.
-%% In the non-distributed case there is no things such as CtrlNode.
-start_link(From,TracerData,CtrlPid,SafetyCatches,Dbg) ->
- gen_server:start_link(?MODULE,
- {self(),From,TracerData,CtrlPid,SafetyCatches,Dbg},
- []).
-
-start_link(From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,Dbg,NodesIn,NodesNotIn) ->
- gen_server:start_link(?MODULE,
- {self(),From,NodeParams,CtrlNode,CtrlPid,
- SafetyCatches,Dbg,NodesIn,NodesNotIn},
- []).
-%% ------------------------------------------------------------------------------
-
-%% Stops tracing where it is ongoing. Fetches all logfiles.
-stop_session(SID,Dir,Prefix) ->
- gen_server:call(SID,{stop_session,Dir,Prefix}).
-%% ------------------------------------------------------------------------------
-
-%% stop_session(SID) = ok
-%%
-%% Cancels the session brutaly. All runtime components are made to stop tracing,
-%% all local log files are removed using the tracerdata we know for them.
-cancel_session(SID) ->
- gen_server:call(SID,cancel_session).
-%% ------------------------------------------------------------------------------
-
-%% reactivate(SID) = {ok,
-%% reactivate(SID,Nodes) = {ok,NodeResults} | {error,Reason}.
-%% SID=session id, pid().
-%% Nodes=[Node,...]
-%% NodeResult=[{Node,Result},...]
-%% Result={Good,Bad}
-%% Good,Bad=integer(), the number of redone activities.
-%%
-%% Function which reactivates runtime components being suspended. This is done
-%% replaying all trace flags (in the correct order) to the corresponding nodes.
-%% Note that this may also mean turning flags off. Like first turning them on
-%% then off a split second later.
-reactivate(SID) ->
- gen_server:call(SID,reactivate). %% NOT IMPLEMENTED YET.
-reactivate(SID,Nodes) ->
- gen_server:call(SID,{reactivate,Nodes}).
-%% ------------------------------------------------------------------------------
-
-
-%% tpl(SessionID,Mod,Func,Arity,MS)=
-%% tpl(SessionID,Mod,Func,Arity,MS,Opts)={ok,N}|{error,Reason}.
-%% tpl(SessionID,Nodes,Mod,Func,Arity,MS)=
-%% tpl(SessionID,Nodes,Mod,Func,Arity,MS,Opts)={ok,Result}|{error,Reason}
-%% Mod='_' | ModuleName | ModRegExp | {DirRegExp,ModRegExp}
-%% ModRegExp=DirRegExp= string()
-%% Func='_' | FunctionName
-%% Arity='_' | integer()
-%% MS=[] | false | a match specification
-%% Opts=[Opts,...]
-%% Opt={arg,Arg}, disable_safety, {expand_regexp_at,NodeName}, only_loaded
-%% Nodes=[NodeName,...]
-tpl(SID,Mod,Func,Arity,MS) ->
- gen_server:call(SID,{tp,tpl,Mod,Func,Arity,MS,[]}).
-tpl(SID,Mod,Func,Arity,MS,Opts) when list(MS);MS==true;MS==false ->
- gen_server:call(SID,{tp,tpl,Mod,Func,Arity,MS,Opts});
-tpl(SID,Nodes,Mod,Func,Arity,MS) when integer(Arity);Arity=='_' ->
- gen_server:call(SID,{tp,tpl,Nodes,Mod,Func,Arity,MS,[]}).
-tpl(SID,Nodes,Mod,Func,Arity,MS,Opts) ->
- gen_server:call(SID,{tp,tpl,Nodes,Mod,Func,Arity,MS,Opts}).
-%% ------------------------------------------------------------------------------
-
-%% ctpl(SessionID,Nodes,Mod,Func,Arity)=
-%% See tpl/X for arguments.
-%%
-%% Removes local trace-patterns from functions.
-ctpl(SID,Nodes,Mod,Func,Arity) ->
- gen_server:call(SID,{ctp,ctpl,Nodes,Mod,Func,Arity}).
-%% ------------------------------------------------------------------------------
-
-
-tpm_localnames(SID,Nodes) ->
- gen_server:call(SID,{tpm_localnames,Nodes}).
-tpm_globalnames(SID,Nodes) ->
- gen_server:call(SID,{tpm_globalnames,Nodes}).
-
-init_tpm(SID,Nodes,Mod,Func,Arity,CallFunc) ->
- gen_server:call(SID,{init_tpm,Nodes,Mod,Func,Arity,CallFunc}).
-init_tpm(SID,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- gen_server:call(SID,
- {init_tpm,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc}).
-tpm(SID,Nodes,Mod,Func,Arity,MS) ->
- gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS}).
-tpm(SID,Nodes,Mod,Func,Arity,MS,CallFunc) ->
- gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS,CallFunc}).
-tpm(SID,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc}).
-
-tpm_ms(SID,Nodes,Mod,Func,Arity,MSname,MS) ->
- gen_server:call(SID,{tpm_ms,Nodes,Mod,Func,Arity,MSname,MS}).
-
-ctpm_ms(SID,Nodes,Mod,Func,Arity,MSname) ->
- gen_server:call(SID,{tpm_ms,Nodes,Mod,Func,Arity,MSname}).
-
-ctpm(SID,Nodes,Mod,Func,Arity) ->
- gen_server:call(SID,{ctpm,Nodes,Mod,Func,Arity}).
-%% ------------------------------------------------------------------------------
-
-
-%% tf(SessionID,Nodes,TraceConfList)=
-%% TraceConfList=[{PidSpec,Flags},...]
-%% PidSpec=pid()|atom()|all|new|existing
-%% Flags=[Flag,...]
-tf(SID,TraceConfList) ->
- gen_server:call(SID,{tf,TraceConfList}).
-tf(SID,Nodes,TraceConfList) ->
- gen_server:call(SID,{tf,Nodes,TraceConfList}).
-%% ------------------------------------------------------------------------------
-
-
-get_loopdata(SID) ->
- gen_server:call(SID,get_loopdata).
-%% ------------------------------------------------------------------------------
-
-%% ==============================================================================
-%% Genserver call-backs.
-%% ==============================================================================
-
-%% Initial function for the session handler process. The nodes participating in
-%% the session must previously have been added to our control component by the tool.
-%% The session handler first finds out the state/status of the specified runtime
-%% components, then it tries to initiate tracing on those where it is applicable.
-%% Note that a reply to the initial (tool)client is done from here instead from
-%% the tool-server.
-init({Parent,From,TracerData,CtrlPid,SafetyCatches,Dbg}) -> % The non-distributed case.
- {ok,StateStatus}=init_rtcomponent_states([],void,CtrlPid,[?LOCAL_RUNTIME]),
- case is_tool_internal_tracerdata(TracerData) of
- false -> % We shall initiate local runtime.
- case inviso:init_tracing(TracerData) of
- ok ->
- gen_server:reply(From,{ok,{self(),ok}}),
- {ok,mk_ld(Parent,
- void,
- CtrlPid,
- to_rtstates([{?LOCAL_RUNTIME,{tracing,?RUNNING},[]}]),
- [{?LOCAL_RUNTIME,TracerData}],
- [],
- SafetyCatches,
- Dbg)};
- {error,Reason} -> % It might have become suspended?!
- gen_server:reply(From,{error,Reason}),
- {ok,mk_ld(Parent,
- void,
- CtrlPid,
- to_rtstates([{?LOCAL_RUNTIME,StateStatus,[]}]),
- [{?LOCAL_RUNTIME,TracerData}],
- [],
- SafetyCatches,
- Dbg)}
- end;
- true -> % We shall not pass this one on.
- gen_server:reply(From,{ok,{self(),ok}}), % Then it is ok.
- {ok,mk_ld(Parent,
- void,
- CtrlPid,
- to_rtstates([{?LOCAL_RUNTIME,StateStatus,[]}]),
- [],
- [?LOCAL_RUNTIME],
- SafetyCatches,
- Dbg)}
- end;
-init({Parent,From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,Dbg,NodesIn,NodesNotIn}) ->
- case init_rtcomponent_states(NodeParams,CtrlNode,CtrlPid,NodesNotIn) of
- {ok,States} -> % A list of {Node,{State,Status},Opts}.
- {NodeParams2,Nodes2}=remove_nodeparams(NodesIn,NodeParams),
- case inviso_tool_lib:inviso_cmd(CtrlNode,init_tracing,[NodeParams2]) of
- {ok,Result} -> % Resulted in state changes!
- RTStates=set_tracing_rtstates(to_rtstates(States),Result),
- ReplyValue=init_fix_resultnodes(NodesIn,Nodes2,Result),
- gen_server:reply(From,{ok,{self(),ReplyValue}}),
- {ok,mk_ld(Parent,CtrlNode,CtrlPid,RTStates,
- NodeParams2,Nodes2,SafetyCatches,Dbg)};
- {error,Reason} -> % Some general failure.
- inviso_tool_lib:inviso_cmd(CtrlNode,unsubscribe,[]),
- gen_server:reply(From,{error,{init_tracing,Reason}}),
- {stop,{init_tracing,Reason}};
- What ->
- io:format("GOT:~n~w~n",[What]),
- exit(foo)
- end;
- {error,Reason} -> % Unable to get the state/status.
- inviso_tool_lib:inviso_cmd(CtrlNode,unsubscribe,[]),
- gen_server:reply(From,{error,Reason}),
- {stop,{error,Reason}};
- What ->
- io:format("GOT:~n~w~n",[What]),
- exit(foo)
- end.
-%% ------------------------------------------------------------------------------
-
-%% To stop a session means stop the tracing and remove all local files on the
-%% runtime nodes. We do have a table with all tracer data and that is how we are
-%% going to recreate what files to remove.
-%% Since runtime components may actually change state when this procedure is
-%% on-going, we do not care! It is the state in the session handling process at
-%% the time of start of this procedure which is used.
-handle_call(cancel_session,_From,LD) ->
- CtrlNode=get_ctrlnode_ld(LD),
- RTStates=get_rtstates_ld(LD),
- Dbg=get_dbg_ld(LD),
- TracingNodes=get_all_tracing_nodes_rtstates(RTStates),
- case stop_all_tracing(CtrlNode,Dbg,TracingNodes) of
- ok-> % Hopefully all nodes are stopped now.
- AvailableNodes=get_all_available_nodes_rtstates(RTStates),
- TRDstorage=get_trdstorage_ld(LD),
- remove_all_local_logs(CtrlNode,TRDstorage,AvailableNodes,Dbg),
- {stop,normal,ok,LD}; % LD actually not correct now!
- {error,Reason} -> % Some serious error when stop_tracing.
- {stop,normal,{error,Reason},LD}
- end;
-%% ------------------------------------------------------------------------------
-
-%% *Stop all tracing on runtime components still tracing.
-%% *Copy all local log files to the collection directory.
-handle_call({stop_session,Dir,Prefix},_From,LD) ->
- case check_directory_exists(Dir) of % Check that this directory exists here.
- true ->
- RTStates=get_rtstates_ld(LD),
- CtrlNode=get_ctrlnode_ld(LD),
- Dbg=get_dbg_ld(LD),
- TracingNodes=get_all_tracing_nodes_rtstates(RTStates),
- case stop_all_tracing(CtrlNode,Dbg,TracingNodes) of
- ok -> % Hopefully no node is still tracing now.
- TRDstorage=get_trdstorage_ld(LD),
- AvailableNodes=get_all_available_nodes_rtstates(RTStates),
- {FailedNodes,FetchedFiles}=
- transfer_logfiles(RTStates,CtrlNode,Dir,Prefix,
- TRDstorage,Dbg,AvailableNodes),
- RemoveNodes= % We only delete local logs where fetch ok.
- lists:filter(fun(N)->
- case lists:keysearch(N,1,FailedNodes) of
- {value,_} ->
- false;
- false ->
- true
- end
- end,
- AvailableNodes),
- remove_all_local_logs(CtrlNode,TRDstorage,RemoveNodes,Dbg),
- {stop,normal,{ok,{FailedNodes,FetchedFiles}},LD};
- {error,Reason} -> % Some general failure, quit.
- {stop,normal,{error,Reason},LD}
- end;
- false -> % You specified a non-existing directory!
- {reply,{error,{faulty_dir,Dir}},LD}
- end;
-%% ------------------------------------------------------------------------------
-
-handle_call({reactivate,Nodes},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- {OurNodes,OtherNodes}=
- remove_nodes_not_ours(Nodes,get_all_session_nodes_rtstates(RTStates)),
- CtrlNode=get_ctrlnode_ld(LD),
- ACTstorage=get_actstorage_ld(LD),
- case h_reactivate(CtrlNode,OurNodes,ACTstorage) of
- {ok,Results} -> % A list of {Node,Result}.
- if
- OtherNodes==[] -> % Normal case, no non-session nodes.
- {reply,{ok,Results},LD};
- true -> % Add error values for non-session nodes.
- {reply,
- {ok,
- lists:map(fun(N)->{N,{error,not_in_session}} end,OtherNodes)++
- Results},
- LD}
- end;
- {error,Reason} -> % Then this error takes presidence.
- {reply,{error,Reason},LD}
- end;
-%% ------------------------------------------------------------------------------
-
-%% Call-back for set trace-pattern for both global and local functions.
-handle_call({tp,PatternFunc,Mod,F,A,MS,Opts},_From,LD) ->
- Reply=h_tp(all,PatternFunc,Mod,F,A,MS,Opts,LD), % For all active nodes in the session.
- {reply,Reply,LD};
-handle_call({tp,PatternFunc,Nodes,Mod,F,A,MS,Opts},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- SNodes=get_all_session_nodes_rtstates(RTStates), % Notes belongoing to the session.
- {Nodes2,FaultyNodes}=remove_nodes_not_ours(Nodes,SNodes),
- Reply=h_tp(Nodes2,PatternFunc,Mod,F,A,MS,Opts,LD),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,FaultyNodes),
- {reply,ErrorReply++Reply,LD};
-%% ------------------------------------------------------------------------------
-
-%% Call-back handling the removal of both local and global trace-patterns.
-%% NOT IMPLEMENTED YET.
-handle_call({ctp,PatternFunc,Nodes,Mod,F,A},_From,LD) ->
- Reply=h_ctp(Nodes,PatternFunc,Mod,F,A,LD),
- {reply,Reply,LD};
-%% ------------------------------------------------------------------------------
-
-handle_call({tpm_localnames,Nodes},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_tpm_localnames(get_ctrlnode_ld(LD),Nodes2,RTStates,ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({init_tpm,Nodes,Mod,Func,Arity,CallFunc},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- init_tpm,
- [Mod,Func,Arity,CallFunc],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({init_tpm,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- init_tpm,
- [Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({tpm,Nodes,Mod,Func,Arity,MS},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),Nodes2,tpm,[Mod,Func,Arity,MS],RTStates,ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({tpm,Nodes,Mod,Func,Arity,MS,CallFunc},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- tpm,
- [Mod,Func,Arity,MS,CallFunc],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({tpm,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- tpm,
- [Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({tpm_ms,Nodes,Mod,Func,Arity,MSname,MS},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- tpm_ms,
- [Mod,Func,Arity,MSname,MS],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({ctpm_ms,Nodes,Mod,Func,Arity,MSname},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- ctpm_ms,
- [Mod,Func,Arity,MSname],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({ctpm,Nodes,Mod,Func,Arity},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),Nodes2,ctpm,[Mod,Func,Arity],RTStates,ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-%% ------------------------------------------------------------------------------
-
-%% Call-back for setting process trace-flags. Handles both distributed and non-
-%% distributed case.
-handle_call({tf,TraceConfList},From,LD) ->
- handle_call({tf,all,TraceConfList},From,LD);
-handle_call({tf,Nodes,TraceConfList},_From,LD) ->
- {Reply,NewACTstorage}=h_tf(get_ctrlnode_ld(LD),
- Nodes,
- TraceConfList,
- get_actstorage_ld(LD),
- get_rtstates_ld(LD)),
- {reply,Reply,put_actstorage_ld(NewACTstorage,LD)};
-%% ------------------------------------------------------------------------------
-
-
-
-handle_call(get_loopdata,_From,LD) ->
- io:format("The loopdata:~n~p~n",[LD]),
- {reply,ok,LD}.
-%% ------------------------------------------------------------------------------
-
-
-%% Clause handling an incomming state-change event from the control component.
-%% Note that it does not have to be one of our nodes since it is not possible
-%% to subscribe to certain node-events.
-%% We may very well get state-change events for state-changes we are the source
-%% to our selves. Those state-changes are already incorporated into the RTStates.
-%% There is however no harm in doing them again since we know that this event
-%% message will reach us before a reply to a potentially following state-change
-%% request will reach us. Hence we will do all state-changes in the correct order,
-%% even if sometimes done twice.
-handle_info({trace_event,CtrlPid,_Time,{state_change,Node,{State,Status}}},LD) ->
- case get_ctrlpid_ld(LD) of
- CtrlPid -> % It is from our control component.
- case {State,Status} of
- {?TRACING,?RUNNING} -> % This is the only case when new tracerdata!
- NewTracerData=add_current_tracerdata_ld(get_ctrlnode_ld(LD),
- Node,
- get_rtstates_ld(LD),
- get_trdstorage_ld(LD)),
- NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)),
- {noreply,put_trdstorage_ld(NewTracerData,
- put_rtstates_ld(NewRTStates,LD))};
- _ -> % In all other cases, just fix rtstates.
- NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)),
- {noreply,put_rtstates_ld(NewRTStates,LD)}
- end;
- _ ->
- {noreply,LD}
- end;
-%% If a new runtime component connects to our trace control component, and it is
-%% in our list of runtime components belonging to this session, we may update its
-%% state to now being present. Otherwise it does not belong to this session.
-%% Note that we avoid updating an already connected runtime component. This
-%% can happend if it connected by itself after we started the session handler,
-%% but before we managed to initiate tracing. Doing so or not will not result in
-%% any error in the long run, but during a short period of time we might be
-%% prevented from doing things with the runtime though it actually is tracing.
-handle_info({trace_event,CtrlPid,_Time,{connected,Node,{_Tag,{State,Status}}}},LD) ->
- case get_ctrlpid_ld(LD) of
- CtrlPid -> % It is from our control component.
- case get_statestatus_rtstates(Node,get_rtstates_ld(LD)) of
- {ok,unavailable} -> % This is the situation when we update!
- NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)),
- {noreply,put_rtstates_ld(NewRTStates,LD)};
- _ -> % In all other cases, let it be.
- {noreply,LD}
- end;
- _ -> % Not from our control component.
- {noreply,LD}
- end;
-%% If a runtime component disconnects we mark it as unavailable. We must also
-%% remove all saved trace-flags in order for them to not be accidently reactivated
-%% should the runtime component reconnect and then suspend.
-handle_info({trace_event,CtrlPid,_Time,{disconnected,Node,_}},LD) ->
- case get_ctrlpid_ld(LD) of
- CtrlPid -> % It is from our control component.
- NewRTStates=set_unavailable_rtstates(Node,get_rtstates_ld(LD)),
- NewACTstorage=del_node_actstorage(Node,get_actstorage_ld(LD)),
- {noreply,put_actstorage_ld(NewACTstorage,put_rtstates_ld(NewRTStates,LD))};
- _ ->
- {noreply,LD}
- end;
-handle_info(_,LD) ->
- {noreply,LD}.
-%% ------------------------------------------------------------------------------
-
-%% In terminate we cancel our subscription to event from the trace control component.
-%% That should actually not be necessary, but lets do it the correct way!
-terminate(_,LD) ->
- case get_ctrlnode_ld(LD) of
- void -> % Non-distributed.
- inviso:unsubscribe();
- Node ->
- inviso_tool_lib:inviso_cmd(Node,unsubscribe,[])
- end.
-%% ------------------------------------------------------------------------------
-
-
-
-%% ==============================================================================
-%% First level help functions to call-backs.
-%% ==============================================================================
-
-%% ------------------------------------------------------------------------------
-%% Help functions to init.
-%% ------------------------------------------------------------------------------
-
-%% Help function which find out the state/status of the runtime components.
-%% Note that since we have just started subscribe to state changes we must
-%% check our inqueue to see that we have no waiting messages for the nodes
-%% we learned the state/status of. If there is a waiting message we don't
-%% know whether that was a state change received before or after the state
-%% check was done. We will then redo the state-check.
-%% Returns {ok,States} or {error,Reason}.
-%% Where States is [{Node,{State,Status},Opts},...].
-%% Note that {error,Reason} can not occur in the non-distributed case.
-init_rtcomponent_states(NodeParams,void,CtrlPid,Nodes) -> % The non-distributed case.
- ok=inviso:subscribe(),
- init_rtcomponent_states_2(NodeParams,void,CtrlPid,Nodes,[]);
-init_rtcomponent_states(NodeParams,CtrlNode,CtrlPid,Nodes) ->
- ok=inviso_tool_lib:inviso_cmd(CtrlNode,subscribe,[]),
- init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,Nodes,[]).
-
-init_rtcomponent_states_2(_,_,_,[],States) ->
- {ok,States};
-init_rtcomponent_states_2(NodeParams,void,CtrlPid,_Nodes,States) ->
- case inviso:get_status() of
- {ok,StateStatus} -> % Got its state/status, now...
- {ProblemNodes,NewStates}=
- init_rtcomponent_states_3(NodeParams,CtrlPid,[{?LOCAL_RUNTIME,{ok,StateStatus}}],
- [],States),
- init_rtcomponent_states_2(NodeParams,void,CtrlPid,ProblemNodes,NewStates);
- {error,_Reason} -> % The runtime is not available!?
- {ok,[{?LOCAL_RUNTIME,unavailable,[]}]} % Create the return value immediately.
- end;
-init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,Nodes,States) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,get_status,[Nodes]) of
- {ok,NodeResult} ->
- {ProblemNodes,NewStates}=
- init_rtcomponent_states_3(NodeParams,CtrlPid,NodeResult,[],States),
- init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,ProblemNodes,NewStates);
- {error,Reason} -> % Severe problem, abort the session.
- {error,{get_status,Reason}}
- end.
-
-%% Traverses the list of returnvalues and checks that we do not have an event
-%% waiting in the message queue. If we do have, it is a problem. That node will
-%% be asked about its state again.
-%% Note that it is here we construct the RTStatesList.
-init_rtcomponent_states_3(NodeParams,CtrlPid,[{Node,{ok,{State,Status}}}|Rest],Problems,States) ->
- receive
- {trace_event,CtrlPid,_Time,{state_change,Node,_}} ->
- init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,[Node|Problems],States)
- after
- 0 -> % Not in msg queue, then we're safe!
- RTState=case lists:keysearch(Node,1,NodeParams) of
- {value,{_Node,_TracerData,Opts}} ->
- {Node,{State,Status},Opts};
- _ -> % No option available, use [].
- {Node,{State,Status},[]}
- end,
- init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,Problems,[RTState|States])
- end;
-init_rtcomponent_states_3(NodeParams,CtrlPid,[{Node,{error,_Reason}}|Rest],Problems,States) ->
- RTState=case lists:keysearch(Node,1,NodeParams) of
- {value,{_Node,_TracerData,Opts}} ->
- {Node,unavailable,Opts};
- _ -> % No option available, use [].
- {Node,unavailable,[]}
- end,
- init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,Problems,[RTState|States]);
-init_rtcomponent_states_3(_,_,[],Problems,States) ->
- {Problems,States}.
-%% ------------------------------------------------------------------------------
-
-%% Help function removing nodes from NodeParams. The reason for this can either
-%% be that we are using a tool internal tracerdata that shall not be forwarded to
-%% the trace control component, or that the node is actually already part of
-%% another session.
-%% Returns {NewNodeParams,NodesWhichShallNotBeInitiated}.
-remove_nodeparams(Nodes,NodesParams) ->
- remove_nodeparams_2(Nodes,NodesParams,[],[]).
-
-remove_nodeparams_2(Nodes,[NodeParam|Rest],NPAcc,NAcc) when % NPAcc=NodeParamsAcc.
- (is_tuple(NodeParam) and ((size(NodeParam)==2) or (size(NodeParam)==3))) ->
- Node=element(1,NodeParam),
- Params=element(2,NodeParam), % This is tracerdata!
- case lists:member(Node,Nodes) of
- true -> % Remove this one, in another session.
- remove_nodeparams_2(Nodes,Rest,NPAcc,NAcc);
- false -> % Ok so far...
- case is_tool_internal_tracerdata(Params) of
- false -> % Then keep it and use it later!
- remove_nodeparams_2(Nodes,Rest,[{Node,Params}|NPAcc],NAcc);
- true -> % Since it is, remove it from the list.
- remove_nodeparams_2(Nodes,Rest,NPAcc,[Node|NAcc])
- end
- end;
-remove_nodeparams_2(Nodes,[_|Rest],NPAcc,NAcc) -> % Faulty NodeParam, skip it!
- remove_nodeparams_2(Nodes,Rest,NPAcc,NAcc);
-remove_nodeparams_2(_,[],NPAcc,NAcc) ->
- {lists:reverse(NPAcc),NAcc}.
-%% ------------------------------------------------------------------------------
-
-%% Help function which adds both the nodes which were already part of another
-%% session and the nodes that we actually did not issue any init_tracing for.
-%% Returns a new Result list of [{Node,NodeResult},...].
-init_fix_resultnodes(NodesOtherSes,NodesNotInit,Result) ->
- NewResult=init_fix_resultnodes_2(NodesOtherSes,{error,in_other_session},Result),
- init_fix_resultnodes_2(NodesNotInit,ok,NewResult).
-
-init_fix_resultnodes_2([Node|Rest],NodeResult,Result) ->
- [{Node,NodeResult}|init_fix_resultnodes_2(Rest,NodeResult,Result)];
-init_fix_resultnodes_2([],_,Result) ->
- Result. % Append Result to the end of the list.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Help functions to reactivate.
-%% ------------------------------------------------------------------------------
-
-h_reactivate(CtrlNode,Nodes,ACTstorage) -> % Distributed case.
- case inviso_tool_lib:inviso_cmd(CtrlNode,cancel_suspension,[Nodes]) of
- {ok,CSuspResults} ->
- {GoodNodes,BadResults}= % Sort out nodes no longer suspended.
- lists:foldl(fun({Node,ok},{GoodNs,BadNs})->
- {[Node|GoodNs],BadNs};
- ({Node,{error,Reason}},{GoodNs,BadNs})->
- {GoodNs,[{Node,{error,{cancel_suspension,Reason}}}|BadNs]}
- end,
- {[],[]},
- CSuspResults),
- Results=h_reactivate_redo_activity(CtrlNode,GoodNodes,ACTstorage,[]),
- {ok,BadResults++Results};
- {error,Reason} -> % General failure cancelling suspend.
- {error,{cancel_suspension,Reason}}
- end.
-%% ------------------------------------------------------------------------------
-
-%% Help function which traverses the list of nodes known to be ours and have
-%% cancelled their suspend. If we fail redoing one of the activities associated
-%% with a node, the node will be reported in the return value as failed. From
-%% that point on its state must be considered unknown since we do not know how
-%% many of the activities were successfully redone.
-h_reactivate_redo_activity(CtrlNode,[Node|Rest],ACTstorage,Acc) ->
- case get_activities_actstorage(Node,ACTstorage) of
- {ok,Activities} -> % The node existed in activity storage.
- {Good,Bad}=h_reactivate_redo_activity_2(CtrlNode,Node,Activities,0,0),
- h_reactivate_redo_activity(CtrlNode,Rest,ACTstorage,[{Node,{Good,Bad}}|Acc]);
- false -> % Node not present in activity storage.
- h_reactivate_redo_activity(CtrlNode,Rest,ACTstorage,[{Node,{0,0}}|Acc])
- end;
-h_reactivate_redo_activity(_CtrlNode,[],_,Acc) ->
- lists:reverse(Acc).
-
-%% Help function actually redoing the activity. Note that there must be one
-%% clause here for every type of activity.
-%% Returns {NrGoodCmds,NrBadCmds}.
-%% The number of good or bad commands refers to inviso commands done. If any
-%% of the subparts of such a command returned an error, the command is concidered
-%% no good.
-h_reactivate_redo_activity_2(CtrlNode,Node,[{tf,{Op,TraceConfList}}|Rest],Good,Bad) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,Op,[[Node],TraceConfList]) of
- {ok,[{_Node,{ok,Answers}}]} ->
- case h_reactivate_redo_activity_check_tf(Answers) of
- ok ->
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good+1,Bad);
- error -> % At least oneReports the first encountered error.
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1)
- end;
- {ok,[{_Node,{error,_Reason}}]} ->
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1);
- {error,_Reason} -> % General error when doing cmd.
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1)
- end;
-h_reactivate_redo_activity_2(CtrlNode,Node,[{tpm,{Op,InvisoCmdParams}}|Rest],Good,Bad) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,Op,[[Node]|InvisoCmdParams]) of
- {ok,[{_Node,ok}]} ->
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good+1,Bad);
- {ok,[{_Node,{error,_Reason}}]} ->
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1);
- {error,_Reason} -> % General error when doing cmd.
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1)
- end;
-h_reactivate_redo_activity_2(_CtrlNode,_Node,[],Good,Bad) ->
- {Good,Bad}.
-
-%% Help function traversing a list of results from inviso:tf/2 or inviso:ctf/2
-%% to see if there were any errors.
-h_reactivate_redo_activity_check_tf([N|Rest]) when integer(N) ->
- h_reactivate_redo_activity_check_tf(Rest);
-h_reactivate_redo_activity_check_tf([{error,_Reason}|_]) ->
- error;
-h_reactivate_redo_activity_check_tf([]) ->
- ok.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Help functions to tp (setting trace patterns, both local and global).
-%% ------------------------------------------------------------------------------
-
-%% Help function which handles both tpl and tp. Note that the non-distributed case
-%% handled with Nodes='all'.
-%% Returns what shall be the reply to the client.
-h_tp(all,PatternFunc,Mod,F,A,MS,Opts,LD) -> % All available runtime nodes.
- Nodes=get_all_available_nodes_rtstates(get_rtstates_ld(LD)),
- h_tp(Nodes,PatternFunc,Mod,F,A,MS,Opts,LD);
-h_tp(Nodes,PatternFunc,Mod,F,A,MS,Opts,LD) -> % Only certain nodes in the session.
- CtrlNode=get_ctrlnode_ld(LD),
- Dbg=get_dbg_ld(LD),
- SafetyCatches=get_safetycatches_ld(LD),
- case inviso_tool_lib:expand_module_names(Nodes,Mod,Opts) of % Take care of any reg-exps.
- {multinode_expansion,NodeMods} ->
- NodeTPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,NodeMods,F,A,MS),
- h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,NodeTPs,[]);
- {singlenode_expansion,Modules} ->
- TPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,Modules,F,A,MS),
- h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg);
- module ->
- TPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,[Mod],F,A,MS),
- h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg);
- wildcard -> % Means do for all modules, no safety.
- h_tp_do_tps(CtrlNode,Nodes,[{Mod,F,A,MS}],PatternFunc,Dbg);
- {error,Reason} ->
- {error,Reason}
- end.
-
-%% Note that this function can never be called in the non-distributed case.
-h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,[{Node,TPs}|Rest],Accum) ->
- case h_tp_do_tps(CtrlNode,[Node],TPs,PatternFunc,Dbg) of
- {ok,[{Node,Result}]} ->
- h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,Rest,[{Node,Result}|Accum]);
- {error,Reason} -> % Failure, but don't stop.
- h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,Rest,[{Node,{error,Reason}}|Accum])
- end;
-h_tp_node_by_node(_,_,_,[],Accum) ->
- {ok,lists:reverse(Accum)}.
-
-%% Help function which does the actual call to the trace control component.
-%% Note that Nodes can be a list of nodes (including a single one) or
-%% ?LOCAL_RUNTIME if we are not distributed. The non-distributed case is otherwise
-%% detected by the 'void' CtrlNode.
-%% Returns {ok,[{Node,{ok,{NrOfFunctions,NrOfErrors}}},{Node,{error,Reason}},...]} or
-%% {error,Reason}. In the non-distributed case {ok,{NrOfFunctions,NrOfErros}} or
-%% {error,Reason}.
-h_tp_do_tps(void,_Nodes,TPs,PatternFunc,Dbg) -> % Non distributed case!
- inviso_tool_lib:debug(tp,Dbg,[TPs,PatternFunc]),
- case inviso:PatternFunc(TPs) of
- {ok,Result} -> % A list of [Nr1,Nr2,error,...].
- {ok,
- lists:foldl(fun(N,{AccNr,AccErr}) when integer(N) ->
- {AccNr+N,AccErr};
- (error,{AccNr,AccErr}) ->
- {AccNr,AccErr+1}
- end,
- {0,0},
- Result)};
- {error,Reason} ->
- {error,{PatternFunc,Reason}}
- end;
-h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg) ->
- inviso_tool_lib:debug(tp,Dbg,[Nodes,TPs,PatternFunc]),
- case inviso_tool_lib:inviso_cmd(CtrlNode,PatternFunc,[Nodes,TPs]) of
- {ok,Result} -> % Result is [{Node,Result},...].
- {ok,
- lists:map(fun({Node,{ok,Res}})->
- {Node,lists:foldl(fun(N,{ok,{AccNr,AccErr}}) when integer(N) ->
- {ok,{AccNr+N,AccErr}};
- (error,{AccNr,AccErr}) ->
- {ok,{AccNr,AccErr+1}}
- end,
- {ok,{0,0}},
- Res)};
- ({_Node,{error,Reason}})->
- {error,Reason}
- end,
- Result)};
- {error,Reason} ->
- {error,{PatternFunc,Reason}}
- end.
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% Help functions for removing trace-patterns.
-%% ------------------------------------------------------------------------------
-
-%% NOT IMPLEMENTED YET.
-h_ctp(Node,PatternFunc,Mod,F,A,LD) ->
- tbd.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Help functions for calling the trace information facility.
-%% ------------------------------------------------------------------------------
-
-
-%% Function handling the meta trace pattern for capturing registration of local
-%% process names.
-h_tpm_localnames(CtrlNode,Nodes,RTStates,ACTstorage) ->
- AvailableNodes=get_all_available_nodes_rtstates(RTStates),
- {Nodes3,FaultyNodes}=remove_nodes_not_ours(Nodes,AvailableNodes),
- case inviso_tool_lib:inviso_cmd(CtrlNode,tpm_localnames,[Nodes3]) of
- {ok,Result} -> % That good we want to modify tpmstorage!
- NewACTstorage=add_tpm_actstorage(Result,tpm_localnames,[],ACTstorage),
- ErrorResult=lists:map(fun(N)->{N,{error,not_available}} end,FaultyNodes),
- {{ok,ErrorResult++Result},NewACTstorage};
- {error,Reason} -> % If general failure, do not modify storage.
- {{error,Reason},ACTstorage}
- end.
-%% ------------------------------------------------------------------------------
-
-%% Functions calling meta trace functions for specified nodes. This function is
-%% intended for use with all tmp function calls, init_tpm,tpm,tpm_ms,ctpm_ms and
-%% ctpm.
-%% Note that we must store called meta trace functions and their parameters in the
-%% activity storage in order to be able to redo them in case of a reactivate.
-h_all_tpm(CtrlNode,Nodes,TpmCmd,InvisoCmdParams,RTStates,ACTstorage) ->
- AvailableNodes=get_all_available_nodes_rtstates(RTStates),
- {Nodes3,FaultyNodes}=remove_nodes_not_ours(Nodes,AvailableNodes),
- case inviso_tool_lib:inviso_cmd(CtrlNode,TpmCmd,[Nodes3|InvisoCmdParams]) of
- {ok,Result} -> % That good we want to modify tpmstorage!
- NewACTstorage=add_tpm_actstorage(Result,TpmCmd,InvisoCmdParams,ACTstorage),
- ErrorResult=lists:map(fun(N)->{N,{error,not_available}} end,FaultyNodes),
- {{ok,ErrorResult++Result},NewACTstorage};
- {error,Reason} -> % If general failure, do not modify storage.
- {{error,Reason},ACTstorage}
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Help functions for set trace flags.
-%% ------------------------------------------------------------------------------
-
-%% Help function which sets the tracepatterns in TraceConfList for all nodes
-%% mentioned in Nodes. Note that non-distributed case is handled with Nodes='all'.
-%% Returns {Reply,NewACTstorage} where Reply is whatever shall be returned to caller
-%% and NewACTstorage is traceflag storage modified with the flags added to the
-%% corresponding nodes.
-h_tf(void,_Nodes,TraceConfList,ACTstorage,_RTStates) -> % The non-distributed case.
- Reply=inviso:tf(TraceConfList),
- NewACTstorage=add_tf_actstorage([{?LOCAL_RUNTIME,Reply}],tf,TraceConfList,ACTstorage),
- {Reply,NewACTstorage};
-h_tf(CtrlNode,all,TraceConfList,ACTstorage,RTStates) ->
- AllNodes=get_all_session_nodes_rtstates(RTStates),
- h_tf(CtrlNode,AllNodes,TraceConfList,ACTstorage,RTStates);
-h_tf(CtrlNode,Nodes,TraceConfList,ACTstorage,_RTStates) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,tf,[Nodes,TraceConfList]) of
- {ok,Result} -> % That good we want to modify actstorage!
- NewACTstorage=add_tf_actstorage(Result,tf,TraceConfList,ACTstorage),
- {{ok,Result},NewACTstorage};
- {error,Reason} -> % If general failure, do not modify actstorage.
- {{error,Reason},ACTstorage}
- end.
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% Help functions to stop_session.
-%% ------------------------------------------------------------------------------
-
-%% This function fetches all local log-files using our stored tracerdata. Note
-%% that there are two major ways of tranfering logfiles. Either via distributed
-%% Erlang or by common filesystem (like NFS). The default is distributed Erlang.
-%% But there may be info in the RTStates structure about a common file-system.
-%% Returns {FailedNodes,FetchedFileNames} where FailedNodes is a list of
-%% nodenames where problems occurred. Note that problems does not necessarily
-%% mean that no files were copied.
-%% FetchedFileNames contains one or two of the tuples {trace_log,Files} and/or
-%% {ti_log,Files}, listing all files successfully fetched. Note that the
-%% list of fetched files contains sublists of filenames. One for each node and
-%% tracerdata.
-%% In the non-distributed system we always use copy (since the files always
-%% resides locally).
-transfer_logfiles(RTStates,CtrlNode,Dir,Prefix,TRDstorage,Dbg,AvailableNodes) ->
- if
- CtrlNode==void -> % When non-distributed, always copy!
- fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,[?LOCAL_RUNTIME]);
- true -> % The distributed case.
- {FetchNodes,CopyNodes}=find_logfile_transfer_methods(AvailableNodes,RTStates),
- {FailedFetchNodes,FetchedFiles}=
- case fetch_logfiles_distributed(CtrlNode,Dir,Prefix,TRDstorage,Dbg,FetchNodes) of
- {ok,Failed,Files} -> % So far no disasters.
- {Failed,Files};
- {error,Reason} -> % Means all fetch-nodes failed!
- inviso_tool_lib:debug(transfer_logfiles,Dbg,[FetchNodes,Reason]),
- {lists:map(fun(N)->{N,error} end,FetchNodes),[]}
- end,
- {FailedCopyNodes,CopiedFiles}=
- fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,CopyNodes),
- {FailedFetchNodes++FailedCopyNodes,FetchedFiles++CopiedFiles}
- end.
-
-%% Help function which finds out which node we have a common file system with
-%% and from which we must make distributed erlang tranfere.
-%% Returns {DistributedNodes,CopyNodes} where CopyNode is [{Node,CopyFromDir},...].
-find_logfile_transfer_methods(Nodes,RTStates) ->
- find_logfile_transfer_methods_2(Nodes,RTStates,[],[]).
-
-find_logfile_transfer_methods_2([Node|Rest],RTStates,FetchAcc,CopyAcc) ->
- {ok,Opts}=get_opts_rtstates(Node,RTStates), % Node must be in RTStates!
- case lists:keysearch(?COPY_LOG_FROM,1,Opts) of
- {value,{_,FromDir}} when list(FromDir) -> % Node has common filesystem.
- find_logfile_transfer_methods_2(Rest,RTStates,FetchAcc,[{Node,FromDir}|CopyAcc]);
- {value,_} -> % Can't understand dir option.
- find_logfile_transfer_methods_2(Rest,RTStates,[Node|FetchAcc],CopyAcc);
- false -> % Then we want to use fetch instead.
- find_logfile_transfer_methods_2(Rest,RTStates,[Node|FetchAcc],CopyAcc)
- end;
-find_logfile_transfer_methods_2([],_,FetchAcc,CopyAcc) ->
- {FetchAcc,CopyAcc}.
-%% ------------------------------------------------------------------------------
-
-%% Help function which transferes all local logfiles according to the tracerdata
-%% stored for the nodes in Nodes.
-%% Returns {ok,FailedNodes,FileNodeSpecs} or {error,Reason}.
-%% FailedNodes is a list of nodes where fetching logs did not succeed, partially
-%% or not at all.
-%% FileNames is a list of list of actually fetched files (the name as it is here, including
-%% Dir). The sublists are files which belong together.
-fetch_logfiles_distributed(CtrlNode,Dir,Prefix,TRDstorage,Dbg,Nodes) ->
- LogSpecList=build_logspeclist(Nodes,TRDstorage),
- case inviso_fetch_log(inviso_tool_lib:inviso_cmd(CtrlNode,
- fetch_log,
- [LogSpecList,Dir,Prefix])) of
- {ok,Result} ->
- Files=get_all_filenames_fetchlog_result(Result,Dbg),
- FailedNodes=get_all_failednodes_fetchlog_result(Result),
- {ok,FailedNodes,Files};
- {error,Reason} -> % Some general failure!
- {error,{fetch_log,Reason}}
- end.
-
-%% Help function which constructs a list {Node,TracerData} for all nodes in Nodes.
-%% Note that there may be more than one tracerdata for a node, resulting in multiple
-%% tuples for that node.
-build_logspeclist(Nodes,TRDstorage) ->
- build_logspeclist_2(Nodes,TRDstorage,[]).
-
-build_logspeclist_2([Node|Rest],TRDstorage,Acc) ->
- TRDlist=find_tracerdata_for_node_trd(Node,TRDstorage), % A list of all tracerdata.
- build_logspeclist_2(Rest,
- TRDstorage,
- [lists:map(fun(TRD)->{Node,TRD} end,TRDlist)|Acc]);
-build_logspeclist_2([],_,Acc) ->
- lists:flatten(Acc).
-
-%% Help function which translates inviso:fetch_log return values to what I
-%% want!
-inviso_fetch_log({error,Reason}) ->
- {error,Reason};
-inviso_fetch_log({_Success,ResultList}) ->
- {ok,ResultList}.
-
-%% Help function which collects all filenames mentioned in a noderesult structure.
-%% The files may or may not be complete.
-%% Returns a list of list of filenames. Each sublist contains files which belong
-%% together, i.e because they are a wrap-set.
-get_all_filenames_fetchlog_result(NodeResult,Dbg) ->
- get_all_filenames_fetchlog_result_2(NodeResult,Dbg,[]).
-
-get_all_filenames_fetchlog_result_2([{Node,{Success,FileInfo}}|Rest],Dbg,Accum)
- when Success=/=error, list(FileInfo) ->
- SubAccum=get_all_filenames_fetchlog_result_3(FileInfo,[]),
- get_all_filenames_fetchlog_result_2(Rest,Dbg,[{Node,SubAccum}|Accum]);
-get_all_filenames_fetchlog_result_2([{Node,{error,FReason}}|Rest],Dbg,Accum) ->
- inviso_tool_lib:debug(fetch_files,Dbg,[Node,FReason]),
- get_all_filenames_fetchlog_result_2(Rest,Dbg,Accum);
-get_all_filenames_fetchlog_result_2([],_Dbg,Accum) ->
- Accum.
-
-get_all_filenames_fetchlog_result_3([{FType,Files}|Rest],SubAccum) ->
- FilesOnly=lists:foldl(fun({ok,FName},Acc)->[FName|Acc];(_,Acc)->Acc end,[],Files),
- get_all_filenames_fetchlog_result_3(Rest,[{FType,FilesOnly}|SubAccum]);
-get_all_filenames_fetchlog_result_3([],SubAccum) ->
- SubAccum.
-
-%% Help function which traverses a noderesult and builds a list as return
-%% value containing the nodenames of all nodes not being complete.
-%% Note that a node may occur multiple times since may have fetched logfiles
-%% for several tracerdata from the same node. Makes sure the list contains
-%% unique node names.
-%% Returns a list nodes.
-get_all_failednodes_fetchlog_result(NodeResult) ->
- get_all_failednodes_fetchlog_result_2(NodeResult,[]).
-
-get_all_failednodes_fetchlog_result_2([{_Node,{complete,_}}|Rest],Acc) ->
- get_all_failednodes_fetchlog_result_2(Rest,Acc);
-get_all_failednodes_fetchlog_result_2([{Node,{_Severity,_}}|Rest],Acc) ->
- case lists:member(Node,Acc) of
- true -> % Already in the list.
- get_all_failednodes_fetchlog_result_2(Rest,Acc);
- false -> % Not in Acc, add it!
- get_all_failednodes_fetchlog_result_2(Rest,[Node|Acc])
- end;
-get_all_failednodes_fetchlog_result_2([],Acc) ->
- Acc.
-%% ------------------------------------------------------------------------------
-
-%% Help function which copies files from one location to Dir and at the same time
-%% adds the Prefix to the filename. NodeSpecs contains full path to the files. The
-%% reason the node information is still part of NodeSpecs is that otherwise we can
-%% not report faulty nodes. Note that one node may occur multiple times since there
-%% may be more than one tracerdata for a node.
-%% Returns {FailedNodes,Files} where FailedNodes is a list of nodes where problems
-%% occurred. Files is a tuple list of [{Node,[{FType,FileNames},...]},...].
-fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,NodeSpecs) ->
- CopySpecList=build_copylist(CtrlNode,Dbg,NodeSpecs,TRDstorage),
- fetch_logfiles_copy_2(Dir,Prefix,Dbg,CopySpecList,[],[]).
-
-fetch_logfiles_copy_2(Dir,Prefix,Dbg,[{Node,CopySpecs}|Rest],FailedNodes,Files) ->
- case fetch_logfiles_copy_3(Dir,Prefix,Dbg,CopySpecs,[],0) of
- {0,LocalFiles} -> % Copy went ok and zero errors.
- fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,FailedNodes,[{Node,LocalFiles}|Files]);
- {_N,LocalFiles} -> % Copied files, but some went wrong.
- case lists:member(Node,FailedNodes) of
- true -> % Node already in FailedNodes.
- fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,FailedNodes,
- [{Node,LocalFiles}|Files]);
- false -> % Node not marked as failed, yet.
- fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,[Node|FailedNodes],
- [{Node,LocalFiles}|Files])
- end
- end;
-fetch_logfiles_copy_2(_,_,_,[],FailedNodes,Files) ->
- {FailedNodes,Files}. % The return value from fetch_logfiles_copy.
-
-fetch_logfiles_copy_3(Dir,Prefix,Dbg,[{FType,RemoteFiles}|Rest],Results,Errors) ->
- {Err,LocalFiles}=fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,RemoteFiles,[],0),
- fetch_logfiles_copy_3(Dir,Prefix,Dbg,Rest,[{FType,LocalFiles}|Results],Errors+Err);
-fetch_logfiles_copy_3(_,_,_,[],Results,Errors) ->
- {Errors,Results}.
-
-%% For each file of one file-type (e.g. trace_log).
-fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,[File|Rest],LocalFiles,Errors) ->
- DestName=Prefix++filename:basename(File),
- Destination=filename:join(Dir,DestName),
- case do_copy_file(File,Destination) of
- ok ->
- fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,Rest,[DestName|LocalFiles],Errors);
- {error,Reason} ->
- inviso_tool_lib:debug(copy_files,Dbg,[File,Destination,Reason]),
- fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,Rest,LocalFiles,Errors+1)
- end;
-fetch_logfiles_copy_3_1(_,_,_,[],LocalFiles,Errors) ->
- {Errors,LocalFiles}.
-
-%% Help function which builds a [{Node,[{Type,[ListOfRemoteFiles]}},...}]
-%% where Type describes trace_log or ti_log and each entry in ListOfRemoteFiles
-%% is a complete path to a file to be copied.
-build_copylist(CtrlNode,Dbg,NodeSpecList,TRDstorage) ->
- build_copylist_2(CtrlNode,Dbg,NodeSpecList,TRDstorage,[]).
-
-%% For each node specified in the NodeSpecList.
-build_copylist_2(CtrlNode,Dbg,[{Node,SourceDir}|Rest],TRDstorage,Acc) ->
- TRDlist=find_tracerdata_for_node_trd(Node,TRDstorage),
- CopySpecList=build_copylist_3(CtrlNode,Dbg,SourceDir,Node,TRDlist),
- build_copylist_2(CtrlNode,Dbg,Rest,TRDstorage,[CopySpecList|Acc]);
-build_copylist_2(_,_,[],_,Acc) ->
- lists:flatten(Acc).
-
-%% For each tracerdata found for the node.
-build_copylist_3(void,Dbg,SourceDir,Node,[TRD|Rest]) -> % The non-distributed case.
- case inviso:list_logs(TRD) of
- {ok,FileSpec} when list(FileSpec) -> % [{trace_log,Dir,Files},...]
- NewFileSpec=build_copylist_4(SourceDir,FileSpec,[]),
- [{Node,NewFileSpec}|build_copylist_3(void,Dbg,SourceDir,Node,Rest)];
- {ok,no_log} -> % This tracedata not associated with any log.
- build_copylist_3(void,Dbg,SourceDir,Node,Rest);
- {error,Reason} ->
- inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]),
- build_copylist_3(void,Dbg,SourceDir,Node,Rest)
- end;
-build_copylist_3(CtrlNode,Dbg,SourceDir,Node,[TRD|Rest]) -> % The distributed case.
- case inviso_tool_lib:inviso_cmd(CtrlNode,list_logs,[[{Node,TRD}]]) of
- {ok,[{Node,{ok,FileSpec}}]} when list(FileSpec) ->
- NewFileSpec=build_copylist_4(SourceDir,FileSpec,[]),
- [{Node,NewFileSpec}|build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest)];
- {ok,[{Node,{ok,no_log}}]} -> % It relays to another node, no files!
- build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest);
- {ok,[{Node,{error,Reason}}]} ->
- inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]),
- build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest);
- {error,Reason} -> % Some general failure.
- inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]),
- build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest)
- end;
-build_copylist_3(_,_,_,_,[]) ->
- [].
-
-%% Help function which makes a [{Type,Files},...] list where each file in Files
-%% is with full path as found from our file-system.
-build_copylist_4(SourceDir,[{Type,_Dir,Files}|Rest],Accum) ->
- NewFiles=
- lists:foldl(fun(FName,LocalAcc)->[filename:join(SourceDir,FName)|LocalAcc] end,
- [],
- Files),
- build_copylist_4(SourceDir,Rest,[{Type,NewFiles}|Accum]);
-build_copylist_4(_,[],Accum) ->
- Accum.
-
-
-%% Help function which copies a file using os:cmd.
-%% Returns 'ok' or {error,Reason}.
-do_copy_file(Source,Destination) ->
- case os:type() of
- {win32,_} ->
- os:cmd("copy "++Source++" "++Destination), % Perhaps a test on success?
- ok;
- {unix,_} ->
- os:cmd("cp "++Source++" "++Destination), % Perhaps a test on success?
- ok
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-
-%% ==============================================================================
-%% Various help functions.
-%% ==============================================================================
-
-%% Help function going through the Nodes list and checking that only nodes
-%% mentioned in OurNodes gets returned. It also makes the nodes in the return
-%% value unique.
-remove_nodes_not_ours(Nodes,OurNodes) ->
- remove_nodes_not_ours_2(Nodes,OurNodes,[],[]).
-
-remove_nodes_not_ours_2([Node|Rest],OurNodes,OurAcc,OtherAcc) ->
- case lists:member(Node,OurNodes) of
- true -> % Ok it is one of our nodes.
- case lists:member(Node,OurAcc) of
- true -> % Already in the list, skip.
- remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,OtherAcc);
- false ->
- remove_nodes_not_ours_2(Rest,OurNodes,[Node|OurAcc],OtherAcc)
- end;
- false ->
- case lists:member(Node,OtherAcc) of
- true ->
- remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,OtherAcc);
- false ->
- remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,[Node|OtherAcc])
- end
- end;
-remove_nodes_not_ours_2([],_,OurAcc,OtherAcc) ->
- {lists:reverse(OurAcc),lists:reverse(OtherAcc)}.
-%% ------------------------------------------------------------------------------
-
-%% Help function which returns 'true' or 'false' depending on if TracerData is
-%% meant to be used by the session handler (true) or if it supposed to be passed
-%% on to the trace system.
-is_tool_internal_tracerdata(_) -> % CURRENTLY NO INTERNAL TRACER DATA!
- false.
-%% ------------------------------------------------------------------------------
-
-%% Help function which checks that all nodes in the first list of nodes exists
-%% in the second list of nodes. Returns 'true' or 'false'. The latter if as much
-%% as one incorrect node was found.
-check_our_nodes([Node|Rest],AllNodes) ->
- case lists:member(Node,AllNodes) of
- true ->
- check_our_nodes(Rest,AllNodes);
- false -> % Then we can stop right here.
- false
- end;
-check_our_nodes([],_) ->
- true.
-%% ------------------------------------------------------------------------------
-
-%% Help function which checks that a directory actually exists. Returns 'true' or
-%% 'false'.
-check_directory_exists(Dir) ->
- case file:read_file_info(Dir) of
- {ok,#file_info{type=directory}} ->
- true;
- _ -> % In all other cases it is not valid.
- false
- end.
-%% ------------------------------------------------------------------------------
-
-%% This function stops the tracing on all nodes in Nodes. Preferably Nodes is a list
-%% of only tracing runtime components. Not that there will actually be any difference
-%% since the return value does not reflect how stopping the nodes went.
-%% Returns 'ok' or {error,Reason}, the latter only in case of general failure.
-stop_all_tracing(void,Dbg,[?LOCAL_RUNTIME]) -> % The non-distributed case, and is tracing.
- case inviso:stop_tracing() of
- {ok,_State} ->
- ok;
- {error,Reason} -> % We actually don't care.
- inviso_tool_lib:debug(stop_tracing,Dbg,[?LOCAL_RUNTIME,Reason]),
- ok
- end;
-stop_all_tracing(void,_,_) -> % There is no local runtime started.
- ok;
-stop_all_tracing(CtrlNode,Dbg,Nodes) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,stop_tracing,[Nodes]) of
- {ok,Result} -> % The result is only used for debug.
- Failed=lists:foldl(fun({N,{error,Reason}},Acc)->[{N,{error,Reason}}|Acc];
- (_,Acc)->Acc
- end,
- [],
- Result),
- if
- Failed==[] ->
- ok;
- true ->
- inviso_tool_lib:debug(stop_tracing,Dbg,[Nodes,Failed]),
- ok
- end;
- {error,Reason} ->
- {error,{stop_tracing,Reason}}
- end.
-%% ------------------------------------------------------------------------------
-
-%% Help function removing all local logs using the tracerdata to determine what
-%% logs to remove from where.
-%% There is no significant return value since it is not really clear what to do
-%% if removal went wrong. The function can make debug-reports thought.
-remove_all_local_logs(CtrlNode,TRDstorage,Nodes,Dbg) ->
- LogSpecList=build_logspeclist_remove_logs(Nodes,TRDstorage),
- case inviso_tool_lib:inviso_cmd(CtrlNode,delete_log,[LogSpecList]) of
- {ok,Results} ->
- case look_for_errors_resultlist(Results) of
- [] -> % No errors found in the result!
- true;
- Errors ->
- inviso_tool_lib:debug(remove_all_local_logs,Dbg,[Errors]),
- true
- end;
- {error,Reason} -> % Some general error.
- inviso_tool_lib:debug(remove_all_local_logs,Dbg,[{error,Reason}]),
- true
- end.
-
-%% Help function which puts together a list of {Node,Tracerdata} tuples. Note that
-%% we must build one tuple for each tracerdata for one node.
-build_logspeclist_remove_logs(Nodes,TRDstorage) ->
- [{Node,TracerData}||Node<-Nodes,TracerData<-find_tracerdata_for_node_trd(Node,TRDstorage)].
-%% ------------------------------------------------------------------------------
-
-%% Help function which traverses a resultlist from an inviso function. Such are
-%% built up as [{Node,SubResults},...] where SubResult is a list of tuples for each
-%% file-type (e.g trace_log) {FType,FileList} where a FileList is either {error,Reason}
-%% or {ok,FileName}.
-%% Returns a list of {Node,[{error,Reason},...]}.
-look_for_errors_resultlist([{Node,{error,Reason}}|Rest]) ->
- [{Node,{error,Reason}}|look_for_errors_resultlist(Rest)];
-look_for_errors_resultlist([{Node,{ok,NResults}}|Rest]) when list(NResults) ->
- case look_for_errors_resultlist_2(NResults,[]) of
- [] ->
- look_for_errors_resultlist(Rest);
- Errors -> % A list of lists.
- [{Node,lists:flatten(Errors)}|look_for_errors_resultlist(Rest)]
- end;
-look_for_errors_resultlist([_|Rest]) ->
- look_for_errors_resultlist(Rest);
-look_for_errors_resultlist([]) ->
- [].
-
-look_for_errors_resultlist_2([{_FType,NSubResult}|Rest],Accum) ->
- case lists:filter(fun({error,_Reason})->true;(_)->false end,NSubResult) of
- [] -> % No errors for this node.
- look_for_errors_resultlist_2(Rest,Accum);
- Errors -> % A list of at least one error.
- look_for_errors_resultlist_2(Rest,[Errors|Accum])
- end;
-look_for_errors_resultlist_2([],Accum) ->
- Accum.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Functions working on the loopdata structure.
-%% Its main purpose is to store information about runtime components participating
-%% in the session and their current status.
-%% ------------------------------------------------------------------------------
-
--record(ld,{parent,
- ctrlnode,
- ctrlpid, % To where to send inviso cmd.
- rtstates,
- tracerdata,
- safetycatches,
- dbg,
- actstorage % Activity storage, for reactivate.
- }).
-
-%% Function creating the initial datastructure.
-%% The datastructure is [{Node,State},...].
-%%
-%% The tracerdata table is a bag simply for the reason that if we try to insert
-%% the same tracerdata for a node twice, we will end up with one tracerdata after
-%% all. This is useful when we insert tracerdata ourselves, the tracerdata will
-%% come as a state-change too.
-mk_ld(Parent,CtrlNode,CtrlPid,RTStates,NodeParams,OtherNodes,SafetyCatches,Dbg) ->
- TRDtableName=list_to_atom("inviso_tool_sh_trdstorage_"++pid_to_list(self())),
- TRDtid=ets:new(TRDtableName,[bag]),
- ACTtableName=list_to_atom("inviso_tool_sh_actstorage_"++pid_to_list(self())),
- ACTtid=ets:new(ACTtableName,[bag]),
- mk_ld_fill_tracerdata(CtrlNode,TRDtid,NodeParams,OtherNodes), % Fill the ETS table.
- #ld{parent=Parent, % The tool main process.
- ctrlnode=CtrlNode, % Node name where the control component is.
- ctrlpid=CtrlPid, % The process id of the control component.
- rtstates=RTStates, % All nodes and their state/status.
- tracerdata=TRDtid,
- safetycatches=SafetyCatches,
- dbg=Dbg,
- actstorage=ACTtid
- }.
-
-%% Help function which inserts tracer data for the nodes. Note that we can get
-%% tracer data either from the return value from init_tracing or by asking the
-%% node for it. The latter is necessary for the nodes which were marked not to
-%% be initiated by the session handler. This maybe because those nodes have
-%% autostarted.
-mk_ld_fill_tracerdata(CtrlNode,TId,NodeParams,OtherNodes) ->
- mk_ld_fill_tracerdata_nodeparams(TId,NodeParams),
- mk_ld_fill_tracerdata_othernodes(CtrlNode,TId,OtherNodes).
-
-mk_ld_fill_tracerdata_nodeparams(TId,[{Node,TracerData}|Rest]) ->
- ets:insert(TId,{Node,TracerData}),
- mk_ld_fill_tracerdata_nodeparams(TId,Rest);
-mk_ld_fill_tracerdata_nodeparams(_,[]) ->
- ok.
-
-mk_ld_fill_tracerdata_othernodes(_,_,[]) -> % Then not necessary to do anything.
- ok;
-mk_ld_fill_tracerdata_othernodes(void,TId,[Node]) -> % The non-distributed case.
- case inviso:get_tracerdata() of
- {error,_Reason} -> % Perhaps in state new or disconnected.
- ok; % Do nothing.
- {ok,TracerData} ->
- ets:insert(TId,{Node,TracerData})
- end;
-mk_ld_fill_tracerdata_othernodes(CtrlNode,TId,Nodes) ->
- case inviso_tool_lib:invisomd(CtrlNode,get_tracerdata,[Nodes]) of
- {ok,Results} ->
- mk_ld_fill_tracerdata_othernodes_2(TId,Results);
- {error,_Reason} -> % Strange, we will probably crash later.
- ok
- end.
-
-mk_ld_fill_tracerdata_othernodes_2(TId,[{_Node,{ok,no_tracerdata}}|Rest]) ->
- mk_ld_fill_tracerdata_othernodes_2(TId,Rest); % It was not initiated then!
-mk_ld_fill_tracerdata_othernodes_2(TId,[{Node,{ok,TracerData}}|Rest]) ->
- ets:insert(TId,{Node,TracerData}),
- mk_ld_fill_tracerdata_othernodes_2(TId,Rest);
-mk_ld_fill_tracerdata_othernodes_2(_,[]) ->
- ok.
-%% ------------------------------------------------------------------------------
-
-get_ctrlnode_ld(#ld{ctrlnode=CtrlNode}) ->
- CtrlNode.
-%% ------------------------------------------------------------------------------
-
-
-get_ctrlpid_ld(#ld{ctrlpid=CtrlPid}) ->
- CtrlPid.
-%% ------------------------------------------------------------------------------
-
-get_rtstates_ld(#ld{rtstates=RTStates}) ->
- RTStates.
-
-put_rtstates_ld(NewRTStates,LD) ->
- LD#ld{rtstates=NewRTStates}.
-%% ------------------------------------------------------------------------------
-
-get_trdstorage_ld(#ld{tracerdata=TId}) ->
- TId.
-
-put_trdstorage_ld(_NewTId,LD) ->
- LD.
-%% ------------------------------------------------------------------------------
-
-%% Help function which adds the current tracerdata of node Node to the tracerdata
-%% storage. We only want to add tracerdata we have not seen before. We therefore
-%% avoid adding it if the node already is in state ?TRACING.
-%% Returns a new tracerdata (what ever it is)!
-add_current_tracerdata_ld(CtrlNode,Node,RTStates,TId) ->
- case get_statestatus_rtstates(Node,RTStates) of
- {ok,{?TRACING,_}} -> % Then we have already added the tracerdata.
- TId; % Then do nothing.
- {ok,_} -> % Since we were not tracing before.
- case add_current_tracerdata_ld_fetchtracerdata(CtrlNode,Node) of
- {ok,TracerData} ->
- ets:insert(TId,{Node,TracerData});
- no_tracerdata -> % Strange, how could we become tracing
- ok;
- {error,_Reason} -> % The node perhaps disconnected!?
- ok
- end;
- false -> % Very strange, not our node!
- ok % Do nothing.
- end.
-
-add_current_tracerdata_ld_fetchtracerdata(void,_Node) ->
- case inviso:get_tracerdata() of
- {ok,TracerData} ->
- {ok,TracerData};
- {error,no_tracerdata} ->
- no_tracerdata;
- {error,Reason} ->
- {error,Reason}
- end;
-add_current_tracerdata_ld_fetchtracerdata(CtrlNode,Node) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,get_tracerdata,[[Node]]) of
- {ok,[{Node,{ok,TracerData}}]} ->
- {ok,TracerData};
- {ok,[{Node,{error,no_tracerdata}}]} ->
- no_tracerdata;
- {ok,[{Node,{error,Reason}}]} ->
- {error,Reason};
- {error,Reason} ->
- {error,Reason}
- end.
-%% ------------------------------------------------------------------------------
-
-
-get_safetycatches_ld(#ld{safetycatches=SCs}) ->
- SCs.
-%% ------------------------------------------------------------------------------
-
-get_dbg_ld(#ld{dbg=Dbg}) ->
- Dbg.
-%% ------------------------------------------------------------------------------
-
-get_actstorage_ld(#ld{actstorage=ACTstorage}) ->
- ACTstorage.
-
-put_actstorage_ld(_NewACTstorage,LD) ->
- LD.
-%% ------------------------------------------------------------------------------
-
-
-
-%% ------------------------------------------------------------------------------
-%% Functions working on the rtstates structure (which is a substructure of loopdata).
-%% It is either:
-%% [{Node,StateStatus,Opts},...]
-%% Node is either the node name of the runtime component erlang node or
-%% ?LOCAL_RUNTIME as returned from the trace control component.
-%% StateStatus is {State,Status}, 'unavailable' or 'unknown'.
-%% Status is the returnvalue from trace control component.
-%% i.e: running | {suspended,Reason}
-%% ------------------------------------------------------------------------------
-
-%% Function contructing an rtstates structure from a list of [{Node,StateStatus,Opts},...].
-to_rtstates(ListOfStates) when list(ListOfStates) ->
- ListOfStates.
-%% ------------------------------------------------------------------------------
-
-%% Function which takes a rtstates structure and returns a list of [{Node,StateStatus},...].
-from_rtstates(RTStates) ->
- RTStates.
-%% ------------------------------------------------------------------------------
-
-%% Function which takes an rtstates structure and a result as returned from
-%% init_tracing. The RTStates is modified for the nodes that changed state as a
-%% result of successful init_tracing.
-%% Returns a new RTStates.
-set_tracing_rtstates([E={Node,_StateStatus,Opts}|Rest],Result) ->
- case lists:keysearch(Node,1,Result) of
- {value,{_,ok}} -> % Means state-change to tracing!
- [{Node,{tracing,running},Opts}|set_tracing_rtstates(Rest,Result)];
- _ -> % Otherwise, leave it as is.
- [E|set_tracing_rtstates(Rest,Result)]
- end;
-set_tracing_rtstates([],_Result) ->
- [].
-%% ------------------------------------------------------------------------------
-
-%% Function updating the state/status for a certain runtime component.
-%% Returns a new RTStates structure. Note that Node must not necessarily be one
-%% of the nodes in the session. Meaning that Node shall not be added to RTStates
-%% should it not already be in there.
-statechange_rtstates(Node,State,Status,RTStates) when list(RTStates) ->
- case lists:keysearch(Node,1,RTStates) of
- {value,{_,_,Opts}} ->
- lists:keyreplace(Node,1,RTStates,{Node,{State,Status},Opts});
- _ -> % Then Node does not exist.
- RTStates % Just keep it as is, as keyreplace would have done.
- end.
-%% ------------------------------------------------------------------------------
-
-%% Function updating the state/status for a certain runtime component. The
-%% state/status is set to 'unavailable'.
-%% Returns a new RTStates structure.
-set_unavailable_rtstates(Node,RTStates) when list(RTStates) ->
- case lists:keysearch(Node,1,RTStates) of
- {value,{_,_,Opts}} ->
- lists:keyreplace(Node,1,RTStates,{Node,unavailable,Opts});
- _ -> % Then Node does not exist.
- RTStates % Just keep it as is, as keyreplace would have done.
- end.
-%% ------------------------------------------------------------------------------
-
-%% Function finding the statestatus associated with Node in the RTStates structure.
-%% Returns {ok,StateStatus} or 'false'.
-get_statestatus_rtstates(Node,RTStates) ->
- case lists:keysearch(Node,1,RTStates) of
- {value,{_,StateStatus,_}} ->
- {ok,StateStatus};
- false ->
- false
- end.
-%% ------------------------------------------------------------------------------
-
-%% Help function which returns a list of all nodes that are currently marked
-%% as available to us in the runtime state structure.
-get_all_available_nodes_rtstates(RTStates) ->
- get_all_session_nodes_rtstates(lists:filter(fun({_N,unavailable,_})->false;
- (_)->true
- end,
- RTStates)).
-%% ------------------------------------------------------------------------------
-
-%% Help function returning a list of all nodes belonging to this session.
-get_all_session_nodes_rtstates(RTStates) ->
- lists:map(fun({Node,_,_})->Node end,RTStates).
-%% ------------------------------------------------------------------------------
-
-%% Function which returns a list of nodes that are indicated as tracing in the
-%% RTStates structure.
-get_all_tracing_nodes_rtstates(RTStates) ->
- lists:map(fun({N,_,_})->N end,
- lists:filter(fun({_,{tracing,_},_})->true;(_)->false end,RTStates)).
-%% ------------------------------------------------------------------------------
-
-%% Returns the options associated with Node in the RTStates structure.
-get_opts_rtstates(Node,RTStates) ->
- case lists:keysearch(Node,1,RTStates) of
- {value,{_,_,Opts}} ->
- {ok,Opts};
- false ->
- false
- end.
-
-%% ------------------------------------------------------------------------------
-%% Functions working on the tracerdata structure, which is a part of the loopdata.
-%% The tracerdata structure is an ETS-table of type bag storing:
-%% {Node,TracerData}.
-%% Note that there can of course be multiple entries for a node.
-%% ------------------------------------------------------------------------------
-
-%% Help function which takes a tracerdata loopdata structure and returns a list
-%% of all stored tracerdata for a certain Node.
-find_tracerdata_for_node_trd(Node,TRD) ->
- case ets:lookup(TRD,Node) of
- Result when list(Result) ->
- lists:map(fun({_Node,TracerData})->TracerData end,Result);
- _ -> % Should probably never happend.
- []
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Functions working on the activity storage structure, which is part of the
-%% loopdata. It stores entries about things that needs to be "redone" in case
-%% of a reactivation of the node. The time order is also important.
-%% Note that for every ActivityType there must be a "handler" in the reactivation
-%% functionality.
-%%
-%% The structure is a bag of {Node,ActivityType,What}.
-%% ActivityType/What=tf/{Op,TraceConfList}|tpm/{Op,[Mod,Func,Arity,MS,CallFunc]}
-%% /{Op,[Mod,Func,Arity,MS,CallFunc,ReturnFunc]}
-%% /{Op,[]}
-%% TraceConfList=[{Proc,Flags},...]
-%% How=true|false
-%% ------------------------------------------------------------------------------
-
-%% Function that adds meta-pattern activities to the activity storage. Note
-%% that one of the parameters to the function is a return value from an
-%% inviso call. In that way we do not enter activities that were unsuccessful.
-%% Op can be either the setting or clearing of a meta pattern.
-%% Returns a new ACTstorage.
-add_tpm_actstorage([{Node,ok}|Rest],Op,InvisoCmdParams,ACTstorage) ->
- true=ets:insert(ACTstorage,{Node,tpm,{Op,InvisoCmdParams}}),
- add_tpm_actstorage(Rest,Op,InvisoCmdParams,ACTstorage);
-add_tpm_actstorage([_|Rest],Op,InvisoCmdParams,ACTstorage) ->
- add_tpm_actstorage(Rest,Op,InvisoCmdParams,ACTstorage);
-add_tpm_actstorage([],_,_,ACTstorage) ->
- ACTstorage.
-
-%% Function that adds process trace-flags to the activity storage. Note that one
-%% of the parameters is the return value from an inviso function. Meaning that
-%% if the flags failed in their entirety, no activity will be saved. If only
-%% some of the flags failed, we will not go through the effort of trying to find
-%% out exactly which.
-%% Returns a new activity storage structure.
-add_tf_actstorage([{_Node,{error,_Reason}}|Rest],Op,TraceConfList,ACTstorage) ->
- add_tf_actstorage(Rest,Op,TraceConfList,ACTstorage);
-add_tf_actstorage([{Node,_Result}|Rest],Op,TraceConfList,ACTstorage) ->
- true=ets:insert(ACTstorage,{Node,tf,{Op,TraceConfList}}),
- add_tf_actstorage(Rest,Op,TraceConfList,ACTstorage);
-add_tf_actstorage([],_,_,ACTstorage) ->
- ACTstorage.
-%% ------------------------------------------------------------------------------
-
-%% Finds all activities associated with Node. Returns a list of them in the
-%% same order as they were inserted.
-get_activities_actstorage(Node,ACTstorage) ->
- case ets:lookup(ACTstorage,Node) of
- [] ->
- false;
- Result when list(Result) ->
- {ok,lists:map(fun({_N,Type,What})->{Type,What} end,Result)}
- end.
-%% ------------------------------------------------------------------------------
-
-%% Function removing all activity entries associated with Node. This is useful
-%% if the Node disconnects for instance.
-del_node_actstorage(Node,ACTstorage) ->
- ets:delete(ACTstorage,Node),
- ACTstorage.
-%% ------------------------------------------------------------------------------
-
+%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% Description: +%% The runtime component of the trace tool Inviso. +%% +%% Authors: +%% Lennart �hman, [email protected] +%% ----------------------------------------------------------------------------- + +-module(inviso_tool_sh). + +%% Inviso Session Handler. +%% This is the code for the session handler process. Its purpose is that we have +%% one session handler process for each trace session started through the +%% start_session inviso tool API. The session handler process is responsible for: +%% +%% -Knowing the state/status of all participating runtime components. +%% -Keeping storage of all tracerdata all our participants have used. This means +%% also to find out the tracerdata of runtime components connecting by them +%% selves. +%% +%% STORAGE STRATEGY +%% ---------------- +%% The local information storage can be changed by two things. Either by executing +%% commands issued through our APIs. Or by receiving trace_event from the control +%% component. When we execute commands, a corresponding event will also follow. +%% Meaning that in those situations we are informed twice. +%% A simple strategy could be to wait for the event even when doing the changes +%% to the runtime components our self (through commands). But that may result in +%% a small time frame where someone might do yet another command and failing +%% because the local information storage is not uptodate as it would have been +%% expected to be. Therefore we always update the local storage when making changes +%% to a runtime component our selves. There will eventually be a double update +%% through an incoming event. But the storage must coop with that, preventing +%% inconsitancies to happend. An example of a strategy is that the tracerdata table +%% is a bag, not allowing for double entries of the same kind. Therefore a double +%% update is harmless there. + +%% ------------------------------------------------------------------------------ +%% Module wide constants. +%% ------------------------------------------------------------------------------ +-define(LOCAL_RUNTIME,local_runtime). % Used as node name when non-disitrbuted. +-define(TRACING,tracing). % A state defined by the control component. +-define(RUNNING,running). % A status according to control componet. + +-define(COPY_LOG_FROM,copy_log_from). % Common fileystem option. +%% ------------------------------------------------------------------------------ + +%% ------------------------------------------------------------------------------ +%% API exports. +%% ------------------------------------------------------------------------------ +-export([start_link/5,start_link/8]). +-export([cancel_session/1,stop_session/3]). +-export([reactivate/1,reactivate/2]). +-export([ctpl/5,tpl/5,tpl/6,tpl/7, + tf/2,tf/3, + tpm_localnames/2,init_tpm/6,init_tpm/9,tpm/6,tpm/7,tpm/10, + tpm_ms/7,ctpm_ms/6,ctpm/5 + ]). +%% ------------------------------------------------------------------------------ + + +%% ------------------------------------------------------------------------------ +%% Internal exports. +%% ------------------------------------------------------------------------------ +-export([init/1,handle_call/3,handle_info/2,terminate/2]). + +-export([get_loopdata/1]). +%% ------------------------------------------------------------------------------ + + +%% ------------------------------------------------------------------------------ +%% Includes. +%% ------------------------------------------------------------------------------ +-include_lib("kernel/include/file.hrl"). % Necessary for file module. +%% ------------------------------------------------------------------------------ + + +%% ============================================================================== +%% Exported API functions. +%% ============================================================================== + +%% start_link(From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,NodesIn,NodesNotIn) = +%% {ok,Pid} | {error,Reason} +%% From= pid(), the initial client expecting the reply. +%% NodeParams=[{Node,TracerData},{Node,TracerData,Opts}...] +%% CtrlNode=atom() | 'void', the node where the trace control component is. +%% CtrlPid=pid(), the pid of the trace control component. +%% SafetyCatches= +%% Dir=string(), where to place fetched logs and the merged log. +%% Dbg=debug structure. +%% NodesIn=[Node,...], list of nodes already in another session. +%% NodesNotIn=[Node,...], list of nodes not in another session. +%% +%% Starts a session-handler. It keeps track of the the state and status of all +%% participating runtime components. Note that there is a non-distributed case too. +%% In the non-distributed case there is no things such as CtrlNode. +start_link(From,TracerData,CtrlPid,SafetyCatches,Dbg) -> + gen_server:start_link(?MODULE, + {self(),From,TracerData,CtrlPid,SafetyCatches,Dbg}, + []). + +start_link(From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,Dbg,NodesIn,NodesNotIn) -> + gen_server:start_link(?MODULE, + {self(),From,NodeParams,CtrlNode,CtrlPid, + SafetyCatches,Dbg,NodesIn,NodesNotIn}, + []). +%% ------------------------------------------------------------------------------ + +%% Stops tracing where it is ongoing. Fetches all logfiles. +stop_session(SID,Dir,Prefix) -> + gen_server:call(SID,{stop_session,Dir,Prefix}). +%% ------------------------------------------------------------------------------ + +%% stop_session(SID) = ok +%% +%% Cancels the session brutaly. All runtime components are made to stop tracing, +%% all local log files are removed using the tracerdata we know for them. +cancel_session(SID) -> + gen_server:call(SID,cancel_session). +%% ------------------------------------------------------------------------------ + +%% reactivate(SID) = {ok, +%% reactivate(SID,Nodes) = {ok,NodeResults} | {error,Reason}. +%% SID=session id, pid(). +%% Nodes=[Node,...] +%% NodeResult=[{Node,Result},...] +%% Result={Good,Bad} +%% Good,Bad=integer(), the number of redone activities. +%% +%% Function which reactivates runtime components being suspended. This is done +%% replaying all trace flags (in the correct order) to the corresponding nodes. +%% Note that this may also mean turning flags off. Like first turning them on +%% then off a split second later. +reactivate(SID) -> + gen_server:call(SID,reactivate). %% NOT IMPLEMENTED YET. +reactivate(SID,Nodes) -> + gen_server:call(SID,{reactivate,Nodes}). +%% ------------------------------------------------------------------------------ + + +%% tpl(SessionID,Mod,Func,Arity,MS)= +%% tpl(SessionID,Mod,Func,Arity,MS,Opts)={ok,N}|{error,Reason}. +%% tpl(SessionID,Nodes,Mod,Func,Arity,MS)= +%% tpl(SessionID,Nodes,Mod,Func,Arity,MS,Opts)={ok,Result}|{error,Reason} +%% Mod='_' | ModuleName | ModRegExp | {DirRegExp,ModRegExp} +%% ModRegExp=DirRegExp= string() +%% Func='_' | FunctionName +%% Arity='_' | integer() +%% MS=[] | false | a match specification +%% Opts=[Opts,...] +%% Opt={arg,Arg}, disable_safety, {expand_regexp_at,NodeName}, only_loaded +%% Nodes=[NodeName,...] +tpl(SID,Mod,Func,Arity,MS) -> + gen_server:call(SID,{tp,tpl,Mod,Func,Arity,MS,[]}). +tpl(SID,Mod,Func,Arity,MS,Opts) when list(MS);MS==true;MS==false -> + gen_server:call(SID,{tp,tpl,Mod,Func,Arity,MS,Opts}); +tpl(SID,Nodes,Mod,Func,Arity,MS) when integer(Arity);Arity=='_' -> + gen_server:call(SID,{tp,tpl,Nodes,Mod,Func,Arity,MS,[]}). +tpl(SID,Nodes,Mod,Func,Arity,MS,Opts) -> + gen_server:call(SID,{tp,tpl,Nodes,Mod,Func,Arity,MS,Opts}). +%% ------------------------------------------------------------------------------ + +%% ctpl(SessionID,Nodes,Mod,Func,Arity)= +%% See tpl/X for arguments. +%% +%% Removes local trace-patterns from functions. +ctpl(SID,Nodes,Mod,Func,Arity) -> + gen_server:call(SID,{ctp,ctpl,Nodes,Mod,Func,Arity}). +%% ------------------------------------------------------------------------------ + + +tpm_localnames(SID,Nodes) -> + gen_server:call(SID,{tpm_localnames,Nodes}). + +%% tpm_globalnames(SID,Nodes) -> +%% gen_server:call(SID,{tpm_globalnames,Nodes}). + +init_tpm(SID,Nodes,Mod,Func,Arity,CallFunc) -> + gen_server:call(SID,{init_tpm,Nodes,Mod,Func,Arity,CallFunc}). +init_tpm(SID,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> + gen_server:call(SID, + {init_tpm,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc}). +tpm(SID,Nodes,Mod,Func,Arity,MS) -> + gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS}). +tpm(SID,Nodes,Mod,Func,Arity,MS,CallFunc) -> + gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS,CallFunc}). +tpm(SID,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) -> + gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc}). + +tpm_ms(SID,Nodes,Mod,Func,Arity,MSname,MS) -> + gen_server:call(SID,{tpm_ms,Nodes,Mod,Func,Arity,MSname,MS}). + +ctpm_ms(SID,Nodes,Mod,Func,Arity,MSname) -> + gen_server:call(SID,{tpm_ms,Nodes,Mod,Func,Arity,MSname}). + +ctpm(SID,Nodes,Mod,Func,Arity) -> + gen_server:call(SID,{ctpm,Nodes,Mod,Func,Arity}). +%% ------------------------------------------------------------------------------ + + +%% tf(SessionID,Nodes,TraceConfList)= +%% TraceConfList=[{PidSpec,Flags},...] +%% PidSpec=pid()|atom()|all|new|existing +%% Flags=[Flag,...] +tf(SID,TraceConfList) -> + gen_server:call(SID,{tf,TraceConfList}). +tf(SID,Nodes,TraceConfList) -> + gen_server:call(SID,{tf,Nodes,TraceConfList}). +%% ------------------------------------------------------------------------------ + + +get_loopdata(SID) -> + gen_server:call(SID,get_loopdata). +%% ------------------------------------------------------------------------------ + +%% ============================================================================== +%% Genserver call-backs. +%% ============================================================================== + +%% Initial function for the session handler process. The nodes participating in +%% the session must previously have been added to our control component by the tool. +%% The session handler first finds out the state/status of the specified runtime +%% components, then it tries to initiate tracing on those where it is applicable. +%% Note that a reply to the initial (tool)client is done from here instead from +%% the tool-server. +init({Parent,From,TracerData,CtrlPid,SafetyCatches,Dbg}) -> % The non-distributed case. + {ok,StateStatus}=init_rtcomponent_states([],void,CtrlPid,[?LOCAL_RUNTIME]), + case is_tool_internal_tracerdata(TracerData) of + false -> % We shall initiate local runtime. + case inviso:init_tracing(TracerData) of + ok -> + gen_server:reply(From,{ok,{self(),ok}}), + {ok,mk_ld(Parent, + void, + CtrlPid, + to_rtstates([{?LOCAL_RUNTIME,{tracing,?RUNNING},[]}]), + [{?LOCAL_RUNTIME,TracerData}], + [], + SafetyCatches, + Dbg)}; + {error,Reason} -> % It might have become suspended?! + gen_server:reply(From,{error,Reason}), + {ok,mk_ld(Parent, + void, + CtrlPid, + to_rtstates([{?LOCAL_RUNTIME,StateStatus,[]}]), + [{?LOCAL_RUNTIME,TracerData}], + [], + SafetyCatches, + Dbg)} + end; + true -> % We shall not pass this one on. + gen_server:reply(From,{ok,{self(),ok}}), % Then it is ok. + {ok,mk_ld(Parent, + void, + CtrlPid, + to_rtstates([{?LOCAL_RUNTIME,StateStatus,[]}]), + [], + [?LOCAL_RUNTIME], + SafetyCatches, + Dbg)} + end; +init({Parent,From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,Dbg,NodesIn,NodesNotIn}) -> + case init_rtcomponent_states(NodeParams,CtrlNode,CtrlPid,NodesNotIn) of + {ok,States} -> % A list of {Node,{State,Status},Opts}. + {NodeParams2,Nodes2}=remove_nodeparams(NodesIn,NodeParams), + case inviso_tool_lib:inviso_cmd(CtrlNode,init_tracing,[NodeParams2]) of + {ok,Result} -> % Resulted in state changes! + RTStates=set_tracing_rtstates(to_rtstates(States),Result), + ReplyValue=init_fix_resultnodes(NodesIn,Nodes2,Result), + gen_server:reply(From,{ok,{self(),ReplyValue}}), + {ok,mk_ld(Parent,CtrlNode,CtrlPid,RTStates, + NodeParams2,Nodes2,SafetyCatches,Dbg)}; + {error,Reason} -> % Some general failure. + inviso_tool_lib:inviso_cmd(CtrlNode,unsubscribe,[]), + gen_server:reply(From,{error,{init_tracing,Reason}}), + {stop,{init_tracing,Reason}}; + What -> + io:format("GOT:~n~w~n",[What]), + exit(foo) + end; + {error,Reason} -> % Unable to get the state/status. + inviso_tool_lib:inviso_cmd(CtrlNode,unsubscribe,[]), + gen_server:reply(From,{error,Reason}), + {stop,{error,Reason}}; + What -> + io:format("GOT:~n~w~n",[What]), + exit(foo) + end. +%% ------------------------------------------------------------------------------ + +%% To stop a session means stop the tracing and remove all local files on the +%% runtime nodes. We do have a table with all tracer data and that is how we are +%% going to recreate what files to remove. +%% Since runtime components may actually change state when this procedure is +%% on-going, we do not care! It is the state in the session handling process at +%% the time of start of this procedure which is used. +handle_call(cancel_session,_From,LD) -> + CtrlNode=get_ctrlnode_ld(LD), + RTStates=get_rtstates_ld(LD), + Dbg=get_dbg_ld(LD), + TracingNodes=get_all_tracing_nodes_rtstates(RTStates), + case stop_all_tracing(CtrlNode,Dbg,TracingNodes) of + ok-> % Hopefully all nodes are stopped now. + AvailableNodes=get_all_available_nodes_rtstates(RTStates), + TRDstorage=get_trdstorage_ld(LD), + remove_all_local_logs(CtrlNode,TRDstorage,AvailableNodes,Dbg), + {stop,normal,ok,LD}; % LD actually not correct now! + {error,Reason} -> % Some serious error when stop_tracing. + {stop,normal,{error,Reason},LD} + end; +%% ------------------------------------------------------------------------------ + +%% *Stop all tracing on runtime components still tracing. +%% *Copy all local log files to the collection directory. +handle_call({stop_session,Dir,Prefix},_From,LD) -> + case check_directory_exists(Dir) of % Check that this directory exists here. + true -> + RTStates=get_rtstates_ld(LD), + CtrlNode=get_ctrlnode_ld(LD), + Dbg=get_dbg_ld(LD), + TracingNodes=get_all_tracing_nodes_rtstates(RTStates), + case stop_all_tracing(CtrlNode,Dbg,TracingNodes) of + ok -> % Hopefully no node is still tracing now. + TRDstorage=get_trdstorage_ld(LD), + AvailableNodes=get_all_available_nodes_rtstates(RTStates), + {FailedNodes,FetchedFiles}= + transfer_logfiles(RTStates,CtrlNode,Dir,Prefix, + TRDstorage,Dbg,AvailableNodes), + RemoveNodes= % We only delete local logs where fetch ok. + lists:filter(fun(N)-> + case lists:keysearch(N,1,FailedNodes) of + {value,_} -> + false; + false -> + true + end + end, + AvailableNodes), + remove_all_local_logs(CtrlNode,TRDstorage,RemoveNodes,Dbg), + {stop,normal,{ok,{FailedNodes,FetchedFiles}},LD}; + {error,Reason} -> % Some general failure, quit. + {stop,normal,{error,Reason},LD} + end; + false -> % You specified a non-existing directory! + {reply,{error,{faulty_dir,Dir}},LD} + end; +%% ------------------------------------------------------------------------------ + +handle_call({reactivate,Nodes},_From,LD) -> + RTStates=get_rtstates_ld(LD), + {OurNodes,OtherNodes}= + remove_nodes_not_ours(Nodes,get_all_session_nodes_rtstates(RTStates)), + CtrlNode=get_ctrlnode_ld(LD), + ACTstorage=get_actstorage_ld(LD), + case h_reactivate(CtrlNode,OurNodes,ACTstorage) of + {ok,Results} -> % A list of {Node,Result}. + if + OtherNodes==[] -> % Normal case, no non-session nodes. + {reply,{ok,Results},LD}; + true -> % Add error values for non-session nodes. + {reply, + {ok, + lists:map(fun(N)->{N,{error,not_in_session}} end,OtherNodes)++ + Results}, + LD} + end; + {error,Reason} -> % Then this error takes presidence. + {reply,{error,Reason},LD} + end; +%% ------------------------------------------------------------------------------ + +%% Call-back for set trace-pattern for both global and local functions. +handle_call({tp,PatternFunc,Mod,F,A,MS,Opts},_From,LD) -> + Reply=h_tp(all,PatternFunc,Mod,F,A,MS,Opts,LD), % For all active nodes in the session. + {reply,Reply,LD}; +handle_call({tp,PatternFunc,Nodes,Mod,F,A,MS,Opts},_From,LD) -> + RTStates=get_rtstates_ld(LD), + SNodes=get_all_session_nodes_rtstates(RTStates), % Notes belongoing to the session. + {Nodes2,FaultyNodes}=remove_nodes_not_ours(Nodes,SNodes), + Reply=h_tp(Nodes2,PatternFunc,Mod,F,A,MS,Opts,LD), + ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,FaultyNodes), + {reply,ErrorReply++Reply,LD}; +%% ------------------------------------------------------------------------------ + +%% Call-back handling the removal of both local and global trace-patterns. +%% NOT IMPLEMENTED YET. +handle_call({ctp,PatternFunc,Nodes,Mod,F,A},_From,LD) -> + Reply=h_ctp(Nodes,PatternFunc,Mod,F,A,LD), + {reply,Reply,LD}; +%% ------------------------------------------------------------------------------ + +handle_call({tpm_localnames,Nodes},_From,LD) -> + RTStates=get_rtstates_ld(LD), + OurNodes=get_all_session_nodes_rtstates(RTStates), + {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes), + ACTstorage=get_actstorage_ld(LD), + {Reply,NewACTstorage}= + h_tpm_localnames(get_ctrlnode_ld(LD),Nodes2,RTStates,ACTstorage), + ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes), + {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)}; + +handle_call({init_tpm,Nodes,Mod,Func,Arity,CallFunc},_From,LD) -> + RTStates=get_rtstates_ld(LD), + OurNodes=get_all_session_nodes_rtstates(RTStates), + {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes), + ACTstorage=get_actstorage_ld(LD), + {Reply,NewACTstorage}= + h_all_tpm(get_ctrlnode_ld(LD), + Nodes2, + init_tpm, + [Mod,Func,Arity,CallFunc], + RTStates, + ACTstorage), + ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes), + {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)}; + +handle_call({init_tpm,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc},_From,LD) -> + RTStates=get_rtstates_ld(LD), + OurNodes=get_all_session_nodes_rtstates(RTStates), + {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes), + ACTstorage=get_actstorage_ld(LD), + {Reply,NewACTstorage}= + h_all_tpm(get_ctrlnode_ld(LD), + Nodes2, + init_tpm, + [Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc], + RTStates, + ACTstorage), + ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes), + {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)}; + +handle_call({tpm,Nodes,Mod,Func,Arity,MS},_From,LD) -> + RTStates=get_rtstates_ld(LD), + OurNodes=get_all_session_nodes_rtstates(RTStates), + {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes), + ACTstorage=get_actstorage_ld(LD), + {Reply,NewACTstorage}= + h_all_tpm(get_ctrlnode_ld(LD),Nodes2,tpm,[Mod,Func,Arity,MS],RTStates,ACTstorage), + ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes), + {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)}; + +handle_call({tpm,Nodes,Mod,Func,Arity,MS,CallFunc},_From,LD) -> + RTStates=get_rtstates_ld(LD), + OurNodes=get_all_session_nodes_rtstates(RTStates), + {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes), + ACTstorage=get_actstorage_ld(LD), + {Reply,NewACTstorage}= + h_all_tpm(get_ctrlnode_ld(LD), + Nodes2, + tpm, + [Mod,Func,Arity,MS,CallFunc], + RTStates, + ACTstorage), + ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes), + {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)}; + +handle_call({tpm,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc},_From,LD) -> + RTStates=get_rtstates_ld(LD), + OurNodes=get_all_session_nodes_rtstates(RTStates), + {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes), + ACTstorage=get_actstorage_ld(LD), + {Reply,NewACTstorage}= + h_all_tpm(get_ctrlnode_ld(LD), + Nodes2, + tpm, + [Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc], + RTStates, + ACTstorage), + ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes), + {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)}; + +handle_call({tpm_ms,Nodes,Mod,Func,Arity,MSname,MS},_From,LD) -> + RTStates=get_rtstates_ld(LD), + OurNodes=get_all_session_nodes_rtstates(RTStates), + {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes), + ACTstorage=get_actstorage_ld(LD), + {Reply,NewACTstorage}= + h_all_tpm(get_ctrlnode_ld(LD), + Nodes2, + tpm_ms, + [Mod,Func,Arity,MSname,MS], + RTStates, + ACTstorage), + ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes), + {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)}; + +handle_call({ctpm_ms,Nodes,Mod,Func,Arity,MSname},_From,LD) -> + RTStates=get_rtstates_ld(LD), + OurNodes=get_all_session_nodes_rtstates(RTStates), + {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes), + ACTstorage=get_actstorage_ld(LD), + {Reply,NewACTstorage}= + h_all_tpm(get_ctrlnode_ld(LD), + Nodes2, + ctpm_ms, + [Mod,Func,Arity,MSname], + RTStates, + ACTstorage), + ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes), + {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)}; + +handle_call({ctpm,Nodes,Mod,Func,Arity},_From,LD) -> + RTStates=get_rtstates_ld(LD), + OurNodes=get_all_session_nodes_rtstates(RTStates), + {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes), + ACTstorage=get_actstorage_ld(LD), + {Reply,NewACTstorage}= + h_all_tpm(get_ctrlnode_ld(LD),Nodes2,ctpm,[Mod,Func,Arity],RTStates,ACTstorage), + ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes), + {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)}; +%% ------------------------------------------------------------------------------ + +%% Call-back for setting process trace-flags. Handles both distributed and non- +%% distributed case. +handle_call({tf,TraceConfList},From,LD) -> + handle_call({tf,all,TraceConfList},From,LD); +handle_call({tf,Nodes,TraceConfList},_From,LD) -> + {Reply,NewACTstorage}=h_tf(get_ctrlnode_ld(LD), + Nodes, + TraceConfList, + get_actstorage_ld(LD), + get_rtstates_ld(LD)), + {reply,Reply,put_actstorage_ld(NewACTstorage,LD)}; +%% ------------------------------------------------------------------------------ + + + +handle_call(get_loopdata,_From,LD) -> + io:format("The loopdata:~n~p~n",[LD]), + {reply,ok,LD}. +%% ------------------------------------------------------------------------------ + + +%% Clause handling an incomming state-change event from the control component. +%% Note that it does not have to be one of our nodes since it is not possible +%% to subscribe to certain node-events. +%% We may very well get state-change events for state-changes we are the source +%% to our selves. Those state-changes are already incorporated into the RTStates. +%% There is however no harm in doing them again since we know that this event +%% message will reach us before a reply to a potentially following state-change +%% request will reach us. Hence we will do all state-changes in the correct order, +%% even if sometimes done twice. +handle_info({trace_event,CtrlPid,_Time,{state_change,Node,{State,Status}}},LD) -> + case get_ctrlpid_ld(LD) of + CtrlPid -> % It is from our control component. + case {State,Status} of + {?TRACING,?RUNNING} -> % This is the only case when new tracerdata! + NewTracerData=add_current_tracerdata_ld(get_ctrlnode_ld(LD), + Node, + get_rtstates_ld(LD), + get_trdstorage_ld(LD)), + NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)), + {noreply,put_trdstorage_ld(NewTracerData, + put_rtstates_ld(NewRTStates,LD))}; + _ -> % In all other cases, just fix rtstates. + NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)), + {noreply,put_rtstates_ld(NewRTStates,LD)} + end; + _ -> + {noreply,LD} + end; +%% If a new runtime component connects to our trace control component, and it is +%% in our list of runtime components belonging to this session, we may update its +%% state to now being present. Otherwise it does not belong to this session. +%% Note that we avoid updating an already connected runtime component. This +%% can happend if it connected by itself after we started the session handler, +%% but before we managed to initiate tracing. Doing so or not will not result in +%% any error in the long run, but during a short period of time we might be +%% prevented from doing things with the runtime though it actually is tracing. +handle_info({trace_event,CtrlPid,_Time,{connected,Node,{_Tag,{State,Status}}}},LD) -> + case get_ctrlpid_ld(LD) of + CtrlPid -> % It is from our control component. + case get_statestatus_rtstates(Node,get_rtstates_ld(LD)) of + {ok,unavailable} -> % This is the situation when we update! + NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)), + {noreply,put_rtstates_ld(NewRTStates,LD)}; + _ -> % In all other cases, let it be. + {noreply,LD} + end; + _ -> % Not from our control component. + {noreply,LD} + end; +%% If a runtime component disconnects we mark it as unavailable. We must also +%% remove all saved trace-flags in order for them to not be accidently reactivated +%% should the runtime component reconnect and then suspend. +handle_info({trace_event,CtrlPid,_Time,{disconnected,Node,_}},LD) -> + case get_ctrlpid_ld(LD) of + CtrlPid -> % It is from our control component. + NewRTStates=set_unavailable_rtstates(Node,get_rtstates_ld(LD)), + NewACTstorage=del_node_actstorage(Node,get_actstorage_ld(LD)), + {noreply,put_actstorage_ld(NewACTstorage,put_rtstates_ld(NewRTStates,LD))}; + _ -> + {noreply,LD} + end; +handle_info(_,LD) -> + {noreply,LD}. +%% ------------------------------------------------------------------------------ + +%% In terminate we cancel our subscription to event from the trace control component. +%% That should actually not be necessary, but lets do it the correct way! +terminate(_,LD) -> + case get_ctrlnode_ld(LD) of + void -> % Non-distributed. + inviso:unsubscribe(); + Node -> + inviso_tool_lib:inviso_cmd(Node,unsubscribe,[]) + end. +%% ------------------------------------------------------------------------------ + + + +%% ============================================================================== +%% First level help functions to call-backs. +%% ============================================================================== + +%% ------------------------------------------------------------------------------ +%% Help functions to init. +%% ------------------------------------------------------------------------------ + +%% Help function which find out the state/status of the runtime components. +%% Note that since we have just started subscribe to state changes we must +%% check our inqueue to see that we have no waiting messages for the nodes +%% we learned the state/status of. If there is a waiting message we don't +%% know whether that was a state change received before or after the state +%% check was done. We will then redo the state-check. +%% Returns {ok,States} or {error,Reason}. +%% Where States is [{Node,{State,Status},Opts},...]. +%% Note that {error,Reason} can not occur in the non-distributed case. +init_rtcomponent_states(NodeParams,void,CtrlPid,Nodes) -> % The non-distributed case. + ok=inviso:subscribe(), + init_rtcomponent_states_2(NodeParams,void,CtrlPid,Nodes,[]); +init_rtcomponent_states(NodeParams,CtrlNode,CtrlPid,Nodes) -> + ok=inviso_tool_lib:inviso_cmd(CtrlNode,subscribe,[]), + init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,Nodes,[]). + +init_rtcomponent_states_2(_,_,_,[],States) -> + {ok,States}; +init_rtcomponent_states_2(NodeParams,void,CtrlPid,_Nodes,States) -> + case inviso:get_status() of + {ok,StateStatus} -> % Got its state/status, now... + {ProblemNodes,NewStates}= + init_rtcomponent_states_3(NodeParams,CtrlPid,[{?LOCAL_RUNTIME,{ok,StateStatus}}], + [],States), + init_rtcomponent_states_2(NodeParams,void,CtrlPid,ProblemNodes,NewStates); + {error,_Reason} -> % The runtime is not available!? + {ok,[{?LOCAL_RUNTIME,unavailable,[]}]} % Create the return value immediately. + end; +init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,Nodes,States) -> + case inviso_tool_lib:inviso_cmd(CtrlNode,get_status,[Nodes]) of + {ok,NodeResult} -> + {ProblemNodes,NewStates}= + init_rtcomponent_states_3(NodeParams,CtrlPid,NodeResult,[],States), + init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,ProblemNodes,NewStates); + {error,Reason} -> % Severe problem, abort the session. + {error,{get_status,Reason}} + end. + +%% Traverses the list of returnvalues and checks that we do not have an event +%% waiting in the message queue. If we do have, it is a problem. That node will +%% be asked about its state again. +%% Note that it is here we construct the RTStatesList. +init_rtcomponent_states_3(NodeParams,CtrlPid,[{Node,{ok,{State,Status}}}|Rest],Problems,States) -> + receive + {trace_event,CtrlPid,_Time,{state_change,Node,_}} -> + init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,[Node|Problems],States) + after + 0 -> % Not in msg queue, then we're safe! + RTState=case lists:keysearch(Node,1,NodeParams) of + {value,{_Node,_TracerData,Opts}} -> + {Node,{State,Status},Opts}; + _ -> % No option available, use []. + {Node,{State,Status},[]} + end, + init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,Problems,[RTState|States]) + end; +init_rtcomponent_states_3(NodeParams,CtrlPid,[{Node,{error,_Reason}}|Rest],Problems,States) -> + RTState=case lists:keysearch(Node,1,NodeParams) of + {value,{_Node,_TracerData,Opts}} -> + {Node,unavailable,Opts}; + _ -> % No option available, use []. + {Node,unavailable,[]} + end, + init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,Problems,[RTState|States]); +init_rtcomponent_states_3(_,_,[],Problems,States) -> + {Problems,States}. +%% ------------------------------------------------------------------------------ + +%% Help function removing nodes from NodeParams. The reason for this can either +%% be that we are using a tool internal tracerdata that shall not be forwarded to +%% the trace control component, or that the node is actually already part of +%% another session. +%% Returns {NewNodeParams,NodesWhichShallNotBeInitiated}. +remove_nodeparams(Nodes,NodesParams) -> + remove_nodeparams_2(Nodes,NodesParams,[],[]). + +remove_nodeparams_2(Nodes,[NodeParam|Rest],NPAcc,NAcc) when % NPAcc=NodeParamsAcc. + (is_tuple(NodeParam) and ((size(NodeParam)==2) or (size(NodeParam)==3))) -> + Node=element(1,NodeParam), + Params=element(2,NodeParam), % This is tracerdata! + case lists:member(Node,Nodes) of + true -> % Remove this one, in another session. + remove_nodeparams_2(Nodes,Rest,NPAcc,NAcc); + false -> % Ok so far... + case is_tool_internal_tracerdata(Params) of + false -> % Then keep it and use it later! + remove_nodeparams_2(Nodes,Rest,[{Node,Params}|NPAcc],NAcc); + true -> % Since it is, remove it from the list. + remove_nodeparams_2(Nodes,Rest,NPAcc,[Node|NAcc]) + end + end; +remove_nodeparams_2(Nodes,[_|Rest],NPAcc,NAcc) -> % Faulty NodeParam, skip it! + remove_nodeparams_2(Nodes,Rest,NPAcc,NAcc); +remove_nodeparams_2(_,[],NPAcc,NAcc) -> + {lists:reverse(NPAcc),NAcc}. +%% ------------------------------------------------------------------------------ + +%% Help function which adds both the nodes which were already part of another +%% session and the nodes that we actually did not issue any init_tracing for. +%% Returns a new Result list of [{Node,NodeResult},...]. +init_fix_resultnodes(NodesOtherSes,NodesNotInit,Result) -> + NewResult=init_fix_resultnodes_2(NodesOtherSes,{error,in_other_session},Result), + init_fix_resultnodes_2(NodesNotInit,ok,NewResult). + +init_fix_resultnodes_2([Node|Rest],NodeResult,Result) -> + [{Node,NodeResult}|init_fix_resultnodes_2(Rest,NodeResult,Result)]; +init_fix_resultnodes_2([],_,Result) -> + Result. % Append Result to the end of the list. +%% ------------------------------------------------------------------------------ + + +%% ------------------------------------------------------------------------------ +%% Help functions to reactivate. +%% ------------------------------------------------------------------------------ + +h_reactivate(CtrlNode,Nodes,ACTstorage) -> % Distributed case. + case inviso_tool_lib:inviso_cmd(CtrlNode,cancel_suspension,[Nodes]) of + {ok,CSuspResults} -> + {GoodNodes,BadResults}= % Sort out nodes no longer suspended. + lists:foldl(fun({Node,ok},{GoodNs,BadNs})-> + {[Node|GoodNs],BadNs}; + ({Node,{error,Reason}},{GoodNs,BadNs})-> + {GoodNs,[{Node,{error,{cancel_suspension,Reason}}}|BadNs]} + end, + {[],[]}, + CSuspResults), + Results=h_reactivate_redo_activity(CtrlNode,GoodNodes,ACTstorage,[]), + {ok,BadResults++Results}; + {error,Reason} -> % General failure cancelling suspend. + {error,{cancel_suspension,Reason}} + end. +%% ------------------------------------------------------------------------------ + +%% Help function which traverses the list of nodes known to be ours and have +%% cancelled their suspend. If we fail redoing one of the activities associated +%% with a node, the node will be reported in the return value as failed. From +%% that point on its state must be considered unknown since we do not know how +%% many of the activities were successfully redone. +h_reactivate_redo_activity(CtrlNode,[Node|Rest],ACTstorage,Acc) -> + case get_activities_actstorage(Node,ACTstorage) of + {ok,Activities} -> % The node existed in activity storage. + {Good,Bad}=h_reactivate_redo_activity_2(CtrlNode,Node,Activities,0,0), + h_reactivate_redo_activity(CtrlNode,Rest,ACTstorage,[{Node,{Good,Bad}}|Acc]); + false -> % Node not present in activity storage. + h_reactivate_redo_activity(CtrlNode,Rest,ACTstorage,[{Node,{0,0}}|Acc]) + end; +h_reactivate_redo_activity(_CtrlNode,[],_,Acc) -> + lists:reverse(Acc). + +%% Help function actually redoing the activity. Note that there must be one +%% clause here for every type of activity. +%% Returns {NrGoodCmds,NrBadCmds}. +%% The number of good or bad commands refers to inviso commands done. If any +%% of the subparts of such a command returned an error, the command is concidered +%% no good. +h_reactivate_redo_activity_2(CtrlNode,Node,[{tf,{Op,TraceConfList}}|Rest],Good,Bad) -> + case inviso_tool_lib:inviso_cmd(CtrlNode,Op,[[Node],TraceConfList]) of + {ok,[{_Node,{ok,Answers}}]} -> + case h_reactivate_redo_activity_check_tf(Answers) of + ok -> + h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good+1,Bad); + error -> % At least oneReports the first encountered error. + h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1) + end; + {ok,[{_Node,{error,_Reason}}]} -> + h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1); + {error,_Reason} -> % General error when doing cmd. + h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1) + end; +h_reactivate_redo_activity_2(CtrlNode,Node,[{tpm,{Op,InvisoCmdParams}}|Rest],Good,Bad) -> + case inviso_tool_lib:inviso_cmd(CtrlNode,Op,[[Node]|InvisoCmdParams]) of + {ok,[{_Node,ok}]} -> + h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good+1,Bad); + {ok,[{_Node,{error,_Reason}}]} -> + h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1); + {error,_Reason} -> % General error when doing cmd. + h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1) + end; +h_reactivate_redo_activity_2(_CtrlNode,_Node,[],Good,Bad) -> + {Good,Bad}. + +%% Help function traversing a list of results from inviso:tf/2 or inviso:ctf/2 +%% to see if there were any errors. +h_reactivate_redo_activity_check_tf([N|Rest]) when integer(N) -> + h_reactivate_redo_activity_check_tf(Rest); +h_reactivate_redo_activity_check_tf([{error,_Reason}|_]) -> + error; +h_reactivate_redo_activity_check_tf([]) -> + ok. +%% ------------------------------------------------------------------------------ + + +%% ------------------------------------------------------------------------------ +%% Help functions to tp (setting trace patterns, both local and global). +%% ------------------------------------------------------------------------------ + +%% Help function which handles both tpl and tp. Note that the non-distributed case +%% handled with Nodes='all'. +%% Returns what shall be the reply to the client. +h_tp(all,PatternFunc,Mod,F,A,MS,Opts,LD) -> % All available runtime nodes. + Nodes=get_all_available_nodes_rtstates(get_rtstates_ld(LD)), + h_tp(Nodes,PatternFunc,Mod,F,A,MS,Opts,LD); +h_tp(Nodes,PatternFunc,Mod,F,A,MS,Opts,LD) -> % Only certain nodes in the session. + CtrlNode=get_ctrlnode_ld(LD), + Dbg=get_dbg_ld(LD), + SafetyCatches=get_safetycatches_ld(LD), + case inviso_tool_lib:expand_module_names(Nodes,Mod,Opts) of % Take care of any reg-exps. + {multinode_expansion,NodeMods} -> + NodeTPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,NodeMods,F,A,MS), + h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,NodeTPs,[]); + {singlenode_expansion,Modules} -> + TPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,Modules,F,A,MS), + h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg); + module -> + TPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,[Mod],F,A,MS), + h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg); + wildcard -> % Means do for all modules, no safety. + h_tp_do_tps(CtrlNode,Nodes,[{Mod,F,A,MS}],PatternFunc,Dbg); + {error,Reason} -> + {error,Reason} + end. + +%% Note that this function can never be called in the non-distributed case. +h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,[{Node,TPs}|Rest],Accum) -> + case h_tp_do_tps(CtrlNode,[Node],TPs,PatternFunc,Dbg) of + {ok,[{Node,Result}]} -> + h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,Rest,[{Node,Result}|Accum]); + {error,Reason} -> % Failure, but don't stop. + h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,Rest,[{Node,{error,Reason}}|Accum]) + end; +h_tp_node_by_node(_,_,_,[],Accum) -> + {ok,lists:reverse(Accum)}. + +%% Help function which does the actual call to the trace control component. +%% Note that Nodes can be a list of nodes (including a single one) or +%% ?LOCAL_RUNTIME if we are not distributed. The non-distributed case is otherwise +%% detected by the 'void' CtrlNode. +%% Returns {ok,[{Node,{ok,{NrOfFunctions,NrOfErrors}}},{Node,{error,Reason}},...]} or +%% {error,Reason}. In the non-distributed case {ok,{NrOfFunctions,NrOfErros}} or +%% {error,Reason}. +h_tp_do_tps(void,_Nodes,TPs,PatternFunc,Dbg) -> % Non distributed case! + inviso_tool_lib:debug(tp,Dbg,[TPs,PatternFunc]), + case inviso:PatternFunc(TPs) of + {ok,Result} -> % A list of [Nr1,Nr2,error,...]. + {ok, + lists:foldl(fun(N,{AccNr,AccErr}) when integer(N) -> + {AccNr+N,AccErr}; + (error,{AccNr,AccErr}) -> + {AccNr,AccErr+1} + end, + {0,0}, + Result)}; + {error,Reason} -> + {error,{PatternFunc,Reason}} + end; +h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg) -> + inviso_tool_lib:debug(tp,Dbg,[Nodes,TPs,PatternFunc]), + case inviso_tool_lib:inviso_cmd(CtrlNode,PatternFunc,[Nodes,TPs]) of + {ok,Result} -> % Result is [{Node,Result},...]. + {ok, + lists:map(fun({Node,{ok,Res}})-> + {Node,lists:foldl(fun(N,{ok,{AccNr,AccErr}}) when integer(N) -> + {ok,{AccNr+N,AccErr}}; + (error,{AccNr,AccErr}) -> + {ok,{AccNr,AccErr+1}} + end, + {ok,{0,0}}, + Res)}; + ({_Node,{error,Reason}})-> + {error,Reason} + end, + Result)}; + {error,Reason} -> + {error,{PatternFunc,Reason}} + end. +%% ------------------------------------------------------------------------------ + +%% ------------------------------------------------------------------------------ +%% Help functions for removing trace-patterns. +%% ------------------------------------------------------------------------------ + +%% NOT IMPLEMENTED YET. +h_ctp(Node,PatternFunc,Mod,F,A,LD) -> + tbd. +%% ------------------------------------------------------------------------------ + + +%% ------------------------------------------------------------------------------ +%% Help functions for calling the trace information facility. +%% ------------------------------------------------------------------------------ + + +%% Function handling the meta trace pattern for capturing registration of local +%% process names. +h_tpm_localnames(CtrlNode,Nodes,RTStates,ACTstorage) -> + AvailableNodes=get_all_available_nodes_rtstates(RTStates), + {Nodes3,FaultyNodes}=remove_nodes_not_ours(Nodes,AvailableNodes), + case inviso_tool_lib:inviso_cmd(CtrlNode,tpm_localnames,[Nodes3]) of + {ok,Result} -> % That good we want to modify tpmstorage! + NewACTstorage=add_tpm_actstorage(Result,tpm_localnames,[],ACTstorage), + ErrorResult=lists:map(fun(N)->{N,{error,not_available}} end,FaultyNodes), + {{ok,ErrorResult++Result},NewACTstorage}; + {error,Reason} -> % If general failure, do not modify storage. + {{error,Reason},ACTstorage} + end. +%% ------------------------------------------------------------------------------ + +%% Functions calling meta trace functions for specified nodes. This function is +%% intended for use with all tmp function calls, init_tpm,tpm,tpm_ms,ctpm_ms and +%% ctpm. +%% Note that we must store called meta trace functions and their parameters in the +%% activity storage in order to be able to redo them in case of a reactivate. +h_all_tpm(CtrlNode,Nodes,TpmCmd,InvisoCmdParams,RTStates,ACTstorage) -> + AvailableNodes=get_all_available_nodes_rtstates(RTStates), + {Nodes3,FaultyNodes}=remove_nodes_not_ours(Nodes,AvailableNodes), + case inviso_tool_lib:inviso_cmd(CtrlNode,TpmCmd,[Nodes3|InvisoCmdParams]) of + {ok,Result} -> % That good we want to modify tpmstorage! + NewACTstorage=add_tpm_actstorage(Result,TpmCmd,InvisoCmdParams,ACTstorage), + ErrorResult=lists:map(fun(N)->{N,{error,not_available}} end,FaultyNodes), + {{ok,ErrorResult++Result},NewACTstorage}; + {error,Reason} -> % If general failure, do not modify storage. + {{error,Reason},ACTstorage} + end. +%% ------------------------------------------------------------------------------ + + +%% ------------------------------------------------------------------------------ +%% Help functions for set trace flags. +%% ------------------------------------------------------------------------------ + +%% Help function which sets the tracepatterns in TraceConfList for all nodes +%% mentioned in Nodes. Note that non-distributed case is handled with Nodes='all'. +%% Returns {Reply,NewACTstorage} where Reply is whatever shall be returned to caller +%% and NewACTstorage is traceflag storage modified with the flags added to the +%% corresponding nodes. +h_tf(void,_Nodes,TraceConfList,ACTstorage,_RTStates) -> % The non-distributed case. + Reply=inviso:tf(TraceConfList), + NewACTstorage=add_tf_actstorage([{?LOCAL_RUNTIME,Reply}],tf,TraceConfList,ACTstorage), + {Reply,NewACTstorage}; +h_tf(CtrlNode,all,TraceConfList,ACTstorage,RTStates) -> + AllNodes=get_all_session_nodes_rtstates(RTStates), + h_tf(CtrlNode,AllNodes,TraceConfList,ACTstorage,RTStates); +h_tf(CtrlNode,Nodes,TraceConfList,ACTstorage,_RTStates) -> + case inviso_tool_lib:inviso_cmd(CtrlNode,tf,[Nodes,TraceConfList]) of + {ok,Result} -> % That good we want to modify actstorage! + NewACTstorage=add_tf_actstorage(Result,tf,TraceConfList,ACTstorage), + {{ok,Result},NewACTstorage}; + {error,Reason} -> % If general failure, do not modify actstorage. + {{error,Reason},ACTstorage} + end. +%% ------------------------------------------------------------------------------ + +%% ------------------------------------------------------------------------------ +%% Help functions to stop_session. +%% ------------------------------------------------------------------------------ + +%% This function fetches all local log-files using our stored tracerdata. Note +%% that there are two major ways of tranfering logfiles. Either via distributed +%% Erlang or by common filesystem (like NFS). The default is distributed Erlang. +%% But there may be info in the RTStates structure about a common file-system. +%% Returns {FailedNodes,FetchedFileNames} where FailedNodes is a list of +%% nodenames where problems occurred. Note that problems does not necessarily +%% mean that no files were copied. +%% FetchedFileNames contains one or two of the tuples {trace_log,Files} and/or +%% {ti_log,Files}, listing all files successfully fetched. Note that the +%% list of fetched files contains sublists of filenames. One for each node and +%% tracerdata. +%% In the non-distributed system we always use copy (since the files always +%% resides locally). +transfer_logfiles(RTStates,CtrlNode,Dir,Prefix,TRDstorage,Dbg,AvailableNodes) -> + if + CtrlNode==void -> % When non-distributed, always copy! + fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,[?LOCAL_RUNTIME]); + true -> % The distributed case. + {FetchNodes,CopyNodes}=find_logfile_transfer_methods(AvailableNodes,RTStates), + {FailedFetchNodes,FetchedFiles}= + case fetch_logfiles_distributed(CtrlNode,Dir,Prefix,TRDstorage,Dbg,FetchNodes) of + {ok,Failed,Files} -> % So far no disasters. + {Failed,Files}; + {error,Reason} -> % Means all fetch-nodes failed! + inviso_tool_lib:debug(transfer_logfiles,Dbg,[FetchNodes,Reason]), + {lists:map(fun(N)->{N,error} end,FetchNodes),[]} + end, + {FailedCopyNodes,CopiedFiles}= + fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,CopyNodes), + {FailedFetchNodes++FailedCopyNodes,FetchedFiles++CopiedFiles} + end. + +%% Help function which finds out which node we have a common file system with +%% and from which we must make distributed erlang tranfere. +%% Returns {DistributedNodes,CopyNodes} where CopyNode is [{Node,CopyFromDir},...]. +find_logfile_transfer_methods(Nodes,RTStates) -> + find_logfile_transfer_methods_2(Nodes,RTStates,[],[]). + +find_logfile_transfer_methods_2([Node|Rest],RTStates,FetchAcc,CopyAcc) -> + {ok,Opts}=get_opts_rtstates(Node,RTStates), % Node must be in RTStates! + case lists:keysearch(?COPY_LOG_FROM,1,Opts) of + {value,{_,FromDir}} when list(FromDir) -> % Node has common filesystem. + find_logfile_transfer_methods_2(Rest,RTStates,FetchAcc,[{Node,FromDir}|CopyAcc]); + {value,_} -> % Can't understand dir option. + find_logfile_transfer_methods_2(Rest,RTStates,[Node|FetchAcc],CopyAcc); + false -> % Then we want to use fetch instead. + find_logfile_transfer_methods_2(Rest,RTStates,[Node|FetchAcc],CopyAcc) + end; +find_logfile_transfer_methods_2([],_,FetchAcc,CopyAcc) -> + {FetchAcc,CopyAcc}. +%% ------------------------------------------------------------------------------ + +%% Help function which transferes all local logfiles according to the tracerdata +%% stored for the nodes in Nodes. +%% Returns {ok,FailedNodes,FileNodeSpecs} or {error,Reason}. +%% FailedNodes is a list of nodes where fetching logs did not succeed, partially +%% or not at all. +%% FileNames is a list of list of actually fetched files (the name as it is here, including +%% Dir). The sublists are files which belong together. +fetch_logfiles_distributed(CtrlNode,Dir,Prefix,TRDstorage,Dbg,Nodes) -> + LogSpecList=build_logspeclist(Nodes,TRDstorage), + case inviso_fetch_log(inviso_tool_lib:inviso_cmd(CtrlNode, + fetch_log, + [LogSpecList,Dir,Prefix])) of + {ok,Result} -> + Files=get_all_filenames_fetchlog_result(Result,Dbg), + FailedNodes=get_all_failednodes_fetchlog_result(Result), + {ok,FailedNodes,Files}; + {error,Reason} -> % Some general failure! + {error,{fetch_log,Reason}} + end. + +%% Help function which constructs a list {Node,TracerData} for all nodes in Nodes. +%% Note that there may be more than one tracerdata for a node, resulting in multiple +%% tuples for that node. +build_logspeclist(Nodes,TRDstorage) -> + build_logspeclist_2(Nodes,TRDstorage,[]). + +build_logspeclist_2([Node|Rest],TRDstorage,Acc) -> + TRDlist=find_tracerdata_for_node_trd(Node,TRDstorage), % A list of all tracerdata. + build_logspeclist_2(Rest, + TRDstorage, + [lists:map(fun(TRD)->{Node,TRD} end,TRDlist)|Acc]); +build_logspeclist_2([],_,Acc) -> + lists:flatten(Acc). + +%% Help function which translates inviso:fetch_log return values to what I +%% want! +inviso_fetch_log({error,Reason}) -> + {error,Reason}; +inviso_fetch_log({_Success,ResultList}) -> + {ok,ResultList}. + +%% Help function which collects all filenames mentioned in a noderesult structure. +%% The files may or may not be complete. +%% Returns a list of list of filenames. Each sublist contains files which belong +%% together, i.e because they are a wrap-set. +get_all_filenames_fetchlog_result(NodeResult,Dbg) -> + get_all_filenames_fetchlog_result_2(NodeResult,Dbg,[]). + +get_all_filenames_fetchlog_result_2([{Node,{Success,FileInfo}}|Rest],Dbg,Accum) + when Success=/=error, list(FileInfo) -> + SubAccum=get_all_filenames_fetchlog_result_3(FileInfo,[]), + get_all_filenames_fetchlog_result_2(Rest,Dbg,[{Node,SubAccum}|Accum]); +get_all_filenames_fetchlog_result_2([{Node,{error,FReason}}|Rest],Dbg,Accum) -> + inviso_tool_lib:debug(fetch_files,Dbg,[Node,FReason]), + get_all_filenames_fetchlog_result_2(Rest,Dbg,Accum); +get_all_filenames_fetchlog_result_2([],_Dbg,Accum) -> + Accum. + +get_all_filenames_fetchlog_result_3([{FType,Files}|Rest],SubAccum) -> + FilesOnly=lists:foldl(fun({ok,FName},Acc)->[FName|Acc];(_,Acc)->Acc end,[],Files), + get_all_filenames_fetchlog_result_3(Rest,[{FType,FilesOnly}|SubAccum]); +get_all_filenames_fetchlog_result_3([],SubAccum) -> + SubAccum. + +%% Help function which traverses a noderesult and builds a list as return +%% value containing the nodenames of all nodes not being complete. +%% Note that a node may occur multiple times since may have fetched logfiles +%% for several tracerdata from the same node. Makes sure the list contains +%% unique node names. +%% Returns a list nodes. +get_all_failednodes_fetchlog_result(NodeResult) -> + get_all_failednodes_fetchlog_result_2(NodeResult,[]). + +get_all_failednodes_fetchlog_result_2([{_Node,{complete,_}}|Rest],Acc) -> + get_all_failednodes_fetchlog_result_2(Rest,Acc); +get_all_failednodes_fetchlog_result_2([{Node,{_Severity,_}}|Rest],Acc) -> + case lists:member(Node,Acc) of + true -> % Already in the list. + get_all_failednodes_fetchlog_result_2(Rest,Acc); + false -> % Not in Acc, add it! + get_all_failednodes_fetchlog_result_2(Rest,[Node|Acc]) + end; +get_all_failednodes_fetchlog_result_2([],Acc) -> + Acc. +%% ------------------------------------------------------------------------------ + +%% Help function which copies files from one location to Dir and at the same time +%% adds the Prefix to the filename. NodeSpecs contains full path to the files. The +%% reason the node information is still part of NodeSpecs is that otherwise we can +%% not report faulty nodes. Note that one node may occur multiple times since there +%% may be more than one tracerdata for a node. +%% Returns {FailedNodes,Files} where FailedNodes is a list of nodes where problems +%% occurred. Files is a tuple list of [{Node,[{FType,FileNames},...]},...]. +fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,NodeSpecs) -> + CopySpecList=build_copylist(CtrlNode,Dbg,NodeSpecs,TRDstorage), + fetch_logfiles_copy_2(Dir,Prefix,Dbg,CopySpecList,[],[]). + +fetch_logfiles_copy_2(Dir,Prefix,Dbg,[{Node,CopySpecs}|Rest],FailedNodes,Files) -> + case fetch_logfiles_copy_3(Dir,Prefix,Dbg,CopySpecs,[],0) of + {0,LocalFiles} -> % Copy went ok and zero errors. + fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,FailedNodes,[{Node,LocalFiles}|Files]); + {_N,LocalFiles} -> % Copied files, but some went wrong. + case lists:member(Node,FailedNodes) of + true -> % Node already in FailedNodes. + fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,FailedNodes, + [{Node,LocalFiles}|Files]); + false -> % Node not marked as failed, yet. + fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,[Node|FailedNodes], + [{Node,LocalFiles}|Files]) + end + end; +fetch_logfiles_copy_2(_,_,_,[],FailedNodes,Files) -> + {FailedNodes,Files}. % The return value from fetch_logfiles_copy. + +fetch_logfiles_copy_3(Dir,Prefix,Dbg,[{FType,RemoteFiles}|Rest],Results,Errors) -> + {Err,LocalFiles}=fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,RemoteFiles,[],0), + fetch_logfiles_copy_3(Dir,Prefix,Dbg,Rest,[{FType,LocalFiles}|Results],Errors+Err); +fetch_logfiles_copy_3(_,_,_,[],Results,Errors) -> + {Errors,Results}. + +%% For each file of one file-type (e.g. trace_log). +fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,[File|Rest],LocalFiles,Errors) -> + DestName=Prefix++filename:basename(File), + Destination=filename:join(Dir,DestName), + case do_copy_file(File,Destination) of + ok -> + fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,Rest,[DestName|LocalFiles],Errors); + {error,Reason} -> + inviso_tool_lib:debug(copy_files,Dbg,[File,Destination,Reason]), + fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,Rest,LocalFiles,Errors+1) + end; +fetch_logfiles_copy_3_1(_,_,_,[],LocalFiles,Errors) -> + {Errors,LocalFiles}. + +%% Help function which builds a [{Node,[{Type,[ListOfRemoteFiles]}},...}] +%% where Type describes trace_log or ti_log and each entry in ListOfRemoteFiles +%% is a complete path to a file to be copied. +build_copylist(CtrlNode,Dbg,NodeSpecList,TRDstorage) -> + build_copylist_2(CtrlNode,Dbg,NodeSpecList,TRDstorage,[]). + +%% For each node specified in the NodeSpecList. +build_copylist_2(CtrlNode,Dbg,[{Node,SourceDir}|Rest],TRDstorage,Acc) -> + TRDlist=find_tracerdata_for_node_trd(Node,TRDstorage), + CopySpecList=build_copylist_3(CtrlNode,Dbg,SourceDir,Node,TRDlist), + build_copylist_2(CtrlNode,Dbg,Rest,TRDstorage,[CopySpecList|Acc]); +build_copylist_2(_,_,[],_,Acc) -> + lists:flatten(Acc). + +%% For each tracerdata found for the node. +build_copylist_3(void,Dbg,SourceDir,Node,[TRD|Rest]) -> % The non-distributed case. + case inviso:list_logs(TRD) of + {ok,FileSpec} when list(FileSpec) -> % [{trace_log,Dir,Files},...] + NewFileSpec=build_copylist_4(SourceDir,FileSpec,[]), + [{Node,NewFileSpec}|build_copylist_3(void,Dbg,SourceDir,Node,Rest)]; + {ok,no_log} -> % This tracedata not associated with any log. + build_copylist_3(void,Dbg,SourceDir,Node,Rest); + {error,Reason} -> + inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]), + build_copylist_3(void,Dbg,SourceDir,Node,Rest) + end; +build_copylist_3(CtrlNode,Dbg,SourceDir,Node,[TRD|Rest]) -> % The distributed case. + case inviso_tool_lib:inviso_cmd(CtrlNode,list_logs,[[{Node,TRD}]]) of + {ok,[{Node,{ok,FileSpec}}]} when list(FileSpec) -> + NewFileSpec=build_copylist_4(SourceDir,FileSpec,[]), + [{Node,NewFileSpec}|build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest)]; + {ok,[{Node,{ok,no_log}}]} -> % It relays to another node, no files! + build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest); + {ok,[{Node,{error,Reason}}]} -> + inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]), + build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest); + {error,Reason} -> % Some general failure. + inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]), + build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest) + end; +build_copylist_3(_,_,_,_,[]) -> + []. + +%% Help function which makes a [{Type,Files},...] list where each file in Files +%% is with full path as found from our file-system. +build_copylist_4(SourceDir,[{Type,_Dir,Files}|Rest],Accum) -> + NewFiles= + lists:foldl(fun(FName,LocalAcc)->[filename:join(SourceDir,FName)|LocalAcc] end, + [], + Files), + build_copylist_4(SourceDir,Rest,[{Type,NewFiles}|Accum]); +build_copylist_4(_,[],Accum) -> + Accum. + + +%% Help function which copies a file using os:cmd. +%% Returns 'ok' or {error,Reason}. +do_copy_file(Source,Destination) -> + case os:type() of + {win32,_} -> + os:cmd("copy "++Source++" "++Destination), % Perhaps a test on success? + ok; + {unix,_} -> + os:cmd("cp "++Source++" "++Destination), % Perhaps a test on success? + ok + end. +%% ------------------------------------------------------------------------------ + + +%% ------------------------------------------------------------------------------ + +%% ============================================================================== +%% Various help functions. +%% ============================================================================== + +%% Help function going through the Nodes list and checking that only nodes +%% mentioned in OurNodes gets returned. It also makes the nodes in the return +%% value unique. +remove_nodes_not_ours(Nodes,OurNodes) -> + remove_nodes_not_ours_2(Nodes,OurNodes,[],[]). + +remove_nodes_not_ours_2([Node|Rest],OurNodes,OurAcc,OtherAcc) -> + case lists:member(Node,OurNodes) of + true -> % Ok it is one of our nodes. + case lists:member(Node,OurAcc) of + true -> % Already in the list, skip. + remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,OtherAcc); + false -> + remove_nodes_not_ours_2(Rest,OurNodes,[Node|OurAcc],OtherAcc) + end; + false -> + case lists:member(Node,OtherAcc) of + true -> + remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,OtherAcc); + false -> + remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,[Node|OtherAcc]) + end + end; +remove_nodes_not_ours_2([],_,OurAcc,OtherAcc) -> + {lists:reverse(OurAcc),lists:reverse(OtherAcc)}. +%% ------------------------------------------------------------------------------ + +%% Help function which returns 'true' or 'false' depending on if TracerData is +%% meant to be used by the session handler (true) or if it supposed to be passed +%% on to the trace system. +is_tool_internal_tracerdata(_) -> % CURRENTLY NO INTERNAL TRACER DATA! + false. +%% ------------------------------------------------------------------------------ + +%% Help function which checks that all nodes in the first list of nodes exists +%% in the second list of nodes. Returns 'true' or 'false'. The latter if as much +%% as one incorrect node was found. +check_our_nodes([Node|Rest],AllNodes) -> + case lists:member(Node,AllNodes) of + true -> + check_our_nodes(Rest,AllNodes); + false -> % Then we can stop right here. + false + end; +check_our_nodes([],_) -> + true. +%% ------------------------------------------------------------------------------ + +%% Help function which checks that a directory actually exists. Returns 'true' or +%% 'false'. +check_directory_exists(Dir) -> + case file:read_file_info(Dir) of + {ok,#file_info{type=directory}} -> + true; + _ -> % In all other cases it is not valid. + false + end. +%% ------------------------------------------------------------------------------ + +%% This function stops the tracing on all nodes in Nodes. Preferably Nodes is a list +%% of only tracing runtime components. Not that there will actually be any difference +%% since the return value does not reflect how stopping the nodes went. +%% Returns 'ok' or {error,Reason}, the latter only in case of general failure. +stop_all_tracing(void,Dbg,[?LOCAL_RUNTIME]) -> % The non-distributed case, and is tracing. + case inviso:stop_tracing() of + {ok,_State} -> + ok; + {error,Reason} -> % We actually don't care. + inviso_tool_lib:debug(stop_tracing,Dbg,[?LOCAL_RUNTIME,Reason]), + ok + end; +stop_all_tracing(void,_,_) -> % There is no local runtime started. + ok; +stop_all_tracing(CtrlNode,Dbg,Nodes) -> + case inviso_tool_lib:inviso_cmd(CtrlNode,stop_tracing,[Nodes]) of + {ok,Result} -> % The result is only used for debug. + Failed=lists:foldl(fun({N,{error,Reason}},Acc)->[{N,{error,Reason}}|Acc]; + (_,Acc)->Acc + end, + [], + Result), + if + Failed==[] -> + ok; + true -> + inviso_tool_lib:debug(stop_tracing,Dbg,[Nodes,Failed]), + ok + end; + {error,Reason} -> + {error,{stop_tracing,Reason}} + end. +%% ------------------------------------------------------------------------------ + +%% Help function removing all local logs using the tracerdata to determine what +%% logs to remove from where. +%% There is no significant return value since it is not really clear what to do +%% if removal went wrong. The function can make debug-reports thought. +remove_all_local_logs(CtrlNode,TRDstorage,Nodes,Dbg) -> + LogSpecList=build_logspeclist_remove_logs(Nodes,TRDstorage), + case inviso_tool_lib:inviso_cmd(CtrlNode,delete_log,[LogSpecList]) of + {ok,Results} -> + case look_for_errors_resultlist(Results) of + [] -> % No errors found in the result! + true; + Errors -> + inviso_tool_lib:debug(remove_all_local_logs,Dbg,[Errors]), + true + end; + {error,Reason} -> % Some general error. + inviso_tool_lib:debug(remove_all_local_logs,Dbg,[{error,Reason}]), + true + end. + +%% Help function which puts together a list of {Node,Tracerdata} tuples. Note that +%% we must build one tuple for each tracerdata for one node. +build_logspeclist_remove_logs(Nodes,TRDstorage) -> + [{Node,TracerData}||Node<-Nodes,TracerData<-find_tracerdata_for_node_trd(Node,TRDstorage)]. +%% ------------------------------------------------------------------------------ + +%% Help function which traverses a resultlist from an inviso function. Such are +%% built up as [{Node,SubResults},...] where SubResult is a list of tuples for each +%% file-type (e.g trace_log) {FType,FileList} where a FileList is either {error,Reason} +%% or {ok,FileName}. +%% Returns a list of {Node,[{error,Reason},...]}. +look_for_errors_resultlist([{Node,{error,Reason}}|Rest]) -> + [{Node,{error,Reason}}|look_for_errors_resultlist(Rest)]; +look_for_errors_resultlist([{Node,{ok,NResults}}|Rest]) when list(NResults) -> + case look_for_errors_resultlist_2(NResults,[]) of + [] -> + look_for_errors_resultlist(Rest); + Errors -> % A list of lists. + [{Node,lists:flatten(Errors)}|look_for_errors_resultlist(Rest)] + end; +look_for_errors_resultlist([_|Rest]) -> + look_for_errors_resultlist(Rest); +look_for_errors_resultlist([]) -> + []. + +look_for_errors_resultlist_2([{_FType,NSubResult}|Rest],Accum) -> + case lists:filter(fun({error,_Reason})->true;(_)->false end,NSubResult) of + [] -> % No errors for this node. + look_for_errors_resultlist_2(Rest,Accum); + Errors -> % A list of at least one error. + look_for_errors_resultlist_2(Rest,[Errors|Accum]) + end; +look_for_errors_resultlist_2([],Accum) -> + Accum. +%% ------------------------------------------------------------------------------ + + +%% ------------------------------------------------------------------------------ +%% Functions working on the loopdata structure. +%% Its main purpose is to store information about runtime components participating +%% in the session and their current status. +%% ------------------------------------------------------------------------------ + +-record(ld,{parent, + ctrlnode, + ctrlpid, % To where to send inviso cmd. + rtstates, + tracerdata, + safetycatches, + dbg, + actstorage % Activity storage, for reactivate. + }). + +%% Function creating the initial datastructure. +%% The datastructure is [{Node,State},...]. +%% +%% The tracerdata table is a bag simply for the reason that if we try to insert +%% the same tracerdata for a node twice, we will end up with one tracerdata after +%% all. This is useful when we insert tracerdata ourselves, the tracerdata will +%% come as a state-change too. +mk_ld(Parent,CtrlNode,CtrlPid,RTStates,NodeParams,OtherNodes,SafetyCatches,Dbg) -> + TRDtableName=list_to_atom("inviso_tool_sh_trdstorage_"++pid_to_list(self())), + TRDtid=ets:new(TRDtableName,[bag]), + ACTtableName=list_to_atom("inviso_tool_sh_actstorage_"++pid_to_list(self())), + ACTtid=ets:new(ACTtableName,[bag]), + mk_ld_fill_tracerdata(CtrlNode,TRDtid,NodeParams,OtherNodes), % Fill the ETS table. + #ld{parent=Parent, % The tool main process. + ctrlnode=CtrlNode, % Node name where the control component is. + ctrlpid=CtrlPid, % The process id of the control component. + rtstates=RTStates, % All nodes and their state/status. + tracerdata=TRDtid, + safetycatches=SafetyCatches, + dbg=Dbg, + actstorage=ACTtid + }. + +%% Help function which inserts tracer data for the nodes. Note that we can get +%% tracer data either from the return value from init_tracing or by asking the +%% node for it. The latter is necessary for the nodes which were marked not to +%% be initiated by the session handler. This maybe because those nodes have +%% autostarted. +mk_ld_fill_tracerdata(CtrlNode,TId,NodeParams,OtherNodes) -> + mk_ld_fill_tracerdata_nodeparams(TId,NodeParams), + mk_ld_fill_tracerdata_othernodes(CtrlNode,TId,OtherNodes). + +mk_ld_fill_tracerdata_nodeparams(TId,[{Node,TracerData}|Rest]) -> + ets:insert(TId,{Node,TracerData}), + mk_ld_fill_tracerdata_nodeparams(TId,Rest); +mk_ld_fill_tracerdata_nodeparams(_,[]) -> + ok. + +mk_ld_fill_tracerdata_othernodes(_,_,[]) -> % Then not necessary to do anything. + ok; +mk_ld_fill_tracerdata_othernodes(void,TId,[Node]) -> % The non-distributed case. + case inviso:get_tracerdata() of + {error,_Reason} -> % Perhaps in state new or disconnected. + ok; % Do nothing. + {ok,TracerData} -> + ets:insert(TId,{Node,TracerData}) + end; +mk_ld_fill_tracerdata_othernodes(CtrlNode,TId,Nodes) -> + case inviso_tool_lib:invisomd(CtrlNode,get_tracerdata,[Nodes]) of + {ok,Results} -> + mk_ld_fill_tracerdata_othernodes_2(TId,Results); + {error,_Reason} -> % Strange, we will probably crash later. + ok + end. + +mk_ld_fill_tracerdata_othernodes_2(TId,[{_Node,{ok,no_tracerdata}}|Rest]) -> + mk_ld_fill_tracerdata_othernodes_2(TId,Rest); % It was not initiated then! +mk_ld_fill_tracerdata_othernodes_2(TId,[{Node,{ok,TracerData}}|Rest]) -> + ets:insert(TId,{Node,TracerData}), + mk_ld_fill_tracerdata_othernodes_2(TId,Rest); +mk_ld_fill_tracerdata_othernodes_2(_,[]) -> + ok. +%% ------------------------------------------------------------------------------ + +get_ctrlnode_ld(#ld{ctrlnode=CtrlNode}) -> + CtrlNode. +%% ------------------------------------------------------------------------------ + + +get_ctrlpid_ld(#ld{ctrlpid=CtrlPid}) -> + CtrlPid. +%% ------------------------------------------------------------------------------ + +get_rtstates_ld(#ld{rtstates=RTStates}) -> + RTStates. + +put_rtstates_ld(NewRTStates,LD) -> + LD#ld{rtstates=NewRTStates}. +%% ------------------------------------------------------------------------------ + +get_trdstorage_ld(#ld{tracerdata=TId}) -> + TId. + +put_trdstorage_ld(_NewTId,LD) -> + LD. +%% ------------------------------------------------------------------------------ + +%% Help function which adds the current tracerdata of node Node to the tracerdata +%% storage. We only want to add tracerdata we have not seen before. We therefore +%% avoid adding it if the node already is in state ?TRACING. +%% Returns a new tracerdata (what ever it is)! +add_current_tracerdata_ld(CtrlNode,Node,RTStates,TId) -> + case get_statestatus_rtstates(Node,RTStates) of + {ok,{?TRACING,_}} -> % Then we have already added the tracerdata. + TId; % Then do nothing. + {ok,_} -> % Since we were not tracing before. + case add_current_tracerdata_ld_fetchtracerdata(CtrlNode,Node) of + {ok,TracerData} -> + ets:insert(TId,{Node,TracerData}); + no_tracerdata -> % Strange, how could we become tracing + ok; + {error,_Reason} -> % The node perhaps disconnected!? + ok + end; + false -> % Very strange, not our node! + ok % Do nothing. + end. + +add_current_tracerdata_ld_fetchtracerdata(void,_Node) -> + case inviso:get_tracerdata() of + {ok,TracerData} -> + {ok,TracerData}; + {error,no_tracerdata} -> + no_tracerdata; + {error,Reason} -> + {error,Reason} + end; +add_current_tracerdata_ld_fetchtracerdata(CtrlNode,Node) -> + case inviso_tool_lib:inviso_cmd(CtrlNode,get_tracerdata,[[Node]]) of + {ok,[{Node,{ok,TracerData}}]} -> + {ok,TracerData}; + {ok,[{Node,{error,no_tracerdata}}]} -> + no_tracerdata; + {ok,[{Node,{error,Reason}}]} -> + {error,Reason}; + {error,Reason} -> + {error,Reason} + end. +%% ------------------------------------------------------------------------------ + + +get_safetycatches_ld(#ld{safetycatches=SCs}) -> + SCs. +%% ------------------------------------------------------------------------------ + +get_dbg_ld(#ld{dbg=Dbg}) -> + Dbg. +%% ------------------------------------------------------------------------------ + +get_actstorage_ld(#ld{actstorage=ACTstorage}) -> + ACTstorage. + +put_actstorage_ld(_NewACTstorage,LD) -> + LD. +%% ------------------------------------------------------------------------------ + + + +%% ------------------------------------------------------------------------------ +%% Functions working on the rtstates structure (which is a substructure of loopdata). +%% It is either: +%% [{Node,StateStatus,Opts},...] +%% Node is either the node name of the runtime component erlang node or +%% ?LOCAL_RUNTIME as returned from the trace control component. +%% StateStatus is {State,Status}, 'unavailable' or 'unknown'. +%% Status is the returnvalue from trace control component. +%% i.e: running | {suspended,Reason} +%% ------------------------------------------------------------------------------ + +%% Function contructing an rtstates structure from a list of [{Node,StateStatus,Opts},...]. +to_rtstates(ListOfStates) when list(ListOfStates) -> + ListOfStates. +%% ------------------------------------------------------------------------------ + +%% Function which takes a rtstates structure and returns a list of [{Node,StateStatus},...]. +from_rtstates(RTStates) -> + RTStates. +%% ------------------------------------------------------------------------------ + +%% Function which takes an rtstates structure and a result as returned from +%% init_tracing. The RTStates is modified for the nodes that changed state as a +%% result of successful init_tracing. +%% Returns a new RTStates. +set_tracing_rtstates([E={Node,_StateStatus,Opts}|Rest],Result) -> + case lists:keysearch(Node,1,Result) of + {value,{_,ok}} -> % Means state-change to tracing! + [{Node,{tracing,running},Opts}|set_tracing_rtstates(Rest,Result)]; + _ -> % Otherwise, leave it as is. + [E|set_tracing_rtstates(Rest,Result)] + end; +set_tracing_rtstates([],_Result) -> + []. +%% ------------------------------------------------------------------------------ + +%% Function updating the state/status for a certain runtime component. +%% Returns a new RTStates structure. Note that Node must not necessarily be one +%% of the nodes in the session. Meaning that Node shall not be added to RTStates +%% should it not already be in there. +statechange_rtstates(Node,State,Status,RTStates) when list(RTStates) -> + case lists:keysearch(Node,1,RTStates) of + {value,{_,_,Opts}} -> + lists:keyreplace(Node,1,RTStates,{Node,{State,Status},Opts}); + _ -> % Then Node does not exist. + RTStates % Just keep it as is, as keyreplace would have done. + end. +%% ------------------------------------------------------------------------------ + +%% Function updating the state/status for a certain runtime component. The +%% state/status is set to 'unavailable'. +%% Returns a new RTStates structure. +set_unavailable_rtstates(Node,RTStates) when list(RTStates) -> + case lists:keysearch(Node,1,RTStates) of + {value,{_,_,Opts}} -> + lists:keyreplace(Node,1,RTStates,{Node,unavailable,Opts}); + _ -> % Then Node does not exist. + RTStates % Just keep it as is, as keyreplace would have done. + end. +%% ------------------------------------------------------------------------------ + +%% Function finding the statestatus associated with Node in the RTStates structure. +%% Returns {ok,StateStatus} or 'false'. +get_statestatus_rtstates(Node,RTStates) -> + case lists:keysearch(Node,1,RTStates) of + {value,{_,StateStatus,_}} -> + {ok,StateStatus}; + false -> + false + end. +%% ------------------------------------------------------------------------------ + +%% Help function which returns a list of all nodes that are currently marked +%% as available to us in the runtime state structure. +get_all_available_nodes_rtstates(RTStates) -> + get_all_session_nodes_rtstates(lists:filter(fun({_N,unavailable,_})->false; + (_)->true + end, + RTStates)). +%% ------------------------------------------------------------------------------ + +%% Help function returning a list of all nodes belonging to this session. +get_all_session_nodes_rtstates(RTStates) -> + lists:map(fun({Node,_,_})->Node end,RTStates). +%% ------------------------------------------------------------------------------ + +%% Function which returns a list of nodes that are indicated as tracing in the +%% RTStates structure. +get_all_tracing_nodes_rtstates(RTStates) -> + lists:map(fun({N,_,_})->N end, + lists:filter(fun({_,{tracing,_},_})->true;(_)->false end,RTStates)). +%% ------------------------------------------------------------------------------ + +%% Returns the options associated with Node in the RTStates structure. +get_opts_rtstates(Node,RTStates) -> + case lists:keysearch(Node,1,RTStates) of + {value,{_,_,Opts}} -> + {ok,Opts}; + false -> + false + end. + +%% ------------------------------------------------------------------------------ +%% Functions working on the tracerdata structure, which is a part of the loopdata. +%% The tracerdata structure is an ETS-table of type bag storing: +%% {Node,TracerData}. +%% Note that there can of course be multiple entries for a node. +%% ------------------------------------------------------------------------------ + +%% Help function which takes a tracerdata loopdata structure and returns a list +%% of all stored tracerdata for a certain Node. +find_tracerdata_for_node_trd(Node,TRD) -> + case ets:lookup(TRD,Node) of + Result when list(Result) -> + lists:map(fun({_Node,TracerData})->TracerData end,Result); + _ -> % Should probably never happend. + [] + end. +%% ------------------------------------------------------------------------------ + + +%% ------------------------------------------------------------------------------ +%% Functions working on the activity storage structure, which is part of the +%% loopdata. It stores entries about things that needs to be "redone" in case +%% of a reactivation of the node. The time order is also important. +%% Note that for every ActivityType there must be a "handler" in the reactivation +%% functionality. +%% +%% The structure is a bag of {Node,ActivityType,What}. +%% ActivityType/What=tf/{Op,TraceConfList}|tpm/{Op,[Mod,Func,Arity,MS,CallFunc]} +%% /{Op,[Mod,Func,Arity,MS,CallFunc,ReturnFunc]} +%% /{Op,[]} +%% TraceConfList=[{Proc,Flags},...] +%% How=true|false +%% ------------------------------------------------------------------------------ + +%% Function that adds meta-pattern activities to the activity storage. Note +%% that one of the parameters to the function is a return value from an +%% inviso call. In that way we do not enter activities that were unsuccessful. +%% Op can be either the setting or clearing of a meta pattern. +%% Returns a new ACTstorage. +add_tpm_actstorage([{Node,ok}|Rest],Op,InvisoCmdParams,ACTstorage) -> + true=ets:insert(ACTstorage,{Node,tpm,{Op,InvisoCmdParams}}), + add_tpm_actstorage(Rest,Op,InvisoCmdParams,ACTstorage); +add_tpm_actstorage([_|Rest],Op,InvisoCmdParams,ACTstorage) -> + add_tpm_actstorage(Rest,Op,InvisoCmdParams,ACTstorage); +add_tpm_actstorage([],_,_,ACTstorage) -> + ACTstorage. + +%% Function that adds process trace-flags to the activity storage. Note that one +%% of the parameters is the return value from an inviso function. Meaning that +%% if the flags failed in their entirety, no activity will be saved. If only +%% some of the flags failed, we will not go through the effort of trying to find +%% out exactly which. +%% Returns a new activity storage structure. +add_tf_actstorage([{_Node,{error,_Reason}}|Rest],Op,TraceConfList,ACTstorage) -> + add_tf_actstorage(Rest,Op,TraceConfList,ACTstorage); +add_tf_actstorage([{Node,_Result}|Rest],Op,TraceConfList,ACTstorage) -> + true=ets:insert(ACTstorage,{Node,tf,{Op,TraceConfList}}), + add_tf_actstorage(Rest,Op,TraceConfList,ACTstorage); +add_tf_actstorage([],_,_,ACTstorage) -> + ACTstorage. +%% ------------------------------------------------------------------------------ + +%% Finds all activities associated with Node. Returns a list of them in the +%% same order as they were inserted. +get_activities_actstorage(Node,ACTstorage) -> + case ets:lookup(ACTstorage,Node) of + [] -> + false; + Result when list(Result) -> + {ok,lists:map(fun({_N,Type,What})->{Type,What} end,Result)} + end. +%% ------------------------------------------------------------------------------ + +%% Function removing all activity entries associated with Node. This is useful +%% if the Node disconnects for instance. +del_node_actstorage(Node,ACTstorage) -> + ets:delete(ACTstorage,Node), + ACTstorage. +%% ------------------------------------------------------------------------------ + |