From 549205db3dee21e83a64a01f03b1e8ed2225b276 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Fri, 11 Oct 2013 16:56:24 +0200 Subject: observer: Consolidate the view of process information Add clickable links to processes in process info Use html pages (contains clickable processes) to view messages, dictionary and other information. --- lib/observer/src/cdv_proc_wx.erl | 4 +- lib/observer/src/crashdump_viewer_html.erl | 100 ++++++++++++----- lib/observer/src/observer_app_wx.erl | 10 +- lib/observer/src/observer_lib.erl | 120 ++++++++++++++------- lib/observer/src/observer_pro_wx.erl | 34 +++--- lib/observer/src/observer_procinfo.erl | 167 +++++++++++++++-------------- lib/observer/src/observer_wx.erl | 16 +++ 7 files changed, 276 insertions(+), 175 deletions(-) diff --git a/lib/observer/src/cdv_proc_wx.erl b/lib/observer/src/cdv_proc_wx.erl index a0f42b5e6b..1320afce28 100644 --- a/lib/observer/src/cdv_proc_wx.erl +++ b/lib/observer/src/cdv_proc_wx.erl @@ -99,6 +99,7 @@ init_stack_page(Parent, Pid, _Info) -> init_memory_page(Parent, Pid, "StackDump"). init_memory_page(Parent, Pid, What) -> + Win = observer_lib:html_window(Parent), Html = case crashdump_viewer:expand_memory(Pid,What) of {ok,Memory} -> @@ -106,7 +107,8 @@ init_memory_page(Parent, Pid, What) -> {error,Reason} -> crashdump_viewer_html:warning(Reason) end, - observer_lib:html_window(Parent,Html). + wxHtmlWindow:setPage(Win,Html), + Win. init_ets_page(Parent, Pid, _Info) -> cdv_virtual_list:start_link(Parent, cdv_ets_wx, Pid). diff --git a/lib/observer/src/crashdump_viewer_html.erl b/lib/observer/src/crashdump_viewer_html.erl index 23d6fd6b16..038126288b 100644 --- a/lib/observer/src/crashdump_viewer_html.erl +++ b/lib/observer/src/crashdump_viewer_html.erl @@ -53,6 +53,7 @@ chunk/3]). -include("crashdump_viewer.hrl"). +-include("observer_defs.hrl"). %%%----------------------------------------------------------------- %%% Welcome frame @@ -497,51 +498,94 @@ expanded_memory(Heading,Expanded) -> expanded_memory_body(Heading,[]) -> [case Heading of "MsgQueue" -> "No messages were found"; - "StackDump" -> "No stack dump was found"; - "Dictionary" -> "No dictionary was found" + "Message Queue" -> "No messages were found"; + "StackDump" -> "No stack dump was found"; + "Dictionary" -> "No dictionary was found"; + "ProcState" -> "Information could not be retrieved," + " system messages may not be handled by this process." end]; expanded_memory_body(Heading,Expanded) -> + Attr = "BORDER=0 CELLPADDING=0 CELLSPACING=1 WIDTH=100%", [case Heading of "MsgQueue" -> - table( - "BORDER=4 CELLPADDING=4", - [tr( - [th("Message"), - th("SeqTraceToken")]) | - lists:map(fun(Msg) -> msgq_table(Msg) end, Expanded)]); + table(Attr, + [tr( + [th("WIDTH=70%","Message"), + th("WIDTH=30%","SeqTraceToken")]) | + element(1, lists:mapfoldl(fun(Msg, Even) -> + {msgq_table(Msg, Even), + not Even} + end, + true, Expanded))]); + "Message Queue" -> + table(Attr, + [tr( + [th("WIDTH=10%","Id"), + th("WIDTH=90%","Message")]) | + element(1, lists:mapfoldl(fun(Msg, {Even,N}) -> + {msgq_table(Msg, N, Even), + {not Even, N+1}} + end, + {true,1}, Expanded))]); "StackDump" -> - table( - "BORDER=4 CELLPADDING=4", - [tr( - [th("Label"), - th("Term")]) | - lists:map(fun(Entry) -> stackdump_table(Entry) end, Expanded)]); + table(Attr, + [tr( + [th("WIDTH=20%","Label"), + th("WIDTH=80%","Term")]) | + element(1, lists:mapfoldl(fun(Entry, Even) -> + {stackdump_table(Entry, Even), + not Even} + end, true, Expanded))]); + "ProcState" -> + table(Attr, + [tr( + [th("WIDTH=20%","Label"), + th("WIDTH=80%","Information")]) | + element(1, lists:mapfoldl(fun(Entry, Even) -> + {proc_state(Entry,Even), + not Even} + end, true, Expanded))]); _ -> - table( - "BORDER=4 CELLPADDING=4", - [tr( - [th("Key"), - th("Value")]) | - lists:map(fun(Entry) -> dict_table(Entry) end, Expanded)]) + table(Attr, + [tr( + [th("WIDTH=30%","Key"), + th("WIDTH=70%","Value")]) | + element(1, lists:mapfoldl(fun(Entry, Even) -> + {dict_table(Entry,Even), + not Even} + end, true, Expanded))]) end]. -msgq_table({Msg0,Token0}) -> +msgq_table({Msg0,Token0}, Even) -> Token = case Token0 of [] -> ""; _ -> io_lib:fwrite("~w",[Token0]) end, Msg = href_proc_port(lists:flatten(io_lib:format("~p",[Msg0]))), - tr([td(pre(Msg)), td(Token)]). - -stackdump_table({Label0,Term0}) -> + tr(color(Even),[td(pre(Msg)), td(Token)]). + +msgq_table(Msg0, Id, Even) -> + Msg = href_proc_port(lists:flatten(io_lib:format("~p",[Msg0]))), + tr(color(Even),[td(integer_to_list(Id)), td(pre(Msg))]). + +stackdump_table({Label0,Term0},Even) -> Label = io_lib:format("~w",[Label0]), Term = href_proc_port(lists:flatten(io_lib:format("~p",[Term0]))), - tr([td("VALIGN=top",Label), td(pre(Term))]). - -dict_table({Key0,Value0}) -> + tr(color(Even), [td("VALIGN=center",pre(Label)), td(pre(Term))]). + +dict_table({Key0,Value0}, Even) -> Key = href_proc_port(lists:flatten(io_lib:format("~p",[Key0]))), Value = href_proc_port(lists:flatten(io_lib:format("~p",[Value0]))), - tr([td("VALIGN=top",pre(Key)), td(pre(Value))]). + tr(color(Even), [td("VALIGN=center",pre(Key)), td(pre(Value))]). + +proc_state({Key0,Value0}, Even) -> + Key = lists:flatten(io_lib:format("~s",[Key0])), + Value = href_proc_port(lists:flatten(io_lib:format("~p",[Value0]))), + tr(color(Even), [td("VALIGN=center",Key), td(pre(Value))]). + + +color(true) -> io_lib:format("BGCOLOR=\"#~2.16.0B~2.16.0B~2.16.0B\"", tuple_to_list(?BG_EVEN)); +color(false) -> io_lib:format("BGCOLOR=\"#~2.16.0B~2.16.0B~2.16.0B\"", tuple_to_list(?BG_ODD)). %%%----------------------------------------------------------------- diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl index 72bafcc5e0..82395fbe08 100644 --- a/lib/observer/src/observer_app_wx.erl +++ b/lib/observer/src/observer_app_wx.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011-2012. All Rights Reserved. +%% Copyright Ericsson AB 2011-2013. 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 @@ -190,8 +190,8 @@ handle_event(#wx{event=#wxCommand{type=command_menu_selected}}, {noreply, State}; handle_event(#wx{id=?ID_PROC_INFO, event=#wxCommand{type=command_menu_selected}}, - State = #state{panel=Panel, sel={#box{s1=#str{pid=Pid}},_}}) -> - observer_procinfo:start(Pid, Panel, self()), + State = #state{sel={#box{s1=#str{pid=Pid}},_}}) -> + observer ! {open_link, Pid}, {noreply, State}; handle_event(#wx{id=?ID_PROC_MSG, event=#wxCommand{type=command_menu_selected}}, @@ -337,8 +337,8 @@ code_change(_, _, State) -> handle_mouse_click(Node = {#box{s1=#str{pid=Pid}},_}, Type, State=#state{app_w=AppWin,panel=Panel}) -> case Type of - left_dclick -> observer_procinfo:start(Pid, Panel, self()); - right_down -> popup_menu(Panel); + left_dclick -> observer ! {open_link, Pid}; + right_down -> popup_menu(Panel); _ -> ok end, observer_wx:set_status(io_lib:format("Pid: ~p", [Pid])), diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl index ce85fb4af6..159afae487 100644 --- a/lib/observer/src/observer_lib.erl +++ b/lib/observer/src/observer_lib.erl @@ -27,7 +27,7 @@ create_attrs/0, set_listctrl_col_size/2, create_status_bar/1, - html_window/2 + html_window/1, html_window/2 ]). -include_lib("wx/include/wx.hrl"). @@ -185,11 +185,27 @@ update_info([Fields|Fs], [{_Header, _Attrib, SubStructure}| Rest]) -> update_info([], []) -> ok. +update_info2([Scroll = {_, _, _}|Fs], [{_, NewInfo}|Rest]) -> + update_scroll_boxes(Scroll, NewInfo), + update_info2(Fs, Rest); +update_info2([Field|Fs], [{_Str, {click, Value}}|Rest]) -> + wxTextCtrl:setValue(Field, to_str(Value)), + update_info2(Fs, Rest); update_info2([Field|Fs], [{_Str, Value}|Rest]) -> wxTextCtrl:setValue(Field, to_str(Value)), update_info2(Fs, Rest); update_info2([], []) -> ok. +update_scroll_boxes({_, _, 0}, {_, []}) -> ok; +update_scroll_boxes({Win, Sizer, _}, {Type, List}) -> + [wxSizerItem:deleteWindows(Child) || Child <- wxSizer:getChildren(Sizer)], + BC = wxWindow:getBackgroundColour(Win), + Cursor = wxCursor:new(?wxCURSOR_HAND), + add_entries(Type, List, Win, Sizer, BC, Cursor), + wxCursor:destroy(Cursor), + wxSizer:recalcSizes(Sizer), + wxWindow:refresh(Win), + ok. to_str(Value) when is_atom(Value) -> atom_to_list(Value); @@ -333,45 +349,51 @@ get_box_info({Title, List}) when is_list(List) -> {Title, ?wxALIGN_LEFT, List}; get_box_info({Title, left, List}) -> {Title, ?wxALIGN_LEFT, List}; get_box_info({Title, right, List}) -> {Title, ?wxALIGN_RIGHT, List}. +add_box(Panel, OuterBox, Cursor, Title, Proportion, {Format, List}) -> + Box = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, Title}]), + Scroll = wxScrolledWindow:new(Panel), + wxScrolledWindow:enableScrolling(Scroll,true,true), + wxScrolledWindow:setScrollbars(Scroll,1,1,0,0), + ScrollSizer = wxBoxSizer:new(?wxVERTICAL), + wxScrolledWindow:setSizer(Scroll, ScrollSizer), + BC = wxWindow:getBackgroundColour(Panel), + wxWindow:setBackgroundColour(Scroll,BC), + add_entries(Format, List, Scroll, ScrollSizer, BC, Cursor), + wxSizer:add(Box,Scroll,[{proportion,1},{flag,?wxEXPAND}]), + wxSizer:add(OuterBox,Box,[{proportion,Proportion},{flag,?wxEXPAND}]), + {Scroll,ScrollSizer,length(List)}. + +add_entries(click, List, Scroll, ScrollSizer, BC, Cursor) -> + Add = fun(Link) -> + TC = link_entry(Scroll, Link, Cursor), + wxWindow:setBackgroundColour(TC,BC), + wxSizer:add(ScrollSizer,TC,[{flag,?wxEXPAND}]) + end, + [Add(Link) || Link <- List]; +add_entries(plain, List, Scroll, ScrollSizer, _, _) -> + Add = fun(String) -> + TC = wxTextCtrl:new(Scroll, ?wxID_ANY, + [{style,?SINGLE_LINE_STYLE}, + {value,String}]), + wxSizer:add(ScrollSizer,TC,[{flag,?wxEXPAND}]) + end, + [Add(String) || String <- List]. + + create_box(_Panel, {scroll_boxes,[]}) -> undefined; create_box(Panel, {scroll_boxes,Data}) -> OuterBox = wxBoxSizer:new(?wxHORIZONTAL), Cursor = wxCursor:new(?wxCURSOR_HAND), - AddBox = fun({Title,Proportion,{Format,List}}) -> - Box = wxStaticBoxSizer:new(?wxVERTICAL, Panel, - [{label, Title}]), - Scroll = wxScrolledWindow:new(Panel), - wxScrolledWindow:enableScrolling(Scroll,true,true), - wxScrolledWindow:setScrollbars(Scroll,1,1,0,0), - ScrollSizer = wxBoxSizer:new(?wxVERTICAL), - wxScrolledWindow:setSizer(Scroll, ScrollSizer), - BC = wxWindow:getBackgroundColour(Panel), - wxWindow:setBackgroundColour(Scroll,BC), - case Format of - click -> - [begin - TC = link_entry(Scroll, Link, Cursor), - wxWindow:setBackgroundColour(TC,BC), - wxSizer:add(ScrollSizer,TC,[{flag,?wxEXPAND}]) - end || Link <- List]; - plain -> - [begin - TC = wxTextCtrl:new(Scroll, ?wxID_ANY, - [{style,?SINGLE_LINE_STYLE}, - {value,String}]), - wxSizer:add(ScrollSizer,TC,[{flag,?wxEXPAND}]) - end || String <- List] - end, - wxSizer:add(Box,Scroll,[{flag,?wxEXPAND}]), - wxSizer:add(OuterBox,Box, - [{proportion,Proportion},{flag,?wxEXPAND}]), - {Scroll,length(List)} + AddBox = fun({Title,Proportion,Format = {_,_}}) -> + add_box(Panel, OuterBox, Cursor, Title, Proportion, Format); + ({Title, Format = {_,_}}) -> + add_box(Panel, OuterBox, Cursor, Title, 1, Format) end, Boxes = [AddBox(Entry) || Entry <- Data], wxCursor:destroy(Cursor), - MaxL = lists:foldl(fun({_,L},Max) when L>Max -> L; + MaxL = lists:foldl(fun({_,_,L},Max) when L>Max -> L; (_,Max) -> Max end, 0, @@ -384,9 +406,9 @@ create_box(Panel, {scroll_boxes,Data}) -> MaxH = if MaxL > 8 -> 8*H; true -> MaxL*H end, - [wxWindow:setMinSize(B,{0,MaxH}) || {B,_} <- Boxes], + [wxWindow:setMinSize(B,{0,MaxH}) || {B,_,_} <- Boxes], wxSizer:layout(OuterBox), - {OuterBox, []}; + {OuterBox, Boxes}; create_box(Panel, Data) -> {Title, Align, Info} = get_box_info(Data), @@ -410,7 +432,7 @@ create_box(Panel, Data) -> [{style,?MULTI_LINE_STYLE}, {value,"unknown"}]); {click,Value} -> - link_entry(Panel,{Value,Value}); + link_entry(Panel,Value); _ -> Value = to_str(Value0), wxTextCtrl:new(Panel, ?wxID_ANY, @@ -431,28 +453,44 @@ create_box(Panel, Data) -> link_entry(Panel, Link) -> Cursor = wxCursor:new(?wxCURSOR_HAND), - TC = link_entry(Panel, Link, Cursor), + TC = link_entry2(Panel, to_link(Link), Cursor), wxCursor:destroy(Cursor), TC. -link_entry(Panel,{Target,Str},Cursor) -> - TC = wxTextCtrl:new(Panel, ?wxID_ANY, - [{style, ?SINGLE_LINE_STYLE}]), +link_entry(Panel, Link, Cursor) -> + link_entry2(Panel, to_link(Link), Cursor). + +link_entry2(Panel,{Target,Str},Cursor) -> + TC = wxTextCtrl:new(Panel, ?wxID_ANY, [{style, ?SINGLE_LINE_STYLE}]), wxTextCtrl:setForegroundColour(TC,?wxBLUE), wxTextCtrl:appendText(TC, Str), wxWindow:setCursor(TC, Cursor), wxTextCtrl:connect(TC, left_down, [{userData,Target}]), wxTextCtrl:connect(TC, enter_window), wxTextCtrl:connect(TC, leave_window), - ToolTip = wxToolTip:new("Click to see properties for " ++ Target), + ToolTip = wxToolTip:new("Click to see properties for " ++ Str), wxWindow:setToolTip(TC, ToolTip), TC. -html_window(Panel,Html) -> +to_link(Tuple = {_Target, _Str}) -> Tuple; +to_link(Target) -> {Target, to_str(Target)}. + +html_window(Panel) -> Win = wxHtmlWindow:new(Panel, [{style, ?wxHW_SCROLLBAR_AUTO}]), - wxHtmlWindow:setPage(Win,Html), + FixedName = case whereis(observer) of + undefined -> "courier"; + _Pid -> + Fixed = observer_wx:get_attrib({font,fixed}), + wxFont:getFaceName(Fixed) + end, + wxHtmlWindow:setFonts(Win, "", FixedName), wxHtmlWindow:connect(Win,command_html_link_clicked), Win. +html_window(Panel, Html) -> + Win = html_window(Panel), + wxHtmlWindow:setPage(Win, Html), + Win. + get_max_size(Panel,Info) -> Txt = wxTextCtrl:new(Panel, ?wxID_ANY, []), Size = get_max_size(Txt,Info,0,0), diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl index 9aaf648ea2..10e2f12e0f 100644 --- a/lib/observer/src/observer_pro_wx.erl +++ b/lib/observer/src/observer_pro_wx.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011-2012. All Rights Reserved. +%% Copyright Ericsson AB 2011-2013. 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 @@ -191,20 +191,16 @@ dump_to_file(Parent, FileName, Holder) -> start_procinfo(undefined, _Frame, Opened) -> Opened; start_procinfo(Pid, Frame, Opened) -> - %% This code doesn't work until we collect which windows have been - %% closed maybe it should moved to observer_wx.erl - %% and add a global menu which remembers windows. - %% case lists:keyfind(Pid, 1, Opened) of - %% false -> - case observer_procinfo:start(Pid, Frame, self()) of - {error, _} -> Opened; - PI -> [{Pid, PI} | Opened] + case lists:keyfind(Pid, 1, Opened) of + false -> + case observer_procinfo:start(Pid, Frame, self()) of + {error, _} -> Opened; + PI -> [{Pid, PI} | Opened] + end; + {_, PI} -> + wxFrame:raise(PI), + Opened end. - %%; - %% {_, PI} -> - %% wxFrame:raise(PI), - %% Opened - %% end. call(Holder, What) -> Ref = erlang:monitor(process, Holder), @@ -235,9 +231,14 @@ handle_info(refresh_interval, #state{holder=Holder}=State) -> handle_info({procinfo_menu_closed, Pid}, #state{procinfo_menu_pids=Opened}=State) -> - NewPids = lists:delete(Pid, Opened), + NewPids = lists:keydelete(Pid, 1, Opened), {noreply, State#state{procinfo_menu_pids=NewPids}}; +handle_info({procinfo_open, Pid}, + #state{panel=Panel, procinfo_menu_pids=Opened}=State) -> + Opened2 = start_procinfo(Pid, Panel, Opened), + {noreply, State#state{procinfo_menu_pids=Opened2}}; + handle_info({active, Node}, #state{holder=Holder, timer=Timer, parent=Parent}=State) -> create_pro_menu(Parent, Holder), @@ -378,8 +379,7 @@ 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, - sel={_, [Pid|_]}}=State) - when Pid =/= undefined -> + sel={_, [Pid|_]}}=State) -> Opened2 = start_procinfo(Pid, Panel, Opened), {noreply, State#state{procinfo_menu_pids=Opened2}}; diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl index 98d0403139..db5eefaa6c 100644 --- a/lib/observer/src/observer_procinfo.erl +++ b/lib/observer/src/observer_procinfo.erl @@ -106,6 +106,22 @@ handle_event(#wx{id=?REFRESH}, #state{frame=Frame, pid=Pid, pages=Pages}=State) end, {noreply, State}; +handle_event(#wx{event=#wxMouse{type=left_down}, userData=TargetPid}, State) -> + observer ! {open_link, TargetPid}, + {noreply, State}; + +handle_event(#wx{obj=Obj, event=#wxMouse{type=enter_window}}, State) -> + wxTextCtrl:setForegroundColour(Obj,{0,0,100,255}), + {noreply, State}; + +handle_event(#wx{obj=Obj, event=#wxMouse{type=leave_window}}, State) -> + wxTextCtrl:setForegroundColour(Obj,?wxBLUE), + {noreply, State}; + +handle_event(#wx{event=#wxHtmlLink{linkInfo=#wxHtmlLinkInfo{href=Info}}}, State) -> + observer ! {open_link, Info}, + {noreply, State}; + handle_event(Event, _State) -> error({unhandled_event, Event}). @@ -139,58 +155,37 @@ init_process_page(Panel, Pid) -> observer_lib:update_info(UpFields, Fields) 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, + Win = observer_lib:html_window(Parent), Update = fun() -> case observer_wx:try_rpc(node(Pid), erlang, process_info, [Pid, messages]) of - {messages,RawMessages} -> - {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; + {messages, Messages} -> + Html = crashdump_viewer_html:expanded_memory("Message Queue", Messages), + wxHtmlWindow:setPage(Win, Html); _ -> throw(process_undefined) end end, Update(), - {Text, Update}. + {Win, Update}. init_dict_page(Parent, Pid) -> - Text = init_text_page(Parent), + Win = observer_lib:html_window(Parent), Update = fun() -> case observer_wx:try_rpc(node(Pid), erlang, process_info, [Pid, dictionary]) of - {dictionary,RawDict} -> - 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); + {dictionary,Dict} -> + Html = crashdump_viewer_html:expanded_memory("Dictionary", Dict), + wxHtmlWindow:setPage(Win, Html); _ -> throw(process_undefined) end end, Update(), - {Text, Update}. + {Win, Update}. init_stack_page(Parent, Pid) -> LCtrl = wxListCtrl:new(Parent, [{style, ?wxLC_REPORT bor ?wxLC_HRULES}]), @@ -236,58 +231,58 @@ init_stack_page(Parent, Pid) -> Update(), {LCtrl, Update}. - init_state_page(Parent, Pid) -> - Text = init_text_page(Parent), + Win = observer_lib:html_window(Parent), Update = fun() -> - %% First, test if sys:get_status/2 have any chance to return an answer - case rpc:call(node(Pid), proc_lib, translate_initial_call, [Pid]) - of - %% Not a gen process - {proc_lib,init_p,5} -> Misc = [{"Information", "Not available"}]; - %% May be a gen process - {M, _F, _A} -> - %% Get the behavio(u)r - I = rpc:call(node(Pid), M, module_info, [attributes]), - case lists:keyfind(behaviour, 1, I) of - false -> case lists:keyfind(behavior, 1, I) of - false -> B = undefined; - {behavior, [B]} -> B - end; - {behaviour, [B]} -> B - end, - %% but not sure that system messages are treated by this process - %% so using a rpc with a small timeout in order not to lag the display - case rpc:call(node(Pid), sys, get_status, [Pid, 200]) - of - {status, _, {module, _}, [_PDict, _SysState, _Parent, _Dbg, - [Header,{data, First},{data, Second}]]} -> - Misc = [{"Behaviour", B}] ++ [Header] ++ First ++ Second; - {status, _, {module, _}, [_PDict, _SysState, _Parent, _Dbg, - [Header,{data, First}, OtherFormat]]} -> - Misc = [{"Behaviour", B}] ++ [Header] ++ First ++ [{"State",OtherFormat}]; - {status, _, {module, _}, [_PDict, _SysState, _Parent, _Dbg, - OtherFormat]} -> - %% Formatted status ? - case lists:keyfind(format_status, 1, rpc:call(node(Pid), M, module_info, [exports])) of - false -> Opt = {"Format", unknown}; - _ -> Opt = {"Format", overriden} - end, - Misc = [{"Behaviour", B}] ++ [Opt, {"State",OtherFormat}]; - {badrpc,{'EXIT',{timeout, _}}} -> - Misc = [{"Information","Timed out"}, - {"Tip","system messages are probably not treated by this process"}] - end; - _ -> Misc=[], throw(process_undefined) - end, - Dict = [io_lib:format("~-20.s ~tp~n", [K, V]) || {K, V} <- Misc], - Last = wxTextCtrl:getLastPosition(Text), - wxTextCtrl:remove(Text, 0, Last), - wxTextCtrl:writeText(Text, Dict) + StateInfo = fetch_state_info(Pid), + Html = crashdump_viewer_html:expanded_memory("ProcState", StateInfo), + wxHtmlWindow:setPage(Win, Html) end, Update(), - {Text, Update}. + {Win, Update}. + +fetch_state_info(Pid) -> + %% First, test if sys:get_status/2 have any chance to return an answer + case rpc:call(node(Pid), proc_lib, translate_initial_call, [Pid]) of + %% Not a gen process + {proc_lib,init_p,5} -> []; + %% May be a gen process + {M, _F, _A} -> fetch_state_info2(Pid, M); + _ -> throw(process_undefined) + end. +fetch_state_info2(Pid, M) -> + %% Get the behavio(u)r + I = rpc:call(node(Pid), M, module_info, [attributes]), + case lists:keyfind(behaviour, 1, I) of + false -> case lists:keyfind(behavior, 1, I) of + false -> B = undefined; + {behavior, [B]} -> B + end; + {behaviour, [B]} -> B + end, + %% but not sure that system messages are treated by this process + %% so using a rpc with a small timeout in order not to lag the display + case rpc:call(node(Pid), sys, get_status, [Pid, 200]) + of + {status, _, {module, _}, + [_PDict, _SysState, _Parent, _Dbg, + [Header,{data, First},{data, Second}]]} -> + [{"Behaviour", B}, Header] ++ First ++ Second; + {status, _, {module, _}, + [_PDict, _SysState, _Parent, _Dbg, + [Header,{data, First}, OtherFormat]]} -> + [{"Behaviour", B}, Header] ++ First ++ [{"State",OtherFormat}]; + {status, _, {module, _}, + [_PDict, _SysState, _Parent, _Dbg, OtherFormat]} -> + %% Formatted status ? + case lists:keyfind(format_status, 1, rpc:call(node(Pid), M, module_info, [exports])) of + false -> Opt = {"Format", unknown}; + _ -> Opt = {"Format", overriden} + end, + [{"Behaviour", B}, Opt, {"State",OtherFormat}]; + {badrpc,{'EXIT',{timeout, _}}} -> [] + end. create_menus(MenuBar) -> Menus = [{"File", [#create_menu{id=?wxID_CLOSE, text="Close"}]}, @@ -301,6 +296,7 @@ process_info_fields(Pid) -> {"Registered Name", registered_name}, {"Status", status}, {"Message Queue Len",message_queue_len}, + {"Group Leader", {click, group_leader}}, {"Priority", priority}, {"Trap Exit", trap_exit}, {"Reductions", reductions}, @@ -311,11 +307,10 @@ process_info_fields(Pid) -> {"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}]}, + {scroll_boxes, + [{"Links", {click, links}}, + {"Monitors", {click, filter_monitor_info()}}, + {"Monitored by", {click, monitored_by}}]}, {"Memory and Garbage Collection", right, [{"Memory", {bytes, memory}}, {"Stack and Heaps", {bytes, total_heap_size}}, @@ -365,3 +360,9 @@ get_gc_info(Arg) -> GC = proplists:get_value(garbage_collection, Data), proplists:get_value(Arg, GC) end. + +filter_monitor_info() -> + fun(Data) -> + Ms = proplists:get_value(monitors, Data), + [Pid || {process, Pid} <- Ms] + end. diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl index 3afe933e5a..9839f8bf7b 100644 --- a/lib/observer/src/observer_wx.erl +++ b/lib/observer/src/observer_wx.erl @@ -346,6 +346,22 @@ handle_info({nodedown, Node}, create_txt_dialog(Frame, Msg, "Node down", ?wxICON_EXCLAMATION), {noreply, State3}; +handle_info({open_link, Pid0}, State = #state{pro_panel=ProcViewer, frame=Frame}) -> + Pid = case Pid0 of + [_|_] -> try list_to_pid(Pid0) catch _:_ -> Pid0 end; + _ -> Pid0 + end, + %% Forward to process tab + case is_pid(Pid) of + true -> wx_object:get_pid(ProcViewer) ! {procinfo_open, Pid}; + false -> + Msg = io_lib:format("Information about ~p is not available or implemented",[Pid]), + Info = wxMessageDialog:new(Frame, Msg), + wxMessageDialog:showModal(Info), + wxMessageDialog:destroy(Info) + end, + {noreply, State}; + handle_info({'EXIT', Pid, _Reason}, State) -> io:format("Child (~s) crashed exiting: ~p ~p~n", [pid2panel(Pid, State), Pid,_Reason]), -- cgit v1.2.3