aboutsummaryrefslogtreecommitdiffstats
path: root/lib/observer/src
diff options
context:
space:
mode:
authorDan Gudmundsson <[email protected]>2013-10-11 16:56:24 +0200
committerDan Gudmundsson <[email protected]>2014-01-27 15:52:52 +0100
commit549205db3dee21e83a64a01f03b1e8ed2225b276 (patch)
tree4cd5511e19ef0eb5277350800a4e48d18cd88786 /lib/observer/src
parente2d565532d25024c1c0552d8eaaddf90eed88629 (diff)
downloadotp-549205db3dee21e83a64a01f03b1e8ed2225b276.tar.gz
otp-549205db3dee21e83a64a01f03b1e8ed2225b276.tar.bz2
otp-549205db3dee21e83a64a01f03b1e8ed2225b276.zip
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.
Diffstat (limited to 'lib/observer/src')
-rw-r--r--lib/observer/src/cdv_proc_wx.erl4
-rw-r--r--lib/observer/src/crashdump_viewer_html.erl100
-rw-r--r--lib/observer/src/observer_app_wx.erl10
-rw-r--r--lib/observer/src/observer_lib.erl120
-rw-r--r--lib/observer/src/observer_pro_wx.erl34
-rw-r--r--lib/observer/src/observer_procinfo.erl167
-rw-r--r--lib/observer/src/observer_wx.erl16
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]),