aboutsummaryrefslogtreecommitdiffstats
path: root/lib/observer/src/observer_procinfo.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/observer/src/observer_procinfo.erl')
-rw-r--r--lib/observer/src/observer_procinfo.erl242
1 files changed, 143 insertions, 99 deletions
diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl
index 98d0403139..8e8a37fc93 100644
--- a/lib/observer/src/observer_procinfo.erl
+++ b/lib/observer/src/observer_procinfo.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2013. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2014. 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
@@ -34,8 +34,11 @@
-record(state, {parent,
frame,
+ notebook,
pid,
- pages=[]
+ pages=[],
+ expand_table,
+ expand_wins=[]
}).
-record(worker, {panel, callback}).
@@ -47,6 +50,7 @@ start(Process, ParentFrame, Parent) ->
init([Pid, ParentFrame, Parent]) ->
try
+ Table = ets:new(observer_expand,[set,public]),
Title=case observer_wx:try_rpc(node(Pid), erlang, process_info, [Pid, registered_name]) of
[] -> io_lib:format("~p",[Pid]);
{registered_name, Registered} -> io_lib:format("~p (~p)",[Registered, Pid]);
@@ -60,11 +64,11 @@ init([Pid, ParentFrame, Parent]) ->
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),
- StatePage = init_panel(Notebook, "State", Pid, fun init_state_page/2),
+ ProcessPage = init_panel(Notebook, "Process Information", [Pid], fun init_process_page/2),
+ MessagePage = init_panel(Notebook, "Messages", [Pid,Table], fun init_message_page/3),
+ DictPage = init_panel(Notebook, "Dictionary", [Pid,Table], fun init_dict_page/3),
+ StackPage = init_panel(Notebook, "Stack Trace", [Pid], fun init_stack_page/2),
+ StatePage = init_panel(Notebook, "State", [Pid,Table], fun init_state_page/3),
wxFrame:connect(Frame, close_window),
wxMenu:connect(Frame, command_menu_selected),
@@ -73,7 +77,9 @@ init([Pid, ParentFrame, Parent]) ->
{Frame, #state{parent=Parent,
pid=Pid,
frame=Frame,
- pages=[ProcessPage,MessagePage,DictPage,StackPage,StatePage]
+ notebook=Notebook,
+ pages=[ProcessPage,MessagePage,DictPage,StackPage,StatePage],
+ expand_table=Table
}}
catch error:{badrpc, _} ->
observer_wx:return_to_localnode(ParentFrame, node(Pid)),
@@ -83,10 +89,10 @@ init([Pid, ParentFrame, Parent]) ->
{stop, normal}
end.
-init_panel(Notebook, Str, Pid, Fun) ->
+init_panel(Notebook, Str, FunArgs, Fun) ->
Panel = wxPanel:new(Notebook),
Sizer = wxBoxSizer:new(?wxHORIZONTAL),
- {Window,Callback} = Fun(Panel, Pid),
+ {Window,Callback} = apply(Fun,[Panel|FunArgs]),
wxSizer:add(Sizer, Window, [{flag, ?wxEXPAND bor ?wxALL}, {proportion, 1}, {border, 5}]),
wxPanel:setSizer(Panel, Sizer),
true = wxNotebook:addPage(Notebook, Panel, Str),
@@ -99,16 +105,64 @@ handle_event(#wx{event=#wxClose{type=close_window}}, State) ->
handle_event(#wx{id=?wxID_CLOSE, event=#wxCommand{type=command_menu_selected}}, State) ->
{stop, normal, State};
-handle_event(#wx{id=?REFRESH}, #state{frame=Frame, pid=Pid, pages=Pages}=State) ->
+handle_event(#wx{id=?REFRESH}, #state{frame=Frame, pid=Pid, pages=Pages, expand_table=T}=State) ->
+ ets:delete_all_objects(T),
try [(W#worker.callback)() || W <- Pages]
catch process_undefined ->
wxFrame:setTitle(Frame, io_lib:format("*DEAD* ~p",[Pid]))
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=Href}}},
+ #state{frame=Frame,expand_table=T,expand_wins=Opened0}=State) ->
+ {Type, Rest} = case Href of
+ "#Term?"++Keys -> {cdv_term_cb, Keys};
+ "#OBSBinary?"++Keys -> {cdv_bin_cb, Keys};
+ _ -> {other, Href}
+ end,
+ case Type of
+ other ->
+ observer ! {open_link, Href},
+ {noreply, State};
+ Callback ->
+ [{"key1",Key1},{"key2",Key2},{"key3",Key3}] = httpd:parse_query(Rest),
+ Id = {obs, {T,{list_to_integer(Key1),
+ list_to_integer(Key2),
+ list_to_integer(Key3)}}},
+ Opened =
+ case lists:keyfind(Id,1,Opened0) of
+ false ->
+ Win = cdv_detail_wx:start_link(Id,Frame,Callback),
+ [{Id,Win}|Opened0];
+ {_,Win} ->
+ wxFrame:raise(Win),
+ Opened0
+ end,
+ {noreply,State#state{expand_wins=Opened}}
+ end;
+
+handle_event(#wx{event=#wxHtmlLink{linkInfo=#wxHtmlLinkInfo{href=Info}}}, State) ->
+ observer ! {open_link, Info},
+ {noreply, State};
+
handle_event(Event, _State) ->
error({unhandled_event, Event}).
+handle_info({get_debug_info, From}, State = #state{notebook=Notebook}) ->
+ From ! {procinfo_debug, Notebook},
+ {noreply, State};
handle_info(_Info, State) ->
%% io:format("~p: ~p, Handle info: ~p~n", [?MODULE, ?LINE, Info]),
{noreply, State}.
@@ -116,10 +170,15 @@ handle_info(_Info, State) ->
handle_call(Call, From, _State) ->
error({unhandled_call, Call, From}).
+handle_cast({detail_win_closed,Id}, #state{expand_wins=Opened0}=State) ->
+ Opened = lists:keydelete(Id,1,Opened0),
+ {noreply,State#state{expand_wins=Opened}};
+
handle_cast(Cast, _State) ->
error({unhandled_cast, Cast}).
-terminate(_Reason, #state{parent=Parent,pid=Pid,frame=Frame}) ->
+terminate(_Reason, #state{parent=Parent,pid=Pid,frame=Frame,expand_table=T}) ->
+ T=/=undefined andalso ets:delete(T),
Parent ! {procinfo_menu_closed, Pid},
case Frame of
undefined -> ok;
@@ -139,58 +198,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,
+
+init_message_page(Parent, Pid, Table) ->
+ 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 = observer_html_lib:expandable_term("Message Queue", Messages, Table),
+ wxHtmlWindow:setPage(Win, Html);
_ ->
throw(process_undefined)
end
end,
Update(),
- {Text, Update}.
+ {Win, Update}.
-init_dict_page(Parent, Pid) ->
- Text = init_text_page(Parent),
+init_dict_page(Parent, Pid, Table) ->
+ 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 = observer_html_lib:expandable_term("Dictionary", Dict, Table),
+ 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 +274,58 @@ init_stack_page(Parent, Pid) ->
Update(),
{LCtrl, Update}.
-
-init_state_page(Parent, Pid) ->
- Text = init_text_page(Parent),
+init_state_page(Parent, Pid, Table) ->
+ 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 = observer_html_lib:expandable_term("ProcState", StateInfo, Table),
+ 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 +339,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 +350,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 +403,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.