aboutsummaryrefslogtreecommitdiffstats
path: root/lib/observer/src
diff options
context:
space:
mode:
authorDan Gudmundsson <[email protected]>2011-10-03 14:12:32 +0200
committerDan Gudmundsson <[email protected]>2011-11-08 08:46:08 +0100
commitffcacd111c61019a502a895b1edcfbc1578ddfa7 (patch)
tree5eb16cad1a8406e261f1e0c3f7cf2228e3d36c06 /lib/observer/src
parent41dc04f0b76474596076bdc909760228fffc6a73 (diff)
downloadotp-ffcacd111c61019a502a895b1edcfbc1578ddfa7.tar.gz
otp-ffcacd111c61019a502a895b1edcfbc1578ddfa7.tar.bz2
otp-ffcacd111c61019a502a895b1edcfbc1578ddfa7.zip
[observer] Clean up code
system tab, timer handling and "etop" code More info in system tab, same timer handling in all tabs. Remove dependency off etop process, do the roughly the same functionality on our own.
Diffstat (limited to 'lib/observer/src')
-rw-r--r--lib/observer/src/Makefile3
-rw-r--r--lib/observer/src/observer.erl25
-rw-r--r--lib/observer/src/observer_defs.hrl6
-rw-r--r--lib/observer/src/observer_lib.erl199
-rw-r--r--lib/observer/src/observer_pro_wx.erl661
-rw-r--r--lib/observer/src/observer_sys.erl131
-rw-r--r--lib/observer/src/observer_sys_wx.erl324
-rw-r--r--lib/observer/src/observer_tv_table.erl18
-rw-r--r--lib/observer/src/observer_tv_wx.erl164
-rw-r--r--lib/observer/src/observer_wx.erl1
10 files changed, 586 insertions, 946 deletions
diff --git a/lib/observer/src/Makefile b/lib/observer/src/Makefile
index 92319434c0..9ea4118809 100644
--- a/lib/observer/src/Makefile
+++ b/lib/observer/src/Makefile
@@ -41,10 +41,11 @@ MODULES= \
etop_gui \
etop_tr \
etop_txt \
+ observer \
+ observer_lib \
observer_wx \
observer_pro_wx \
observer_procinfo \
- observer_sys \
observer_sys_wx \
observer_trace_wx \
observer_traceoptions_wx \
diff --git a/lib/observer/src/observer.erl b/lib/observer/src/observer.erl
new file mode 100644
index 0000000000..098100e8ee
--- /dev/null
+++ b/lib/observer/src/observer.erl
@@ -0,0 +1,25 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2011. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+-module(observer).
+
+-export([start/0]).
+
+
+start() ->
+ observer_wx:start().
diff --git a/lib/observer/src/observer_defs.hrl b/lib/observer/src/observer_defs.hrl
index 7990cc248a..0af15f6422 100644
--- a/lib/observer/src/observer_defs.hrl
+++ b/lib/observer/src/observer_defs.hrl
@@ -16,12 +16,6 @@
%%
%% %CopyrightEnd%
-%% -define(OBS, observer_wx).
-%% -define(OBS_SYS_LOGIC, observer_sys).
-%% -define(OBS_SYS_WX, observer_sys_wx).
-%% -define(OBS_PRO_WX, observer_pro_wx).
-
-
-record(trace_options, {send = false,
treceive = false,
functions = false,
diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl
new file mode 100644
index 0000000000..cab9d5ccf2
--- /dev/null
+++ b/lib/observer/src/observer_lib.erl
@@ -0,0 +1,199 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2011. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+-module(observer_lib).
+
+-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]).
+
+-include_lib("wx/include/wx.hrl").
+
+get_wx_parent(Window) ->
+ Parent = wxWindow:getParent(Window),
+ case wx:is_null(Parent) of
+ true -> Window;
+ false -> get_wx_parent(Parent)
+ end.
+
+interval_dialog(Parent0, {Timer, Value}, Min, Max) ->
+ Parent = get_wx_parent(Parent0),
+ Dialog = wxDialog:new(Parent, ?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, Timer /= false),
+ 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, Timer /= false}]),
+ InnerSizer = wxBoxSizer:new(?wxVERTICAL),
+ Buttons = wxDialog:createButtonSizer(Dialog, ?wxOK bor ?wxCANCEL),
+ 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}]),
+ wxWindow:setSizerAndFit(Dialog, TopSizer),
+ wxSizer:setSizeHints(TopSizer, Dialog),
+ wxCheckBox:connect(Check, command_checkbox_clicked,
+ [{callback, fun(#wx{event=#wxCommand{commandInt=Enable0}},_) ->
+ Enable = Enable0 > 0,
+ wxWindow:enable(Slider, [{enable, Enable}])
+ end}]),
+ Res = case wxDialog:showModal(Dialog) of
+ ?wxID_OK ->
+ Enabled = wxCheckBox:isChecked(Check),
+ setup_timer(Enabled, {Timer, wxSlider:getValue(Slider)});
+ ?wxID_CANCEL ->
+ {Timer, Value}
+ end,
+ wxDialog:destroy(Dialog),
+ Res.
+
+stop_timer(Timer = {false, _}) -> Timer;
+stop_timer(Timer = {true, _}) -> Timer;
+stop_timer(Timer = {_, Intv}) ->
+ setup_timer(false, Timer),
+ {true, Intv}.
+start_timer(Intv) when is_integer(Intv) ->
+ setup_timer(true, {true, Intv});
+start_timer(Timer) ->
+ setup_timer(true, Timer).
+
+setup_timer(false, {Timer, Value})
+ when is_boolean(Timer) ->
+ {false, Value};
+setup_timer(true, {false, Value}) ->
+ {ok, Timer} = timer:send_interval(Value * 1000, refresh_interval),
+ {Timer, Value};
+setup_timer(Bool, {Timer, Old}) ->
+ timer:cancel(Timer),
+ setup_timer(Bool, {false, Old}).
+
+display_info_dialog(Str) ->
+ Dlg = wxMessageDialog:new(wx:null(), Str),
+ wxMessageDialog:showModal(Dlg),
+ wxMessageDialog:destroy(Dlg),
+ ok.
+
+%% display_info(Parent, [{Title, [{Label, Info}]}]) -> {Panel, Sizer, InfoFieldsToUpdate}
+display_info(Frame, Info) ->
+ Panel = wxPanel:new(Frame),
+ wxWindow:setBackgroundColour(Panel, {255,255,255}),
+ Sizer = wxBoxSizer:new(?wxVERTICAL),
+ wxSizer:addSpacer(Sizer, 5),
+ Add = fun(BoxInfo) ->
+ {Box, InfoFs} = create_box(Panel, BoxInfo),
+ wxSizer:add(Sizer, Box, [{flag, ?wxEXPAND bor ?wxALL},
+ {border, 5}]),
+ wxSizer:addSpacer(Sizer, 5),
+ InfoFs
+ end,
+ InfoFs = [Add(I) || I <- Info],
+ wxWindow:setSizerAndFit(Panel, Sizer),
+ {Panel, Sizer, InfoFs}.
+
+update_info([Fields|Fs], [{_Header, SubStructure}| Rest]) ->
+ update_info2(Fields, SubStructure),
+ update_info(Fs, Rest);
+update_info([Fields|Fs], [{_Header, _Attrib, SubStructure}| Rest]) ->
+ update_info2(Fields, SubStructure),
+ update_info(Fs, Rest);
+update_info([], []) ->
+ ok.
+
+update_info2([Field|Fs], [{_Str, Value}|Rest]) ->
+ wxStaticText:setLabel(Field, to_str(Value)),
+ update_info2(Fs, Rest);
+update_info2([], []) -> ok.
+
+
+to_str(Value) when is_atom(Value) ->
+ atom_to_list(Value);
+to_str({bytes, B}) ->
+ KB = B div 1024,
+ MB = KB div 1024,
+ if
+ MB > 10 -> integer_to_list(MB) ++ " mB";
+ KB > 0 -> integer_to_list(KB) ++ " kB";
+ true -> integer_to_list(B) ++ " B "
+ end;
+to_str({time_ms, MS}) ->
+ S = MS div 1000,
+ Min = S div 60,
+ Hours = Min div 60,
+ Days = Hours div 24,
+ if
+ Days > 0 -> integer_to_list(Days) ++ " Days";
+ Hours > 0 -> integer_to_list(Hours) ++ " Hours";
+ Min > 0 -> integer_to_list(Min) ++ " Mins";
+ true -> integer_to_list(S) ++ " Secs"
+ end;
+
+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}).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+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}.
+
+create_box(Panel, Data) ->
+ {Title, Align, Info} = get_box_info(Data),
+ Box = wxStaticBoxSizer:new(?wxHORIZONTAL, Panel, [{label, Title}]),
+ Left = wxBoxSizer:new(?wxVERTICAL),
+ Right = wxBoxSizer:new(?wxVERTICAL),
+ Expand = [{flag, ?wxEXPAND}],
+ ExpAlign = [{flag, Align}],
+ AddRow = fun({Desc, Value}) ->
+ wxSizer:add(Left, wxStaticText:new(Panel, ?wxID_ANY, Desc ++ ":"), Expand),
+ Field = wxStaticText:new(Panel, ?wxID_ANY, to_str(Value)),
+ wxSizer:add(Right, Field, ExpAlign),
+ Field
+ end,
+ InfoFields = [AddRow(Entry) || Entry <- Info],
+ wxSizer:add(Box, Left),
+ wxSizer:addSpacer(Box, 10),
+ wxSizer:add(Box, Right),
+ wxSizer:addSpacer(Box, 30),
+ {Box, InfoFields}.
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}.
diff --git a/lib/observer/src/observer_sys.erl b/lib/observer/src/observer_sys.erl
deleted file mode 100644
index 8db7bb0e46..0000000000
--- a/lib/observer/src/observer_sys.erl
+++ /dev/null
@@ -1,131 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2011. 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
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-
--module(observer_sys).
-
--export([node_info/0, node_name_str/1, no_procs_str/1, no_cpu_str/1,
- no_cpu_available_str/1, no_cpu_online_str/1, tot_alloc_str/1,
- proc_used_str/1, proc_alloc_str/1, atom_used_str/1, atom_alloc_str/1,
- binary_alloc_str/1, code_alloc_str/1, ets_alloc_str/1]).
-
--record(node_info, {node_name,
- no_procs, % number of processes
- no_cpu, % number of logical cpu's
- no_cpu_available, %number of logical cpu's available
- no_cpu_online, % number of logical cpu's online
- tot_alloc, % total memory allocated
- proc_used, % memory used by processes
- proc_alloc, % memory alloc by processes,
- atom_used, % memory used by atoms
- atom_alloc, % memory allocated by atoms
- binary_alloc, % memory allocated for binaries
- code_alloc, % memory allocated by code
- ets_alloc}).% memory allocated by ets
-
-
-%%%%%%%%%%%%%%%%%%%%%%% functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-node_info() ->
- #node_info{node_name = node_name(),
- no_procs = process_count(),
- no_cpu = logical_cpus(),
- no_cpu_available = logical_cpus_available(),
- no_cpu_online = logical_cpus_online(),
- tot_alloc = total_alloc(),
- proc_used = processes_used(),
- proc_alloc = processes_alloc(),
- atom_used = atom_used(),
- atom_alloc = atom_alloc(),
- binary_alloc = binary_alloc(),
- code_alloc = code_alloc(),
- ets_alloc = ets_alloc()
- }.
-
-node_name() ->
- node().
-
-process_count() ->
- erlang:system_info(process_count).
-
-logical_cpus() ->
- erlang:system_info(logical_processors). % detected number of logical cpus configured on system
-
-logical_cpus_available() -> % detected number of logical cpus available to erlang runtime system
- erlang:system_info(logical_processors_available).
-
-logical_cpus_online() -> % detected number of logical cpus online on system
- erlang:system_info(logical_processors_online).
-
-total_alloc() ->
- erlang:memory(total). % total amount of memory currently allocated
-
-processes_used() -> % amount of memory currently used by the erlang processes
- erlang:memory(processes_used).
-
-processes_alloc() -> % allocated by erlang processes
- erlang:memory(processes).
-
-atom_used() -> % amount of memory used for atoms
- erlang:memory(atom_used).
-
-atom_alloc() -> % amount allocated for atoms
- erlang:memory(atom).
-
-binary_alloc() -> % amount allocated for binaries
- erlang:memory(binary).
-
-code_alloc() -> % amount allocated for code
- erlang:memory(code).
-
-ets_alloc() -> % amount allocated for ets tables
- erlang:memory(ets).
-
-
-%% formatting functions, from the record-value to string
-node_name_str(#node_info{node_name = ToReturn}) ->
- erlang:atom_to_list(ToReturn).
-no_procs_str(#node_info{no_procs = ToReturn}) ->
- erlang:integer_to_list(ToReturn).
-no_cpu_str(#node_info{no_cpu = ToReturn}) ->
- erlang:integer_to_list(ToReturn).
-no_cpu_available_str(#node_info{no_cpu_available = ToReturn}) ->
- erlang:integer_to_list(ToReturn).
-no_cpu_online_str(#node_info{no_cpu_online = ToReturn}) ->
- erlang:integer_to_list(ToReturn).
-tot_alloc_str(#node_info{tot_alloc = ToReturn}) ->
- erlang:integer_to_list(ToReturn).
-
-proc_used_str(#node_info{proc_used = ToReturn}) ->
- erlang:integer_to_list(ToReturn).
-
-proc_alloc_str(#node_info{proc_alloc = ToReturn}) ->
- erlang:integer_to_list(ToReturn).
-
-atom_used_str(#node_info{atom_used = ToReturn}) ->
- erlang:integer_to_list(ToReturn).
-
-atom_alloc_str(#node_info{atom_alloc = ToReturn}) ->
- erlang:integer_to_list(ToReturn).
-
-binary_alloc_str(#node_info{binary_alloc = ToReturn}) ->
- erlang:integer_to_list(ToReturn).
-
-code_alloc_str(#node_info{code_alloc = ToReturn}) ->
- erlang:integer_to_list(ToReturn).
-
-ets_alloc_str(#node_info{ets_alloc = ToReturn}) ->
- erlang:integer_to_list(ToReturn).
diff --git a/lib/observer/src/observer_sys_wx.erl b/lib/observer/src/observer_sys_wx.erl
index 5116891e91..5bc5fd49f6 100644
--- a/lib/observer/src/observer_sys_wx.erl
+++ b/lib/observer/src/observer_sys_wx.erl
@@ -24,6 +24,8 @@
-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3,
handle_event/2, handle_cast/2]).
+-export([sys_info/0]).
+
-include_lib("wx/include/wx.hrl").
-include("observer_defs.hrl").
@@ -33,172 +35,102 @@
%% Records
-record(sys_wx_state,
{parent,
- panel,
- menubar,
- parent_notebook,
- no_procs,
- no_cpu,
- no_cpu_available,
- no_cpu_online,
- tot_alloc,
- proc_used,
- proc_alloc,
- atom_used,
- atom_alloc,
- binary_alloc,
- code_alloc,
- ets_alloc,
- node_label,
node,
- refr_timer = false,
- refr_intv = 30}).
+ parent_notebook,
+ panel, sizer,
+ menubar,
+ fields,
+ timer}).
start_link(Notebook, Parent) ->
wx_object:start_link(?MODULE, [Notebook, Parent], []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-init([Notebook, Parent]) ->
- SysPanel = wxPanel:new(Notebook, []),
-
- %% Setup sizers
- SysSizer = wxBoxSizer:new(?wxVERTICAL),
-
- SysNodeSizer = wxStaticBoxSizer:new(?wxHORIZONTAL, SysPanel, [{label, "Node:"}]),
-
- SysLoadSizer = wxStaticBoxSizer:new(?wxHORIZONTAL, SysPanel, [{label, "Load:"}]),
- SysLeftLoadSizer = wxBoxSizer:new(?wxVERTICAL),
- SysMidLoadSizer = wxBoxSizer:new(?wxHORIZONTAL),
- SysRightLoadSizer = wxBoxSizer:new(?wxVERTICAL),
-
- SysMemSizer = wxStaticBoxSizer:new(?wxHORIZONTAL, SysPanel, [{label, "Memory:"}]),
- SysLeftMemSizer = wxBoxSizer:new(?wxVERTICAL),
- SysMidMemSizer = wxBoxSizer:new(?wxHORIZONTAL),
- SysRightMemSizer = wxBoxSizer:new(?wxVERTICAL),
-
- wxSizer:add(SysSizer, SysNodeSizer, [{flag, ?wxEXPAND}]),
- wxSizer:add(SysSizer, SysLoadSizer, [{flag, ?wxEXPAND}]),
- wxSizer:add(SysSizer, SysMemSizer, [{flag, ?wxEXPAND}]),
- wxSizer:add(SysLoadSizer, SysLeftLoadSizer),
- wxSizer:add(SysLoadSizer, SysMidLoadSizer),
- wxSizer:add(SysLoadSizer, SysRightLoadSizer),
-
- wxSizer:add(SysMemSizer, SysLeftMemSizer),
- wxSizer:add(SysMemSizer, SysMidMemSizer),
- wxSizer:add(SysMemSizer, SysRightMemSizer),
-
- wxSizer:addSpacer(SysMidLoadSizer, 90),
- wxSizer:addSpacer(SysMidMemSizer, 70),
-
- %% Create labels
- NodeInfo = get_syspage_info(node()),
- NodeLabel = create_info_label(SysPanel, SysNodeSizer, observer_sys:node_name_str(NodeInfo)),
-
- create_info_label(SysPanel, SysLeftLoadSizer, "logical CPU's:"),
- create_info_label(SysPanel, SysLeftLoadSizer, "logical CPU's available:"),
- create_info_label(SysPanel, SysLeftLoadSizer, "logical CPU's online:"),
- create_info_label(SysPanel, SysLeftLoadSizer, "existing processes:"),
- NoCpuTxt = create_info_label(SysPanel, SysRightLoadSizer, observer_sys:no_cpu_str(NodeInfo)),
- NoCpuAvTxt = create_info_label(SysPanel, SysRightLoadSizer, observer_sys:no_cpu_available_str(NodeInfo)),
- NoCpuOnTxt = create_info_label(SysPanel, SysRightLoadSizer, observer_sys:no_cpu_online_str(NodeInfo)),
- NoProcsTxt = create_info_label(SysPanel, SysRightLoadSizer, observer_sys:no_procs_str(NodeInfo)),
-
- create_info_label(SysPanel, SysLeftMemSizer, "total allocated:"),
- create_info_label(SysPanel, SysLeftMemSizer, "used by processes:"),
- create_info_label(SysPanel, SysLeftMemSizer, "allocated for processes:"),
- create_info_label(SysPanel, SysLeftMemSizer, "used by atoms:"),
- create_info_label(SysPanel, SysLeftMemSizer, "allocated for atoms:"),
- create_info_label(SysPanel, SysLeftMemSizer, "allocated for binaries:"),
- create_info_label(SysPanel, SysLeftMemSizer, "allocated for code"),
- create_info_label(SysPanel, SysLeftMemSizer, "allocated for ETS:"),
- TotAllocTxt = create_info_label(SysPanel, SysRightMemSizer, observer_sys:tot_alloc_str(NodeInfo)),
- ProcUsedTxt = create_info_label(SysPanel, SysRightMemSizer, observer_sys:proc_used_str(NodeInfo)),
- ProcAllocTxt = create_info_label(SysPanel, SysRightMemSizer, observer_sys:proc_alloc_str(NodeInfo)),
- AtomUsedTxt = create_info_label(SysPanel, SysRightMemSizer, observer_sys:atom_used_str(NodeInfo)),
- AtomAllocTxt = create_info_label(SysPanel, SysRightMemSizer, observer_sys:atom_alloc_str(NodeInfo)),
- BinaryAllocTxt = create_info_label(SysPanel, SysRightMemSizer, observer_sys:binary_alloc_str(NodeInfo)),
- CodeAllocTxt = create_info_label(SysPanel, SysRightMemSizer, observer_sys:code_alloc_str(NodeInfo)),
- EtsAllocTxt = create_info_label(SysPanel, SysRightMemSizer, observer_sys:ets_alloc_str(NodeInfo)),
-
- %% Create StateRecord
- SysPanelState = #sys_wx_state{
- parent = Parent,
- panel = SysPanel,
- parent_notebook = Notebook,
- node_label = NodeLabel,
- no_procs = NoProcsTxt,
- no_cpu = NoCpuTxt,
- no_cpu_available = NoCpuAvTxt,
- no_cpu_online= NoCpuOnTxt,
- tot_alloc = TotAllocTxt,
- proc_used = ProcUsedTxt,
- proc_alloc = ProcAllocTxt,
- atom_used = AtomUsedTxt,
- atom_alloc = AtomAllocTxt,
- binary_alloc = BinaryAllocTxt,
- code_alloc = CodeAllocTxt,
- ets_alloc = EtsAllocTxt,
- node = node()},
-
- wxPanel:setSizer(SysPanel, SysSizer),
- {SysPanel, SysPanelState}.
-
-get_syspage_info(Node) ->
- observer_wx:try_rpc(Node, observer_sys, node_info, []).
-
-create_info_label(Panel, Sizer, Msg) ->
- WxText = wxStaticText:new(Panel, ?wxID_ANY, Msg),
- wxSizer:add(Sizer, WxText),
- WxText.
+init(Args) ->
+ try
+ init2(Args)
+ catch E:R ->
+ io:format("~p:~p ~p~n",[E,R, erlang:get_stacktrace()])
+ end.
+
+init2([Notebook, Parent]) ->
+ SysInfo = sys_info(),
+ {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)),
+ 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},
+ {proportion, 1}, {border, 5}]),
+ wxPanel:setSizer(Panel, Sizer),
+ Timer = observer_lib:start_timer(10),
+ {Panel, #sys_wx_state{parent=Parent,
+ parent_notebook=Notebook,
+ panel=Panel, sizer=Sizer,
+ timer=Timer, fields=Fields0 ++ Fields1}}.
create_sys_menu(Parent) ->
- View = {"View", [#create_menu{id = ?ID_REFRESH, text = "Refresh"},
+ View = {"View", [#create_menu{id = ?ID_REFRESH, text = "Refresh\tCtrl-R"},
#create_menu{id = ?ID_REFRESH_INTERVAL, text = "Refresh interval"}]},
observer_wx:create_menus(Parent, [View]).
-update_syspage(#sys_wx_state{node = Node} = State) ->
- Info = get_syspage_info(Node),
- update_info_label(node_label, Info, State#sys_wx_state.node_label),
- update_info_label(no_procs, Info, State#sys_wx_state.no_procs),
- update_info_label(no_cpu, Info, State#sys_wx_state.no_cpu),
- update_info_label(no_cpu_available, Info, State#sys_wx_state.no_cpu_available),
- update_info_label(no_cpu_online, Info, State#sys_wx_state.no_cpu_online),
- update_info_label(tot_alloc, Info, State#sys_wx_state.tot_alloc),
- update_info_label(proc_used, Info, State#sys_wx_state.proc_used),
- update_info_label(proc_alloc, Info, State#sys_wx_state.proc_alloc),
- update_info_label(atom_used, Info, State#sys_wx_state.atom_used),
- update_info_label(atom_alloc, Info, State#sys_wx_state.atom_alloc),
- update_info_label(binary_alloc, Info, State#sys_wx_state.binary_alloc),
- update_info_label(code_alloc, Info, State#sys_wx_state.code_alloc),
- update_info_label(ets_alloc, Info, State#sys_wx_state.ets_alloc).
-
-update_info_label(node_label, Info, WxTxt) ->
- wxStaticText:setLabel(WxTxt, observer_sys:node_name_str(Info));
-update_info_label(no_procs, Info, WxTxt) ->
- wxStaticText:setLabel(WxTxt, observer_sys:no_procs_str(Info));
-update_info_label(no_cpu, Info, WxTxt) ->
- wxStaticText:setLabel(WxTxt, observer_sys:no_cpu_str(Info));
-update_info_label(no_cpu_available, Info, WxTxt) ->
- wxStaticText:setLabel(WxTxt, observer_sys:no_cpu_available_str(Info));
-update_info_label(no_cpu_online, Info, WxTxt) ->
- wxStaticText:setLabel(WxTxt, observer_sys:no_cpu_online_str(Info));
-update_info_label(tot_alloc, Info, WxTxt) ->
- wxStaticText:setLabel(WxTxt, observer_sys:tot_alloc_str(Info));
-update_info_label(proc_used, Info, WxTxt) ->
- wxStaticText:setLabel(WxTxt, observer_sys:proc_used_str(Info));
-update_info_label(proc_alloc, Info, WxTxt) ->
- wxStaticText:setLabel(WxTxt, observer_sys:proc_alloc_str(Info));
-update_info_label(atom_used, Info, WxTxt) ->
- wxStaticText:setLabel(WxTxt, observer_sys:atom_used_str(Info));
-update_info_label(atom_alloc, Info, WxTxt) ->
- wxStaticText:setLabel(WxTxt, observer_sys:atom_alloc_str(Info));
-update_info_label(binary_alloc, Info, WxTxt) ->
- wxStaticText:setLabel(WxTxt, observer_sys:binary_alloc_str(Info));
-update_info_label(code_alloc, Info, WxTxt) ->
- wxStaticText:setLabel(WxTxt, observer_sys:code_alloc_str(Info));
-update_info_label(ets_alloc, Info, WxTxt) ->
- wxStaticText:setLabel(WxTxt, observer_sys:ets_alloc_str(Info)).
+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)),
+ wxSizer:layout(Sizer)
+ catch E:R ->
+ io:format("~p:~p ~p~n",[E,R, erlang:get_stacktrace()])
+ end,
+ ok.
+info_fields() ->
+ Info = [{"System and Architecture",
+ [{"System Version", otp_release},
+ {"Erts Version", version},
+ {"Compiled for", system_architecture},
+ {"Emulator Wordsize", wordsize_external},
+ {"Process Wordsize", wordsize_internal},
+ {"Smp Support", smp_support},
+ {"Thread Support", threads},
+ {"Async thread pool size", thread_pool_size}
+ ]},
+ {"CPU's and Threads",
+ [{"System Logical CPU's", logical_processors},
+ {"Erlang Logical CPU's", logical_processors_online},
+ {"Used Logical CPU's", logical_processors_available}
+ ]}
+ ],
+ Stat = [{"Memory Usage", right,
+ [{"Total", total},
+ {"Processes", processes},
+ {"Atoms", atom},
+ {"Binaries", binary},
+ {"Code", code},
+ {"Ets", ets}
+ ]},
+ {"Statistics", right,
+ [{"Up time", uptime},
+ {"Max Processes", process_limit},
+ {"Processes", process_count},
+ {"Run Queue", run_queue},
+ {"IO Input", io_input},
+ {"IO Output", io_output}
+ ]}
+ ],
+ {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 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -216,45 +148,26 @@ handle_info({node, Node}, #sys_wx_state{panel = Panel} = State) ->
try
update_syspage(UpdState),
{noreply, UpdState}
-
catch error:{badrpc, _} ->
observer_wx:return_to_localnode(Panel, Node),
{noreply, State}
end;
-handle_info({active, Node},
- #sys_wx_state{parent = Parent,
- panel = Panel,
- refr_timer = Timer0,
- refr_intv = Intv} = State) ->
+handle_info({active, Node}, #sys_wx_state{parent = Parent, panel = Panel,
+ timer = Timer} = State) ->
UpdState = State#sys_wx_state{node = Node},
create_sys_menu(Parent),
try
update_syspage(UpdState),
- Timer = case Timer0 of
- true ->
- {ok, Ref} = timer:send_interval(Intv*1000, refresh_interval),
- Ref;
- false ->
- false
- end,
- {noreply, UpdState#sys_wx_state{refr_timer = Timer}}
-
+ {noreply, UpdState#sys_wx_state{timer=observer_lib:start_timer(Timer)}}
catch error:{badrpc, _} ->
observer_wx:return_to_localnode(Panel, Node),
{noreply, State}
end;
-handle_info(not_active, #sys_wx_state{refr_timer = Timer0} = State) ->
- Timer = case Timer0 of
- false -> false;
- true -> true;
- Timer0 ->
- timer:cancel(Timer0),
- true
- end,
- {noreply, State#sys_wx_state{refr_timer = Timer}};
+handle_info(not_active, #sys_wx_state{timer = Timer} = State) ->
+ {noreply, State#sys_wx_state{timer = observer_lib:stop_timer(Timer)}};
handle_info(Info, State) ->
io:format("~p, ~p, Handle info: ~p~n", [?MODULE, ?LINE, Info]),
@@ -286,28 +199,43 @@ handle_event(#wx{id = ?ID_REFRESH, event = #wxCommand{type = command_menu_select
handle_event(#wx{id = ?ID_REFRESH_INTERVAL,
event = #wxCommand{type = command_menu_selected}},
- #sys_wx_state{refr_timer = Timer0,
- refr_intv = Intv0,
- parent_notebook = Notebook} = State) ->
- Parent = observer_tv_wx:get_wx_parent(Notebook),
- case observer_tv_wx:interval_dialog(Parent, Timer0 /= false, Intv0, 1, 5*60) of
- cancel ->
- {noreply, State};
- {true, Intv} ->
- case Timer0 of
- false -> ok;
- _ -> timer:cancel(Timer0)
- end,
- {ok, Timer} = timer:send_interval(Intv * 1000, refresh_interval),
- {noreply, State#sys_wx_state{refr_timer=Timer, refr_intv=Intv}};
- {false, _} ->
- case Timer0 of
- false -> ok;
- _ -> timer:cancel(Timer0)
- end,
- {noreply, State#sys_wx_state{refr_timer=false}}
- end;
+ #sys_wx_state{timer = Timer0, parent_notebook = Notebook} = State) ->
+ Timer = observer_lib:interval_dialog(Notebook, Timer0, 1, 5*60),
+ {noreply, State#sys_wx_state{timer=Timer}};
handle_event(Event, State) ->
io:format("handle event ~p\n", [Event]),
{noreply, State}.
+
+
+sys_info() ->
+ {{_,Input},{_,Output}} = erlang:statistics(io),
+ [{process_count, erlang:system_info(process_count)},
+ {process_limit, erlang:system_info(process_limit)},
+ {uptime, {time_ms, element(1, erlang:statistics(wall_clock))}},
+ {run_queue, erlang:statistics(run_queue)},
+ {io_input, {bytes, Input}},
+ {io_output, {bytes, Output}},
+ {logical_processors, erlang:system_info(logical_processors)},
+ {logical_processors_available, erlang:system_info(logical_processors_available)},
+ {logical_processors_online, erlang:system_info(logical_processors_online)},
+
+ {total, {bytes, erlang:memory(total)}},
+ %%{processes_used, erlang:memory(processes_used)},
+ {processes, {bytes, erlang:memory(processes)}},
+ %%{atom_used, erlang:memory(atom_used)},
+ {atom, {bytes, erlang:memory(atom)}},
+ {binary, {bytes, erlang:memory(binary)}},
+ {code, {bytes, erlang:memory(code)}},
+ {ets, {bytes, erlang:memory(ets)}},
+
+ {otp_release, erlang:system_info(otp_release)},
+ {version, erlang:system_info(version)},
+ {system_architecture, erlang:system_info(system_architecture)},
+ {kernel_poll, erlang:system_info(kernel_poll)},
+ {smp_support, erlang:system_info(smp_support)},
+ {threads, erlang:system_info(threads)},
+ {thread_pool_size, erlang:system_info(thread_pool_size)},
+ {wordsize_internal, erlang:system_info({wordsize, internal})},
+ {wordsize_external, erlang:system_info({wordsize, external})}
+ ].
diff --git a/lib/observer/src/observer_tv_table.erl b/lib/observer/src/observer_tv_table.erl
index 3228f7e571..a2797700d8 100644
--- a/lib/observer/src/observer_tv_table.erl
+++ b/lib/observer/src/observer_tv_table.erl
@@ -26,7 +26,7 @@
-export([get_table/3]).
--import(observer_pro_wx, [to_str/1]).
+-import(observer_lib, [to_str/1]).
-behaviour(wx_object).
-include_lib("wx/include/wx.hrl").
@@ -58,7 +58,8 @@
pid,
source,
tab,
- attrs
+ attrs,
+ timer
}).
-record(opt,
@@ -401,6 +402,11 @@ handle_event(#wx{id=?ID_TABLE_INFO},
observer_tv_wx:display_table_info(Frame, Node, Source, Table),
{noreply, State};
+handle_event(#wx{id=?ID_REFRESH_INTERVAL},
+ State = #state{grid=Grid, timer=Timer0}) ->
+ Timer = observer_lib:interval_dialog(Grid, Timer0, 10, 5*60),
+ {noreply, State#state{timer=Timer}};
+
handle_event(Event, State) ->
io:format("~p:~p, handle event ~p\n", [?MODULE, ?LINE, Event]),
{noreply, State}.
@@ -586,10 +592,10 @@ sort(Col, S=#holder{n=N, parent=Parent, sort=Opt0, table=Table0}) ->
sort(Col, Opt = #opt{sort_key=Col, sort_incr=Bool}, Table) ->
{Opt#opt{sort_incr=not Bool}, lists:reverse(Table)};
-sort(Col, #opt{sort_incr=true}, Table) ->
- {#opt{sort_key=Col}, keysort(Col, Table)};
-sort(Col, #opt{sort_incr=false}, Table) ->
- {#opt{sort_key=Col}, lists:reverse(keysort(Col, Table))}.
+sort(Col, S=#opt{sort_incr=true}, Table) ->
+ {S#opt{sort_key=Col}, keysort(Col, Table)};
+sort(Col, S=#opt{sort_incr=false}, Table) ->
+ {S=#opt{sort_key=Col}, lists:reverse(keysort(Col, Table))}.
keysort(Col, Table) ->
Sort = fun([A0|_], [B0|_]) ->
diff --git a/lib/observer/src/observer_tv_wx.erl b/lib/observer/src/observer_tv_wx.erl
index 10b29cb1b7..230195c9de 100644
--- a/lib/observer/src/observer_tv_wx.erl
+++ b/lib/observer/src/observer_tv_wx.erl
@@ -25,10 +25,6 @@
-export([get_table_list/1]). %% RPC called move to runtime tools?
--export([get_wx_parent/1, interval_dialog/5]).
-
--import(observer_pro_wx, [to_str/1]).
-
-behaviour(wx_object).
-include_lib("wx/include/wx.hrl").
-include("observer_defs.hrl").
@@ -58,8 +54,7 @@
opt=#opt{},
selected,
tabs,
- refr_timer=false,
- refr_intv=30
+ timer
}).
start_link(Notebook, Parent) ->
@@ -97,7 +92,8 @@ init([Notebook, Parent]) ->
wxListCtrl:connect(Grid, size, [{skip, true}]),
wxWindow:setFocus(Grid),
- {Panel, #state{grid=Grid, parent=Parent}}.
+ Timer = observer_lib:start_timer(10),
+ {Panel, #state{grid=Grid, parent=Parent, timer=Timer}}.
handle_event(#wx{id=?ID_REFRESH},
State = #state{node=Node, grid=Grid, opt=Opt}) ->
@@ -164,31 +160,14 @@ handle_event(#wx{id=?ID_TABLE_INFO},
{noreply, State};
R when is_integer(R) ->
Table = lists:nth(Sel+1, Tabs),
- Parent = get_wx_parent(Grid),
- display_table_info(Parent, Node, Type, Table),
+ display_table_info(Grid, Node, Type, Table),
{noreply, State}
end;
handle_event(#wx{id=?ID_REFRESH_INTERVAL},
- State = #state{grid=Grid, refr_timer=Timer0, refr_intv=Intv0}) ->
- Parent = get_wx_parent(Grid),
- case interval_dialog(Parent, Timer0 /= false, Intv0, 10, 5*60) of
- cancel ->
- {noreply, State};
- {true, Intv} ->
- case Timer0 of
- false -> ok;
- _ -> timer:cancel(Timer0)
- end,
- {ok, Timer} = timer:send_interval(Intv * 1000, refresh_interval),
- {noreply, State#state{refr_timer=Timer, refr_intv=Intv}};
- {false, _} ->
- case Timer0 of
- false -> ok;
- _ -> timer:cancel(Timer0)
- end,
- {noreply, State#state{refr_timer=false}}
- end;
+ State = #state{grid=Grid, timer=Timer0}) ->
+ Timer = observer_lib:interval_dialog(Grid, Timer0, 10, 5*60),
+ {noreply, State#state{timer=Timer}};
handle_event(Event, State) ->
io:format("~p:~p, handle event ~p\n", [?MODULE, ?LINE, Event]),
@@ -207,36 +186,22 @@ handle_cast(Event, State) ->
{noreply, State}.
handle_info(refresh_interval, State = #state{node=Node, grid=Grid, opt=Opt}) ->
- io:format("refresh interval ~p~n", [time()]),
Tables = get_tables(Node, Opt),
Tabs = update_grid(Grid, Opt, Tables),
{noreply, State#state{tabs=Tabs}};
-handle_info({active, Node},
- State = #state{parent=Parent, grid=Grid, opt=Opt,
- refr_timer = Refr, refr_intv=Intv}) ->
+handle_info({active, Node}, State = #state{parent=Parent, grid=Grid, opt=Opt,
+ timer=Timer0}) ->
Tables = get_tables(Node, Opt),
Tabs = update_grid(Grid, Opt, Tables),
wxWindow:setFocus(Grid),
create_menus(Parent, Opt),
- Timer = case Refr of
- true ->
- {ok, Ref} = timer:send_interval(Intv*1000, refresh_interval),
- Ref;
- false ->
- false
- end,
- {noreply, State#state{node=Node, tabs=Tabs, refr_timer=Timer}};
-
-handle_info(not_active, State = #state{refr_timer = Timer0}) ->
- Timer = case Timer0 of
- false -> false;
- true -> true;
- Timer0 ->
- timer:cancel(Timer0),
- true
- end,
- {noreply, State#state{refr_timer=Timer}};
+ Timer = observer_lib:start_timer(Timer0),
+ {noreply, State#state{node=Node, tabs=Tabs, timer=Timer}};
+
+handle_info(not_active, State = #state{timer = Timer0}) ->
+ Timer = observer_lib:stop_timer(Timer0),
+ {noreply, State#state{timer=Timer}};
handle_info({node, Node}, State = #state{grid=Grid, opt=Opt}) ->
Tables = get_tables(Node, Opt),
@@ -370,7 +335,8 @@ get_table_list(#opt{type=mnesia, sys_hidden=HideSys}) ->
end,
lists:foldl(Info, [], mnesia:system_info(tables)).
-display_table_info(Parent, Node, Source, Table) ->
+display_table_info(Parent0, Node, Source, Table) ->
+ Parent = observer_lib:get_wx_parent(Parent0),
Title = "Table Info: " ++ atom_to_list(Table#tab.name),
Frame = wxMiniFrame:new(Parent, ?wxID_ANY, Title,
[{style, ?wxCAPTION bor ?wxCLOSE_BOX bor ?wxRESIZE_BORDER}]),
@@ -407,10 +373,10 @@ display_table_info(Parent, Node, Source, Table) ->
| MnesiaSettings ]},
Memory = {"Memory Usage",
[{"Number of objects", Table#tab.size},
- {"Memory allocated", integer_to_list(Table#tab.memory div 1024) ++ "kB"},
+ {"Memory allocated", {bytes, Table#tab.memory}},
{"Compressed", Table#tab.compressed}]},
- Sizer = display_info_wx(Frame, [IdInfo, Settings, Memory]),
+ {_, Sizer, _} = observer_lib:display_info(Frame, [IdInfo,Settings,Memory]),
wxSizer:setSizeHints(Sizer, Frame),
wxFrame:center(Frame),
wxFrame:show(Frame).
@@ -420,44 +386,6 @@ list_to_strings([A]) -> integer_to_list(A);
list_to_strings([A,B]) ->
integer_to_list(A) ++ " ," ++ list_to_strings(B).
-get_wx_parent(Window) ->
- Parent = wxWindow:getParent(Window),
- case wx:is_null(Parent) of
- true -> Window;
- false -> get_wx_parent(Parent)
- end.
-
-display_info_wx(Frame, Info) ->
- Panel = wxPanel:new(Frame),
- wxWindow:setBackgroundColour(Panel, {255,255,255}),
- Sizer = wxBoxSizer:new(?wxVERTICAL),
- wxSizer:addSpacer(Sizer, 5),
- Add = fun(BoxInfo) ->
- Box = create_box(Panel, BoxInfo),
- wxSizer:add(Sizer, Box, [{flag, ?wxEXPAND bor ?wxALL},
- {border, 5}])
- end,
- [Add(I) || I <- Info],
- wxSizer:addSpacer(Sizer, 5),
- wxWindow:setSizerAndFit(Panel, Sizer),
- Sizer.
-
-create_box(Panel, {Title, Info}) ->
- Box = wxStaticBoxSizer:new(?wxHORIZONTAL, Panel, [{label, Title}]),
- Left = wxBoxSizer:new(?wxVERTICAL),
- Right = wxBoxSizer:new(?wxVERTICAL),
- Expand = [{flag, ?wxEXPAND}],
- AddRow = fun({Desc, Value}) ->
- wxSizer:add(Left, wxStaticText:new(Panel, ?wxID_ANY, Desc ++ ":"), Expand),
- wxSizer:add(Right, wxStaticText:new(Panel, ?wxID_ANY, to_str(Value)), Expand)
- end,
- [AddRow(Entry) || Entry <- Info],
- wxSizer:add(Box, Left),
- wxSizer:addSpacer(Box, 10),
- wxSizer:add(Box, Right),
- wxSizer:addSpacer(Box, 30),
- Box.
-
sys_tables() ->
[ac_tab, asn1,
cdv_dump_index_table, cdv_menu_table, cdv_decode_heap_table,
@@ -512,56 +440,8 @@ mnesia_tables() ->
].
handle_error(Foo) ->
- try
- Str = io_lib:format("ERROR: ~s~n",[Foo]),
- display_info(Str)
- catch _:_ ->
- display_info(io_lib:format("ERROR: ~p~n",[Foo]))
- end,
- ok.
-
-display_info(Str) ->
- Dlg = wxMessageDialog:new(wx:null(), Str),
- wxMessageDialog:showModal(Dlg),
- wxMessageDialog:destroy(Dlg),
- ok.
-
-interval_dialog(Parent, Enabled, Value, Min, Max) ->
- Dialog = wxDialog:new(Parent, ?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),
- Buttons = wxDialog:createButtonSizer(Dialog, ?wxOK bor ?wxCANCEL),
- 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}]),
- wxWindow:setSizerAndFit(Dialog, TopSizer),
- wxSizer:setSizeHints(TopSizer, Dialog),
- wxCheckBox:connect(Check, command_checkbox_clicked,
- [{callback, fun(#wx{event=#wxCommand{commandInt=Enable0}},_) ->
- Enable = Enable0 > 0,
- wxWindow:enable(Slider, [{enable, Enable}])
- end}]),
- Res = case wxDialog:showModal(Dialog) of
- ?wxID_OK ->
- {wxCheckBox:isChecked(Check), wxSlider:getValue(Slider)};
- ?wxID_CANCEL ->
- cancel
- end,
- wxDialog:destroy(Dialog),
- Res.
-
+ Str = io_lib:format("ERROR: ~s~n",[Foo]),
+ observer_lib:display_info_dialog(Str).
update_grid(Grid, Opt, Tables) ->
wx:batch(fun() -> update_grid2(Grid, Opt, Tables) end).
@@ -582,7 +462,7 @@ update_grid2(Grid, #opt{sort_key=Sort,sort_incr=Dir}, Tables) ->
lists:foreach(fun({_, ignore}) -> ignore;
({Col, Val}) ->
- wxListCtrl:setItem(Grid, Row, Col, to_str(Val))
+ wxListCtrl:setItem(Grid, Row, Col, observer_lib:to_str(Val))
end,
[{0,Name}, {1,Id}, {2,Size}, {3, Memory div 1024},
{4,Owner}, {5,RegName}]),
diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl
index b4a85cd43b..ef3c6bb304 100644
--- a/lib/observer/src/observer_wx.erl
+++ b/lib/observer/src/observer_wx.erl
@@ -77,6 +77,7 @@ init(_Args) ->
UpdState = setup(State),
wxFrame:show(Frame),
net_kernel:monitor_nodes(true),
+ process_flag(trap_exit, true),
{Frame, UpdState}.
setup(#state{frame = Frame} = State) ->