%%
%% %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_pro_wx).
-behaviour(wx_object).
-export([start_link/2]).
%% 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]).
-include_lib("wx/include/wx.hrl").
-include_lib("runtime_tools/include/observer_backend.hrl").
-include("observer_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_MSG, 5).
-define(COL_FUN, 6).
-define(ID_KILL, 201).
-define(ID_PROC, 202).
-define(ID_REFRESH, 203).
-define(ID_REFRESH_INTERVAL, 204).
-define(ID_DUMP_TO_FILE, 205).
-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
%% Records
-record(attrs, {even, odd, deleted, changed, searched}).
-record(holder, {parent,
info,
attrs}).
-record(pro_wx_state, {parent,
etop_monitor,
holder_monitor,
grid,
panel,
popup_menu,
parent_notebook,
trace_options = #trace_options{},
match_specs = [],
refr_timer = false,
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),
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 = wxPanel:new(Notebook, []),
Grid = create_list_box(ProPanel, Holder, Count),
Sizer = wxBoxSizer:new(?wxVERTICAL),
wxSizer:add(Sizer, Grid, [{flag, ?wxEXPAND bor ?wxALL},
{proportion, 1},
{border,4}]),
wxWindow:setSizer(ProPanel, Sizer),
Popup = create_popup_menu(ProPanel),
State = #pro_wx_state{parent = Parent,
grid = Grid,
panel = ProPanel,
popup_menu = Popup,
parent_notebook = Notebook,
tracemenu_opened = false,
holder = Holder},
{ProPanel, State}.
generate_matchspecs() ->
try
StrMs1 = "[{'_', [], [{return_trace}]}].",
StrMs2 = "[{'_', [], [{exception_trace}]}].",
StrMs3 = "[{'_', [], [{message, {caller}}]}].",
StrMs4 = "[{'_', [], [{message, {process_dump}}]}].",
{ok, Tokens1, _} = erl_scan:string(StrMs1),
{ok, Tokens2, _} = erl_scan:string(StrMs2),
{ok, Tokens3, _} = erl_scan:string(StrMs3),
{ok, Tokens4, _} = erl_scan:string(StrMs4),
{ok, Term1} = erl_parse:parse_term(Tokens1),
{ok, Term2} = erl_parse:parse_term(Tokens2),
{ok, Term3} = erl_parse:parse_term(Tokens3),
{ok, Term4} = erl_parse:parse_term(Tokens4),
[#match_spec{term_ms = Term1, str_ms = StrMs1},
#match_spec{term_ms = Term2, str_ms = StrMs2},
#match_spec{term_ms = Term3, str_ms = StrMs3},
#match_spec{term_ms = Term4, str_ms = StrMs4}]
catch
_:_ ->
[]
end.
%% UI-creation
create_pro_menu(Parent) ->
MenuEntries = [{"View",
[#create_menu{id = ?ID_REFRESH, text = "Refresh"},
#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"},
#create_menu{id = ?ID_TRACE_ALL_MENU, text = "Trace all processes"}]}
],
observer_wx:create_menus(Parent, MenuEntries).
create_popup_menu(ParentFrame) ->
MiniFrame = wxMiniFrame:new(ParentFrame, ?wxID_ANY, "Options", [{style, ?wxFRAME_FLOAT_ON_PARENT}]),
Panel = wxPanel:new(MiniFrame),
Sizer = wxBoxSizer:new(?wxVERTICAL),
TraceBtn = wxButton:new(Panel, ?ID_TRACEMENU, [{label, "Trace selected"},
{style, ?wxNO_BORDER}]),
ProcBtn = wxButton:new(Panel, ?ID_PROC, [{label, "Process info"},
{style, ?wxNO_BORDER}]),
KillBtn = wxButton:new(Panel, ?ID_KILL, [{label, "Kill process"},
{style, ?wxNO_BORDER}]),
wxButton:connect(TraceBtn, command_button_clicked),
wxButton:connect(ProcBtn, command_button_clicked),
wxButton:connect(KillBtn, command_button_clicked),
wxSizer:add(Sizer, TraceBtn, [{flag, ?wxEXPAND}, {proportion, 1}]),
wxSizer:add(Sizer, ProcBtn, [{flag, ?wxEXPAND}, {proportion, 1}]),
wxSizer:add(Sizer, KillBtn, [{flag, ?wxEXPAND}, {proportion, 1}]),
wxPanel:setSizer(Panel, Sizer),
wxSizer:setSizeHints(Sizer, MiniFrame),
MiniFrame.
create_list_box(Panel, Holder, Count) ->
Style = ?wxLC_REPORT bor ?wxLC_VIRTUAL,
ListCtrl = wxListCtrl:new(Panel, [{style, Style},
{onGetItemText,
fun(_, Item, Col) -> get_row(Holder, Item, Col) end},
{onGetItemAttr,
fun(_, Item) -> get_attr(Holder, Item) end}
]),
Li = wxListItem:new(),
AddListEntry = fun({Name, Align, DefSize}, Col) ->
wxListItem:setText(Li, Name),
wxListItem:setAlign(Li, Align),
wxListCtrl:insertColumn(ListCtrl, Col, Li),
wxListCtrl:setColumnWidth(ListCtrl, Col, DefSize),
Col + 1
end,
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},
{"Current Function", ?wxLIST_FORMAT_LEFT, 200}],
lists:foldl(AddListEntry, 0, ListItems),
wxListItem:destroy(Li),
wxListCtrl:connect(ListCtrl, size, [{skip, true}]),
wxListCtrl:connect(ListCtrl, command_list_item_activated),
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),
wxListCtrl:setItemState(Grid, I, 0, ?wxLIST_STATE_FOCUSED)
end,
lists:seq(0, wxListCtrl:getItemCount(Grid))).
set_selected_items(Grid, Holder, Pids) ->
Count = wxListCtrl:getItemCount(Grid),
set_selected_items(Grid, Holder, 0, Pids, Count, []).
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),
case lists:member(Pid, Pids) of
true ->
wxListCtrl:setItemState(Grid, Index,
?wxLIST_STATE_SELECTED,
?wxLIST_STATE_SELECTED),
set_selected_items(Grid, Holder, Index+1, lists:delete(Pid, Pids), Max, [Pid | Acc]);
false ->
set_selected_items(Grid, Holder, Index+1, Pids, Max, Acc)
end.
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}]),
case Item of
-1 ->
lists:reverse(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),
#attrs{even = wx:typeCast(wx:null(), wxListItemAttr),
odd = wxListItemAttr:new(Text, {240,240,255}, Font),
searched = wxListItemAttr:new(Text, {235,215,90}, Font)
}.
dump_to_file(Parent, FileName, Holder) ->
case file:open(FileName, [write]) of
{ok, Fd} ->
Holder ! {dump, Fd};
{error, Reason} ->
FailMsg = file:format_error(Reason),
MD = wxMessageDialog:new(Parent, FailMsg),
wxDialog:showModal(MD),
wxDialog:destroy(MD)
end.
start_procinfo(Node, Pid, Frame, Opened) ->
case lists:member(Pid, Opened) of
true ->
Opened;
false ->
observer_procinfo:start(Node, Pid, Frame, self()),
[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) ->
Ref = erlang:monitor(process, Holder),
Holder ! {get_row, self(), Row, Column},
receive
{'DOWN', Ref, _, _, _} -> "";
{Holder, Res} ->
erlang:demonitor(Ref),
Res
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: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),
{noreply, State};
handle_info({tracemenu_closed, TraceOpts, MatchSpecs}, State) ->
{noreply, State#pro_wx_state{tracemenu_opened = false,
trace_options = TraceOpts,
match_specs = MatchSpecs}};
handle_info({procinfo_menu_closed, Pid},
#pro_wx_state{procinfo_menu_pids = Opened} = State) ->
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({node, Node}, #pro_wx_state{holder = Holder, sort_dir = Dir} = State) ->
change_node(Node),
refresh_grid(Holder, Dir),
{noreply, State#pro_wx_state{selected_pids = [],
last_selected = undefined}};
handle_info(Info, State) ->
io:format("~p, ~p, Handled unexpected info: ~p~n", [?MODULE, ?LINE, Info]),
{noreply, State}.
terminate(Reason, #pro_wx_state{holder = Holder}) ->
io:format("~p terminating. Reason: ~p~n", [?MODULE, Reason]),
Holder ! stop,
etop:stop(),
ok.
code_change(_, _, State) ->
{stop, not_yet_implemented, State}.
handle_call(Msg, _From, State) ->
io:format("~p~p: Got Call ~p~n",[?MODULE, ?LINE, Msg]),
{reply, ok, State}.
handle_cast(Msg, State) ->
io:format("~p ~p: Unhandled cast ~p~n", [?MODULE, ?LINE, Msg]),
{noreply, State}.
%%%%%%%%%%%%%%%%%%%%LOOP%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
handle_event(#wx{id = ?ID_DUMP_TO_FILE},
#pro_wx_state{panel = Panel,
holder = Holder} = State) ->
FD = wxFileDialog:new(Panel,
[{style,?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT}]),
case wxFileDialog:showModal(FD) of
?wxID_OK ->
Path = wxFileDialog:getPath(FD),
wxDialog:destroy(FD),
dump_to_file(Panel, Path, Holder);
_ ->
wxDialog:destroy(FD)
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),
{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),
{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;
handle_event(#wx{id = ?ID_KILL}, #pro_wx_state{popup_menu = Pop,
selected_pids = Pids,
last_selected = ToKill} = State) ->
wxWindow:show(Pop, [{show, false}]),
exit(ToKill, kill),
Pids2 = lists:delete(ToKill, Pids),
{noreply, State#pro_wx_state{selected_pids = Pids2, last_selected = undefined}};
handle_event(#wx{id = ?ID_PROC},
#pro_wx_state{panel = Panel,
popup_menu = Pop,
last_selected = Pid,
procinfo_menu_pids = Opened} = State) ->
wxWindow:show(Pop, [{show, false}]),
Node = get_node(),
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,
trace_options = Options,
match_specs = MatchSpecs,
selected_pids = Pids,
tracemenu_opened = false,
panel = Panel} = State) ->
wxWindow:show(Pop, [{show, false}]),
case Pids of
[] ->
observer_wx:create_txt_dialog(Panel, "No selected processes", "Tracer", ?wxICON_EXCLAMATION),
{noreply, State};
Pids ->
Node = get_node(),
observer_trace_wx:start(Node,
Pids,
Options,
MatchSpecs,
Panel,
self()),
{noreply, State#pro_wx_state{tracemenu_opened = true}}
end;
handle_event(#wx{id = ?ID_TRACE_ALL_MENU, event = #wxCommand{type = command_menu_selected}},
#pro_wx_state{trace_options = Options,
match_specs = MatchSpecs,
tracemenu_opened = false,
panel = Panel} = State) ->
Node = get_node(),
observer_trace_wx:start(Node,
all,
Options,
MatchSpecs,
Panel,
self()),
{noreply, State#pro_wx_state{tracemenu_opened = true}};
handle_event(#wx{id = ?ID_TRACE_NEW_MENU, event = #wxCommand{type = command_menu_selected}},
#pro_wx_state{trace_options = Options,
match_specs = MatchSpecs,
tracemenu_opened = false,
panel = Panel} = State) ->
Node = get_node(),
observer_trace_wx:start(Node,
new,
Options,
MatchSpecs,
Panel,
self()),
{noreply, State#pro_wx_state{tracemenu_opened = true}};
handle_event(#wx{event=#wxSize{size={W,_}}},
#pro_wx_state{grid=Grid} = State) ->
wx:batch(fun() ->
Cols = wxListCtrl:getColumnCount(Grid),
Last = lists:foldl(fun(I, Last) ->
Last - wxListCtrl:getColumnWidth(Grid, I)
end, W-2, lists:seq(0, Cols - 2)),
Size = max(200, Last),
wxListCtrl:setColumnWidth(Grid, Cols-1, Size)
end),
{noreply, State};
handle_event(#wx{event = #wxList{type = command_list_item_right_click,
itemIndex = Row}},
#pro_wx_state{popup_menu = Popup,
holder = Holder} = State) ->
case get_row(Holder, Row, pid) of
{error, undefined} ->
wxWindow:show(Popup, [{show, false}]),
undefined;
{ok, _} ->
wxWindow:move(Popup, wx_misc:getMousePosition()),
wxWindow:show(Popup)
end,
{noreply, State};
handle_event(#wx{event = #wxList{type = command_list_item_selected,
itemIndex = Row}},
#pro_wx_state{grid = Grid,
popup_menu = Pop,
holder = Holder} = State) ->
NewPid = case get_row(Holder, 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)),
{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}};
handle_event(#wx{event = #wxList{type = command_list_item_activated}},
#pro_wx_state{panel = Panel,
procinfo_menu_pids= Opened,
last_selected = Pid} = State) when Pid =/= undefined ->
Node = get_node(),
Opened2 = start_procinfo(Node, Pid, Panel, Opened),
{noreply, State#pro_wx_state{procinfo_menu_pids = Opened2}};
handle_event(Event, State) ->
io:format("~p~p, handle event ~p\n", [?MODULE, ?LINE, Event]),
{noreply, State}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%TABLE HOLDER%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init_table_holder(Parent, Info, Attrs) ->
table_holder(#holder{parent = Parent,
info = Info,
attrs = Attrs}).
table_holder(#holder{parent = Parent,
info = Info,
attrs = Attrs} = S0) ->
receive
{get_row, From, Row, Col} ->
get_row(From, Row, Col, Info#etop_info.procinfo),
table_holder(S0);
{get_attr, From, Row} ->
get_attr(From, Row, Attrs),
table_holder(S0);
{get_pids, From, Indices} ->
get_pids(From, Indices, Info#etop_info.procinfo),
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},
table_holder(S0);
stop ->
ok;
What ->
io:format("Table holder got ~p~n",[What]),
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.
get_pids(From, Indices, ProcInfo) ->
From ! {self(),
[X#etop_proc_info.pid || X <-
[lists:nth(I, ProcInfo) || I <- Indices]]}.
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))}
end,
From ! {self(), Pid};
get_row(From, Row, Col, Info) ->
Data = case Row+1 > length(Info) of
true ->
null;
false ->
ProcInfo = lists:nth(Row+1, Info),
get_procinfo_data(Col, ProcInfo)
end,
From ! {self(), io_lib:format("~p", [Data])}.
get_attr(From, Row, Attrs) ->
Attribute = case Row rem 2 =:= 0 of
true ->
Attrs#attrs.even;
false ->
Attrs#attrs.odd
end,
From ! {self(), Attribute}.