aboutsummaryrefslogblamecommitdiffstats
path: root/lib/observer/src/observer_procinfo.erl
blob: 260010916196b4e3573aaceab086c4e51616a945 (plain) (tree)





















                                                                         
                   






                                                                           
                      


                          









                                                                 


                                                                                
                                   
       

























                                                                                                  
                              




                                                                               

        







                                                                                             

                                                                                
                                                              

                            
                                                                                         

                            


                                                            
















                                                                     
                                                                

                                         

                                   




                                       
















































































































































                                                                                                 
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2011. 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(observer_procinfo).

-behaviour(wx_object).

-export([start/3]).

-export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3,
	 handle_call/3, handle_info/2]).

-include_lib("wx/include/wx.hrl").
-include("observer_defs.hrl").

-define(REFRESH, 601).
-define(SELECT_ALL, 603).
-define(ID_NOTEBOOK, 604).

-record(state, {parent,
		frame,
		pid,
		pages=[]
	       }).

-record(worker, {panel, callback}).

start(Process, ParentFrame, Parent) ->
    wx_object:start(?MODULE, [Process, ParentFrame, Parent], []).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

init([Pid, ParentFrame, Parent]) ->
    try
	Title=case observer_wx:try_rpc(node(Pid), erlang, process_info, [Pid, registered_name]) of
		  [] -> io_lib:format("~p",[Pid]);
		  {registered_name, Registered} -> atom_to_list(Registered)
	      end,
	Frame=wxFrame:new(ParentFrame, ?wxID_ANY, [atom_to_list(node(Pid)), $:, Title],
			  [{style, ?wxDEFAULT_FRAME_STYLE}, {size, {800,700}}]),
	MenuBar = wxMenuBar:new(),
	create_menus(MenuBar),
	wxFrame:setMenuBar(Frame, MenuBar),

	Notebook = wxNotebook:new(Frame, ?ID_NOTEBOOK, [{style, ?wxBK_DEFAULT}]),

	ProcessPage = init_panel(Notebook, "Process Information", Pid, fun init_process_page/2),
	MessagePage = init_panel(Notebook, "Messages", Pid, fun init_message_page/2),
	DictPage    = init_panel(Notebook, "Dictionary", Pid, fun init_dict_page/2),
	StackPage   = init_panel(Notebook, "Stack Trace", Pid, fun init_stack_page/2),

	wxFrame:connect(Frame, close_window),
	wxMenu:connect(Frame, command_menu_selected),
	%% wxNotebook:connect(Notebook, command_notebook_page_changed, [{skip,true}]),
	wxFrame:show(Frame),
	{Frame, #state{parent=Parent,
		       pid=Pid,
		       frame=Frame,
		       pages=[ProcessPage,MessagePage,DictPage,StackPage]
		      }}
    catch error:{badrpc, _} ->
	    observer_wx:return_to_localnode(ParentFrame, node(Pid)),
	    {stop, badrpc, #state{parent=Parent, pid=Pid}};
	  Error:Reason ->
	    io:format("~p:~p: ~p ~p~n ~p~n",
		      [?MODULE, ?LINE, Error, Reason, erlang:get_stacktrace()])
    end.

init_panel(Notebook, Str, Pid, Fun) ->
    Panel  = wxPanel:new(Notebook),
    Sizer  = wxBoxSizer:new(?wxHORIZONTAL),
    {Window,Callback} = Fun(Panel, Pid),
    wxSizer:add(Sizer, Window, [{flag, ?wxEXPAND bor ?wxALL}, {proportion, 1}, {border, 5}]),
    wxPanel:setSizer(Panel, Sizer),
    true = wxNotebook:addPage(Notebook, Panel, Str),
    #worker{panel=Panel, callback=Callback}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%Callbacks%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
handle_event(#wx{event=#wxClose{type=close_window}}, State) ->
    {stop, shutdown, State};

handle_event(#wx{id=?wxID_CLOSE, event=#wxCommand{type=command_menu_selected}}, State) ->
    {stop, shutdown, State};

handle_event(#wx{id=?REFRESH}, #state{pages=Pages}=State) ->
    [(W#worker.callback)() || W <- Pages],
    {noreply, State};

handle_event(Event, State) ->
    io:format("~p: ~p, Handle event: ~p~n", [?MODULE, ?LINE, Event]),
    {noreply, State}.

handle_info(Info, State) ->
    io:format("~p: ~p, Handle info: ~p~n", [?MODULE, ?LINE, Info]),
    {noreply, State}.

handle_call(Call, _From, State) ->
    io:format("~p ~p: Got call ~p~n",[?MODULE, ?LINE, Call]),
    {reply, ok, State}.

handle_cast(Cast, State) ->
    io:format("~p ~p: Got cast ~p~n", [?MODULE, ?LINE, Cast]),
    {noreply, State}.

terminate(_Reason, #state{parent=Parent,pid=Pid,frame=Frame}) ->
    Parent ! {procinfo_menu_closed, Pid},
    case Frame of
	undefined ->  ok;
	_ -> wxFrame:destroy(Frame)
    end,
    ok.

code_change(_, _, State) ->
    {stop, not_yet_implemented, State}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init_process_page(Panel, Pid) ->
    Fields = process_info_fields(Pid),
    {FPanel, _, UpFields} = observer_lib:display_info(Panel, Fields),
    {FPanel, fun() -> observer_lib:update_info(UpFields, process_info_fields(Pid)) end}.

init_text_page(Parent) ->
    Style = ?wxTE_MULTILINE bor ?wxTE_RICH2 bor ?wxTE_READONLY,
    Text = wxTextCtrl:new(Parent, ?wxID_ANY, [{style, Style}]),
    Font = observer_wx:get_attrib({font, modern}),
    Attr = wxTextAttr:new(?wxBLACK, [{font, Font}]),
    true = wxTextCtrl:setDefaultStyle(Text, Attr),
    wxTextAttr:destroy(Attr),
    Text.

init_message_page(Parent, Pid) ->
    Text = init_text_page(Parent),
    Format = fun(Message, Number) ->
		     {io_lib:format("~-4.w ~p~n", [Number, Message]),
		      Number+1}
	     end,
    Update = fun() ->
		     {messages,RawMessages} =
			 observer_wx:try_rpc(node(Pid), erlang, process_info, [Pid, messages]),
		     {Messages,_} = lists:mapfoldl(Format, 1, RawMessages),
		     Last = wxTextCtrl:getLastPosition(Text),
		     wxTextCtrl:remove(Text, 0, Last),
		     case Messages =:= [] of
			 true ->
			     wxTextCtrl:writeText(Text, "No messages");
			 false ->
			     wxTextCtrl:writeText(Text, Messages)
		     end
	     end,
    Update(),
    {Text, Update}.

init_dict_page(Parent, Pid) ->
    Text = init_text_page(Parent),
    Update = fun() ->
		     {dictionary,RawDict} =
			 observer_wx:try_rpc(node(Pid), erlang, process_info, [Pid, dictionary]),
		     Dict = [io_lib:format("~-20.w ~p~n", [K, V]) || {K, V} <- RawDict],
		     Last = wxTextCtrl:getLastPosition(Text),
		     wxTextCtrl:remove(Text, 0, Last),
		     wxTextCtrl:writeText(Text, Dict)
	     end,
    Update(),
    {Text, Update}.

init_stack_page(Parent, Pid) ->
    Text = init_text_page(Parent),
    Format = fun({Mod, Fun, Arg, Info}) ->
		     Str = io_lib:format("~w:~w/~w", [Mod,Fun,Arg]),
		     case Info of
			 [{file,File},{line,Line}] ->
			     io_lib:format("~-45.s ~s:~w~n", [Str,File,Line]);
			 _ ->
			     [Str,$\n]
		     end
	     end,
    Update = fun() ->
		     {current_stacktrace,RawBt} =
			 observer_wx:try_rpc(node(Pid), erlang, process_info,
					     [Pid, current_stacktrace]),
		     Last = wxTextCtrl:getLastPosition(Text),
		     wxTextCtrl:remove(Text, 0, Last),
		     [wxTextCtrl:writeText(Text, Format(Entry)) || Entry <- RawBt]
	     end,
    Update(),
    {Text, Update}.

create_menus(MenuBar) ->
    Menus = [{"File", [#create_menu{id=?wxID_CLOSE, text="Close"}]},
	     {"View", [#create_menu{id=?REFRESH, text="Refresh\tCtrl-R"}]}],
    observer_lib:create_menus(Menus, MenuBar, new_window).

process_info_fields(Pid) ->
    RawInfo = observer_wx:try_rpc(node(Pid), erlang, process_info, [Pid, item_list()]),
    Struct = [{"Overview",
	       [{"Initial Call",     initial_call},
		{"Current Function", current_function},
		{"Registered Name",  registered_name},
		{"Status",           status},
		{"Message Queue Len",message_queue_len},
		{"Priority",         priority},
		{"Trap Exit",        trap_exit},
		{"Reductions",       reductions},
		{"Binary",           binary},
		{"Last Calls",       last_calls},
		{"Catch Level",      catchlevel},
		{"Trace",            trace},
		{"Suspending",       suspending},
		{"Sequential Trace Token", sequential_trace_token},
		{"Error Handler",    error_handler}]},
	      {"Connections",
	       [{"Group Leader",     group_leader},
		{"Links",            links},
		{"Monitors",         monitors},
		{"Monitored by",     monitored_by}]},
	      {"Memory and Garbage Collection", right,
	       [{"Memory",           {bytes, memory}},
		{"Stack and Heaps",  {bytes, total_heap_size}},
		{"Heap Size",        {bytes, heap_size}},
		{"Stack Size",       {bytes, stack_size}},
		{"GC Min Heap Size", {bytes, get_gc_info(min_heap_size)}},
		{"GC FullSweep After", get_gc_info(fullsweep_after)}
	       ]}],
    observer_lib:fill_info(Struct, RawInfo).

item_list() ->
    [ %% backtrace,
      binary,
      catchlevel,
      current_function,
      %% dictionary,
      error_handler,
      garbage_collection,
      group_leader,
      heap_size,
      initial_call,
      last_calls,
      links,
      memory,
      message_queue_len,
      %% messages,
      monitored_by,
      monitors,
      priority,
      reductions,
      registered_name,
      sequential_trace_token,
      stack_size,
      status,
      suspending,
      total_heap_size,
      trace,
      trap_exit].

get_gc_info(Arg) ->
    fun(Data) ->
	    GC = proplists:get_value(garbage_collection, Data),
	    proplists:get_value(Arg, GC)
    end.