From b7b88933672591d7f7c2a71a4c1643b6ca486f23 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Wed, 9 Nov 2011 15:48:02 +0100 Subject: [observer] Improve process_info window Also refactor some (re)used code --- lib/observer/src/observer_lib.erl | 26 +- lib/observer/src/observer_pro_wx.erl | 22 +- lib/observer/src/observer_procinfo.erl | 644 +++++++++++---------------------- lib/observer/src/observer_sys_wx.erl | 17 +- lib/observer/src/observer_wx.erl | 34 +- 5 files changed, 261 insertions(+), 482 deletions(-) (limited to 'lib/observer') diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl index b36aaa7541..8bc79ffddd 100644 --- a/lib/observer/src/observer_lib.erl +++ b/lib/observer/src/observer_lib.erl @@ -21,7 +21,7 @@ -export([get_wx_parent/1, display_info_dialog/1, interval_dialog/4, start_timer/1, stop_timer/1, - display_info/2, update_info/2, to_str/1, + display_info/2, fill_info/2, update_info/2, to_str/1, create_menus/3, create_menu_item/3, create_attrs/0 ]). @@ -117,6 +117,22 @@ display_info(Frame, Info) -> wxWindow:setSizerAndFit(Panel, Sizer), {Panel, Sizer, InfoFs}. +fill_info([{Str, Key}|Rest], Data) when is_atom(Key); is_function(Key) -> + [{Str, get_value(Key, Data)} | fill_info(Rest, Data)]; +fill_info([{Str, {Format, Key}}|Rest], Data) + when is_atom(Key); is_function(Key), is_atom(Format) -> + [{Str, {Format, get_value(Key,Data)}} | fill_info(Rest, Data)]; +fill_info([{Str,SubStructure}|Rest], Data) when is_list(SubStructure) -> + [{Str, fill_info(SubStructure, Data)}|fill_info(Rest,Data)]; +fill_info([{Str,Attrib,SubStructure}|Rest], Data) -> + [{Str, Attrib, fill_info(SubStructure, Data)}|fill_info(Rest,Data)]; +fill_info([], _) -> []. + +get_value(Key, Data) when is_atom(Key) -> + proplists:get_value(Key,Data); +get_value(Fun, Data) when is_function(Fun) -> + Fun(Data). + update_info([Fields|Fs], [{_Header, SubStructure}| Rest]) -> update_info2(Fields, SubStructure), update_info(Fs, Rest); @@ -154,9 +170,9 @@ to_str({time_ms, MS}) -> true -> integer_to_list(S) ++ " Secs" end; -to_str({A, B}) -> +to_str({A, B}) when is_atom(A), is_atom(B) -> lists:concat([A, ":", B]); -to_str({M,F,A}) -> +to_str({M,F,A}) when is_atom(M), is_atom(F), is_integer(A) -> lists:concat([M, ":", F, "/", A]); to_str(Value) when is_list(Value) -> case lists:all(fun(X) -> is_integer(X) end, Value) of @@ -172,8 +188,8 @@ to_str(Pid) when is_pid(Pid) -> pid_to_list(Pid); to_str(No) when is_integer(No) -> integer_to_list(No); -to_str(ShouldNotGetHere) -> - erlang:error({?MODULE, to_str, ShouldNotGetHere}). +to_str(Term) -> + io_lib:format("~w", [Term]). create_menus(Menus, MenuBar, Type) -> Add = fun({Tag, Ms}, Index) -> diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl index 8e3e28caec..4bc6da6476 100644 --- a/lib/observer/src/observer_pro_wx.erl +++ b/lib/observer/src/observer_pro_wx.erl @@ -233,14 +233,14 @@ dump_to_file(Parent, FileName, Holder) -> wxDialog:destroy(MD) end. -start_procinfo(_Node, undefined, _Frame, Opened) -> +start_procinfo(undefined, _Frame, Opened) -> Opened; -start_procinfo(Node, Pid, Frame, Opened) -> +start_procinfo(Pid, Frame, Opened) -> case lists:member(Pid, Opened) of true -> Opened; false -> - observer_procinfo:start(Node, Pid, Frame, self()), + observer_procinfo:start(Pid, Frame, self()), [Pid | Opened] end. @@ -356,23 +356,21 @@ handle_event(#wx{id=?ID_KILL}, {noreply, State#state{sel={Ids,Pids}}}; -handle_event(#wx{id=?ID_PROC}, +handle_event(#wx{id = ?ID_PROC}, #state{panel=Panel, popup_menu=Pop, - holder=Holder, sel={_, [Pid|_]}, procinfo_menu_pids=Opened}=State) -> wxWindow:show(Pop, [{show, false}]), - Node = call(Holder, {get_node, self()}), - Opened2 = start_procinfo(Node, Pid, Panel, Opened), + Opened2 = start_procinfo(Pid, Panel, Opened), {noreply, State#state{procinfo_menu_pids=Opened2}}; -handle_event(#wx{id=?ID_TRACEMENU}, +handle_event(#wx{id = ?ID_TRACEMENU}, #state{holder=Holder, popup_menu=Pop, trace_options=Options, match_specs=MatchSpecs, - sel=Pids, + sel={_, Pids}, tracemenu_opened=false, panel=Panel}=State) -> wxWindow:show(Pop, [{show, false}]), @@ -471,15 +469,13 @@ handle_event(#wx{event=#wxList{type=command_list_col_click, col=Col}}, handle_event(#wx{event=#wxList{type=command_list_item_activated}}, #state{panel=Panel, procinfo_menu_pids=Opened, - holder=Holder, sel={_, [Pid|_]}}=State) when Pid =/= undefined -> - Node = call(Holder, {get_node, self()}), - Opened2=start_procinfo(Node, Pid, Panel, Opened), + Opened2 = start_procinfo(Pid, Panel, Opened), {noreply, State#state{procinfo_menu_pids=Opened2}}; handle_event(Event, State) -> - io:format("~p~p, handle event ~p\n", [?MODULE, ?LINE, Event]), + io:format("~p:~p: handle event ~p\n", [?MODULE, ?LINE, Event]), {noreply, State}. diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl index 6414a26ec9..2600109161 100644 --- a/lib/observer/src/observer_procinfo.erl +++ b/lib/observer/src/observer_procinfo.erl @@ -20,7 +20,7 @@ -behaviour(wx_object). --export([start/4]). +-export([start/3]). -export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3, handle_call/3, handle_info/2]). @@ -28,452 +28,78 @@ -include_lib("wx/include/wx.hrl"). -include("observer_defs.hrl"). --define(CLOSE, 601). --define(REFRESH, 602). +-define(REFRESH, 601). -define(SELECT_ALL, 603). -define(ID_NOTEBOOK, 604). --record(procinfo_state, {parent, - frame, - node, - pid, - module, - procinfo_stc, - modinfo_stc, - modcode_stc, - checklistbox, - current_view, % proc_info::atom | module_info::atom | module_code::atom - itemlist = [{backtrace, false}, - {binary, false}, - {catchlevel, false}, - {current_function, false}, - {dictionary, false}, - {error_handler, true}, - {garbage_collection, true}, - {group_leader, true}, - {heap_size, true}, - {initial_call, false}, - {last_calls, false}, - {links, true}, - {memory, false}, - {message_queue_len, true}, - {messages, false}, - {monitored_by, false}, - {monitors, false}, - {priority, true}, - {reductions, false}, - {registered_name, false}, - {sequential_trace_token, false}, - {stack_size, false}, - {status, false}, - {suspending, false}, - {total_heap_size, false}, - {trace, false}, - {trap_exit,true}] - }). - - - - -start(Node, Process, ParentFrame, Parent) -> - wx_object:start(?MODULE, [Node, Process, ParentFrame, Parent], []). +-record(state, {parent, + frame, + pid, + pages=[] + }). + +-record(worker, {panel, callback}). + +start(Process, ParentFrame, Parent) -> + wx_object:start(?MODULE, [Process, ParentFrame, Parent], []). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -init([Node, Process, ParentFrame, Parent]) -> +init([Pid, ParentFrame, Parent]) -> try - State = #procinfo_state{parent = Parent, - node = Node, - pid = Process, - current_view = proc_info - }, - ItemList = State#procinfo_state.itemlist, - Name = case observer_wx:try_rpc(Node, erlang, process_info, [Process, registered_name]) of - [] -> - undefined; - {registered_name, M} -> - M - end, - {initial_call, {Module, _, _}} = observer_wx:try_rpc(Node, erlang, process_info, [Process, initial_call]), - {Frame, ProcStc, CheckListBox, ModInfoStc, ModCodeStc} = setup(ParentFrame, Node, Process, ItemList, Module, Name), - {Frame, State#procinfo_state{frame = Frame, - module = Module, - procinfo_stc = ProcStc, - modinfo_stc = ModInfoStc, - modcode_stc = ModCodeStc, - checklistbox = CheckListBox}} + 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), - {stop, badrpc, #procinfo_state{parent = Parent, - pid = Process}} + 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. - -setup(ParentFrame, Node, Pid, ItemList, Module, Name) -> - Title = case Name of - undefined -> - atom_to_list(Node) ++ ":" ++ atom_to_list(Module); - Name -> - atom_to_list(Node) ++ ":" ++ atom_to_list(Name) - end, - Frame = wxFrame:new(ParentFrame, ?wxID_ANY, Title, - [{style, ?wxDEFAULT_FRAME_STYLE}, - {size, {900,900}}]), - Panel = wxPanel:new(Frame, []), - MainSz = wxBoxSizer:new(?wxHORIZONTAL), - Notebook = wxNotebook:new(Panel, ?ID_NOTEBOOK, [{style, ?wxBK_DEFAULT}]), - wxNotebook:connect(Notebook, command_notebook_page_changed), - {CodePanel, CheckListBox, CodeStc} = create_procinfo_page(Notebook, Node, Pid, ItemList), - {ModInfoPanel, ModInfoStc} = create_page(Notebook, Node, Module, module_info), - {ModCodePanel, ModCodeStc} = create_page(Notebook, Node, Module, module_code), - wxNotebook:addPage(Notebook, CodePanel, "Process information", []), - wxNotebook:addPage(Notebook, ModInfoPanel, "Module information", []), - wxNotebook:addPage(Notebook, ModCodePanel, "Module code", []), - MenuBar = wxMenuBar:new(), - create_menus(MenuBar), - wxWindow:setSizer(Panel, MainSz), - wxSizer:add(MainSz, Notebook, [{flag, ?wxEXPAND}, {proportion, 1}]), - wxFrame:setMenuBar(Frame, MenuBar), - wxFrame:show(Frame), - wxFrame:connect(Frame, close_window), - wxMenu:connect(Frame, command_menu_selected), - {Frame, CodeStc, CheckListBox, ModInfoStc, ModCodeStc}. - - -create_procinfo_page(Notebook, Node, Pid, ItemList) -> - Panel = wxPanel:new(Notebook), - MainSz = wxBoxSizer:new(?wxHORIZONTAL), - CheckSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, "View"}]), - BtnSz = wxBoxSizer:new(?wxHORIZONTAL), - - Stc = create_styled_txtctrl(Panel, proc_info), - Txt = get_formatted_values(Node, Pid, ItemList), - set_text(Stc, Txt, text), - Choices = [atom_to_list(Tag) || {Tag, _} <- ItemList], - CheckListBox = wxCheckListBox:new(Panel, ?wxID_ANY, [{choices, Choices}, - {style, ?wxLB_EXTENDED}, - {style, ?wxLB_SORT}, - {style, ?wxLB_NEEDED_SB}]), - check_boxes(CheckListBox, ItemList), - wxCheckListBox:connect(CheckListBox, command_checklistbox_toggled), - - SelAllBtn = wxButton:new(Panel, ?SELECT_ALL, [{label, "Select all"}]), - DeSelAllBtn = wxButton:new(Panel, ?SELECT_ALL, [{label, "Deselect all"}]), - - wxButton:connect(SelAllBtn, command_button_clicked, [{userData, true}]), - wxButton:connect(DeSelAllBtn, command_button_clicked, [{userData, false}]), - wxWindow:setSizer(Panel, MainSz), - wxSizer:add(MainSz, Stc, [{proportion, 1}, {flag, ?wxEXPAND}]), - wxSizer:add(CheckSz, CheckListBox, [{proportion, 1}]), - wxSizer:add(BtnSz, SelAllBtn), - wxSizer:add(BtnSz, DeSelAllBtn), - wxSizer:add(CheckSz, BtnSz), - wxSizer:add(MainSz, CheckSz, [{flag, ?wxEXPAND}]), - {Panel, CheckListBox, Stc}. - -create_page(Notebook, Node, Module, What) -> - Panel = wxPanel:new(Notebook, []), - Sizer = wxBoxSizer:new(?wxVERTICAL), - Stc = create_styled_txtctrl(Panel, What), - {Sort, Txt} = case What of - module_info -> - {text, get_formatted_modinfo(Node, Module)}; - module_code -> - case get_src_file(Node, Module) of - {ok, File} -> - {file, File}; - error-> - {text, "Error! Could not read sourcefile"} - end - end, - set_text(Stc, Txt, Sort), - wxWindow:setSizer(Panel, Sizer), - wxSizer:add(Sizer, Stc, [{flag, ?wxEXPAND}, {proportion, 1}]), - {Panel, Stc}. - -create_menus(MenuBar) -> - Menus = [{"File", [#create_menu{id = ?CLOSE, text = "Close"}]}, - {"View", [#create_menu{id = ?REFRESH, text = "Refresh"}]}], - observer_lib:create_menus(Menus, MenuBar, new_window). - -check_boxes(CheckListBox, Bool, all) -> - lists:foreach(fun(Index) -> - wxCheckListBox:check(CheckListBox, Index, [{check, Bool}]) - end, - lists:seq(0, wxControlWithItems:getCount(CheckListBox))). -check_boxes(CheckListBox, ItemList) -> - lists:foldl(fun({_, Bool}, Index) -> - wxCheckListBox:check(CheckListBox, Index, [{check, Bool}]), - Index+1 - end, - 0, ItemList). - -create_styled_txtctrl(Parent, View) -> - FixedFont = wxFont:new(11, ?wxFONTFAMILY_TELETYPE, ?wxFONTSTYLE_NORMAL, ?wxNORMAL,[]), - Stc = wxStyledTextCtrl:new(Parent), - wxStyledTextCtrl:styleClearAll(Stc), - wxStyledTextCtrl:styleSetFont(Stc, ?wxSTC_STYLE_DEFAULT, FixedFont), - wxStyledTextCtrl:setLexer(Stc, ?wxSTC_LEX_ERLANG), - wxStyledTextCtrl:setMarginType(Stc, 2, ?wxSTC_MARGIN_NUMBER), - W = wxStyledTextCtrl:textWidth(Stc, ?wxSTC_STYLE_LINENUMBER, "9"), - wxStyledTextCtrl:setMarginWidth(Stc, 2, W*3), - - wxStyledTextCtrl:setSelectionMode(Stc, ?wxSTC_SEL_LINES), - wxStyledTextCtrl:setUseHorizontalScrollBar(Stc, false), - - Styles = [{?wxSTC_ERLANG_DEFAULT, {0,0,0}}, - {?wxSTC_ERLANG_COMMENT, {160,53,35}}, - {?wxSTC_ERLANG_VARIABLE, {150,100,40}}, - {?wxSTC_ERLANG_NUMBER, {5,5,100}}, - {?wxSTC_ERLANG_KEYWORD, {130,40,172}}, - {?wxSTC_ERLANG_STRING, {170,45,132}}, - {?wxSTC_ERLANG_OPERATOR, {30,0,0}}, - {?wxSTC_ERLANG_ATOM, {0,0,0}}, - {?wxSTC_ERLANG_FUNCTION_NAME, {64,102,244}}, - {?wxSTC_ERLANG_CHARACTER,{236,155,172}}, - {?wxSTC_ERLANG_MACRO, {40,144,170}}, - {?wxSTC_ERLANG_RECORD, {40,100,20}}, - {?wxSTC_ERLANG_SEPARATOR,{0,0,0}}, - {?wxSTC_ERLANG_NODE_NAME,{0,0,0}}], - SetStyle = fun({Style, Color}) -> - wxStyledTextCtrl:styleSetFont(Stc, Style, FixedFont), - wxStyledTextCtrl:styleSetForeground(Stc, Style, Color) - end, - [SetStyle(Style) || Style <- Styles], - - KeyWords = case View of - proc_info -> - get_procinfo_keywords(); - module_info -> - get_modinfo_keywords(); - module_code -> - get_erl_keywords() - end, - wxStyledTextCtrl:setKeyWords(Stc, 0, KeyWords), - Stc. - -get_erl_keywords() -> - L = ["after","begin","case","try","cond","catch","andalso","orelse", - "end","fun","if","let","of","query","receive","when","bnot","not", - "div","rem","band","and","bor","bxor","bsl","bsr","or","xor"], - lists:flatten([K ++ " "|| K <- L] ++ [0]). -get_procinfo_keywords() -> - L = ["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"], - lists:flatten([K ++ " "|| K <- L] ++ [0]). -get_modinfo_keywords() -> - L = ["exports", "imports", "attributes", "compile"], - lists:flatten([K ++ " "|| K <- L] ++ [0]). - -get_formatted_values(Node, Process, ItemList) -> - TagList = [Tag || {Tag, Bool} <- ItemList, Bool =:= true], - Values = observer_wx:try_rpc(Node, erlang, process_info, [Process, TagList]), - lists:flatten(format_value(Values, [])). - -format_value([], Acc) -> - lists:reverse(Acc); -format_value([{backtrace, Bin} | T], Acc) -> - format_value(T, [io_lib:format("{backtrace,~s}~n", [binary_to_list(Bin)]) | Acc]); -format_value([H|T], Acc) -> - format_value(T, [io_lib:format("~p~n", [H]) | Acc]). - -get_formatted_modinfo(Node, Module) -> - Info = observer_wx:try_rpc(Node, Module, module_info, []), - lists:flatten([io_lib:format("~p~n", [I]) || I <- Info]). -get_src_remote(Node, Module) -> - case observer_wx:try_rpc(Node, filename, find_src, [Module]) of - {error, _} -> - error; - {SrcFile, _} -> - case observer_wx:try_rpc(Node, file, read_file_info, [SrcFile ++ ".erl"]) of - {error, _} -> - error; - {ok, _} -> - {ok, SrcFile ++ ".erl"} - end - end. - -get_src_local(Module) -> - case filename:find_src(Module) of - {error, _} -> - error; - {SrcFile, _} -> - case file:read_file_info(SrcFile ++ ".erl") of - {error, _} -> - error; - {ok, _} -> - {ok, SrcFile ++ ".erl"} - end - end. - -get_src_file(Node, Module) -> - case get_src_remote(Node, Module) of - {ok, SrcFile} -> - {ok, SrcFile}; - error -> - get_src_local(Module) - end. - - -set_text(Stc, Text, text) -> - wxStyledTextCtrl:setReadOnly(Stc, false), - wxStyledTextCtrl:setText(Stc, Text), - wxStyledTextCtrl:setReadOnly(Stc, true); -set_text(Stc, File, file) -> - wxStyledTextCtrl:setReadOnly(Stc, false), - wxStyledTextCtrl:loadFile(Stc, File), - wxStyledTextCtrl:setReadOnly(Stc, true). - -update_procinfo_page(Stc, Node, Process, ItemList) -> - Txt = get_formatted_values(Node, Process, ItemList), - set_text(Stc, Txt, text). -update_modinfo_page(Stc, Node, Module) -> - Txt = get_formatted_modinfo(Node, Module), - set_text(Stc, Txt, text). -update_modcode_page(Stc, Node, Module) -> - case get_src_file(Node, Module) of - {ok, File} -> - set_text(Stc, File, file); - error -> - set_text(Stc, "Error! Could not read sourcefile", text) - 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) -> +handle_event(#wx{event=#wxClose{type=close_window}}, State) -> {stop, shutdown, State}; -handle_event(#wx{id = ?CLOSE, - event = #wxCommand{type = command_menu_selected}}, - State) -> +handle_event(#wx{id=?wxID_CLOSE, event=#wxCommand{type=command_menu_selected}}, State) -> {stop, shutdown, State}; -handle_event(#wx{id = ?REFRESH, - event = #wxCommand{type = command_menu_selected}}, - #procinfo_state{current_view = Current, - frame = Frame, - node = Node, - pid = Pid, - procinfo_stc = Stc, - itemlist = ItemList} = State) when Current =:= proc_info -> - try - update_procinfo_page(Stc, Node, Pid, ItemList), - {noreply, State} - catch error:{badrpc, _} -> - observer_wx:return_to_localnode(Frame, Node), - {stop, badrpc, State} - end; - -handle_event(#wx{id = ?REFRESH, - event = #wxCommand{type = command_menu_selected}}, - #procinfo_state{current_view = Current, - frame = Frame, - node = Node, - modinfo_stc = Stc, - module = Module} = State) when Current =:= module_info -> - try - update_modinfo_page(Stc, Node, Module), - {noreply, State} - catch error:{badrpc, _} -> - observer_wx:return_to_localnode(Frame, Node), - {stop, badrpc, State} - end; - -handle_event(#wx{id = ?REFRESH, - event = #wxCommand{type = command_menu_selected}}, - #procinfo_state{current_view = Current, - modcode_stc = Stc, - frame = Frame, - node = Node, - module = Module} = State) when Current =:= module_code -> - try - update_modcode_page(Stc, Node, Module), - {noreply, State} - catch error:{badrpc, _} -> - observer_wx:return_to_localnode(Frame, Node), - {stop, badrpc, State} - end; - - -handle_event(#wx{obj = Notebook, id = ?ID_NOTEBOOK, - event = #wxNotebook{type = command_notebook_page_changed}}, - #procinfo_state{frame = Frame, - module = Module, - procinfo_stc = ProcStc, - modcode_stc = CodeStc, - modinfo_stc = ModInfoStc, - node = Node, - pid = Pid, - itemlist = ItemList} = State) -> - try - Current = case observer_wx:check_page_title(Notebook) of - "Process information" -> - update_procinfo_page(ProcStc, Node, Pid, ItemList), - proc_info; - "Module information" -> - update_modinfo_page(ModInfoStc, Node, Module), - module_info; - "Module code" -> - update_modcode_page(CodeStc, Node, Module), - module_code - end, - {noreply, State#procinfo_state{current_view = Current}} - catch error:{badrpc, _} -> - observer_wx:return_to_localnode(Frame, Node), - {stop, badrpc, State} - end; - -handle_event(#wx{event = #wxCommand{type = command_checklistbox_toggled, - commandInt = Index}, - obj = CheckListbox}, - #procinfo_state{frame = Frame, - node = Node, - pid = Process, - procinfo_stc = Stc, - itemlist = ItemList} = State) -> - try - {Tag, _} = lists:nth(Index+1, ItemList), - ItemList2 = case wxCheckListBox:isChecked(CheckListbox, Index) of - true -> - lists:keyreplace(Tag, 1, ItemList, {Tag, true}); - false -> - lists:keyreplace(Tag, 1, ItemList, {Tag, false}) - end, - Txt = get_formatted_values(Node, Process, ItemList2), - set_text(Stc, Txt, text), - {noreply, State#procinfo_state{itemlist = ItemList2}} - - catch error:{badrpc, _} -> - observer_wx:return_to_localnode(Frame, Node), - {stop, badrpc, State} - end; - -handle_event(#wx{id = ?SELECT_ALL, - event = #wxCommand{type = command_button_clicked}, - userData = Bool}, - #procinfo_state{frame = Frame, - node = Node, - pid = Process, - itemlist = ItemList, - procinfo_stc = Stc, - checklistbox = CheckListBox} = State) -> - try - check_boxes(CheckListBox, Bool, all), - ItemList2 = lists:keymap(fun(_) -> - Bool - end, - 2, ItemList), - Txt = get_formatted_values(Node, Process, ItemList2), - set_text(Stc, Txt, text), - {noreply, State#procinfo_state{itemlist = ItemList2}} - catch error:{badrpc, _} -> - observer_wx:return_to_localnode(Frame, Node), - {stop, badrpc, State} - end; +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]), @@ -491,18 +117,158 @@ handle_cast(Cast, State) -> io:format("~p ~p: Got cast ~p~n", [?MODULE, ?LINE, Cast]), {noreply, State}. -terminate(Reason, #procinfo_state{parent = Parent, - pid = Pid, - frame = Frame}) -> - io:format("~p terminating. Reason: ~p~n", [?MODULE, Reason]), +terminate(_Reason, #state{parent=Parent,pid=Pid,frame=Frame}) -> Parent ! {procinfo_menu_closed, Pid}, case Frame of - undefined -> - ok; - _ -> - wxFrame:destroy(Frame) + 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. diff --git a/lib/observer/src/observer_sys_wx.erl b/lib/observer/src/observer_sys_wx.erl index 5bc5fd49f6..36aa226bd1 100644 --- a/lib/observer/src/observer_sys_wx.erl +++ b/lib/observer/src/observer_sys_wx.erl @@ -59,8 +59,10 @@ init2([Notebook, Parent]) -> {Info, Stat} = info_fields(), Panel = wxPanel:new(Notebook), Sizer = wxBoxSizer:new(?wxHORIZONTAL), - {FPanel0, _FSizer0, Fields0} = observer_lib:display_info(Panel, fill_info(Info, SysInfo)), - {FPanel1, _FSizer1, Fields1} = observer_lib:display_info(Panel, fill_info(Stat, SysInfo)), + {FPanel0, _FSizer0, Fields0} = + observer_lib:display_info(Panel, observer_lib:fill_info(Info, SysInfo)), + {FPanel1, _FSizer1, Fields1} = + observer_lib:display_info(Panel, observer_lib:fill_info(Stat, SysInfo)), wxSizer:add(Sizer, FPanel0, [{flag, ?wxEXPAND bor ?wxTOP bor ?wxBOTTOM bor ?wxLEFT}, {proportion, 1}, {border, 5}]), wxSizer:add(Sizer, FPanel1, [{flag, ?wxEXPAND bor ?wxTOP bor ?wxBOTTOM bor ?wxRIGHT}, @@ -81,7 +83,8 @@ update_syspage(#sys_wx_state{node = Node, fields=Fields, sizer=Sizer}) -> try SysInfo = observer_wx:try_rpc(Node, ?MODULE, sys_info, []), {Info, Stat} = info_fields(), - observer_lib:update_info(Fields, fill_info(Info, SysInfo) ++ fill_info(Stat, SysInfo)), + observer_lib:update_info(Fields, observer_lib:fill_info(Info, SysInfo) ++ + observer_lib:fill_info(Stat, SysInfo)), wxSizer:layout(Sizer) catch E:R -> io:format("~p:~p ~p~n",[E,R, erlang:get_stacktrace()]) @@ -124,14 +127,6 @@ info_fields() -> ], {Info, Stat}. -fill_info([{Str, Key}|Rest], Data) when is_atom(Key) -> - [{Str, proplists:get_value(Key,Data)} | fill_info(Rest, Data)]; -fill_info([{Str,SubStructure}|Rest], Data) -> - [{Str, fill_info(SubStructure, Data)}|fill_info(Rest,Data)]; -fill_info([{Str,Attrib,SubStructure}|Rest], Data) -> - [{Str, Attrib, fill_info(SubStructure, Data)}|fill_info(Rest,Data)]; -fill_info([], _) -> []. - %%%%%%%%%%%%%%%%%%%%%%% Callbacks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% handle_info(refresh_interval, #sys_wx_state{panel = Panel, diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl index 4688e271ee..b39b34c240 100644 --- a/lib/observer/src/observer_wx.erl +++ b/lib/observer/src/observer_wx.erl @@ -20,8 +20,8 @@ -behaviour(wx_object). -export([start/0]). --export([create_menus/2, create_txt_dialog/4, try_rpc/4, - return_to_localnode/2]). +-export([create_menus/2, get_attrib/1, + create_txt_dialog/4, try_rpc/4, return_to_localnode/2]). -export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3, handle_call/3, handle_info/2, check_page_title/1]). @@ -62,6 +62,9 @@ start() -> create_menus(Object, Menus) when is_list(Menus) -> wx_object:call(Object, {create_menus, Menus}). +get_attrib(What) -> + wx_object:call(observer, {get_attrib, What}). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -79,6 +82,7 @@ init(_Args) -> wxFrame:show(Frame), net_kernel:monitor_nodes(true), process_flag(trap_exit, true), + register(observer, self()), {Frame, UpdState}. setup(#state{frame = Frame} = State) -> @@ -133,16 +137,19 @@ setup(#state{frame = Frame} = State) -> node = node(), nodes = Nodes }, + %% Create resources which we don't want to duplicate + SysFont = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT), + SysFontSize = wxFont:getPointSize(SysFont), + Modern = wxFont:new(SysFontSize, ?wxFONTFAMILY_MODERN, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL), + put({font, modern}, Modern), UpdState. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%Callbacks -handle_event(#wx{obj = Notebook, id = ?ID_NOTEBOOK, - event = Ev = #wxNotebook{type = command_notebook_page_changing}}, - #state{active_tab=Previous, node=Node, notebook = Notebook} = State) -> - io:format("Command notebook changed ~p ~n", [Ev]), +handle_event(#wx{event=#wxNotebook{type=command_notebook_page_changing}}, + #state{active_tab=Previous, node=Node} = State) -> Pid = get_active_pid(State), Previous ! not_active, Pid ! {active, Node}, @@ -232,8 +239,7 @@ handle_event(Event, State) -> Pid ! Event, {noreply, State}. -handle_cast(Cast, State) -> - io:format("~p:~p: Got cast ~p~n", [?MODULE, ?LINE, Cast]), +handle_cast(_Cast, State) -> {noreply, State}. handle_call({create_menus, TabMenus}, _From, @@ -244,8 +250,10 @@ handle_call({create_menus, TabMenus}, _From, end), {reply, ok, State#state{menus=TabMenus}}; -handle_call(Msg, _From, State) -> - io:format("~p~p: Got Call ~p~n",[?MODULE, ?LINE, Msg]), +handle_call({get_attrib, Attrib}, _From, State) -> + {reply, get(Attrib), State}; + +handle_call(_Msg, _From, State) -> {reply, ok, State}. handle_info({nodeup, _Node}, State) -> @@ -265,13 +273,11 @@ handle_info({nodedown, Node}, create_txt_dialog(Frame, Msg, "Node down", ?wxICON_EXCLAMATION), {noreply, State3}; -handle_info(Info, State) -> - io:format("~p, ~p, Handle info: ~p~n", [?MODULE, ?LINE, Info]), +handle_info(_Info, State) -> {noreply, State}. -terminate(Reason, #state{frame = Frame}) -> +terminate(_Reason, #state{frame = Frame}) -> wxFrame:destroy(Frame), - io:format("~p terminating. Reason: ~p~n", [?MODULE, Reason]), ok. code_change(_, _, State) -> -- cgit v1.2.3