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 + -
显示快捷键?