From ffcacd111c61019a502a895b1edcfbc1578ddfa7 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Mon, 3 Oct 2011 14:12:32 +0200 Subject: [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. --- lib/observer/src/Makefile | 3 +- lib/observer/src/observer.erl | 25 ++ lib/observer/src/observer_defs.hrl | 6 - lib/observer/src/observer_lib.erl | 199 ++++++++++ lib/observer/src/observer_pro_wx.erl | 661 ++++++++++----------------------- lib/observer/src/observer_sys.erl | 131 ------- lib/observer/src/observer_sys_wx.erl | 324 +++++++--------- lib/observer/src/observer_tv_table.erl | 18 +- lib/observer/src/observer_tv_wx.erl | 164 ++------ lib/observer/src/observer_wx.erl | 1 + 10 files changed, 586 insertions(+), 946 deletions(-) create mode 100644 lib/observer/src/observer.erl create mode 100644 lib/observer/src/observer_lib.erl delete mode 100644 lib/observer/src/observer_sys.erl (limited to 'lib/observer') 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) -> -- cgit v1.2.3