aboutsummaryrefslogtreecommitdiffstats
path: root/lib/observer/src/observer_procinfo.erl
diff options
context:
space:
mode:
authorDan Gudmundsson <[email protected]>2011-11-19 08:46:53 +0100
committerDan Gudmundsson <[email protected]>2011-11-19 08:46:53 +0100
commitf9ab9f01694473e2843f3f1526ef60129a2f123b (patch)
treea106ad6c8d473a3527507c730f2f42f1c0a78b78 /lib/observer/src/observer_procinfo.erl
parentde1dc9682e8c6c56e03d5deb20019ff98d0b569a (diff)
parent39730743916b300eb3e229c4e6e8a2987487d797 (diff)
downloadotp-f9ab9f01694473e2843f3f1526ef60129a2f123b.tar.gz
otp-f9ab9f01694473e2843f3f1526ef60129a2f123b.tar.bz2
otp-f9ab9f01694473e2843f3f1526ef60129a2f123b.zip
Merge branch 'dgud/observer/gui/OTP-4779'
* dgud/observer/gui/OTP-4779: [observer] Implemented basic tracing functionality [observer] Work around wxWidgets windows bug [observer] Rework tracing part of the gui [observer] Fix portability bugs [observer] Improve process_info window [observer] Misc cleanup and fixes [observer] Clean up code [observer] Add a [d]ets viewer, tv replacement [observer] Started on a wx gui
Diffstat (limited to 'lib/observer/src/observer_procinfo.erl')
-rw-r--r--lib/observer/src/observer_procinfo.erl274
1 files changed, 274 insertions, 0 deletions
diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl
new file mode 100644
index 0000000000..127599a39e
--- /dev/null
+++ b/lib/observer/src/observer_procinfo.erl
@@ -0,0 +1,274 @@
+%%
+%% %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, fixed}),
+ 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.