inviso_c.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,290 行 · 第 1/4 页

ERL
1,290
字号
%% ``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$%%%% Author: Ann-Marie L鰂, ann-marie.lof@st.se%%         Lennart 謍man, lennart.ohman@st.se%%%% Description: The controlling part of the trace tool Inviso%%%% This code implements the inviso control component meant to be run on an Erlang%% node doing any tracing. It can also be used in a non distributed system where%% the control component and the runtime component will run on the same virtual%% machine.%% The control component is not meant to be started by a supervisor but rather%% directly by the user when needed.%% This module does not provide any APIs to users. Those are found in inviso.erl.%% This module merly has the gen_server call-backs.%% -------------------------------------------------------------------------------module(inviso_c).-behavior(gen_server).%% ------------------------------------------------------------------------------%% gen_server callbacks.%% -------------------------------------------------------------------------------export([init/1,	 handle_call/3,handle_cast/2,handle_info/2, 	 terminate/2,	 code_change/3]).%% ------------------------------------------------------------------------------%% ------------------------------------------------------------------------------%% Exported internal functions (used in spawn of help process).%% -------------------------------------------------------------------------------export([log_rec_init/4]).%% ------------------------------------------------------------------------------%% ------------------------------------------------------------------------------%% Records.%% ------------------------------------------------------------------------------%% #state%% Record used in the loopdata.-record(state,{	  nodes=[],                          % [#node,...]	  distributed,                       % false | true	  subscribers=[],                    % [{pid(),monitor_ref()},...]	  rt_options=[{dependency,{infinity,node()}},{overload, default}]	 }).%% ------------------------------------------------------------------------------%% #node%% Record storing information about a runtime component connected to this control%% component.-record(node,{	  node,                              % [atom(),...]	  pid,                               % pid()	  vsn,	  ref,                               % monitor_ref()	  tag                                % term()	 }).%% ------------------------------------------------------------------------------%% ------------------------------------------------------------------------------%% Macros used in this module.%% -------------------------------------------------------------------------------define(RUNTIME,inviso_rt).                  % The module API name of the runtime.%% ------------------------------------------------------------------------------%% ==============================================================================%% Controller component implmentation.%% ==============================================================================init({_Parent,Options}) ->    process_flag(trap_exit,true),    case check_options(Options,start) of	{ok,Options2} ->	    LoopData=initiate_state(Options2),	    {ok,LoopData};	Error ->	    {stop,Error}    end.%% ------------------------------------------------------------------------------handle_call({subscribe,Pid},_From,LD) when pid(Pid) ->    MRef=erlang:monitor(process,Pid),    {reply,ok,LD#state{subscribers=[{Pid,MRef}|LD#state.subscribers]}};handle_call({subscribe,Faulty},_From,LD) ->    {reply,{error,{badarg,Faulty}},LD};handle_call({unsubscribe,Pid},_From,LD) ->    case lists:keysearch(Pid,1,LD#state.subscribers) of	{value,{_,MRef}} ->	    erlang:demonitor(MRef),	    {reply,ok,LD#state{subscribers=lists:keydelete(Pid,1,LD#state.subscribers)}};	false ->	    {reply,ok,LD}    end;handle_call({add_nodes,Nodes,Opts,Tag,Condition},_From,LD) ->    case check_options(Opts,add_node) of	{ok,Opts2} ->	    Opts3=merge_options(LD#state.rt_options,Opts2),	    {NewLD,Reply}=do_add_nodes(Nodes,LD,Opts3,Tag,Condition),	    {reply,adapt_reply(NewLD,Reply),NewLD};	Error ->	    {reply,Error,LD}    end;handle_call({change_options,Nodes,Opts},_From,LD) ->    case check_options(Opts,add_node) of	{ok,Opts2} ->	    {reply,adapt_reply(LD,do_change_option(Nodes,Opts2,LD)),LD};	Error ->	    {reply,Error,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) ->    TracerDataList=	lists:map(fun(N)->{N,TracerData} end,started_trace_nodes(Nodes,LD)),    {reply,adapt_reply(LD,do_init_tracing(TracerDataList,LD)),LD};handle_call({init_tracing,Nodes,_TracerData},_From,LD) ->    {reply,{error,{badarg,Nodes}},LD};handle_call({trace_pattern,Nodes,Patterns,FlagList},_From,LD) ->    {reply,adapt_reply(LD,distribute_tp(Nodes,Patterns,FlagList,LD)),LD};handle_call({trace_flags,Nodes,Args,How},_From,LD) ->    {reply,adapt_reply(LD,distribute_tf(Nodes,Args,How,LD)),LD};handle_call({trace_flags,NodeArgs,How},_From,LD) ->    {reply,distribute_tf(NodeArgs,How,LD),LD}; % Always distributed here.handle_call({trace_flags,NodeArgHows},_From,LD) ->    {reply,distribute_tf(NodeArgHows,LD),LD}; % Always distributed here.handle_call({meta_pattern,Nodes,Args},_From,LD) ->    {reply,adapt_reply(LD,distribute_metapattern(Nodes,Args,LD)),LD};handle_call({ctp_all,Nodes},_From,LD) ->    {reply,adapt_reply(LD,do_ctp_all(Nodes,LD)),LD};handle_call({suspend,Reason,Nodes},_From,LD) ->    {reply,adapt_reply(LD,do_suspend(Nodes,Reason,LD)),LD};handle_call({cancel_suspension,Nodes},_From,LD) ->    {reply,adapt_reply(LD,do_cancel_suspension(Nodes,LD)),LD};handle_call({stop_tracing,Nodes},_From,LD) ->    {reply,adapt_reply(LD,do_stop_tracing(Nodes,LD)),LD};handle_call({get_status,Nodes},_From,LD) ->    {reply,adapt_reply(LD,do_get_status(Nodes,LD)),LD};handle_call({get_tracerdata,Nodes},_From,LD) ->    {reply,adapt_reply(LD,do_get_tracerdata(Nodes,LD)),LD};handle_call(list_logs,_From,LD) ->    {reply,adapt_reply(LD,do_list_logs(all,LD)),LD};handle_call({list_logs,TracerDataOrNodesList},_From,LD) ->    {reply,adapt_reply(LD,do_list_logs(TracerDataOrNodesList,LD)),LD};handle_call({fetch_log,ToNode,Spec,Dest,Prefix},From,LD) ->    case LD#state.distributed of	true ->                              % It is a distributed system.	    do_fetch_log(ToNode,Spec,Dest,Prefix,From,LD),	    {noreply,LD};                    % Reply will come from collector pid.	false ->                             % Stupidity! you dont want this!	    {reply,{error,not_distributed},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) ->    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) ->    {reply,{error,{badarg,FaultyNodes}},LD};handle_call({clear,Nodes,Options},_From,LD) ->    {reply,adapt_reply(LD,do_clear(Nodes,LD,Options)),LD};handle_call({stop_nodes,Nodes},_From,LD) ->    {NewLD,Reply}=do_stop_nodes(Nodes,LD),    {reply,adapt_reply(NewLD,Reply),NewLD};handle_call(stop,_From,LD) ->    {stop,normal,shutdown,LD};handle_call(stop_all,_From,LD) ->    {NewLD,Reply}=do_stop_nodes(started_trace_nodes(all,LD),LD),    {stop,normal,adapt_reply(NewLD,Reply),NewLD};handle_call(Request,_From,LD) -> %% for debug purpose only    {reply,{error,{invalid_request,Request}},LD}.handle_cast(_Request,LD) ->                  % There are no casts.    {noreply,LD}.handle_info({connect,_Node,Pid,_VSN,Tag},LD) -> % From connecting runtime.    {noreply,do_confirm_connection(Pid,Tag,LD)};handle_info({trace_event,Event},LD) ->       % A runtime component issues an event.    send_to_subscribers(Event,LD),           % Relay to our subscribers.    {noreply,LD};handle_info({'DOWN',Ref,process,_From,Info},LD) -> % A runtime component died?    {noreply,do_down_msg(Ref,Info,LD)};handle_info(state,LD) ->                     % For debug purposes.    io:format("trace_c state: ~p~n",[LD]),    {noreply,LD};handle_info(_Msg,LD) ->    {noreply,LD}.terminate(_Reason, _LD) ->    ok.code_change(_OldVsn, LoopData, _Extra) ->    {ok, LoopData}.%% -----------------------------------------------------------------------------%% -----------------------------------------------------------------------------%% Handle call help functions.%% -----------------------------------------------------------------------------%% Help function which adapts a reply based on if this is a distributed system%% or not. The first argument indicates distribution (='true').%% If we are not running a distributed system, all node references are removed.adapt_reply(#state{distributed=Distributed},Reply) ->    adapt_reply(Distributed,Reply);adapt_reply(true,Reply) ->                   % We are distributed.    Reply;adapt_reply(false,{ok,[{_Node,LocalReply}]}) ->    LocalReply;adapt_reply(false,{ok,[]}) ->    {error,not_an_added_node};               % 腞 DET H腞 VERKLIGEN RIKTIGT?adapt_reply(false,Reply) ->    Reply.%% ------------------------------------------------------------------------------%% ==============================================================================%% First level help functions to handler functions.%% ==============================================================================%% Function starting a runtime component at the nodes in Nodes. If doing non%% distributed, Nodes will be a list of nonode@nohost.%% Returns {NewLD,{ok,Replies}} or {LD,{error,Reason}}.do_add_nodes(Nodes,LD,Options,Tag,Condition) ->    do_add_nodes_2(Nodes,LD,Options,Tag,Condition,[]).do_add_nodes_2([Node|Tail],LD,Options,Tag,Condition,Replies) ->    case find(fun is_started/2,LD#state.nodes,Node) of	{value,true} ->                      % Already started by us.	    do_add_nodes_2(Tail,LD,Options,Tag,Condition,[{Node,{ok,already_added}}|Replies]);	no_match ->	    case ?RUNTIME:start(Node,Options,Tag,Condition) of		{node_info,_Node,Pid,VSN,State,Status,new} ->		    NewLD=do_add_nodes_3(Node,LD,Tag,Pid,VSN,State,Status),		    do_add_nodes_2(Tail,NewLD,Options,Tag,Condition,[{Node,{ok,new}}|Replies]);		{node_info,_Node,Pid,VSN,State,Status,{tag,Tag2}} ->		    NewLD=do_add_nodes_3(Node,LD,Tag,Pid,VSN,State,Status),		    do_add_nodes_2(Tail,NewLD,Options,Tag,Condition,				   [{Node,{ok,{adopted,State,Status,Tag2}}}|Replies]);		Error ->		    do_add_nodes_2(Tail,LD,Options,Tag,Condition,[{Node,Error}|Replies])	    end    end;do_add_nodes_2([],LD,_,_,_,Replies) ->    {LD,{ok,lists:reverse(Replies)}};do_add_nodes_2(Faulty,LD,_,_,_,_) ->         % Not a list of nodes.    {LD,{error,{badarg,Faulty}}}.do_add_nodes_3(Node,LD,Tag,Pid,VSN,State,Status) ->    MRef=erlang:monitor(process,Pid),    NodeRec=#node{node=Node,pid=Pid,vsn=VSN,ref=MRef,tag=Tag},    send_to_subscribers({connected,Node,{Tag,{State,Status}}},LD),    _NewLD=set_node_rec(NodeRec,LD).%% ------------------------------------------------------------------------------%% Function calling change_options sequensially on all nodes in Nodes.%% Returns {ok,Replies} or {error,Reason}.do_change_option(Nodes,Options,LD) ->    do_change_option_2(started_trace_nodes(Nodes,LD#state.nodes),Options,LD,[]).do_change_option_2([Node|Tail],Options,LD,Replies) ->    case get_node_rec(Node,LD) of	Rec when record(Rec,node) ->	    Answer=?RUNTIME:change_options(Rec#node.pid,Options),	    do_change_option_2(Tail,Options,LD,[{Node,Answer}|Replies]);	Error ->	    do_change_option_2(Tail,Options,LD,[{Node,Error}|Replies])    end;do_change_option_2([],_Options,_LD,Replies) ->    {ok,Replies};do_change_option_2(Faulty,_,_,_) ->    {error,{badarg,Faulty}}.%% ------------------------------------------------------------------------------%% Function which calls the runtime components in TracerDataList and initiates%% the tracing. TracerDataList may either be just one tracer data which shall be%% applied to all runtime components, or a list of nodes and tracerdata.do_init_tracing(TracerDataList,LD) ->    case inviso_rt_lib:is_tracerdata(TracerDataList) of	true ->                              % Then we must add all our nodes.	    List=lists:map(fun(N)->{N,TracerDataList} end,started_trace_nodes(all,LD)),	    do_init_tracing_2(List,LD,[]);	false ->                             % Assume it is a list of {Node,Tracerdata}	    do_init_tracing_2(TracerDataList,LD,[])    end.do_init_tracing_2([{Node,TracerData}|Tail],LD,Replies) ->    case get_node_rec(Node,LD) of	Rec when is_record(Rec,node) ->	    case check_modify_tracerdata(TracerData,LD) of		{ok,NewTracerData} ->        % Tracerdata ok and node->pid.		    Reply=?RUNTIME:init_tracing(Rec#node.pid,NewTracerData),		    do_init_tracing_2(Tail,LD,[{Node,Reply}|Replies]);		{error,Reason} ->            % Unknown tracerdata.		    do_init_tracing_2(Tail,LD,[{Node,{error,Reason}}|Replies])	    end;	{error,Reason} ->	    do_init_tracing_2(Tail,LD,[{Node,{error,Reason}}|Replies])    end;do_init_tracing_2([_|Tail],LD,Replies) ->    % Just ignore item we don't understand.    do_init_tracing_2(Tail,LD,Replies);      % Will not end up in Replies either.do_init_tracing_2([],_LD,Replies) ->

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?