process_info.erl

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

ERL
663
字号
%% ``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$%%-module(process_info).-behavior(gen_server).-export([start/0, start_link/0, stop/0]).-export([is_node/1, get_nodes/0,	 get_applications/1, get_application_keys/2,	 get_processes/3, get_process_data/2,	 send_trace/1]).%% gen_server callbacks-export([init/1, handle_call/3, handle_cast/2, handle_info/2,	 terminate/2, code_change/3]).-record(data, {que=undef,	       procs=undef,	       links=undef,	       links2=undef}).%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%                                                                    %%%% Functions to retrieve information about which application          %%%% at the node                                                        %%%%                                                                    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%start() ->    gen_server:start({local, proc_info}, process_info, [], []).start_link() ->    gen_server:start_link({local, proc_info}, process_info, [], []).stop() ->    gen_server:call(proc_info, stop, 1000).%% is_node(NodeS) -> {bool(), Node, NodeS2}%%   NodeS = NodeS2 = string()%%   Node = node()is_node(NodeS) ->    Node = list_to_atom(NodeS),    case lists:member(Node, [node()|nodes()]) of	true->	    {true, Node, NodeS};	false ->	    {false, node(), atom_to_list(node())}    end.%% get_nodes() -> [node()]get_nodes() ->    [node()|nodes()].%% get_applications(Node) -> [App]%%   Node = node()%%   App = atom()%% Returns the list of all applications with a supervision tree (that%% is, not library applications such as stdlib) at Node.get_applications(Node) ->    Info = rpc:call(Node, application, info, []),    {value, {running, Apps}} = lists:keysearch(running, 1, Info),    [App || {App, Pid} <- Apps, is_pid(Pid)].%% get_application_keys(App, Node) -> {ok, Keys} | {error, Reason}%%   Node = node()%%   App = atom()%%   Keys = [{Key, Val}]%%     Key = atom()%%     Val = term()%%   Reason = badapp | badrpcget_application_keys(App, Node) ->    case rpc:call(Node, application, get_all_key, [App]) of	{ok, Keys} ->	    {ok, Keys};	undefined ->	    {error, badapp};	{badrpc, _} ->	    {error, badrpc}    end.%% get_processes(App, Mode, Node) -> {Tree, Dict} | unknown%%   App = atom()%%   Mode = sup | sup_child | all%%   Node = node()get_processes(App, Mode, Node) ->    gen_server:call(proc_info, {get_processes, App, Mode, Node}).%% get_process_data(Pid, Node) -> ProcData%%   Pid = pid()%%   Node = node()%%   ProcData -- see erlang:process_info/1get_process_data(Pid, Node) ->    case rpc:call(Node, erlang, process_info, [Pid]) of	{badrpc, _} ->	    [{error,"Please try again"}];	Res ->	    Res    end.%% send_trace(PidL) -> void()send_trace(PidL) ->    gen_server:call(proc_info, {send_trace, PidL}).%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%                                                                    %%%%  gen_server callbacks                                              %%%%                                                                    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%init([]) ->    {ok, ets:new(procs, [])}.handle_call({get_processes, App, Mode, Node}, _From, State) ->    case do_get_processes(App, Mode, Node) of	unknown ->	    {reply, unknown, State};	Tree ->	    {reply, {Tree, State}, State}    end;handle_call({send_trace, PidL}, _From, State) ->    do_send_trace(PidL, State),    {reply, ok, State};handle_call(stop, _From, State) ->    {stop, normal, ok, State}.handle_cast(_, State) ->    {noreply, State}.handle_info(_, State) ->    {noreply, State}.terminate(_Reason, _State) ->    ok.code_change(_, State, _) ->    {ok, State}.%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%                                                                    %%%% Internal functions                                                 %%%%                                                                    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% do_get_processes(App, Mode, Node) -> Tree | unknown%%   App = atom()%%   Mode = all | sup | sup_childs%%   Node = node()%%   Tree = term()do_get_processes(App, Mode, Node) ->    case rpc:call(Node, application_controller, get_master, [App]) of	Pid when is_pid(Pid) ->	    start_collecting_data(Pid, Mode, Node);	undefined ->	    unknown    end.%% Initiate the database, get the processes and the links.%% Then build lists and return them.start_collecting_data(Pid, Mode, Node) ->    Db = get_database(),    {Db2, Tree} = build_graph({master,Pid}, Db, Pid, Mode, Node),    delete_database(Db2),    Tree.get_database() ->    P = ets:new(procs,[]),    L = ets:new(link,[bag]),    L2 = ets:new(link2,[bag]),    Q = queue:new(),    ets:insert(P, {whereis(application_controller), crap}),    ets:insert(P, {whereis(gs), crap}),    #data{que=Q, procs=P, links=L, links2=L2}.delete_database(Db) ->    ets:delete(Db#data.procs),    ets:delete(Db#data.links),    ets:delete(Db#data.links2).%% The thought is%% 1. Get the processes that links to Pid.%%    Pid is the application master the first time.%% 2. Add the processes to the database and clear the list of children%%    from processes which for some resason not should be there.%% 3. Queue the children, so we later can se if they have any links.%% 4. Add links to the childrens.%% 5. When the whole tree is retreived remove the unnecessary processes%%    depending on the mode.%% 6. Take all links that point to the same pid and sort out%%    the primary and secondary relations.%%    If more than one process links to the same process, the relation%%    between a supervisor and a process is primary. The rest is%%    secondary, there is no different in real world just in logic%%    between a secondary and a primary relation.%% When all processes in the application is collected,%% fix secondary links and return the tree.build_graph(finish, Db, Grp, Mode, Node) ->    Db = fix_links(Db, Grp, Node),     delete_unwanted(Db, Mode, Grp),    Tree = start_tree(Db, Node),    {Db, Tree};build_graph(Pid, Db, Grp, Mode, Node) ->    Children = get_children(Pid, Mode, Node),    Children2 = add_and_remove(Children, Pid, Db, Grp, Node),    Q2 = queue_children(Db#data.que, Children2),    add_children(Pid, Db, Children2, 1),    case queue:out(Q2) of	{empty, _}->	    build_graph(finish, Db, Grp, Mode, Node);	{{value,NPid}, Q3}->	    Db2 = Db#data{que=Q3},	    build_graph(NPid,Db2,Grp,Mode,Node)    end.%% Collect the processes which the current process has a link to%% Pid is now the application_master and the application master's%% child is the application supervisor but in reality there are two%% application master processes.%% Fix this by reordering the processes a little.get_children({master,Pid}, _Mode, Node) when is_pid(Pid) ->    %% Get the master pid    MPid = case application_master:get_child(Pid) of	       {Pid1, _App} -> Pid1;	       Pid1 -> Pid1	   end,    %% Get the second appplication master process and order them    %% correctly    case rpc:call(Node, erlang, process_info, [MPid,links]) of	{links, [H|T]} -> [H,MPid|T];	{links, []} -> MPid    end;get_children({Pid, _Name}, _Mode, Node) when is_pid(Pid),					   Node==node(Pid) ->    {links,Links} = rpc:call(Node, erlang, process_info, [Pid,links]),    Links;get_children(Pid, _Mode, Node) when is_pid(Pid), Node==node(Pid) ->    {links,Links} = rpc:call(Node, erlang, process_info, [Pid,links]),    Links;get_children(Pid, _Mode, Node) when is_pid(Pid), Node/=node(Pid) ->    [];get_children(Port, _Mode, _Node) when is_port(Port) ->    [].%% Add the links to the database.%% The first case -- when it is the application master process -- there%% is only one real child even though there are more links.add_children({master,Pid}, Db, [Child|_Rest], N) ->    add_child(Pid, Db, Child, N);add_children(_Pid, _Db, [], _N) ->    ok;add_children(Pid, Db, [Child|Rest], N) ->    add_child(Pid, Db, Child, N),    add_children(Pid, Db, Rest, N+1).add_child(Pid, Db, Child, N) ->    case ets:match_object(Db#data.links, {Pid,Child,'_'}) of	[] ->	    ets:insert(Db#data.links, {Pid,Child,N});	_ ->	    ok    end.%% Add the list of processes to the queue.queue_children(Queue, []) ->    Queue;queue_children(Queue, [H|T]) ->    Q = queue:in(H, Queue),    queue_children(Q, T).    %% The processess that we already has added to the database are%% not children to the current process, so we don't need to add them a%% second time. remove_used_children([], _Db, New_list) ->    lists:reverse(New_list);remove_used_children([Child|Rest], Db, New) ->    case ets:lookup(Db#data.procs, Child) of	[] ->	    remove_used_children(Rest, Db, [Child|New]);	_ ->	    remove_used_children(Rest, Db, New)    end.%% Take the list of links and separate it into a list with ports and a%% list with pids.separate_ports([], Pids, Ports) ->    {Pids, Ports};separate_ports([Child|Rest], Pids, Ports) ->    if	is_port(Child) ->	    separate_ports(Rest, Pids, [Child|Ports]);	is_pid(Child) ->	    separate_ports(Rest, [Child|Pids], Ports)    end.%% Add the current pid to the ets table with processes and clear%% the list of children from processes that should not be there.%% In the first case, no children are used so it's not necessary.add_and_remove(Children, {master,Pid}, Db, _Grp, Node)  when is_pid(Pid), Node==node(Pid) ->    ets:insert(Db#data.procs, {Pid, {master,master}, controller}),    {_Pids,Ports} = separate_ports(Children, [], []),    Ports++Children;%% This clause is removable when using only link as retrieving mode  .add_and_remove(Children, {Pid,_Name}, Db, Grp, Node)  when is_pid(Pid), Node==node(Pid) ->    ets:insert(Db#data.procs, {Pid,			       rpc:call(Node,erlang,process_info,					[Pid,registered_name])}),     {Pids, Ports} = separate_ports(Children, [], []),    Children1 = remove_used_children(Pids, Db, []),    Children2 = remove_others_children(Children1, Grp, Node),    Ports++Children2;add_and_remove(Children, Pid, Db, Grp, Node) when is_pid(Pid),						  Node==node(Pid) ->    ets:insert(Db#data.procs, {Pid,			       rpc:call(Node,erlang,process_info,					[Pid,registered_name])}),     {Pids, Ports} = separate_ports(Children, [], []),    Children1 = remove_used_children(Pids, Db, []),

⌨️ 快捷键说明

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