appmon_a.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,117 行 · 第 1/3 页
ERL
1,117 行
%% ``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(appmon_a).%%----------------------------------------------------------------------%%%% Monitors an application, i.e its supervision tree.%%%%----------------------------------------------------------------------%%%%%% INTRODUCTION%% ------------%%%% This file contains a description of the files involved%% and the communication between the appmon_a display%% manager and the appmon_a2 information gatherer. Further%% information on the placement algorithm can be found in%% the place.erl file.%%%%%% FILES%% -----%%%% The supervision tree graphical software consists of%% the following files:%%%% appmon_a Gen server driving the process display window.%% Responsible for assigning gs identifiers to all %% processes and process link%% appmon_a2 The process information gathering routines. %% Works by following the process links from application %% master once every second%% dg The process database is implemented as a shared %% digraph (see manual pages for digraph) and this is %% the routines handling this digraph. Since the digraph %% is shared appmon_a2 will put some info into it that the %% appmon_a later will modify. The structures used are %% described in dg.hrl%% place Places a tree, decides the x and y coordinates (not %% necessarily corresponding to window coordinates) of %% processes (or vertices to be specific). Note that %% special routines are used to transform the possibly %% cyclic digraph into a strict tree before trying to %% place it.%%%%%%%% IMPLEMENTATION DETAIL%% ---------------------%%%% The appmon_a module will follow links between processes,%% starting with the application master. A unique%% reference is used to prevent infinite recursion. Note%% that this process and link gathering is done in the%% live digraph so that already known processes are%% updated with the reference and new ones are added to%% the digraph. After all processes and links have been%% added or updated a search is made for those processes%% and links that have an old reference. These are those%% processes and links that are not present in the%% application any more. Those are extracted from the%% digraph and then deleted and the extracts are then%% used (by appmon_a) to delete the appropriate gs%% objects. The responsibilities of appmon_a is thus 1) add%% all new processes and links to the digraph and 2) make%% a list of all those objects from the digraph that have%% been deleted.%%%% When appmon_a2 has gathered all necessary information it%% notifies the appmon_a display manager. Note that this is%% implemented as a call (as opposed to a cast) to%% prevent appmon_a2 from changing the digraph while appmon_a%% uses it. appmon_a places all processes using the place%% module. place will place the processes in the x y%% planes, hopefully in a nice way, re-forming the%% digraph during the process into a strict tree using%% some simple heuristics, some links that makes the%% graph cyclic will be considered secondary and later%% coloured red. Note that the process links are not%% placed since their coordinates are those of the%% processes that they are links between. The place%% module is only concerned at a fairly high level of%% abstraction. Currently its x coordinates are used as%% real coordinates while the y coordinates must be%% scaled to correct values, thus the x plane is%% continous and the y plane is disctrete.%%%% Having placed processes the new ones are drawn on the%% display along with all new process links, then all%% processes and process links are moved to their%% possibly new positions. The place module is not%% sensitive to changes in position and therefore has no%% concept of which nodes will have to be moved. hence%% all nodes are moved (but most of them probably to the%% same position as before)%%%%%%%%%%-----------------------------------------------------------------------export([start/2, start/3, stop/0]).-record(astate, {app, name, client, digraph}).-import(lists, [foreach/2]).%% gen server stuff-behaviour(gen_server).-export([init/1, handle_cast/2, handle_info/2, terminate/2]).-export([handle_call/3, code_change/3]).-define(APPSPACE, 10). % The space between apps-define(NODEAREA_H, 90). % The height of a node-define(BUTTAREA_H, 80). % The button area height-define(APPBUTT_H, 20). % Height of appl button-define(EDITORW, 260).-define(MAXWIDTH, 800).-define(MINWIDTH, 382).-define(MAXHEIGHT, 450).-define(MINHEIGHT, 325).-define(SUPVIEWTXT, "Sup. view").-define(PROCVIEWTXT, "Proc. view").-define(CLOSETXT, "Close").-define(REFRESHTXT, "Refresh").-define(SAVEOPTSTXT, "Save options").-define(HELPTXT, "Help").-define(CHARWIDTH, 7). %Should use GS primitives-define( darkkhaki, {189, 183, 107}).-define( palegoldenrod, {238, 232, 170}).-define( peachpuff4, {139, 119, 101}).-define( red, red).-define( darkgrey, {169, 169, 169}).-define( lightgrey, {211, 211, 211}).-define( royalblue, {65, 105, 225}).-define( aquamarine4, {69, 139, 116}).-define( palegreen4, {84, 139, 84}).-define( darkseagreen, {105, 139, 105}).-define( f_line_col, {150, 150, 255}).-include("appmon_dg.hrl").%%------------------------------------------------------------%%------------------------------------------------------------start(NodeName, AppName) -> gen_server:start_link(?MODULE, {NodeName, AppName, AppName}, []).start(NodeName, AppName, AppId) -> gen_server:start_link(?MODULE, {NodeName, AppName, AppId}, []).stop() -> ok.%%------------------------------------------------------------%% Public interface%%------------------------------------------------------------%% Administration%% AppName is the name of the application, usually an atom like sasl%% or kernel, AppId is the application pid or the application name,%% either goes.init({NodeName, AppName, AppId}) -> process_flag(trap_exit, true), {ok, Client} = appmon_info:start_link(NodeName, self(), []), init_ref(), init_foreign_places(), DG = digraph:new([cyclic, private]), State = #astate{app=AppId, name=AppName, client=Client, digraph=DG}, refresh(State), setup_base_win(NodeName, AppName), {ok, State}.terminate(_Reason, _State) -> ok.code_change(_OldVsn, State, _Extra) -> {ok, State}.handle_call(norequest, _From, State) -> {reply, null, State}.%%------------------------------------------------------------%% handle castshandle_cast({ping, _Node, _From}, State) -> {noreply, State};handle_cast(_Other, State) -> {noreply, State}.%%------------------------------------------------------------%% handle infohandle_info({gs, _, click, _, [?CLOSETXT|_]}, State) -> {stop, normal, State};handle_info({gs, _, destroy, _, _}, State) -> {stop, normal, State};handle_info({gs, _, click, _, [?REFRESHTXT|_]}, State) -> refresh(State), {noreply, State};handle_info({gs, _, click, _, [?HELPTXT|_]}, State) -> HelpFile = filename:join([code:lib_dir(appmon), "doc", "html", "part_frame.html"]), tool_utils:open_help(win(), HelpFile), {noreply, State};handle_info({gs, Id, click, {mode, Mode}, _}, State) -> %%io:format("handle_info: Setting mode: ~p~n", [Mode]), set_mode(Id, Mode), {noreply, State};handle_info({gs, _, click, _, [?SUPVIEWTXT|_]}, State) -> refresh(State, [{info_type, sup}]), {noreply, State};handle_info({gs, _, click, _, [?PROCVIEWTXT|_]}, State) -> refresh(State, [{info_type, link}]), {noreply, State};handle_info({gs, Id, buttonpress, _,[1, X, Y|_]}, State) -> %%io:format("Id clicked: ~p~n", [gs:read(Id, {find, {X, Y}})]), catch find_pid(State, Id, X, Y), set_default_mode(), {noreply, State};handle_info({gs, Win, configure, _Data, [W, H|_]}, State) -> case win() of Win -> user_driven_resize(W, H); _-> ok end, {noreply, State};handle_info({delivery, _S, pinfo, _N, Res}, State) -> appmon_txt:print(Res), {noreply, State};handle_info({delivery, S, app, N, Res}, State) -> {delivery, _Serv, app, _Name, {Root, Vs, Ls, SecLs}} = flush({delivery, S, app, N, Res}), update2(Vs, Root, Ls, SecLs, State), {noreply, State};handle_info({kill}, State) -> {stop, normal, State};handle_info({state}, State) -> {noreply, State};handle_info({'EXIT', _Pid, _Reason}, State) -> {noreply, State};handle_info(_Other, State) -> {noreply, State}.%% Refresh sets new options for the request and forces an update of%% the screen ant status.refresh(State) -> refresh(State, []).refresh(State, Opts) -> appmon_info:app(State#astate.client, State#astate.name, true, Opts).%% find_pid finds the pid of the clicked object. The scenario is that%% the user clicks on an item in his window, that ObjId is searched%% for among all nodes (vertices) and if found action is taken%% depending on the current mode (see handle_info)find_pid(State, Id, X, Y) -> %% Try to manage both versions of GS, remove first case later. ObjList = case gs:read(Id, {find, {X, Y}}) of {error, _} -> gs:read(Id, {hit, {X, Y}}); % Try new format Num when is_integer(Num) -> [Num]; _Other -> [] end, DG = State#astate.digraph, All = appmon_dg:get(all, DG), find_pid2(ObjList, All, DG, State).find_pid2([Id | Ids], All, DG, State) -> case search_for_pid(All, DG, Id) of {ok, _KeyStr, Pid} -> handle_proc_press(mode(), Pid, State); _ -> find_pid2(Ids, All, DG, State) end;find_pid2([], _All, _DG, _State) -> ok.search_for_pid([V|Vs], DG, ObjId) -> VD = appmon_dg:get(data, DG, V), if ObjId==VD#vdata.txt_obj -> {ok, V, VD#vdata.type}; true -> search_for_pid(Vs, DG, ObjId) end;search_for_pid([], _DG, _ObjId) -> false.%%%% called when a process has been clicked on.%%handle_proc_press(info, Pid, State) -> appmon_info:pinfo(State#astate.client, Pid, true, [{timeout, at_most_once}]);handle_proc_press(send, Pid, _State) -> {P, RawStr} = two_entries(winroot(), 250, 70, "Send", "To: ", "Msg: ", pid_to_list(Pid), "", bg()), Str = case lists:last(RawStr) of 46 -> RawStr; _ -> RawStr++"." end, case erl_scan:string(Str) of {ok, Tokens, _} -> case erl_parse:parse_term(Tokens) of {ok, Term} -> case catch list_to_pid(P) of To when is_pid(To) -> To ! Term; _ -> error end; _Error -> error end; _Error -> error end;handle_proc_press(trace, Pid, _State) -> case trace_state(Pid) of true -> io:format("Removing trace on ~p~n", [Pid]), sys:trace(Pid, false), set_trace_state(Pid, false); _Other -> io:format("Putting trace on ~p~n", [Pid]), sys:trace(Pid, true, 1000), set_trace_state(Pid, true) end;handle_proc_press(kill, Pid, _State) -> exit(Pid, kill).trace_state(Pid) -> get({trace_state, Pid}).set_trace_state(Pid, State) -> put({trace_state, Pid}, State).set_default_mode() -> {Id, Mode} = get(default_mode), case mode() of Mode -> true; _Other -> set_mode(Id, Mode) end.set_default_mode(Id, Mode) ->
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?