aboutsummaryrefslogtreecommitdiffstats
path: root/lib/observer/src/observer_pro_wx.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/observer/src/observer_pro_wx.erl')
-rw-r--r--lib/observer/src/observer_pro_wx.erl661
1 files changed, 199 insertions, 462 deletions
diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl
index 7080da8231..c99abd371a 100644
--- a/lib/observer/src/observer_pro_wx.erl
+++ b/lib/observer/src/observer_pro_wx.erl
@@ -23,19 +23,19 @@
%% wx_object callbacks
-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3,
- handle_event/2, handle_cast/2, to_str/1]).
--export([get_row/4, get_attr/3]).
+ handle_event/2, handle_cast/2]).
-include_lib("wx/include/wx.hrl").
--include_lib("runtime_tools/include/observer_backend.hrl").
+-include("../include/etop.hrl").
-include("observer_defs.hrl").
+-include("etop_defs.hrl").
%% Defines
-define(COL_PID, 0).
-define(COL_NAME, 1).
-define(COL_TIME, 2).
-define(COL_REDS, 3).
--define(COL_MEM, 4).
+-define(COL_MEM, 4).
-define(COL_MSG, 5).
-define(COL_FUN, 6).
@@ -47,66 +47,57 @@
-define(ID_TRACEMENU, 206).
-define(ID_TRACE_ALL_MENU, 207).
-define(ID_TRACE_NEW_MENU, 208).
--define(ID_NO_OF_LINES, 209).
--define(ID_ACCUMULATE, 210).
-
--define(START_LINES, 50). %% hardcoded startvalue representing the number of visible lines
+-define(ID_ACCUMULATE, 209).
%% Records
-record(attrs, {even, odd, deleted, changed, searched}).
+-record(sort,
+ {
+ sort_key=?COL_REDS,
+ sort_incr=false
+ }).
+
-record(holder, {parent,
info,
- attrs}).
-
+ sort = #sort{},
+ accum = [],
+ attrs,
+ node,
+ backend_pid
+ }).
-record(pro_wx_state, {parent,
- etop_monitor,
- holder_monitor,
grid,
panel,
popup_menu,
parent_notebook,
trace_options = #trace_options{},
match_specs = [],
- refr_timer = false,
+ timer,
tracemenu_opened,
procinfo_menu_pids = [],
selected_pids = [],
last_selected,
- sort_dir = decr, % decr::atom | incr::incr
holder}).
start_link(Notebook, Parent) ->
wx_object:start_link(?MODULE, [Notebook, Parent], []).
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init([Notebook, Parent]) ->
- {EtopMonitor, Config} = etop:start(observer),
- SortDir = decr,
Attrs = create_attrs(),
Self = self(),
- change_lines(?START_LINES),
- Info = etop:update(Config, SortDir),
- {Holder, HolderMon} = spawn_monitor(fun() ->
- init_table_holder(Self,
- Info,
- Attrs)
- end),
- Count = length(Info#etop_info.procinfo),
- {ProPanel, State} = setup(Notebook, Parent, Holder, Count),
- refresh_grid(Holder, SortDir),
+ Holder = spawn_link(fun() -> init_table_holder(Self, Attrs) end),
+ {ProPanel, State} = setup(Notebook, Parent, Holder),
MatchSpecs = generate_matchspecs(),
- {ProPanel, State#pro_wx_state{etop_monitor = EtopMonitor,
- holder_monitor = HolderMon,
- sort_dir = SortDir,
- match_specs = MatchSpecs}}.
-setup(Notebook, Parent, Holder, Count) ->
+ {ProPanel, State#pro_wx_state{holder = Holder, match_specs = MatchSpecs}}.
+
+setup(Notebook, Parent, Holder) ->
ProPanel = wxPanel:new(Notebook, []),
- Grid = create_list_box(ProPanel, Holder, Count),
+ Grid = create_list_box(ProPanel, Holder),
Sizer = wxBoxSizer:new(?wxVERTICAL),
wxSizer:add(Sizer, Grid, [{flag, ?wxEXPAND bor ?wxALL},
{proportion, 1},
@@ -122,7 +113,9 @@ setup(Notebook, Parent, Holder, Count) ->
popup_menu = Popup,
parent_notebook = Notebook,
tracemenu_opened = false,
- holder = Holder},
+ holder = Holder,
+ timer = {false, 10}
+ },
{ProPanel, State}.
generate_matchspecs() ->
@@ -152,14 +145,16 @@ generate_matchspecs() ->
%% UI-creation
-create_pro_menu(Parent) ->
- MenuEntries = [{"View",
- [#create_menu{id = ?ID_REFRESH, text = "Refresh"},
+create_pro_menu(Parent, Holder) ->
+ MenuEntries = [{"File",
+ [#create_menu{id = ?ID_DUMP_TO_FILE, text = "Dump to file"}]},
+ {"View",
+ [#create_menu{id = ?ID_ACCUMULATE, text = "Accumulate",
+ type = check,
+ check = call(Holder, {get_accum, self()})},
+ separator,
+ #create_menu{id = ?ID_REFRESH, text = "Refresh\tCtrl-R"},
#create_menu{id = ?ID_REFRESH_INTERVAL, text = "Refresh Interval"}]},
- {"Options",
- [#create_menu{id = ?ID_DUMP_TO_FILE, text = "Dump to file"},
- #create_menu{id = ?ID_ACCUMULATE, text = "Accumulate", type = check},
- #create_menu{id = ?ID_NO_OF_LINES, text = "Number of lines"}]},
{"Trace",
[#create_menu{id = ?ID_TRACEMENU, text = "Trace selected processes"},
#create_menu{id = ?ID_TRACE_NEW_MENU, text = "Trace new processes"},
@@ -188,13 +183,17 @@ create_popup_menu(ParentFrame) ->
wxSizer:setSizeHints(Sizer, MiniFrame),
MiniFrame.
-create_list_box(Panel, Holder, Count) ->
+create_list_box(Panel, Holder) ->
Style = ?wxLC_REPORT bor ?wxLC_VIRTUAL,
ListCtrl = wxListCtrl:new(Panel, [{style, Style},
{onGetItemText,
- fun(_, Item, Col) -> get_row(Holder, Item, Col) end},
+ fun(_, Row, Col) ->
+ call(Holder, {get_row, self(), Row, Col})
+ end},
{onGetItemAttr,
- fun(_, Item) -> get_attr(Holder, Item) end}
+ fun(_, Item) ->
+ call(Holder, {get_attr, self(), Item})
+ end}
]),
Li = wxListItem:new(),
AddListEntry = fun({Name, Align, DefSize}, Col) ->
@@ -207,9 +206,9 @@ create_list_box(Panel, Holder, Count) ->
ListItems = [{"Pid", ?wxLIST_FORMAT_CENTRE, 120},
{"Name or Initial Func", ?wxLIST_FORMAT_LEFT, 200},
{"Time", ?wxLIST_FORMAT_CENTRE, 50},
- {"Reds", ?wxLIST_FORMAT_CENTRE, 50},
- {"Memory", ?wxLIST_FORMAT_CENTRE, 50},
- {"MsgQ", ?wxLIST_FORMAT_LEFT, 50},
+ {"Reds", ?wxLIST_FORMAT_RIGHT, 100},
+ {"Memory", ?wxLIST_FORMAT_RIGHT, 100},
+ {"MsgQ", ?wxLIST_FORMAT_RIGHT, 50},
{"Current Function", ?wxLIST_FORMAT_LEFT, 200}],
lists:foldl(AddListEntry, 0, ListItems),
wxListItem:destroy(Li),
@@ -219,111 +218,8 @@ create_list_box(Panel, Holder, Count) ->
wxListCtrl:connect(ListCtrl, command_list_item_right_click),
wxListCtrl:connect(ListCtrl, command_list_col_click),
wxListCtrl:connect(ListCtrl, command_list_item_selected),
- wxListCtrl:setItemCount(ListCtrl, Count),
ListCtrl.
-change_node(Node) ->
- etop_server ! {config, {node, Node}}.
-
-get_node() ->
- etop_server ! {get_opt, node, self()},
- receive
- {node, Node} ->
- Node
- end.
-
-change_accum(Bool) ->
- etop_server ! {config, {accumulate, Bool}}.
-
-change_lines(Int) when is_integer(Int) ->
- etop_server ! {config, {lines, Int}}.
-
-get_lines() ->
- etop_server ! {get_opt, lines, self()},
- receive
- {lines, Lines} ->
- Lines
- end.
-
-change_intv(NewIntv) ->
- etop_server ! {config, {interval, NewIntv}}.
-
-get_intv() ->
- etop_server ! {get_opt, intv, self()},
- receive
- {intv, Intv} ->
- Intv
- end.
-
-get_sort() ->
- etop_server ! {get_opt, sort, self()},
- receive {sort, Sort} ->
- Sort
- end.
-
-refresh_grid(Holder, Dir) ->
- etop_server ! {update, Holder, Dir}.
-
-change_sort(Col, Dir) ->
- case get_sort() =:= map_sort_order(Col) of
- true when Dir =:= incr->
- decr;
- true when Dir =:= decr ->
- incr;
- false ->
- change_sort(Col),
- Dir
- end.
-change_sort(?COL_PID) ->
- etop_server ! {config, {sort, pid}};
-change_sort(?COL_NAME) ->
- etop_server ! {config, {sort, name}};
-change_sort(?COL_TIME) ->
- etop_server ! {config, {sort, runtime}};
-change_sort(?COL_REDS) ->
- etop_server ! {config, {sort, reductions}};
-change_sort(?COL_MEM) ->
- etop_server ! {config, {sort, memory}};
-change_sort(?COL_MSG) ->
- etop_server ! {config, {sort, msg_q}};
-change_sort(?COL_FUN) ->
- etop_server ! {config, {sort, cf}}.
-
-
-map_sort_order(Col) ->
- case Col of
- ?COL_PID -> pid;
- ?COL_NAME -> name;
- ?COL_TIME -> runtime;
- ?COL_REDS -> reductions;
- ?COL_MEM -> memory;
- ?COL_MSG -> msg_q;
- ?COL_FUN -> cf
- end.
-
-to_str(Value) when is_atom(Value) ->
- atom_to_list(Value);
-to_str({A, B}) ->
- lists:concat([A, ":", B]);
-to_str({M,F,A}) ->
- lists:concat([M, ":", F, "/", A]);
-to_str(Value) when is_list(Value) ->
- case lists:all(fun(X) -> is_integer(X) end, Value) of
- true -> Value;
- false ->
- lists:foldl(fun(X, Acc) ->
- to_str(X) ++ " " ++ Acc end,
- "", Value)
- end;
-to_str(Port) when is_port(Port) ->
- erlang:port_to_list(Port);
-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}).
-
clear_all(Grid) ->
lists:foreach(fun(I) ->
wxListCtrl:setItemState(Grid, I, 0, ?wxLIST_STATE_SELECTED),
@@ -338,7 +234,7 @@ set_selected_items(Grid, Holder, Pids) ->
set_selected_items(_, _, Index, Pids, Max, Acc) when Pids =:= []; Index =:= Max ->
Acc;
set_selected_items(Grid, Holder, Index, Pids, Max, Acc) ->
- {ok, Pid} = get_row(Holder, Index, pid),
+ {ok, Pid} = call(Holder, {get_row, self(), Index, pid}),
case lists:member(Pid, Pids) of
true ->
wxListCtrl:setItemState(Grid, Index,
@@ -353,8 +249,8 @@ get_selected_items(Grid) ->
get_selected_items(Grid, -1, []).
get_selected_items(Grid, Index, ItemAcc) ->
- Item = wxListCtrl:getNextItem(Grid, Index,
- [{geometry, ?wxLIST_NEXT_ALL}, {state, ?wxLIST_STATE_SELECTED}]),
+ Item = wxListCtrl:getNextItem(Grid, Index, [{geometry, ?wxLIST_NEXT_ALL},
+ {state, ?wxLIST_STATE_SELECTED}]),
case Item of
-1 ->
lists:reverse(ItemAcc);
@@ -362,117 +258,6 @@ get_selected_items(Grid, Index, ItemAcc) ->
get_selected_items(Grid, Item, [Item+1 | ItemAcc])
end.
-interval_dialog(ParentFrame, ParentPid, Enabled, Value, Min, Max) ->
- Dialog = wxDialog:new(ParentFrame, ?wxID_ANY, "Update Interval",
- [{style, ?wxDEFAULT_DIALOG_STYLE bor
- ?wxRESIZE_BORDER}]),
- Panel = wxPanel:new(Dialog),
- Check = wxCheckBox:new(Panel, ?wxID_ANY, "Periodical refresh"),
- wxCheckBox:setValue(Check, Enabled),
- Style = ?wxSL_HORIZONTAL bor ?wxSL_AUTOTICKS bor ?wxSL_LABELS,
- Slider = wxSlider:new(Panel, ?wxID_ANY, Value, Min, Max,
- [{style, Style}, {size, {200, -1}}]),
- wxWindow:enable(Slider, [{enable, Enabled}]),
- InnerSizer = wxBoxSizer:new(?wxVERTICAL),
-
- OKBtn = wxButton:new(Dialog, ?wxID_OK),
- CancelBtn = wxButton:new(Dialog, ?wxID_CANCEL),
- Buttons = wxStdDialogButtonSizer:new(),
- wxStdDialogButtonSizer:addButton(Buttons, OKBtn),
- wxStdDialogButtonSizer:addButton(Buttons, CancelBtn),
-
- Flags = [{flag, ?wxEXPAND bor ?wxALL}, {border, 2}],
- wxSizer:add(InnerSizer, Check, Flags),
- wxSizer:add(InnerSizer, Slider, Flags),
- wxPanel:setSizer(Panel, InnerSizer),
- TopSizer = wxBoxSizer:new(?wxVERTICAL),
- wxSizer:add(TopSizer, Panel, [{flag, ?wxEXPAND bor ?wxALL}, {border, 5}]),
- wxSizer:add(TopSizer, Buttons, [{flag, ?wxEXPAND}]),
- wxStdDialogButtonSizer:realize(Buttons),
- wxWindow:setSizerAndFit(Dialog, TopSizer),
- wxSizer:setSizeHints(TopSizer, Dialog),
- wxCheckBox:connect(Check, command_checkbox_clicked,
- [{callback, fun(#wx{event=#wxCommand{type = command_checkbox_clicked,
- commandInt=Enable0}},_) ->
- Enable = Enable0 > 0,
- wxWindow:enable(Slider, [{enable, Enable}])
- end}]),
-
- wxButton:connect(OKBtn, command_button_clicked,
- [{callback,
- fun(#wx{id = ?wxID_OK,
- event = #wxCommand{type = command_button_clicked}},_) ->
- ParentPid ! {wxCheckBox:isChecked(Check), wxSlider:getValue(Slider)},
- wxDialog:destroy(Dialog)
- end}]),
- wxButton:connect(CancelBtn, command_button_clicked,
- [{callback,
- fun(#wx{id = ?wxID_CANCEL,
- event = #wxCommand{type = command_button_clicked}},_) ->
- ParentPid ! cancel,
- wxDialog:destroy(Dialog)
- end}]),
-
- wxDialog:show(Dialog).
-
-
-
-line_dialog(ParentFrame, OldLines, Holder, Dir) ->
- Dialog = wxDialog:new(ParentFrame, ?wxID_ANY, "Enter number of lines",
- [{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER}]),
- Panel = wxPanel:new(Dialog),
- TxtCtrl = wxTextCtrl:new(Panel, ?wxID_ANY, [{value, OldLines}]),
- InnerSz = wxBoxSizer:new(?wxVERTICAL),
- wxSizer:add(InnerSz, TxtCtrl, [{flag, ?wxEXPAND bor ?wxALL}]),
- wxPanel:setSizer(Panel, InnerSz),
-
- OKBtn = wxButton:new(Dialog, ?wxID_OK),
- CancelBtn = wxButton:new(Dialog, ?wxID_CANCEL),
-
- Buttons = wxStdDialogButtonSizer:new(),
- wxStdDialogButtonSizer:addButton(Buttons, OKBtn),
- wxStdDialogButtonSizer:addButton(Buttons, CancelBtn),
-
- TopSz = wxBoxSizer:new(?wxVERTICAL),
- wxSizer:add(TopSz, Panel, [{flag, ?wxEXPAND bor ?wxALL}, {border, 5}]),
- wxSizer:add(TopSz, Buttons, [{flag, ?wxEXPAND}]),
- wxStdDialogButtonSizer:realize(Buttons),
- wxWindow:setSizerAndFit(Dialog, TopSz),
- wxSizer:setSizeHints(TopSz, Dialog),
-
- wxButton:connect(OKBtn, command_button_clicked,
- [{callback,
- fun(#wx{id = ?wxID_OK,
- event = #wxCommand{type = command_button_clicked}},_) ->
- try
- NewLines = list_to_integer(wxTextCtrl:getValue(TxtCtrl)),
- case NewLines >= 0 of
- true ->
- change_lines(NewLines),
- refresh_grid(Holder, Dir);
- false ->
- observer_wx:create_txt_dialog(Panel,
- "Invalid input",
- "Error",
- ?wxICON_ERROR)
- end
- catch error:badarg ->
- observer_wx:create_txt_dialog(Panel,
- "Invalid input",
- "Error",
- ?wxICON_ERROR)
- end,
- wxDialog:destroy(Dialog)
- end}]),
- wxButton:connect(CancelBtn, command_button_clicked,
- [{callback,
- fun(#wx{id = ?wxID_CANCEL,
- event = #wxCommand{type = command_button_clicked}},_) ->
- wxDialog:destroy(Dialog)
- end}]),
- wxDialog:show(Dialog).
-
-
create_attrs() ->
Font = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT),
Text = wxSystemSettings:getColour(?wxSYS_COLOUR_LISTBOXTEXT),
@@ -484,6 +269,7 @@ create_attrs() ->
dump_to_file(Parent, FileName, Holder) ->
case file:open(FileName, [write]) of
{ok, Fd} ->
+ %% Holder closes the file when it's done
Holder ! {dump, Fd};
{error, Reason} ->
FailMsg = file:format_error(Reason),
@@ -501,67 +287,34 @@ start_procinfo(Node, Pid, Frame, Opened) ->
[Pid | Opened]
end.
-
-
-get_selected_pids(Holder, Indices) ->
- Ref = erlang:monitor(process, Holder),
- Holder ! {get_pids, self(), Indices},
- receive
- {'DOWN', Ref, _, _, _} -> [];
- {Holder, Res} ->
- erlang:demonitor(Ref),
- Res
- end.
-
-get_row(Holder, Row, Column) ->
+call(Holder, What) ->
Ref = erlang:monitor(process, Holder),
- Holder ! {get_row, self(), Row, Column},
+ Holder ! What,
receive
{'DOWN', Ref, _, _, _} -> "";
{Holder, Res} ->
erlang:demonitor(Ref),
Res
+ after 2000 ->
+ io:format("Hanging call ~p~n",[What])
end.
-
-get_attr(Holder, Item) ->
- Ref = erlang:monitor(process, Holder),
- Holder ! {get_attr, self(), Item},
- receive
- {'DOWN', Ref, _, _, _} -> "";
- {Holder, Res} ->
- erlang:demonitor(Ref),
- Res
- end.
-
-
%%%%%%%%%%%%%%%%%%%%%%% Callbacks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-handle_info({'DOWN', Ref, _, _, _},
- #pro_wx_state{etop_monitor = EtopMon} = State) when Ref =:= EtopMon ->
- io:format("Etop died~n"),
- {stop, shutdown, State};
-
-handle_info({'DOWN', Ref, _, _, _},
- #pro_wx_state{holder_monitor = HMonitor} = State) when Ref =:= HMonitor ->
- io:format("Holder died~n"),
- {stop, shutdown, State};
-
handle_info({holder_updated, Count}, #pro_wx_state{grid = Grid,
holder = Holder,
selected_pids = Pids} = State) ->
Pids2 = wx:batch(fun() ->
- wxListCtrl:setItemCount(Grid, Count),
clear_all(Grid),
Pids2 = set_selected_items(Grid, Holder, Pids),
+ wxListCtrl:setItemCount(Grid, Count),
wxListCtrl:refreshItems(Grid, 0, Count),
Pids2
end),
{noreply, State#pro_wx_state{selected_pids = Pids2}};
-handle_info(refresh_interval, #pro_wx_state{sort_dir = Dir,
- holder = Holder} = State) ->
- refresh_grid(Holder, Dir),
+handle_info(refresh_interval, #pro_wx_state{holder = Holder} = State) ->
+ Holder ! refresh,
{noreply, State};
handle_info({tracemenu_closed, TraceOpts, MatchSpecs}, State) ->
@@ -574,38 +327,18 @@ handle_info({procinfo_menu_closed, Pid},
NewPids = lists:delete(Pid, Opened),
{noreply, State#pro_wx_state{procinfo_menu_pids = NewPids}};
-handle_info({active, Node}, #pro_wx_state{holder = Holder,
- sort_dir = Dir,
- refr_timer = Timer0,
- parent = Parent} = State) ->
- create_pro_menu(Parent),
- change_node(Node),
- refresh_grid(Holder, Dir),
- Timer = case Timer0 of
- true ->
- Intv = get_intv(),
- {ok, Ref} = timer:send_interval(Intv, refresh_interval),
- Ref;
- false ->
- false
- end,
- {noreply, State#pro_wx_state{refr_timer = Timer}};
-
-handle_info(not_active, #pro_wx_state{refr_timer = Timer0} = State) ->
- Timer = case Timer0 of
- false -> false;
- true -> true;
- Timer0 ->
- timer:cancel(Timer0),
- true
- end,
- {noreply, State#pro_wx_state{refr_timer=Timer,
- selected_pids = [],
- last_selected = undefined}};
+handle_info({active, Node},
+ #pro_wx_state{holder = Holder, timer = Timer, parent = Parent} = State) ->
+ create_pro_menu(Parent, Holder),
+ Holder ! {change_node, Node},
+ {noreply, State#pro_wx_state{timer = observer_lib:start_timer(Timer)}};
-handle_info({node, Node}, #pro_wx_state{holder = Holder, sort_dir = Dir} = State) ->
- change_node(Node),
- refresh_grid(Holder, Dir),
+handle_info(not_active, #pro_wx_state{timer = Timer0} = State) ->
+ Timer = observer_lib:stop_timer(Timer0),
+ {noreply, State#pro_wx_state{timer=Timer, selected_pids = [], last_selected = undefined}};
+
+handle_info({node, Node}, #pro_wx_state{holder = Holder} = State) ->
+ Holder ! {change_node, Node},
{noreply, State#pro_wx_state{selected_pids = [],
last_selected = undefined}};
@@ -649,57 +382,21 @@ handle_event(#wx{id = ?ID_DUMP_TO_FILE},
end,
{noreply, State};
-handle_event(#wx{id = ?ID_ACCUMULATE, event = #wxCommand{type = command_menu_selected,
- commandInt = CmdInt}},
- #pro_wx_state{holder = Holder,
- sort_dir = Dir} = State) when CmdInt =:= 1->
- change_accum(true),
- refresh_grid(Holder, Dir),
- {noreply, State};
-
-handle_event(#wx{id = ?ID_ACCUMULATE, event = #wxCommand{type = command_menu_selected,
- commandInt = CmdInt}},
- #pro_wx_state{holder = Holder,
- sort_dir = Dir} = State) when CmdInt =:= 0 ->
- change_accum(false),
- refresh_grid(Holder, Dir),
- {noreply, State};
-
-handle_event(#wx{id = ?ID_NO_OF_LINES, event = #wxCommand{type = command_menu_selected}},
- #pro_wx_state{panel = Panel,
- sort_dir = Dir,
- holder = Holder} = State) ->
- OldLines = integer_to_list(get_lines()),
- line_dialog(Panel, OldLines, Holder, Dir),
+handle_event(#wx{id = ?ID_ACCUMULATE,
+ event = #wxCommand{type = command_menu_selected, commandInt = CmdInt}},
+ #pro_wx_state{holder = Holder} = State) ->
+ Holder ! {accum, CmdInt =:= 1},
{noreply, State};
handle_event(#wx{id = ?ID_REFRESH, event = #wxCommand{type = command_menu_selected}},
- #pro_wx_state{sort_dir = Dir, holder = Holder} = State) ->
- refresh_grid(Holder, Dir),
+ #pro_wx_state{holder = Holder} = State) ->
+ Holder ! refresh,
{noreply, State};
handle_event(#wx{id = ?ID_REFRESH_INTERVAL},
- #pro_wx_state{panel = Panel, refr_timer=Timer0} = State) ->
- Intv0 = get_intv() div 1000,
- interval_dialog(Panel, self(), Timer0 /= false, Intv0, 1, 5*60),
- receive
- cancel ->
- {noreply, State};
- {true, Intv} ->
- case Timer0 of
- false -> ok;
- _ -> timer:cancel(Timer0)
- end,
- change_intv(Intv),
- {ok, Timer} = timer:send_interval(Intv * 1000, refresh_interval),
- {noreply, State#pro_wx_state{refr_timer=Timer}};
- {false, _} ->
- case Timer0 of
- false -> ok;
- _ -> timer:cancel(Timer0)
- end,
- {noreply, State#pro_wx_state{refr_timer=false}}
- end;
+ #pro_wx_state{panel = Panel, timer=Timer0} = State) ->
+ Timer = observer_lib:interval_dialog(Panel, Timer0, 1, 5*60),
+ {noreply, State#pro_wx_state{timer=Timer}};
handle_event(#wx{id = ?ID_KILL}, #pro_wx_state{popup_menu = Pop,
selected_pids = Pids,
@@ -712,17 +409,19 @@ handle_event(#wx{id = ?ID_KILL}, #pro_wx_state{popup_menu = Pop,
handle_event(#wx{id = ?ID_PROC},
- #pro_wx_state{panel = Panel,
+ #pro_wx_state{holder=Holder,
+ panel = Panel,
popup_menu = Pop,
last_selected = Pid,
procinfo_menu_pids = Opened} = State) ->
wxWindow:show(Pop, [{show, false}]),
- Node = get_node(),
+ Node = call(Holder, {get_node, self()}),
Opened2 = start_procinfo(Node, Pid, Panel, Opened),
{noreply, State#pro_wx_state{procinfo_menu_pids = Opened2}};
handle_event(#wx{id = ?ID_TRACEMENU},
- #pro_wx_state{popup_menu = Pop,
+ #pro_wx_state{holder=Holder,
+ popup_menu = Pop,
trace_options = Options,
match_specs = MatchSpecs,
selected_pids = Pids,
@@ -734,7 +433,7 @@ handle_event(#wx{id = ?ID_TRACEMENU},
observer_wx:create_txt_dialog(Panel, "No selected processes", "Tracer", ?wxICON_EXCLAMATION),
{noreply, State};
Pids ->
- Node = get_node(),
+ Node = call(Holder, {get_node, self()}),
observer_trace_wx:start(Node,
Pids,
Options,
@@ -745,11 +444,12 @@ handle_event(#wx{id = ?ID_TRACEMENU},
end;
handle_event(#wx{id = ?ID_TRACE_ALL_MENU, event = #wxCommand{type = command_menu_selected}},
- #pro_wx_state{trace_options = Options,
+ #pro_wx_state{holder=Holder,
+ trace_options = Options,
match_specs = MatchSpecs,
tracemenu_opened = false,
panel = Panel} = State) ->
- Node = get_node(),
+ Node = call(Holder, {get_node, self()}),
observer_trace_wx:start(Node,
all,
Options,
@@ -760,11 +460,12 @@ handle_event(#wx{id = ?ID_TRACE_ALL_MENU, event = #wxCommand{type = command_menu
handle_event(#wx{id = ?ID_TRACE_NEW_MENU, event = #wxCommand{type = command_menu_selected}},
- #pro_wx_state{trace_options = Options,
+ #pro_wx_state{holder=Holder,
+ trace_options = Options,
match_specs = MatchSpecs,
tracemenu_opened = false,
panel = Panel} = State) ->
- Node = get_node(),
+ Node = call(Holder, {get_node, self()}),
observer_trace_wx:start(Node,
new,
Options,
@@ -790,7 +491,7 @@ handle_event(#wx{event = #wxList{type = command_list_item_right_click,
#pro_wx_state{popup_menu = Popup,
holder = Holder} = State) ->
- case get_row(Holder, Row, pid) of
+ case call(Holder, {get_row, self(), Row, pid}) of
{error, undefined} ->
wxWindow:show(Popup, [{show, false}]),
undefined;
@@ -806,29 +507,28 @@ handle_event(#wx{event = #wxList{type = command_list_item_selected,
popup_menu = Pop,
holder = Holder} = State) ->
- NewPid = case get_row(Holder, Row, pid) of
+ NewPid = case call(Holder, {get_row, self(), Row, pid}) of
{error, undefined} ->
undefined;
{ok, P} when is_pid(P) ->
P
end,
wxWindow:show(Pop, [{show, false}]),
- Pids = get_selected_pids(Holder, get_selected_items(Grid)),
+ Pids = call(Holder, {get_pids, self(), get_selected_items(Grid)}),
{noreply, State#pro_wx_state{selected_pids = Pids,
last_selected = NewPid}};
handle_event(#wx{event = #wxList{type = command_list_col_click, col = Col}},
- #pro_wx_state{sort_dir = OldDir,
- holder = Holder} = State) ->
- NewDir = change_sort(Col, OldDir),
- refresh_grid(Holder, NewDir),
- {noreply, State#pro_wx_state{sort_dir = NewDir}};
+ #pro_wx_state{holder = Holder} = State) ->
+ Holder ! {change_sort, Col},
+ {noreply, State};
handle_event(#wx{event = #wxList{type = command_list_item_activated}},
- #pro_wx_state{panel = Panel,
+ #pro_wx_state{holder=Holder,
+ panel = Panel,
procinfo_menu_pids= Opened,
last_selected = Pid} = State) when Pid =/= undefined ->
- Node = get_node(),
+ Node = call(Holder, {get_node, self()}),
Opened2 = start_procinfo(Node, Pid, Panel, Opened),
{noreply, State#pro_wx_state{procinfo_menu_pids = Opened2}};
@@ -837,56 +537,59 @@ handle_event(Event, State) ->
{noreply, State}.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%TABLE HOLDER%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-init_table_holder(Parent, Info, Attrs) ->
+init_table_holder(Parent, Attrs) ->
+ Backend = spawn_link(node(), observer_backend,etop_collect,[self()]),
table_holder(#holder{parent = Parent,
- info = Info,
- attrs = Attrs}).
-
-
-table_holder(#holder{parent = Parent,
- info = Info,
- attrs = Attrs} = S0) ->
+ info = #etop_info{procinfo=[]},
+ node = node(),
+ backend_pid = Backend,
+ attrs = Attrs
+ }).
+
+table_holder(#holder{info=#etop_info{procinfo=Info}, attrs=Attrs,
+ node=Node, backend_pid=Backend} = S0) ->
receive
{get_row, From, Row, Col} ->
- get_row(From, Row, Col, Info#etop_info.procinfo),
+ get_row(From, Row, Col, Info),
table_holder(S0);
{get_attr, From, Row} ->
get_attr(From, Row, Attrs),
table_holder(S0);
+ {Backend, EtopInfo = #etop_info{}} ->
+ State = handle_update(EtopInfo, S0),
+ table_holder(State#holder{backend_pid=undefined});
+ refresh when is_pid(Backend)->
+ table_holder(S0); %% Already updating
+ refresh ->
+ Pid = spawn_link(Node,observer_backend,etop_collect,[self()]),
+ table_holder(S0#holder{backend_pid=Pid});
+ {change_sort, Col} ->
+ State = change_sort(Col, S0),
+ table_holder(State);
{get_pids, From, Indices} ->
- get_pids(From, Indices, Info#etop_info.procinfo),
+ get_pids(From, Indices, Info),
+ table_holder(S0);
+ {get_node, From} ->
+ From ! {self(), Node},
+ table_holder(S0);
+ {change_node, NewNode} ->
+ case Node == NewNode of
+ true ->
+ table_holder(S0);
+ false ->
+ self() ! refresh,
+ table_holder(S0#holder{node=NewNode})
+ end;
+ {accum, Bool} ->
+ table_holder(change_accum(Bool,S0));
+ {get_accum, From} ->
+ From ! {self(), S0#holder.accum == true},
table_holder(S0);
- {update, #etop_info{procinfo = ProcInfo} = NewInfo} ->
- Parent ! {holder_updated, length(ProcInfo)},
- table_holder(S0#holder{info = NewInfo});
{dump, Fd} ->
- etop_server ! {observer_dump, Fd, Info},
+ etop_txt:do_update(Fd, S0#holder.info, #opts{node=Node}),
+ file:close(Fd),
table_holder(S0);
stop ->
ok;
@@ -895,33 +598,69 @@ table_holder(#holder{parent = Parent,
table_holder(S0)
end.
-
-get_procinfo_data(?COL_PID, #etop_proc_info{pid = Pid}) ->
- Pid;
-get_procinfo_data(?COL_NAME, #etop_proc_info{name = Name}) ->
- Name;
-get_procinfo_data(?COL_MEM, #etop_proc_info{mem = Mem}) ->
- Mem;
-get_procinfo_data(?COL_TIME, #etop_proc_info{runtime = RT}) ->
- RT;
-get_procinfo_data(?COL_REDS, #etop_proc_info{reds = Reds}) ->
- Reds;
-get_procinfo_data(?COL_FUN, #etop_proc_info{cf = CF}) ->
- CF;
-get_procinfo_data(?COL_MSG, #etop_proc_info{mq = MQ}) ->
- MQ.
+change_sort(Col, S0 = #holder{parent=Parent, info=EI=#etop_info{procinfo=Data}, sort=Sort0}) ->
+ {Sort, ProcInfo} = sort(Col, Sort0, Data),
+ Parent ! {holder_updated, length(Data)},
+ S0#holder{info=EI#etop_info{procinfo=ProcInfo}, sort=Sort}.
+
+change_accum(true, S0) ->
+ S0#holder{accum=true};
+change_accum(false, S0 = #holder{info=#etop_info{procinfo=Info}}) ->
+ self() ! refresh,
+ S0#holder{accum=lists:sort(Info)}.
+
+handle_update(EI=#etop_info{procinfo=ProcInfo0},
+ S0 = #holder{parent=Parent, sort=Sort=#sort{sort_key=KeyField}}) ->
+ {ProcInfo1, S1} = accum(ProcInfo0, S0),
+ {_SO, ProcInfo} = sort(KeyField, Sort#sort{sort_key=undefined}, ProcInfo1),
+ Parent ! {holder_updated, length(ProcInfo)},
+ S1#holder{info=EI#etop_info{procinfo=ProcInfo}}.
+
+accum(ProcInfo, State = #holder{accum=true}) ->
+ {ProcInfo, State};
+accum(ProcInfo0, State = #holder{accum=Previous}) ->
+ ProcInfo = lists:sort(ProcInfo0),
+ {accum2(ProcInfo,Previous,[]), State#holder{accum=ProcInfo}}.
+
+accum2([PI = #etop_proc_info{pid=Pid, reds=Reds, runtime=RT}|PIs],
+ [#etop_proc_info{pid=Pid, reds=OldReds, runtime=OldRT}|Old], Acc) ->
+ accum2(PIs, Old, [PI#etop_proc_info{reds=Reds-OldReds, runtime=RT-OldRT}|Acc]);
+accum2(PIs = [#etop_proc_info{pid=Pid}|_], [#etop_proc_info{pid=OldPid}|Old], Acc)
+ when Pid > OldPid ->
+ accum2(PIs, Old, Acc);
+accum2([PI|PIs], Old, Acc) ->
+ accum2(PIs, Old, [PI|Acc]);
+accum2([], _, Acc) -> Acc.
+
+sort(Col, Opt = #sort{sort_key=Col, sort_incr=Bool}, Table) ->
+ {Opt#sort{sort_incr=not Bool}, lists:reverse(Table)};
+sort(Col, S=#sort{sort_incr=true}, Table) ->
+ {S#sort{sort_key=Col}, lists:keysort(col_to_element(Col), Table)};
+sort(Col, S=#sort{sort_incr=false}, Table) ->
+ {S#sort{sort_key=Col}, lists:reverse(lists:keysort(col_to_element(Col), Table))}.
+
+
+
+
+
+get_procinfo_data(Col, Info) ->
+ element(col_to_element(Col), Info).
+col_to_element(?COL_PID) -> #etop_proc_info.pid;
+col_to_element(?COL_NAME) -> #etop_proc_info.name;
+col_to_element(?COL_MEM) -> #etop_proc_info.mem;
+col_to_element(?COL_TIME) -> #etop_proc_info.runtime;
+col_to_element(?COL_REDS) -> #etop_proc_info.reds;
+col_to_element(?COL_FUN) -> #etop_proc_info.cf;
+col_to_element(?COL_MSG) -> #etop_proc_info.mq.
get_pids(From, Indices, ProcInfo) ->
- From ! {self(),
- [X#etop_proc_info.pid || X <-
- [lists:nth(I, ProcInfo) || I <- Indices]]}.
+ Processes = [lists:nth(I, ProcInfo) || I <- Indices],
+ From ! {self(), [X#etop_proc_info.pid || X <- Processes]}.
get_row(From, Row, pid, Info) ->
Pid = case Row =:= -1 of
- true ->
- {error, undefined};
- false ->
- {ok, get_procinfo_data(?COL_PID, lists:nth(Row+1, Info))}
+ true -> {error, undefined};
+ false -> {ok, get_procinfo_data(?COL_PID, lists:nth(Row+1, Info))}
end,
From ! {self(), Pid};
get_row(From, Row, Col, Info) ->
@@ -936,9 +675,7 @@ get_row(From, Row, Col, Info) ->
get_attr(From, Row, Attrs) ->
Attribute = case Row rem 2 =:= 0 of
- true ->
- Attrs#attrs.even;
- false ->
- Attrs#attrs.odd
+ true -> Attrs#attrs.even;
+ false -> Attrs#attrs.odd
end,
From ! {self(), Attribute}.