diff options
Diffstat (limited to 'lib/pman/src/pman_main.erl')
-rw-r--r-- | lib/pman/src/pman_main.erl | 789 |
1 files changed, 0 insertions, 789 deletions
diff --git a/lib/pman/src/pman_main.erl b/lib/pman/src/pman_main.erl deleted file mode 100644 index 2f51284293..0000000000 --- a/lib/pman/src/pman_main.erl +++ /dev/null @@ -1,789 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2012. 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% -%% --module(pman_main). --compile([{nowarn_deprecated_function,{gs,config,2}}, - {nowarn_deprecated_function,{gs,read,2}}]). - -%% Main process and window - --export([init/2]). - --record(state, {win, % GS top window - frame, % GS top frame - grid, % GS process info grid - - size, % int() No. of displayed procs - w, % int() Window width - h, % int() Window height - - hide_system=false, % bool() Auto-hide system procs - hide_new=false, % bool() Auto-hide new processes - - hide_modules, % ordset() Excluded modules - - hide_all=[], % [{node(), bool()}] Hide all - hide_pids=[], % [{node(), Ordset}] Processes - % explicitly to hide, per node - show_pids=[], % [{node(), Ordset}] Processes - % explicitly to show, per node - - shown_pids=[], % [{node(), Ordset}] Processes - % actually shown, per node - - node, % node() Current node - nodes=[], % [node()] All known nodes - - focus=1, % int() Grid line with focus - focus_pid=undefined, % pid() | undefined Proc in focus - - noshell, % bool() Noshell mode on - - options}). % term() Trace options settings - - --include("pman_win.hrl"). - --define(REFRESH_TIME,5000). - --define(REQUIRES_FOCUS, % List of menus that should - ['Trace Process', % be disabled if no process - 'Kill', % is in focus - 'Hide Selected Process', - 'Module']). - -%%--Process init and loop----------------------------------------------- - -init(PidCaller, OSModuleExcluded) -> - process_flag(trap_exit, true), - - %% Monitor all nodes in a distributed system - case is_alive() of - - %% We have a distributed system - true -> net_kernel:monitor_nodes(true); - - %% No distribution - false -> ignore - end, - Nodes = [node()|nodes()], - - %% Create the main window - %% For some extremely strange reason, the frame must be resized - %% or the grid won't be visible... - GridSize = length(processes()) + 61, - {Window, Grid, Frame, Visible, W, H} = - pman_win:pman_window(GridSize, OSModuleExcluded, Nodes), - gse:resize(Frame, ?WIN_WIDTH, ?WIN_HEIGHT-?MENU_HEIGHT), - - Noshell = case pman_shell:find_shell() of - noshell -> true; - _ -> false - end, - - State1 = #state{win=Window, frame=Frame, grid=Grid, - size=Visible, - w=W, h=H, - hide_modules=OSModuleExcluded, - node=node(), - noshell=Noshell}, - - State2 = lists:foldl(fun(Node, State) -> add_node(Node, State) end, - State1, - Nodes), - - State3 = refresh(State2), - - %% Notify caller that the process appears - %% to have been started. - PidCaller ! {initialization_complete, self()}, - - %% Initiate a 'catch all' trace pattern so call tracing works - erlang:trace_pattern({'_', '_', '_'}, true, [local]), - - %% Read default options file - Options = restore_options(State3), - - loop(State3#state{options=Options}). - -add_node(Node, State) -> - pman_win:add_menu(node, [Node], "Show"), - State#state{hide_all=nl_update(Node, false, State#state.hide_all), - hide_pids=nl_update(Node, [], State#state.hide_pids), - show_pids=nl_update(Node, [], State#state.show_pids), - shown_pids=nl_update(Node, [], State#state.shown_pids), - nodes=[Node|State#state.nodes]}. - -%% Restore saved options from default file -restore_options(State)-> - File = options_file(), - case pman_options:read_from_file(File) of - {ok, Options} -> - Options; - {error, ReasonStr, DefOptions} -> - Parent = State#state.win, - Msg = io_lib:format( - "Problems reading default option file~n~s:~n~s", - [File, ReasonStr]), - tool_utils:notify(Parent, Msg), - DefOptions - end. - -options_file() -> - {ok, [[Home]]} = init:get_argument(home), - filename:join([Home, ".erlang_tools", "pman.opts"]). - -loop(State) -> - receive - {nodeup, Node} -> - case nl_exists(Node, State#state.hide_all) of - true -> - pman_win:add_menu(node, [Node], "Show"), - loop(State#state{nodes=[Node|State#state.nodes]}); - false -> - loop(add_node(Node, State)) - end; - - {nodedown, Node} -> - pman_win:remove_menu([Node]), - - Msg = io_lib:format("Node~n~p~ndown.", [Node]), - spawn_link(tool_utils, notify, [State#state.win, Msg]), - - %% We remove Node from the list of nodes but not from - %% the other lists of State, in case Node reappears later - Nodes = lists:delete(Node, State#state.nodes), - State2 = State#state{nodes=Nodes}, - - %% If it was the shown node that went down, - %% change overview to this node - if - Node==State#state.node -> - State3 = execute_cmd({node,node()}, State2, [], []), - loop(State3); - true -> - loop(State2) - end; - - %% Ignore EXIT signals from help processes - {'EXIT', _Pid, _Reason} -> - loop(State); - - %% GS events - {gs, _Obj, _Event, _Data, _Args} = Cmd -> - case gs_cmd(Cmd, State) of - stop -> - exit(topquit); - State2 -> - loop(State2) - end - - after ?REFRESH_TIME -> - State2 = refresh(State), - loop(State2) - end. - -%% gs_cmd(Event, State) -> stop | State' -gs_cmd(Event, State) -> - case Event of - - %% --- Window manager commands --- - - %% Window is moved or resized - {gs, _, configure, _Data, Args} -> - configure(Args, State); - - %% Window closed, stop Pman - {gs, _, destroy, _, _} -> - stop; - - %% --- Dynamic commands --- - - %% Click in any object where the GS Data field is a 2-tuple - {gs, _, click, Data, Args} when is_tuple(Data), size(Data)==2 -> - execute_cmd(Data, State, [], Args); - - %% Single click in the grid sets focus to selected process - {gs, _, click, {pidfunc,_,_}, [_,Row|_]} when is_integer(Row) -> - focus(Row, State); - - %% Double click in the grid starts tracing of selected process - {gs, _, doubleclick, {pidfunc,_,_}, [_Col,Row| _]} when is_integer(Row) -> - execute_cmd('Trace Process', State, [], []); - - %% Click in named GS objects - {gs, Cmd, click, Data, Args} when is_atom(Cmd); - is_atom(element(1, Cmd)) -> - execute_cmd(Cmd, State, Data, Args); - - %% --- Keyboard accelerator commands --- - - %% Move focus up and down - {gs, _, keypress, [], ['Up',_,0,0]} -> - execute_cmd(focus_previous, State, [], []); - {gs, _, keypress, [], ['Down',_,0,0]} -> - execute_cmd(focus_next, State, [], []); - - %% Other keyboard shortcuts - {gs, _, keypress, [], ['Return',_,0,0]} -> - execute_cmd('Trace Process', State, [], []); - {gs, _, keypress, [], [Key,_,0,1]} -> - execute_cmd(shortcut(Key), State, [], []); - - %% Ignore all other GS events - _Other -> - State - end. - -%% Keyboard shortcuts - -%% File menu -shortcut(o) -> 'Default Options'; -shortcut(e) -> 'Exit'; -shortcut(z) -> 'Exit'; - -%% View menu -shortcut(i) -> 'Hide All'; -shortcut(u) -> 'Hide Modules'; -shortcut(d) -> 'Hide Selected Process'; -shortcut(m) -> 'Module'; -shortcut(r) -> 'Refresh'; - -%% Trace menu -shortcut(k) -> 'Kill'; -shortcut(t) -> 'Trace Process'; -shortcut(s) -> 'Trace Shell'; - -%% Help menu -shortcut(h) -> 'Help'; - -%% Keyboard command only -shortcut(l) -> 'All Links'; - -%% Process grid traversal -shortcut(p) -> focus_previous; -shortcut(n) -> focus_next; -shortcut(_) -> dummy. - -%% configure([W,H,X,Y|_], State) -> State' -%% Window has been moved or resized -configure([W,H|_], State) -> - if - W==State#state.w, H==State#state.h -> - ignore; - - true -> - gse:resize(State#state.frame, W, H-?MENU_HEIGHT), - - Grid = State#state.grid, - case abs(W - gs:read(Grid,width) - 6) of - 0 -> - ok; %% Avoid refreshing width if possible - _Anything -> - Cols = pman_win:calc_columnwidths(W-6), - gs:config(Grid, Cols) - end, - pman_win:configwin(Grid, W, H) - end, - State. - -%% focus(Row, State) -> State' -%% Row = int() Grid row -%% User has selected a row in the grid. -%% Row==1 means header row. -focus(Row, State) -> - - Pid = case get_pid_in_focus(Row, State#state.grid) of - {true, {pidfunc,Pid0,_}} -> - pman_win:change_colour(State#state.grid, - State#state.focus, Row), - enable_pid_actions(), - Pid0; - false -> - disable_pid_actions(), - undefined - end, - - State#state{focus=Row, focus_pid=Pid}. - -%% get_pid_in_focus(Row, Grid) -> {true, Data} | false -%% Data = {pidfunc, Pid, Func} -%% Func = {Mod,Name,Arity} | term() -%% Return the data associated with the process in focus if there is one, -get_pid_in_focus(1, _Grid) -> - false; -get_pid_in_focus(Row, Grid) -> - case gs:read(Grid, {obj_at_row,Row}) of - undefined -> false; - GridLine -> - Data = gs:read(GridLine, data), - {true, Data} - end. - -%% execute_cmd(Cmd, State, Data, Args) -> stop | State' - -%% Checkbutton "Hide System Processes" -execute_cmd('Hide System', State, _Data, Args) -> - [_Text, _Group, Bool|_Rest] = Args, - State2 = State#state{hide_system=Bool}, - refresh(State2); - -%% Checkbutton "Auto-Hide New" -execute_cmd('Auto Hide New', State, _Data, Args ) -> - [_Text, _Group, Bool|_Rest] = Args, - refresh(State#state{hide_new=Bool}); - -%% File->Options... -execute_cmd('Default Options', State, _Data, _Args) -> - OldOptions = State#state.options, - NewOptions = pman_options:dialog(State#state.win, - "Default Trace Options", - OldOptions), - case NewOptions of - {error, _Reason} -> - State; - Options -> - State#state{options=Options} - end; - -%% File->Save Options -%% Save the set default options to the user's option file -execute_cmd('Save Options', State, _Data, _Args)-> - Options = State#state.options, - File = options_file(), - Parent = State#state.win, - - case pman_options:save_to_file(Options, File) of - ok -> - tool_utils:notify(Parent, "Options saved to\n" ++ File); - {error, ReasonStr} -> - Msg = io_lib:format("Could not save options to~n~s:~n~s", - [File, ReasonStr]), - tool_utils:notify(Parent, Msg) - end, - State; - -%% File->Exit -%% Exit the application -execute_cmd('Exit', _State, _Data, _Args) -> - stop; - -%% View->Hide All Processes -execute_cmd('Hide All', State, _Data, _Args) -> - Node = State#state.node, - HideAll = nl_update(Node, true, State#state.hide_all), - ShowPids = nl_del_all(State#state.node, State#state.show_pids), - State2 = State#state{hide_all=HideAll, show_pids=ShowPids}, - refresh(State2, true); - -%% View->Hide modules... -%% Opens a dialog where the user can select from a list of -%% the loaded modules. -%% The selected module is added to the list of hidden modules. -execute_cmd('Hide Modules', State, _Data, _Args) -> - - %% Get all loaded modules that are not already hidden - AllModules = lists:map(fun({Module, _File}) -> Module end, - code:all_loaded()), - ModulesSet = ordsets:subtract(ordsets:from_list(AllModules), - State#state.hide_modules), - - %% Let the user select which of the loaded modules to exclude from - %% the process overview - Title = "Module selection", - case pman_tool:select(State#state.win, Title, ModulesSet) of - Modules when is_list(Modules) -> - HideModules = ordsets:union(State#state.hide_modules, - ordsets:from_list(Modules)), - refresh(State#state{hide_modules=HideModules}); - cancelled -> State - end; - -%% View->Hide Selected Process -%% The process in focus should explicitly be hidden -execute_cmd('Hide Selected Process', State, _Data, _Args) -> - case State#state.focus_pid of - undefined -> State; - Pid -> - Node = State#state.node, - HidePids = nl_add(Node, Pid, State#state.hide_pids), - ShowPids = nl_del(Node, Pid, State#state.show_pids), - refresh(State#state{hide_pids=HidePids, show_pids=ShowPids}) - end; - -%% View->Module Info... -%% Open window with module information. -execute_cmd('Module', State, _Data, _Args) -> - case get_pid_in_focus(State#state.focus, State#state.grid) of - {true, {pidfunc, _Pid, {Module,_Name,_Arity}}} -> - pman_module_info:start(Module); - _ -> % false | {true, {pidfunc, Pid, Other}} - ignore - end, - State; - -%% View->Refresh -%% Refresh the main window. -%% (Called automatically every ?REFRESH_TIME millisecond) -execute_cmd('Refresh', State, _Data, _Args) -> - refresh(State); - -%% View->Show All Processes -%% Makes all processes visible except system processes and new -%% processes, if those buttons are checked. -%% Note: Also un-hides all hidden modules! -execute_cmd('Show All', State, _Data, _Args) -> - Node = State#state.node, - HideAll = nl_update(Node, false, State#state.hide_all), - HidePids = nl_del_all(State#state.node, State#state.hide_pids), - ShowPids = nl_del_all(State#state.node, State#state.show_pids), - State2 = State#state{hide_modules=ordsets:new(), hide_all=HideAll, - hide_pids=HidePids, show_pids=ShowPids}, - refresh(State2, true); - -%% View->Show Processes... -%% Open a list of all hidden processes, if the user selects one this -%% process should explicitly be shown -execute_cmd('Show Selected', State, _Data, _Args) -> - Node = State#state.node, - - All = pman_process:r_processes(Node), - Hidden = case nl_lookup(Node, State#state.hide_all) of - true -> - All; - false -> - Shown = nl_lookup(Node, State#state.shown_pids), - ordsets:subtract(All, Shown) - end, - - %% Selection window - Title = "Select Processes to Show", - Tuples = - lists:map(fun(Pid) -> - {M,F,A} = pman_process:function_info(Pid), - Str = case pman_process:get_name(Pid) of - " " -> - io_lib:format("~p:~p/~p", - [M, F, A]); - Name -> - io_lib:format("[~p] ~p:~p/~p", - [Name, M, F, A]) - end, - {Pid, Str} - end, - Hidden), - case pman_tool:select(State#state.win, Title, Tuples) of - Pids when is_list(Pids) -> - HidePids = nl_del(Node, Pids, State#state.hide_pids), - ShowPids = nl_add(Node, Pids, State#state.show_pids), - refresh(State#state{hide_pids=HidePids,show_pids=ShowPids}); - cancelled -> State - end; - -%% Trace->Kill -execute_cmd('Kill', State, _Data, _Args) -> - case State#state.focus_pid of - Pid when is_pid(Pid) -> - exit(Pid, kill); - undefined -> - ignore - end, - State; - -%% Trace->Selected Process -execute_cmd('Trace Process', State, _Data, _Args) -> - case State#state.focus_pid of - Pid when is_pid(Pid) -> - pman_shell:start({Pid,self()}, State#state.options); - undefined -> - ignore - end, - State; - -%% Trace->Shell Process -execute_cmd('Trace Shell', State, _Data, _Args) -> - case pman_shell:find_shell() of - noshell -> - State; - Shell -> - pman_shell:start({{shell,Shell},self()}, - State#state.options), - State#state{noshell=false} - end; - -%% Nodes->Show <Node> -%% Change shown node -execute_cmd({node,Node}, State, _Data, _Args) -> - gse:config(State#state.win, - [{title,lists:concat(["Pman: Overview on ", Node])}]), - gse:disable(Node), - catch gse:enable(State#state.node), % Menu may not exist any more - refresh(State#state{node=Node}, true); - -%% Help->Help -execute_cmd('Help', State, _Data, _Args) -> - Win = State#state.win, - HelpFile = - filename:join([code:lib_dir(pman),"doc","html","index.html"]), - tool_utils:open_help(Win, HelpFile), - State; - -%% Keyboard shortcut Ctrl-l -execute_cmd('All Links', State, _Data, _Args) -> - case State#state.focus_pid of - Pid when is_pid(Pid) -> - case process_info(Pid, links) of - {links, Pids} -> - pman_shell:start_list(Pids, self(), - State#state.options); - undefined -> - ignore - end; - undefined -> ignore - end, - State; - -%% Keyboard shortcuts for process grid traversal -execute_cmd(focus_previous, State, _Data, _Args) -> - focus(previous_row(State), State); -execute_cmd(focus_next, State, _Data, _Args) -> - focus(next_row(State), State); - -%% Keyboard combinations that are not shortcuts -execute_cmd(dummy, State, _Data, _Args) -> - State. - -%% Convenience functions for disabling/enabling menu items that require -%% that a process is selected. -disable_pid_actions() -> - lists:foreach(fun(X) -> gse:disable(X) end, ?REQUIRES_FOCUS). - -enable_pid_actions() -> - lists:foreach(fun(X) -> gse:enable(X) end, ?REQUIRES_FOCUS). - -%% refresh(State) -> State' -%% refresh(State, ForceP) -> State' -%% Refreshes the main window. -refresh(State) -> - refresh(State, false). -refresh(#state{node=Node} = State, ForceP) -> - - %% Update shown processes - - %% First, get an ordset of all processes running at the current node - All = pman_process:r_processes(Node), - - Shown = nl_lookup(Node, State#state.shown_pids), - ExpShown = nl_lookup(Node, State#state.show_pids), - - {Show, State2} = - case nl_lookup(Node, State#state.hide_all) of - - %% If the user has selected "Hide All Processes", only - %% explicitly selected processes which still exist should - %% be shown - true -> - {ordsets:intersection(ExpShown, All), State}; - - false -> - %% Compute which processes should be hidden according - %% to the flags/menu items selected - Hidden = hidden_pids(All, State), - - NotHidden = ordsets:subtract(All, Hidden), - - Show0 = case State#state.hide_new of - %% If the user has selected "Auto-Hide New", - %% then only those processes in NotHidden - %% which are already shown, should be shown, - %% together with explicitly selected - %% processes which still exist - true -> - ordsets:union( - ordsets:intersection(NotHidden,Shown), - ordsets:intersection(ExpShown, All)); - - %% Otherwise, show all processes in - %% NotHidden, together with explicitly - %% selected processes which still exist - false -> - ordsets:union( - NotHidden, - ordsets:intersection(ExpShown, All)) - end, - - ShownPids = nl_update(Node, Show0, - State#state.shown_pids), - {Show0, State#state{shown_pids=ShownPids}} - end, - - NoOfHidden = length(All) - length(Show), - - if - Show==Shown, not ForceP -> - pman_win:update(NoOfHidden), - State; - - true -> - ShowInfo = display_info(Show), - pman_win:update(State#state.grid, ShowInfo, NoOfHidden), - - %% Set the focus appropriately - State3 = case State2#state.focus_pid of - undefined -> - disable_pid_actions(), - State2; - Pid -> - Row = get_row(Pid, Show), - focus(Row, State2) - end, - - trace_shell_possible(State3), - - Size = length(Show), - case Size of - 1 -> gse:disable('Hide All'); - _ -> gse:enable('Hide All') - end, - - State3#state{size=Size} - end. - -%% hidden_pids(All, State) -> Hidden -hidden_pids(All, State) -> - - %% Processes hidden because they are system processes - HideSys = case State#state.hide_system of - true -> - lists:filter( - fun(Pid) -> - pman_process:is_system_process(Pid) - end, - All); - false -> - [] - end, - - %% Process hidden because they are executing code in a hidden module - Mods = State#state.hide_modules, - HideMod = - lists:filter(fun(Pid) -> - pman_process:is_hidden_by_module(Pid, Mods) - end, - All), - - %% Explicitly hidden processes - HideExp = nl_lookup(State#state.node, State#state.hide_pids), - - %% All hidden processes - ordsets:union([HideSys, HideMod, HideExp]). - -display_info(Pids) -> - lists:map(fun(Pid) -> - Func = pman_process:function_info(Pid), - Name = pman_process:get_name(Pid), - Msgs = pman_process:msg(Pid), - Reds = pman_process:reds(Pid), - Size = pman_process:psize(Pid), - {Pid, Func, Name, Msgs, Reds, Size} - end, - Pids). - -get_row(Pid, List) -> - get_row(Pid, List, length(List)+1). - -get_row(Pid, [Pid | _], Row) -> - Row; -get_row(Pid, [_ | T], Row) -> - get_row(Pid, T, Row-1); -get_row(_Pid, [], _Row) -> - 1. - -next_row(#state{size=Size, focus=Row}) -> - check_row(Row+1, Size). - -previous_row(#state{size=Size, focus=Row}) -> - check_row(Row-1, Size). - -check_row(1, Size) -> - Size+1; -check_row(Row, Size) when Row==Size+2 -> - 2; -check_row(Row, _Size) -> - Row. - -%% Check if node is running in noshell mode and if so disable the -%% 'Trace Shell' menu option. -trace_shell_possible(#state{noshell=true}) -> - gse:disable('Trace Shell'); -trace_shell_possible(_) -> - ok. - -%% -- Functions for manipulating {Node, Data} lists -- - -%% nl_add(Node, Elem|Elems, NList) -> NList' -nl_add(Node, Elems, [{Node, Ordset} | T]) when is_list(Elems) -> - [{Node, ordsets:union(Elems, Ordset)} | T]; -nl_add(Node, Elem, [{Node, Ordset} | T]) -> - [{Node, ordsets:add_element(Elem, Ordset)} | T]; -nl_add(Node, Elem, [H | T]) -> - [H | nl_add(Node, Elem, T)]; -nl_add(Node, Elems, []) when is_list(Elems) -> - [{Node, Elems}]; -nl_add(Node, Elem, []) -> - [{Node, ordsets:add_element(Elem, ordsets:new())}]. - -%% nl_del(Node, Elem|Elems, NList) -> NList' -nl_del(Node, Elems, [{Node, Ordset} | T]) when is_list(Elems) -> - [{Node, ordsets:subtract(Ordset, Elems)} | T]; -nl_del(Node, Elem, [{Node, Ordset} | T]) -> - [{Node, ordsets:del_element(Elem, Ordset)} | T]; -nl_del(Node, Elem, [H | T]) -> - [H | nl_del(Node, Elem, T)]; -nl_del(_Node, _Elem, []) -> - []. - -%% nl_del_all(Node, NList) -> NList' -nl_del_all(Node, [{Node, _Ordset} | T]) -> - [{Node, ordsets:new()} | T]; -nl_del_all(Node, [H | T]) -> - [H | nl_del_all(Node, T)]; -nl_del_all(_Node, []) -> - []. - -%% nl_update(Node, Val, NList) -> NList' -nl_update(Node, Val, [{Node, _OldVal} | T]) -> - [{Node, Val} | T]; -nl_update(Node, Val, [H | T]) -> - [H | nl_update(Node, Val, T)]; -nl_update(Node, Val, []) -> - [{Node, Val}]. - -%% nl_lookup(Node, NList) -> Val -nl_lookup(Node, NList) -> - {value, {_Node,Val}} = lists:keysearch(Node, 1, NList), - Val. - -%% nl_exists(Node, NList) -> bool() -nl_exists(Node, NList) -> - case lists:keysearch(Node, 1, NList) of - {value, _Val} -> - true; - false -> - false - end. |