From 96175f1ebfd109f1268898d68a735ec5a16e9933 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 20 Apr 2016 11:43:08 +0200 Subject: [ttb] Set trace patterns on messages Functions ttb:tpe/2 and ttb:ctpe/1 are added. --- lib/observer/src/ttb.erl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'lib/observer/src') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 4d6eb3ba8d..32c2fad775 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -25,7 +25,8 @@ -export([tracer/0,tracer/1,tracer/2,p/2,stop/0,stop/1,start_trace/4]). -export([get_et_handler/0]). -export([tp/2, tp/3, tp/4, ctp/0, ctp/1, ctp/2, ctp/3, tpl/2, tpl/3, tpl/4, - ctpl/0, ctpl/1, ctpl/2, ctpl/3, ctpg/0, ctpg/1, ctpg/2, ctpg/3]). + ctpl/0, ctpl/1, ctpl/2, ctpl/3, ctpg/0, ctpg/1, ctpg/2, ctpg/3, + tpe/2, ctpe/1]). -export([seq_trigger_ms/0,seq_trigger_ms/1]). -export([write_trace_info/2]). -export([write_config/2,write_config/3,run_config/1,run_config/2,list_config/1]). @@ -479,6 +480,11 @@ tpl(A,B,C,D) -> store(tpl,[A,B,C,ms(D)]), dbg:tpl(A,B,C,ms(D)). +tpe(A,B) -> + ensure_no_overloaded_nodes(), + store(tpe,[A,ms(B)]), + dbg:tpe(A,ms(B)). + ctp() -> store(ctp,[]), dbg:ctp(). @@ -518,6 +524,10 @@ ctpg(A,B,C) -> store(ctpg,[A,B,C]), dbg:ctpg(A,B,C). +ctpe(A) -> + store(ctpe,[A]), + dbg:ctpe(A). + ms(return) -> [{'_',[],[{return_trace}]}]; ms(caller) -> -- cgit v1.2.3 From b41327bb7f006c1379301336b9b9d6c1e077f34e Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 21 Apr 2016 17:28:20 +0200 Subject: [observer] Add functionality in GUI for trace pattern on messages --- lib/observer/src/observer_trace_wx.erl | 53 ++++++++++++++++++++++----- lib/observer/src/observer_traceoptions_wx.erl | 48 +++++++++++++++++------- 2 files changed, 78 insertions(+), 23 deletions(-) (limited to 'lib/observer/src') diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl index 9c0243e4a7..08710ca9a6 100644 --- a/lib/observer/src/observer_trace_wx.erl +++ b/lib/observer/src/observer_trace_wx.erl @@ -112,12 +112,24 @@ create_window(Notebook, ParentPid) -> match_specs=default_matchspecs()}}. default_matchspecs() -> - Ms = [{"Return Trace", [{'_', [], [{return_trace}]}], "fun(_) -> return_trace() end"}, - {"Exception Trace", [{'_', [], [{exception_trace}]}], "fun(_) -> exception_trace() end"}, - {"Message Caller", [{'_', [], [{message,{caller}}]}], "fun(_) -> message(caller()) end"}, - {"Message Dump", [{'_', [], [{message,{process_dump}}]}], "fun(_) -> message(process_dump()) end"}], + [{Key,default_matchspecs(Key)} || Key <- [funcs,send,'receive']]. +default_matchspecs(Key) -> + Ms = get_default_matchspecs(Key), [make_ms(Name,Term,FunStr) || {Name,Term,FunStr} <- Ms]. +get_default_matchspecs(funcs) -> + [{"Skeleton", [{'$1', [], [true]}], "fun(Args) -> true end"}, + {"Return Trace", [{'_', [], [{return_trace}]}], + "fun(_) -> return_trace() end"}, + {"Exception Trace", [{'_', [], [{exception_trace}]}], "fun(_) -> exception_trace() end"}, + {"Message Caller", [{'_', [], [{message,{caller}}]}], "fun(_) -> message(caller()) end"}, + {"Message Dump", [{'_', [], [{message,{process_dump}}]}], "fun(_) -> message(process_dump()) end"}]; +get_default_matchspecs(send) -> + [{"Skeleton", [{['$1','$2'], [], [true]}], "fun([Pid,Msg]) -> true end"}]; +get_default_matchspecs('receive') -> + [{"Skeleton", [{['$1','$2','$3'], [], [true]}], "fun([Node,Pid,Msg]) -> true end"}]. + + create_process_view(Parent) -> Panel = wxPanel:new(Parent), MainSz = wxBoxSizer:new(?wxHORIZONTAL), @@ -192,7 +204,7 @@ create_menues(Parent) -> #create_menu{id = ?SAVE_TRACEOPTS, text = "Save settings"}]}, {"Options", [#create_menu{id = ?TRACE_OUTPUT, text = "Output"}, - #create_menu{id = ?TRACE_DEFMS, text = "Match Specifications"}, + #create_menu{id = ?TRACE_DEFMS, text = "Default Match Specifications for Functions"}, #create_menu{id = ?TRACE_DEFPS, text = "Default Process Options"}]} ], observer_wx:create_menus(Parent, Menus). @@ -378,7 +390,7 @@ handle_event(#wx{id=?TRACE_DEFPS}, #state{panel=Panel, def_trace_opts=PO} = Stat handle_event(#wx{id=?TRACE_DEFMS}, #state{panel=Panel, match_specs=Ms} = State) -> try %% Return selected MS and sends new MS's to us - observer_traceoptions_wx:select_matchspec(self(), Panel, Ms) + observer_traceoptions_wx:select_matchspec(self(), Panel, Ms, funcs) catch _:_ -> cancel end, @@ -391,10 +403,22 @@ handle_event(#wx{id=?EDIT_FUNCS_MS}, #state{panel=Panel, tpatterns=TPs, try [Module] = get_selected_items(Mview, lists:sort(dict:fetch_keys(TPs))), Selected = get_selected_items(LCtrl, dict:fetch(Module, TPs)), - Ms = observer_traceoptions_wx:select_matchspec(self(), Panel, Mss), + Key = case Module of + 'Events' -> + SelectedEvents = [Event || #tpattern{fa=Event} <- Selected], + E1 = hd(SelectedEvents), + case lists:all(fun(E) when E==E1 -> true; (_) -> false end, + SelectedEvents) of + true -> E1; + false -> throw({error,"Can not set match specs for multiple event types"}) + end; + _ -> funcs + end, + Ms = observer_traceoptions_wx:select_matchspec(self(), Panel, Mss, Key), Changed = [TP#tpattern{ms=Ms} || TP <- Selected], {noreply, do_add_patterns({Module, Changed}, State)} - catch _:_ -> + catch {error, Msg} -> + observer_wx:create_txt_dialog(Panel, Msg, "Error", ?wxICON_ERROR), {noreply, State} end; @@ -564,10 +588,17 @@ update_modules_view(Mods, Module, LCtrl) -> update_functions_view(Funcs, LCtrl) -> wxListCtrl:deleteAllItems(LCtrl), - wx:foldl(fun(#tpattern{fa=FA, ms=#match_spec{str=Ms}}, Row) -> + wx:foldl(fun(#tpattern{m=M, fa=FA, ms=#match_spec{str=Ms}}, Row) -> _Item = wxListCtrl:insertItem(LCtrl, Row, ""), ?EVEN(Row) andalso wxListCtrl:setItemBackgroundColour(LCtrl, Row, ?BG_EVEN), - wxListCtrl:setItem(LCtrl, Row, 0, observer_lib:to_str({func,FA})), + FuncStr = + case M of + 'Events' -> + observer_lib:to_str(FA); + _ -> + observer_lib:to_str({func,FA}) + end, + wxListCtrl:setItem(LCtrl, Row, 0, FuncStr), wxListCtrl:setItem(LCtrl, Row, 1, Ms), Row+1 end, 0, Funcs). @@ -695,6 +726,8 @@ setup_tps([First|Rest], Prev) -> setup_tps([], Prev) -> [setup_tp(TP) || TP <- lists:reverse(Prev)]. +setup_tp(#tpattern{m='Events',fa=Event, ms=#match_spec{term=Ms}}) -> + ttb:tpe(Event,Ms); setup_tp(#tpattern{m=M,fa={F,A}, ms=#match_spec{term=Ms}}) -> ttb:tpl(M,F,A,Ms). diff --git a/lib/observer/src/observer_traceoptions_wx.erl b/lib/observer/src/observer_traceoptions_wx.erl index 9ba9b72b6f..f9b4929ec4 100644 --- a/lib/observer/src/observer_traceoptions_wx.erl +++ b/lib/observer/src/observer_traceoptions_wx.erl @@ -23,7 +23,7 @@ -include("observer_defs.hrl"). -export([process_trace/2, trace_pattern/4, select_nodes/2, - output/2, select_matchspec/3]). + output/2, select_matchspec/4]). process_trace(Parent, Default) -> Dialog = wxDialog:new(Parent, ?wxID_ANY, "Process Options", @@ -100,10 +100,17 @@ process_trace(Parent, Default) -> trace_pattern(ParentPid, Parent, Node, MatchSpecs) -> try - Module = module_selector(Parent, Node), - MFAs = function_selector(Parent, Node, Module), - MatchSpec = select_matchspec(ParentPid, Parent, MatchSpecs), - {Module, [#tpattern{m=M,fa={F,A},ms=MatchSpec} || {M,F,A} <- MFAs]} + {Module,MFAs,MatchSpec} = + case module_selector(Parent, Node) of + {'$trace_event',Event} -> + MS = select_matchspec(ParentPid, Parent, MatchSpecs, Event), + {'Events',[{'Events',Event}],MS}; + Mod -> + MFAs0 = function_selector(Parent, Node, Mod), + MS = select_matchspec(ParentPid, Parent, MatchSpecs, funcs), + {Mod,MFAs0,MS} + end, + {Module, [#tpattern{m=M,fa=FA,ms=MatchSpec} || {M,FA} <- MFAs]} catch cancel -> cancel end. @@ -112,7 +119,7 @@ select_nodes(Parent, Nodes) -> check_selector(Parent, Choices). module_selector(Parent, Node) -> - Dialog = wxDialog:new(Parent, ?wxID_ANY, "Select Module", + Dialog = wxDialog:new(Parent, ?wxID_ANY, "Select Module or Event", [{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER}, {size, {400, 400}}]), Panel = wxPanel:new(Dialog), @@ -136,7 +143,9 @@ module_selector(Parent, Node) -> wxWindow:setFocus(TxtCtrl), %% init data Modules = get_modules(Node), - AllModules = [{atom_to_list(X), X} || X <- Modules], + Events = [{"Messages sent",{'$trace_event',send}}, + {"Messages received",{'$trace_event','receive'}}], + AllModules = Events ++ [{atom_to_list(X), X} || X <- Modules], filter_listbox_data("", AllModules, ListBox), wxTextCtrl:connect(TxtCtrl, command_text_updated, [{callback, fun(#wx{event=#wxCommand{cmdString=Input}}, _) -> @@ -174,9 +183,9 @@ function_selector(Parent, Node, Module) -> not(erl_internal:guard_bif(Name, Arity))]), ParsedChoices = parse_function_names(Choices), case check_selector(Parent, ParsedChoices) of - [] -> [{Module, '_', '_'}]; + [] -> [{Module, {'_', '_'}}]; FAs -> - [{Module, F, A} || {F,A} <- FAs] + [{Module, {F, A}} || {F,A} <- FAs] end. check_selector(Parent, ParsedChoices) -> @@ -268,7 +277,12 @@ get_checked(ListBox, Acc) -> lists:reverse(Acc) end. -select_matchspec(Pid, Parent, MatchSpecs) -> +select_matchspec(Pid, Parent, AllMatchSpecs, Key) -> + {MatchSpecs,RestMS} = + case lists:keytake(Key,1,AllMatchSpecs) of + {value,{Key,MSs0},Rest} -> {MSs0,Rest}; + false -> {[],AllMatchSpecs} + end, Dialog = wxDialog:new(Parent, ?wxID_ANY, "Trace Match Specifications", [{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER}, {size, {400, 400}}]), @@ -314,7 +328,11 @@ select_matchspec(Pid, Parent, MatchSpecs) -> Add = fun(_,_) -> case edit_ms(TextCtrl, new, Parent) of - Ms = #match_spec{} -> add_and_select(-1, Ms, ListBox); + Ms = #match_spec{} -> + add_and_select(-1, Ms, ListBox), + wxWindow:enable(OkButt), + wxWindow:enable(EditMsBtn), + wxWindow:enable(DelMsBtn); Else -> Else end end, @@ -324,7 +342,11 @@ select_matchspec(Pid, Parent, MatchSpecs) -> true -> #match_spec{name=Name} = wxListBox:getClientData(ListBox,SelId), case edit_ms(TextCtrl, Name, Parent) of - Ms = #match_spec{} -> add_and_select(SelId, Ms, ListBox); + Ms = #match_spec{} -> + add_and_select(SelId, Ms, ListBox), + wxWindow:enable(OkButt), + wxWindow:enable(EditMsBtn), + wxWindow:enable(DelMsBtn); Else -> Else end; false -> @@ -367,7 +389,7 @@ select_matchspec(Pid, Parent, MatchSpecs) -> Count = wxListBox:getCount(ListBox), MSs = [wxListBox:getClientData(ListBox, Id) || Id <- lists:seq(0, Count-1)], - Pid ! {update_ms, MSs}, + Pid ! {update_ms, [{Key,MSs}|RestMS]}, MS = lists:nth(SelId+1, MSs), wxDialog:destroy(Dialog), MS; -- cgit v1.2.3 From eeb094055101971ff94449cff08986126fb6add1 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 27 Apr 2016 12:21:26 +0200 Subject: [observer] Add Ports tab in GUI --- lib/observer/src/Makefile | 1 + lib/observer/src/observer.app.src | 1 + lib/observer/src/observer_port_wx.erl | 396 ++++++++++++++++++++++++++++++++++ lib/observer/src/observer_tv_wx.erl | 1 + lib/observer/src/observer_wx.erl | 30 ++- 5 files changed, 420 insertions(+), 9 deletions(-) create mode 100644 lib/observer/src/observer_port_wx.erl (limited to 'lib/observer/src') diff --git a/lib/observer/src/Makefile b/lib/observer/src/Makefile index 85dc5933c1..dd7831fa2b 100644 --- a/lib/observer/src/Makefile +++ b/lib/observer/src/Makefile @@ -67,6 +67,7 @@ MODULES= \ observer_html_lib \ observer_lib \ observer_perf_wx \ + observer_port_wx \ observer_pro_wx \ observer_procinfo \ observer_sys_wx \ diff --git a/lib/observer/src/observer.app.src b/lib/observer/src/observer.app.src index 5ddf65fa59..3a5bd172e7 100644 --- a/lib/observer/src/observer.app.src +++ b/lib/observer/src/observer.app.src @@ -51,6 +51,7 @@ observer_html_lib, observer_lib, observer_perf_wx, + observer_port_wx, observer_pro_wx, observer_procinfo, observer_sys_wx, diff --git a/lib/observer/src/observer_port_wx.erl b/lib/observer/src/observer_port_wx.erl new file mode 100644 index 0000000000..64502f0d16 --- /dev/null +++ b/lib/observer/src/observer_port_wx.erl @@ -0,0 +1,396 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011-2014. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +-module(observer_port_wx). + +-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_sync_event/3, handle_cast/2]). + +-behaviour(wx_object). +-include_lib("wx/include/wx.hrl"). +-include("observer_defs.hrl"). + +-define(GRID, 300). +-define(ID_REFRESH, 301). +-define(ID_REFRESH_INTERVAL, 302). +-define(ID_PORT_INFO, 303). +-define(ID_TRACE_PORTS, 304). +-define(ID_TRACE_NAMES, 305). +-define(ID_TRACE_NEW, 306). +-define(ID_TRACE_ALL, 307). +-define(ID_CLOSE_PORT, 308). + +-define(TRACE_PORTS_STR, "Trace selected ports"). +-define(TRACE_NAMES_STR, "Trace selected ports, " + "if a process have a registered name " + "processes with same name will be traced on all nodes"). + +-record(port, + {id, + connected, + name, + controls, + slot, + id_str, + links, + monitors}). + +-record(opt, {sort_key=2, + sort_incr=true + }). + +-record(state, + { + parent, + grid, + panel, + node=node(), + opt=#opt{}, + selected, + ports, + timer, + open_wins=[] + }). + +start_link(Notebook, Parent) -> + wx_object:start_link(?MODULE, [Notebook, Parent], []). + +init([Notebook, Parent]) -> + Panel = wxPanel:new(Notebook), + Sizer = wxBoxSizer:new(?wxVERTICAL), + Style = ?wxLC_REPORT bor ?wxLC_SINGLE_SEL bor ?wxLC_HRULES, + Grid = wxListCtrl:new(Panel, [{winid, ?GRID}, {style, Style}]), + wxSizer:add(Sizer, Grid, [{flag, ?wxEXPAND bor ?wxALL}, + {proportion, 1}, {border, 5}]), + wxWindow:setSizer(Panel, Sizer), + Li = wxListItem:new(), + AddListEntry = fun({Name, Align, DefSize}, Col) -> + wxListItem:setText(Li, Name), + wxListItem:setAlign(Li, Align), + wxListCtrl:insertColumn(Grid, Col, Li), + wxListCtrl:setColumnWidth(Grid, Col, DefSize), + Col + 1 + end, + ListItems = [{"Id", ?wxLIST_FORMAT_LEFT, 150}, + {"Connected", ?wxLIST_FORMAT_LEFT, 150}, + {"Name", ?wxLIST_FORMAT_LEFT, 150}, + {"Controls", ?wxLIST_FORMAT_LEFT, 200}, + {"Slot", ?wxLIST_FORMAT_RIGHT, 50}], + lists:foldl(AddListEntry, 0, ListItems), + wxListItem:destroy(Li), + + wxListCtrl:connect(Grid, command_list_item_right_click), + wxListCtrl:connect(Grid, command_list_item_activated), + wxListCtrl:connect(Grid, command_list_item_selected), + wxListCtrl:connect(Grid, command_list_col_click), + wxListCtrl:connect(Grid, size, [{skip, true}]), + + wxWindow:setFocus(Grid), + {Panel, #state{grid=Grid, parent=Parent, panel=Panel, timer={false, 10}}}. + +handle_event(#wx{id=?ID_REFRESH}, + State = #state{node=Node, grid=Grid, opt=Opt}) -> + Ports0 = get_ports(Node), + Ports = update_grid(Grid, Opt, Ports0), + {noreply, State#state{ports=Ports}}; + +handle_event(#wx{obj=Obj, event=#wxClose{}}, #state{open_wins=Opened} = State) -> + NewOpened = + case lists:keytake(Obj,2,Opened) of + false -> Opened; + {value,_,Rest} -> Rest + end, + {noreply, State#state{open_wins=NewOpened}}; + +handle_event(#wx{event=#wxList{type=command_list_col_click, col=Col}}, + State = #state{node=Node, grid=Grid, + opt=Opt0=#opt{sort_key=Key, sort_incr=Bool}}) -> + Opt = case Col+2 of + Key -> Opt0#opt{sort_incr=not Bool}; + NewKey -> Opt0#opt{sort_key=NewKey} + end, + Ports0 = get_ports(Node), + Ports = update_grid(Grid, Opt, Ports0), + wxWindow:setFocus(Grid), + {noreply, State#state{opt=Opt, ports=Ports}}; + +handle_event(#wx{event=#wxSize{size={W,_}}}, State=#state{grid=Grid}) -> + observer_lib:set_listctrl_col_size(Grid, W), + {noreply, State}; + +handle_event(#wx{event=#wxList{type=command_list_item_activated, + itemIndex=Index}}, + State=#state{grid=Grid, ports=Ports, open_wins=Opened}) -> + Port = lists:nth(Index+1, Ports), + NewOpened = display_port_info(Grid, Port, Opened), + {noreply, State#state{open_wins=NewOpened}}; + +handle_event(#wx{event=#wxList{type=command_list_item_right_click}}, + State=#state{panel=Panel}) -> + + Menu = wxMenu:new(), + wxMenu:append(Menu, ?ID_PORT_INFO, "Port info"), + wxMenu:append(Menu, ?ID_TRACE_PORTS, "Trace ports", + [{help, ?TRACE_PORTS_STR}]), + wxMenu:append(Menu, ?ID_TRACE_NAMES, "Trace named ports (all nodes)", + [{help, ?TRACE_NAMES_STR}]), + wxMenu:append(Menu, ?ID_CLOSE_PORT, "Close Port"), + wxWindow:popupMenu(Panel, Menu), + wxMenu:destroy(Menu), + {noreply, State}; + +handle_event(#wx{event=#wxList{type=command_list_item_selected, itemIndex=Index}}, + State) -> + {noreply, State#state{selected=Index}}; + +handle_event(#wx{id=?ID_PORT_INFO}, + State = #state{grid=Grid, ports=Ports, + selected=Sel, open_wins=Opened}) -> + case Sel of + undefined -> + {noreply, State}; + R when is_integer(R) -> + Port = lists:nth(Sel+1, Ports), + NewOpened = display_port_info(Grid, Port, Opened), + {noreply, State#state{open_wins = NewOpened}} + end; + +handle_event(#wx{id=?ID_CLOSE_PORT}, State = #state{selected=Sel, ports=Ports}) -> + case Sel of + undefined -> + {noreply, State}; + R when is_integer(R) -> + Port = lists:nth(Sel+1, Ports), + erlang:port_close(Port#port.id), + {noreply, State#state{selected=undefined}} + end; + +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(#wx{event=#wxMouse{type=left_down}, userData=TargetPid}, State) -> + observer ! {open_link, TargetPid}, + {noreply, State}; + +handle_event(#wx{obj=Obj, event=#wxMouse{type=enter_window}}, State) -> + wxTextCtrl:setForegroundColour(Obj,{0,0,100,255}), + {noreply, State}; + +handle_event(#wx{obj=Obj, event=#wxMouse{type=leave_window}}, State) -> + wxTextCtrl:setForegroundColour(Obj,?wxBLUE), + {noreply, State}; + +handle_event(Event, _State) -> + error({unhandled_event, Event}). + +handle_sync_event(_Event, _Obj, _State) -> + ok. + +handle_call(Event, From, _State) -> + error({unhandled_call, Event, From}). + +handle_cast(Event, _State) -> + error({unhandled_cast, Event}). + +handle_info({portinfo_open, PortIdStr}, + State = #state{grid=Grid, ports=Ports, open_wins=Opened}) -> + Port = lists:keyfind(PortIdStr,#port.id_str,Ports), + NewOpened = display_port_info(Grid, Port, Opened), + {noreply, State#state{open_wins = NewOpened}}; + +handle_info(refresh_interval, State = #state{node=Node, grid=Grid, opt=Opt, + ports=OldPorts}) -> + case get_ports(Node) of + OldPorts -> + %% no change + {noreply, State}; + Ports0 -> + Ports = update_grid(Grid, Opt, Ports0), + {noreply, State#state{ports=Ports}} + end; + +handle_info({active, Node}, State = #state{parent=Parent, grid=Grid, opt=Opt, + timer=Timer0}) -> + Ports0 = get_ports(Node), + Ports = update_grid(Grid, Opt, Ports0), + wxWindow:setFocus(Grid), + create_menus(Parent), + Timer = observer_lib:start_timer(Timer0), + {noreply, State#state{node=Node, ports=Ports, timer=Timer}}; + +handle_info(not_active, State = #state{timer = Timer0}) -> + Timer = observer_lib:stop_timer(Timer0), + {noreply, State#state{timer=Timer}}; + +handle_info({error, Error}, State) -> + handle_error(Error), + {noreply, State}; + +handle_info(_Event, State) -> + {noreply, State}. + +terminate(_Event, _State) -> + ok. + +code_change(_, _, State) -> + State. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +create_menus(Parent) -> + MenuEntries = + [{"View", + [#create_menu{id = ?ID_PORT_INFO, text = "Port information\tCtrl-I"}, + separator, + #create_menu{id = ?ID_REFRESH, text = "Refresh\tCtrl-R"}, + #create_menu{id = ?ID_REFRESH_INTERVAL, text = "Refresh Interval..."} + ]}], + observer_wx:create_menus(Parent, MenuEntries). + +get_ports(Node) -> + case get_ports2(Node) of + Error = {error, _} -> + self() ! Error, + []; + Res -> + Res + end. +get_ports2(Node) -> + case rpc:call(Node, observer_backend, get_port_list, []) of + {badrpc, Error} -> + {error, Error}; + Error = {error, _} -> + Error; + Result -> + [list_to_portrec(Port) || Port <- Result] + end. + +list_to_portrec(PL) -> + %% PortInfo: + %% {registered_name, RegisteredName :: atom()} | + %% {id, Index :: integer() >= 0} | + %% {connected, Pid :: pid()} | + %% {links, Pids :: [pid()]} | + %% {name, String :: string()} | + %% {input, Bytes :: integer() >= 0} | + %% {output, Bytes :: integer() >= 0} | + %% {os_pid, OsPid :: integer() >= 0 | undefined}, + PortId = proplists:get_value(port_id, PL), + #port{id = PortId, + id_str = erlang:port_to_list(PortId), + slot = proplists:get_value(id, PL), + connected = proplists:get_value(connected, PL), + links = proplists:get_value(links, PL, ignore), + name = proplists:get_value(registered_name, PL, ignore), + monitors = proplists:get_value(monitors, PL, ignore), + controls = proplists:get_value(name, PL)}. + +portrec_to_list(#port{id = Id, + slot = Slot, + connected = Connected, + links = Links, + name = Name, + monitors = Monitors, + controls = Controls}) -> + [{id,Id}, + {slot,Slot}, + {connected,Connected}, + {links,Links}, + {name,Name}, + {monitors,Monitors}, + {controls,Controls}]. + +display_port_info(Parent, PortRec, Opened) -> + PortIdStr = PortRec#port.id_str, + case lists:keyfind(PortIdStr,1,Opened) of + false -> + Frame = do_display_port_info(Parent, PortRec), + [{PortIdStr,Frame}|Opened]; + {_,Win} -> + wxFrame:raise(Win), + Opened + end. + +do_display_port_info(Parent0, PortRec) -> + Parent = observer_lib:get_wx_parent(Parent0), + Title = "Port Info: " ++ PortRec#port.id_str, + Frame = wxMiniFrame:new(Parent, ?wxID_ANY, Title, + [{style, ?wxSYSTEM_MENU bor ?wxCAPTION + bor ?wxCLOSE_BOX bor ?wxRESIZE_BORDER}]), + + Port = portrec_to_list(PortRec), + Fields0 = port_info_fields(Port), + {_FPanel, _Sizer, _UpFields} = observer_lib:display_info(Frame, Fields0), + wxFrame:center(Frame), + wxFrame:connect(Frame, close_window, [{skip, true}]), + wxFrame:show(Frame), + Frame. + + +port_info_fields(Port) -> + Struct = + [{"Overview", + [{"Name", name}, + {"Connected", {click,connected}}, + {"Slot", slot}, + {"Controls", controls}]}, + {scroll_boxes, + [{"Links",1,{click,links}}, + {"Monitors",1,{click,monitors}}]}], + observer_lib:fill_info(Struct, Port). + +handle_error(Foo) -> + Str = io_lib:format("ERROR: ~s~n",[Foo]), + observer_lib:display_info_dialog(Str). + +update_grid(Grid, Opt, Ports) -> + wx:batch(fun() -> update_grid2(Grid, Opt, Ports) end). +update_grid2(Grid, #opt{sort_key=Sort,sort_incr=Dir}, Ports) -> + wxListCtrl:deleteAllItems(Grid), + Update = + fun(#port{id = Id, + slot = Slot, + connected = Connected, + name = Name, + controls = Ctrl}, + Row) -> + _Item = wxListCtrl:insertItem(Grid, Row, ""), + if (Row rem 2) =:= 0 -> + wxListCtrl:setItemBackgroundColour(Grid, Row, ?BG_EVEN); + true -> ignore + end, + + lists:foreach(fun({_, ignore}) -> ignore; + ({Col, Val}) -> + wxListCtrl:setItem(Grid, Row, Col, + observer_lib:to_str(Val)) + end, + [{0,Id},{1,Connected},{2,Name},{3,Ctrl},{4,Slot}]), + Row + 1 + end, + PortInfo = case Dir of + false -> lists:reverse(lists:keysort(Sort, Ports)); + true -> lists:keysort(Sort, Ports) + end, + lists:foldl(Update, 0, PortInfo), + PortInfo. diff --git a/lib/observer/src/observer_tv_wx.erl b/lib/observer/src/observer_tv_wx.erl index 1860f2f469..6acaf4fc73 100644 --- a/lib/observer/src/observer_tv_wx.erl +++ b/lib/observer/src/observer_tv_wx.erl @@ -315,6 +315,7 @@ display_table_info(Parent0, Node, Source, Table) -> {_, Sizer, _} = observer_lib:display_info(Frame, [IdInfo,Settings,Memory]), wxSizer:setSizeHints(Sizer, Frame), + wxWindow:setMinSize(Frame, {300, -1}), wxFrame:center(Frame), wxFrame:show(Frame). diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl index eba603eab5..d896da1291 100644 --- a/lib/observer/src/observer_wx.erl +++ b/lib/observer/src/observer_wx.erl @@ -55,6 +55,7 @@ notebook, main_panel, pro_panel, + port_panel, tv_panel, sys_panel, trace_panel, @@ -164,6 +165,10 @@ setup(#state{frame = Frame} = State) -> ProPanel = observer_pro_wx:start_link(Notebook, self()), wxNotebook:addPage(Notebook, ProPanel, "Processes", []), + %% Port Panel + PortPanel = observer_port_wx:start_link(Notebook, self()), + wxNotebook:addPage(Notebook, PortPanel, "Ports", []), + %% Table Viewer Panel TVPanel = observer_tv_wx:start_link(Notebook, self()), wxNotebook:addPage(Notebook, TVPanel, "Table Viewer", []), @@ -187,6 +192,7 @@ setup(#state{frame = Frame} = State) -> status_bar = StatusBar, sys_panel = SysPanel, pro_panel = ProPanel, + port_panel = PortPanel, tv_panel = TVPanel, trace_panel = TracePanel, app_panel = AppPanel, @@ -406,16 +412,21 @@ handle_info({nodedown, Node}, create_txt_dialog(Frame, Msg, "Node down", ?wxICON_EXCLAMATION), {noreply, State3}; -handle_info({open_link, Pid0}, State = #state{pro_panel=ProcViewer, frame=Frame}) -> - Pid = case Pid0 of - [_|_] -> try list_to_pid(Pid0) catch _:_ -> Pid0 end; - _ -> Pid0 +handle_info({open_link, Id0}, State = #state{pro_panel=ProcViewer, + port_panel=PortViewer, + frame=Frame}) -> + Id = case Id0 of + [_|_] -> try list_to_pid(Id0) catch _:_ -> Id0 end; + _ -> Id0 end, %% Forward to process tab - case is_pid(Pid) of - true -> wx_object:get_pid(ProcViewer) ! {procinfo_open, Pid}; - false -> - Msg = io_lib:format("Information about ~p is not available or implemented",[Pid]), + case Id of + Pid when is_pid(Pid) -> + wx_object:get_pid(ProcViewer) ! {procinfo_open, Pid}; + "#Port" ++ _ = Port -> + wx_object:get_pid(PortViewer) ! {portinfo_open, Port}; + _ -> + Msg = io_lib:format("Information about ~p is not available or implemented",[Id]), Info = wxMessageDialog:new(Frame, Msg), wxMessageDialog:showModal(Info), wxMessageDialog:destroy(Info) @@ -513,10 +524,11 @@ check_page_title(Notebook) -> get_active_pid(#state{notebook=Notebook, pro_panel=Pro, sys_panel=Sys, tv_panel=Tv, trace_panel=Trace, app_panel=App, - perf_panel=Perf, allc_panel=Alloc + perf_panel=Perf, allc_panel=Alloc, port_panel=Port }) -> Panel = case check_page_title(Notebook) of "Processes" -> Pro; + "Ports" -> Port; "System" -> Sys; "Table Viewer" -> Tv; ?TRACE_STR -> Trace; -- cgit v1.2.3 From 7c605482cb179dc81b219d239d15c585049b6433 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 27 Apr 2016 14:15:57 +0200 Subject: [observer] Set correct parent in Label dialog The dialog for setting label on match specs in observer had faulty parent. This is now corrected. --- lib/observer/src/observer_traceoptions_wx.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/observer/src') diff --git a/lib/observer/src/observer_traceoptions_wx.erl b/lib/observer/src/observer_traceoptions_wx.erl index f9b4929ec4..8a6d1403a8 100644 --- a/lib/observer/src/observer_traceoptions_wx.erl +++ b/lib/observer/src/observer_traceoptions_wx.erl @@ -327,7 +327,7 @@ select_matchspec(Pid, Parent, AllMatchSpecs, Key) -> filter_listbox_data("", Choices, ListBox), Add = fun(_,_) -> - case edit_ms(TextCtrl, new, Parent) of + case edit_ms(TextCtrl, new, Dialog) of Ms = #match_spec{} -> add_and_select(-1, Ms, ListBox), wxWindow:enable(OkButt), @@ -341,7 +341,7 @@ select_matchspec(Pid, Parent, AllMatchSpecs, Key) -> case SelId >= 0 of true -> #match_spec{name=Name} = wxListBox:getClientData(ListBox,SelId), - case edit_ms(TextCtrl, Name, Parent) of + case edit_ms(TextCtrl, Name, Dialog) of Ms = #match_spec{} -> add_and_select(SelId, Ms, ListBox), wxWindow:enable(OkButt), -- cgit v1.2.3 From 90989e58d9e7f7c6fc3c4b52e4191d66d8ff2a96 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 28 Apr 2016 09:52:48 +0200 Subject: [ttb] Allow setting trace flags on ports --- lib/observer/src/ttb.erl | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) (limited to 'lib/observer/src') diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 32c2fad775..e41f2c36fc 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -398,16 +398,16 @@ arg_list([A1|A],Acc) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Set trace flags on processes -p(Procs0,Flags0) -> +p(ProcsPorts0,Flags0) -> ensure_no_overloaded_nodes(), - store(p,[Procs0,Flags0]), - no_store_p(Procs0,Flags0). -no_store_p(Procs0,Flags0) -> + store(p,[ProcsPorts0,Flags0]), + no_store_p(ProcsPorts0,Flags0). +no_store_p(ProcsPorts0,Flags0) -> case transform_flags(to_list(Flags0)) of {error,Reason} -> {error,Reason}; Flags -> - Procs = procs(Procs0), + ProcsPorts = procs_ports(ProcsPorts0), case lists:foldl(fun(P,{PMatched,Ps}) -> case dbg:p(P,Flags) of {ok,Matched} -> {[{P,Matched}|PMatched],[P|Ps]}; @@ -415,7 +415,7 @@ no_store_p(Procs0,Flags0) -> display_warning(P,Reason), {PMatched,Ps} end - end,{[],[]},Procs) of + end,{[],[]},ProcsPorts) of {[],[]} -> {error, no_match}; {SuccMatched,Succ} -> no_store_write_trace_info(flags,{Succ,Flags}), @@ -430,20 +430,22 @@ transform_flags(Flags) -> dbg:transform_flags([timestamp | Flags]). -procs(Procs) when is_list(Procs) -> - lists:foldl(fun(P,Acc) -> proc(P)++Acc end,[],Procs); -procs(Proc) -> - proc(Proc). +procs_ports(Procs) when is_list(Procs) -> + lists:foldl(fun(P,Acc) -> proc_port(P)++Acc end,[],Procs); +procs_ports(Proc) -> + proc_port(Proc). -proc(Procs) when Procs=:=all; Procs=:=ports; Procs=:=processes; - Procs=:=existing; Procs=:=existing_ports; Procs=:=existing_processes; - Procs=:=new; Procs=:=new_ports; Procs=:=new_processes -> - [Procs]; -proc(Name) when is_atom(Name) -> +proc_port(P) when P=:=all; P=:=ports; P=:=processes; + P=:=existing; P=:=existing_ports; P=:=existing_processes; + P=:=new; P=:=new_ports; P=:=new_processes -> + [P]; +proc_port(Name) when is_atom(Name) -> [Name]; % can be registered on this node or other node -proc(Pid) when is_pid(Pid) -> +proc_port(Pid) when is_pid(Pid) -> [Pid]; -proc({global,Name}) -> +proc_port(Port) when is_port(Port) -> + [Port]; +proc_port({global,Name}) -> case global:whereis_name(Name) of Pid when is_pid(Pid) -> [Pid]; -- cgit v1.2.3 From 7621e4aded2b5e8ad1a04102718c9bce21d4dc4c Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 28 Apr 2016 14:13:33 +0200 Subject: [observer] Add tracing of ports --- lib/observer/src/observer_port_wx.erl | 32 ++ lib/observer/src/observer_pro_wx.erl | 2 +- lib/observer/src/observer_trace_wx.erl | 411 +++++++++++++++++--------- lib/observer/src/observer_traceoptions_wx.erl | 38 ++- 4 files changed, 349 insertions(+), 134 deletions(-) (limited to 'lib/observer/src') diff --git a/lib/observer/src/observer_port_wx.erl b/lib/observer/src/observer_port_wx.erl index 64502f0d16..a09637161f 100644 --- a/lib/observer/src/observer_port_wx.erl +++ b/lib/observer/src/observer_port_wx.erl @@ -183,6 +183,38 @@ handle_event(#wx{id=?ID_CLOSE_PORT}, State = #state{selected=Sel, ports=Ports}) {noreply, State#state{selected=undefined}} end; +handle_event(#wx{id=?ID_TRACE_PORTS}, #state{selected=Sel, ports=Ports}=State) -> + case Sel of + undefined -> + observer_wx:create_txt_dialog(State#state.panel, "No selected port", + "Tracer", ?wxICON_EXCLAMATION), + {noreply, State}; + R when is_integer(R) -> + Port = lists:nth(Sel+1, Ports), + observer_trace_wx:add_ports(observer_wx:get_tracer(), [Port#port.id]), + {noreply, State} + end; + +handle_event(#wx{id=?ID_TRACE_NAMES}, #state{selected=Sel, ports=Ports}=State) -> + case Sel of + undefined -> + observer_wx:create_txt_dialog(State#state.panel, "No selected port", + "Tracer", ?wxICON_EXCLAMATION), + {noreply, State}; + R when is_integer(R) -> + Port = lists:nth(Sel+1, Ports), + IdOrReg = case Port#port.name of + ignore -> Port#port.id; + Name -> Name + end, + observer_trace_wx:add_ports(observer_wx:get_tracer(), [IdOrReg]), + {noreply, State} + end; + +handle_event(#wx{id=?ID_TRACE_NEW, event=#wxCommand{type=command_menu_selected}}, State) -> + observer_trace_wx:add_ports(observer_wx:get_tracer(), [new_ports]), + {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), diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl index bd914cdf65..ca0ddab657 100644 --- a/lib/observer/src/observer_pro_wx.erl +++ b/lib/observer/src/observer_pro_wx.erl @@ -335,7 +335,7 @@ handle_event(#wx{id=?ID_TRACE_NAMES}, #state{sel={SelIds,_Pids}, holder=Holder, end; handle_event(#wx{id=?ID_TRACE_NEW, event=#wxCommand{type=command_menu_selected}}, State) -> - observer_trace_wx:add_processes(observer_wx:get_tracer(), [new]), + observer_trace_wx:add_processes(observer_wx:get_tracer(), [new_processes]), {noreply, State}; handle_event(#wx{event=#wxSize{size={W,_}}}, diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl index 08710ca9a6..9f4a54c304 100644 --- a/lib/observer/src/observer_trace_wx.erl +++ b/lib/observer/src/observer_trace_wx.erl @@ -19,7 +19,7 @@ -module(observer_trace_wx). --export([start_link/2, add_processes/2]). +-export([start_link/2, add_processes/2, add_ports/2]). -export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3, handle_event/2, handle_cast/2]). @@ -31,11 +31,13 @@ -define(SAVE_TRACEOPTS, 305). -define(LOAD_TRACEOPTS, 306). -define(TOGGLE_TRACE, 307). --define(ADD_NEW, 308). --define(ADD_TP, 309). --define(TRACE_OUTPUT, 310). --define(TRACE_DEFMS, 311). --define(TRACE_DEFPS, 312). +-define(ADD_NEW_PROCS, 308). +-define(ADD_NEW_PORTS, 309). +-define(ADD_TP, 310). +-define(TRACE_OUTPUT, 311). +-define(TRACE_DEFMS, 312). +-define(DEF_PROC_OPTS, 313). +-define(DEF_PORT_OPTS, 314). -define(NODES_WIN, 330). -define(ADD_NODES, 331). @@ -45,30 +47,36 @@ -define(EDIT_PROCS, 341). -define(REMOVE_PROCS, 342). --define(MODULES_WIN, 350). +-define(PORT_WIN, 350). +-define(EDIT_PORTS, 351). +-define(REMOVE_PORTS, 352). --define(FUNCS_WIN, 360). --define(EDIT_FUNCS_MS, 361). --define(REMOVE_FUNCS_MS, 362). +-define(MODULES_WIN, 360). --define(LOG_WIN, 370). --define(LOG_SAVE, 321). --define(LOG_CLEAR, 322). +-define(FUNCS_WIN, 370). +-define(EDIT_FUNCS_MS, 371). +-define(REMOVE_FUNCS_MS, 372). + +-define(LOG_WIN, 380). +-define(LOG_SAVE, 381). +-define(LOG_CLEAR, 382). -record(state, {parent, panel, - n_view, p_view, m_view, f_view, %% The listCtrl's + n_view, proc_view, port_view, m_view, f_view, %% The listCtrl's logwin, %% The latest log window nodes = [], toggle_button, - tpids = [], %% #tpid - def_trace_opts = [], + tpids = [], % #titem + tports = [], % #titem + def_proc_flags = [], + def_port_flags = [], output = [], tpatterns = dict:new(), % Key =:= Module::atom, Value =:= {M, F, A, MatchSpec} match_specs = []}). % [ #match_spec{} ] --record(tpid, {pid, opts}). +-record(titem, {id, opts}). start_link(Notebook, ParentPid) -> wx_object:start_link(?MODULE, [Notebook, ParentPid], []). @@ -76,6 +84,9 @@ start_link(Notebook, ParentPid) -> add_processes(Tracer, Pids) when is_list(Pids) -> wx_object:cast(Tracer, {add_processes, Pids}). +add_ports(Tracer, Ports) when is_list(Ports) -> + wx_object:cast(Tracer, {add_ports, Ports}). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init([Notebook, ParentPid]) -> @@ -87,11 +98,13 @@ create_window(Notebook, ParentPid) -> Sizer = wxBoxSizer:new(?wxVERTICAL), Splitter = wxSplitterWindow:new(Panel, [{size, wxWindow:getClientSize(Panel)}, {style, ?SASH_STYLE}]), - {NodeProcView, NodeView, ProcessView} = create_process_view(Splitter), + {NodeProcView, NodeView, ProcessView, PortView} = + create_proc_port_view(Splitter), {MatchSpecView,ModView,FuncView} = create_matchspec_view(Splitter), wxSplitterWindow:setSashGravity(Splitter, 0.5), wxSplitterWindow:setMinimumPaneSize(Splitter,50), - wxSplitterWindow:splitHorizontally(Splitter, NodeProcView, MatchSpecView), + wxSplitterWindow:splitHorizontally(Splitter, NodeProcView, MatchSpecView, + [{sashPosition,368}]), wxSizer:add(Sizer, Splitter, [{flag, ?wxEXPAND bor ?wxALL}, {border, 5}, {proportion, 1}]), %% Buttons Buttons = wxBoxSizer:new(?wxHORIZONTAL), @@ -99,7 +112,8 @@ create_window(Notebook, ParentPid) -> wxSizer:add(Buttons, ToggleButton, [{flag, ?wxALIGN_CENTER_VERTICAL}]), wxSizer:addSpacer(Buttons, 15), wxSizer:add(Buttons, wxButton:new(Panel, ?ADD_NODES, [{label, "Add Nodes"}])), - wxSizer:add(Buttons, wxButton:new(Panel, ?ADD_NEW, [{label, "Add 'new' Process"}])), + wxSizer:add(Buttons, wxButton:new(Panel, ?ADD_NEW_PROCS, [{label, "Add 'new' Processes"}])), + wxSizer:add(Buttons, wxButton:new(Panel, ?ADD_NEW_PORTS, [{label, "Add 'new' Ports"}])), wxSizer:add(Buttons, wxButton:new(Panel, ?ADD_TP, [{label, "Add Trace Pattern"}])), wxMenu:connect(Panel, command_togglebutton_clicked, [{skip, true}]), wxMenu:connect(Panel, command_button_clicked, [{skip, true}]), @@ -107,7 +121,8 @@ create_window(Notebook, ParentPid) -> {border, 5}, {proportion,0}]), wxWindow:setSizer(Panel, Sizer), {Panel, #state{parent=ParentPid, panel=Panel, - n_view=NodeView, p_view=ProcessView, m_view=ModView, f_view=FuncView, + n_view=NodeView, proc_view=ProcessView, port_view=PortView, + m_view=ModView, f_view=FuncView, toggle_button = ToggleButton, match_specs=default_matchspecs()}}. @@ -130,13 +145,15 @@ get_default_matchspecs('receive') -> [{"Skeleton", [{['$1','$2','$3'], [], [true]}], "fun([Node,Pid,Msg]) -> true end"}]. -create_process_view(Parent) -> +create_proc_port_view(Parent) -> Panel = wxPanel:new(Parent), MainSz = wxBoxSizer:new(?wxHORIZONTAL), Style = ?wxLC_REPORT bor ?wxLC_HRULES, Splitter = wxSplitterWindow:new(Panel, [{style, ?SASH_STYLE}]), Nodes = wxListCtrl:new(Splitter, [{winid, ?NODES_WIN}, {style, Style}]), - Procs = wxListCtrl:new(Splitter, [{winid, ?PROC_WIN}, {style, Style}]), + ProcsPortsSplitter = wxSplitterWindow:new(Splitter, [{style, ?SASH_STYLE}]), + Procs = wxListCtrl:new(ProcsPortsSplitter, [{winid,?PROC_WIN},{style,Style}]), + Ports = wxListCtrl:new(ProcsPortsSplitter, [{winid,?PORT_WIN},{style,Style}]), Li = wxListItem:new(), wxListItem:setText(Li, "Nodes"), wxListCtrl:insertColumn(Nodes, 0, Li), @@ -148,24 +165,44 @@ create_process_view(Parent) -> wxListCtrl:setColumnWidth(Procs, Col, DefSize), Col + 1 end, - ListItems = [{"Process Id", ?wxLIST_FORMAT_CENTER, 120}, - {"Trace Options", ?wxLIST_FORMAT_LEFT, 300}], - lists:foldl(AddProc, 0, ListItems), + ProcListItems = [{"Process Id", ?wxLIST_FORMAT_CENTER, 120}, + {"Trace Options", ?wxLIST_FORMAT_LEFT, 300}], + lists:foldl(AddProc, 0, ProcListItems), + + AddPort = fun({Name, Align, DefSize}, Col) -> + wxListItem:setText(Li, Name), + wxListItem:setAlign(Li, Align), + wxListCtrl:insertColumn(Ports, Col, Li), + wxListCtrl:setColumnWidth(Ports, Col, DefSize), + Col + 1 + end, + PortListItems = [{"Port Id", ?wxLIST_FORMAT_CENTER, 120}, + {"Trace Options", ?wxLIST_FORMAT_LEFT, 300}], + lists:foldl(AddPort, 0, PortListItems), + wxListItem:destroy(Li), wxSplitterWindow:setSashGravity(Splitter, 0.0), wxSplitterWindow:setMinimumPaneSize(Splitter,50), - wxSplitterWindow:splitVertically(Splitter, Nodes, Procs, [{sashPosition, 155}]), + wxSplitterWindow:splitVertically(Splitter, Nodes, ProcsPortsSplitter, + [{sashPosition, 155}]), wxSizer:add(MainSz, Splitter, [{flag, ?wxEXPAND}, {proportion, 1}]), + wxSplitterWindow:setSashGravity(ProcsPortsSplitter, 0.5), + wxSplitterWindow:setMinimumPaneSize(ProcsPortsSplitter,50), + wxSplitterWindow:splitHorizontally(ProcsPortsSplitter, Procs, Ports, + [{sashPosition, 182}]), + wxListCtrl:connect(Procs, command_list_item_right_click), + wxListCtrl:connect(Ports, command_list_item_right_click), wxListCtrl:connect(Nodes, command_list_item_right_click), wxListCtrl:connect(Procs, size, [{skip, true}]), + wxListCtrl:connect(Ports, size, [{skip, true}]), wxListCtrl:connect(Nodes, size, [{skip, true}]), wxPanel:setSizer(Panel, MainSz), wxWindow:setFocus(Procs), - {Panel, Nodes, Procs}. + {Panel, Nodes, Procs, Ports}. create_matchspec_view(Parent) -> Panel = wxPanel:new(Parent), @@ -205,7 +242,8 @@ create_menues(Parent) -> {"Options", [#create_menu{id = ?TRACE_OUTPUT, text = "Output"}, #create_menu{id = ?TRACE_DEFMS, text = "Default Match Specifications for Functions"}, - #create_menu{id = ?TRACE_DEFPS, text = "Default Process Options"}]} + #create_menu{id = ?DEF_PROC_OPTS, text = "Default Process Options"}, + #create_menu{id = ?DEF_PORT_OPTS, text = "Default Port Options"}]} ], observer_wx:create_menus(Parent, Menus). @@ -218,11 +256,19 @@ handle_event(#wx{obj=Obj, event=#wxSize{size={W,_}}}, State) -> end, {noreply, State}; -handle_event(#wx{id=?ADD_NEW}, State = #state{panel=Parent, def_trace_opts=TraceOpts}) -> +handle_event(#wx{id=?ADD_NEW_PROCS}, State = #state{panel=Parent, def_proc_flags=TraceOpts}) -> try Opts = observer_traceoptions_wx:process_trace(Parent, TraceOpts), - Process = #tpid{pid=new, opts=Opts}, - {noreply, do_add_processes([Process], State#state{def_trace_opts=Opts})} + Process = #titem{id=new_processes, opts=Opts}, + {noreply, do_add_processes([Process], State#state{def_proc_flags=Opts})} + catch cancel -> {noreply, State} + end; + +handle_event(#wx{id=?ADD_NEW_PORTS}, State = #state{panel=Parent, def_port_flags=TraceOpts}) -> + try + Opts = observer_traceoptions_wx:port_trace(Parent, TraceOpts), + Port = #titem{id=new_ports, opts=Opts}, + {noreply, do_add_ports([Port], State#state{def_port_flags=Opts})} catch cancel -> {noreply, State} end; @@ -249,22 +295,23 @@ handle_event(#wx{event = #wxCommand{type = command_togglebutton_clicked, command #state{panel = Panel, nodes = Nodes, tpids = TProcs, + tports = TPorts, tpatterns = TPs0, toggle_button = ToggleBtn, output = Opts } = State) -> try TPs = dict:to_list(TPs0), - (TProcs == []) andalso throw({error, "No processes traced"}), + (TProcs == []) andalso (TPorts == []) andalso throw({error, "No processes or ports traced"}), (Nodes == []) andalso throw({error, "No nodes traced"}), - HaveCallTrace = fun(#tpid{opts=Os}) -> lists:member(functions,Os) end, + HaveCallTrace = fun(#titem{opts=Os}) -> lists:member(functions,Os) end, WStr = "Call trace actived but no trace patterns used", (TPs == []) andalso lists:any(HaveCallTrace, TProcs) andalso observer_wx:create_txt_dialog(Panel, WStr, "Warning", ?wxICON_WARNING), {TTB, LogWin} = ttb_output_args(Panel, Opts), {ok, _} = ttb:tracer(Nodes, TTB), - setup_ttb(TPs, TProcs), + setup_ttb(TPs, TProcs, TPorts), wxToggleButton:setLabel(ToggleBtn, "Stop Trace"), {noreply, State#state{logwin=LogWin}} catch {error, Msg} -> @@ -314,7 +361,8 @@ handle_event(#wx{id=?LOG_SAVE, userData=TCtrl}, #state{panel=Panel} = State) -> handle_event(#wx{id = ?SAVE_TRACEOPTS}, #state{panel = Panel, - def_trace_opts = TraceOpts, + def_proc_flags = ProcFlags, + def_port_flags = PortFlags, match_specs = MatchSpecs, tpatterns = TracePatterns, output = Output @@ -324,7 +372,7 @@ handle_event(#wx{id = ?SAVE_TRACEOPTS}, ?wxID_OK -> Path = wxFileDialog:getPath(Dialog), write_file(Panel, Path, - TraceOpts, MatchSpecs, Output, + ProcFlags, PortFlags, MatchSpecs, Output, dict:to_list(TracePatterns) ); _ -> @@ -351,6 +399,9 @@ handle_event(#wx{id=Type, event=#wxList{type=command_list_item_right_click}}, ?PROC_WIN -> [{?EDIT_PROCS, "Edit process options"}, {?REMOVE_PROCS, "Remove processes"}]; + ?PORT_WIN -> + [{?EDIT_PORTS, "Edit port options"}, + {?REMOVE_PORTS, "Remove ports"}]; ?FUNCS_WIN -> [{?EDIT_FUNCS_MS, "Edit matchspecs"}, {?REMOVE_FUNCS_MS, "Remove trace patterns"}]; @@ -364,26 +415,50 @@ handle_event(#wx{id=Type, event=#wxList{type=command_list_item_right_click}}, wxMenu:destroy(Menu), {noreply, State}; -handle_event(#wx{id=?EDIT_PROCS}, #state{panel=Panel, tpids=Tpids, p_view=Ps} = State) -> +handle_event(#wx{id=?EDIT_PROCS}, #state{panel=Panel, tpids=Tpids, proc_view=Procs} = State) -> try - [#tpid{opts=DefOpts}|_] = Selected = get_selected_items(Ps, Tpids), + [#titem{opts=DefOpts}|_] = Selected = get_selected_items(Procs, Tpids), Opts = observer_traceoptions_wx:process_trace(Panel, DefOpts), - Changed = [Tpid#tpid{opts=Opts} || Tpid <- Selected], - {noreply, do_add_processes(Changed, State#state{def_trace_opts=Opts})} + Changed = [Tpid#titem{opts=Opts} || Tpid <- Selected], + {noreply, do_add_processes(Changed, State#state{def_proc_flags=Opts})} catch _:_ -> {noreply, State} end; -handle_event(#wx{id=?REMOVE_PROCS}, #state{tpids=Tpids, p_view=LCtrl} = State) -> +handle_event(#wx{id=?REMOVE_PROCS}, #state{tpids=Tpids, proc_view=LCtrl} = State) -> Selected = get_selected_items(LCtrl, Tpids), Pids = Tpids -- Selected, - update_process_view(Pids, LCtrl), + update_p_view(Pids, LCtrl), {noreply, State#state{tpids=Pids}}; -handle_event(#wx{id=?TRACE_DEFPS}, #state{panel=Panel, def_trace_opts=PO} = State) -> +handle_event(#wx{id=?EDIT_PORTS}, #state{panel=Panel, tports=Tports, port_view=Ports} = State) -> + try + [#titem{opts=DefOpts}|_] = Selected = get_selected_items(Ports, Tports), + Opts = observer_traceoptions_wx:port_trace(Panel, DefOpts), + Changed = [Tport#titem{opts=Opts} || Tport <- Selected], + {noreply, do_add_ports(Changed, State#state{def_port_flags=Opts})} + catch _:_ -> + {noreply, State} + end; + +handle_event(#wx{id=?REMOVE_PORTS}, #state{tports=Tports, port_view=LCtrl} = State) -> + Selected = get_selected_items(LCtrl, Tports), + Ports = Tports -- Selected, + update_p_view(Ports, LCtrl), + {noreply, State#state{tports=Ports}}; + +handle_event(#wx{id=?DEF_PROC_OPTS}, #state{panel=Panel, def_proc_flags=PO} = State) -> try Opts = observer_traceoptions_wx:process_trace(Panel, PO), - {noreply, State#state{def_trace_opts=Opts}} + {noreply, State#state{def_proc_flags=Opts}} + catch _:_ -> + {noreply, State} + end; + +handle_event(#wx{id=?DEF_PORT_OPTS}, #state{panel=Panel, def_port_flags=PO} = State) -> + try + Opts = observer_traceoptions_wx:port_trace(Panel, PO), + {noreply, State#state{def_port_flags=Opts}} catch _:_ -> {noreply, State} end; @@ -401,24 +476,34 @@ handle_event(#wx{id=?EDIT_FUNCS_MS}, #state{panel=Panel, tpatterns=TPs, match_specs=Mss } = State) -> try - [Module] = get_selected_items(Mview, lists:sort(dict:fetch_keys(TPs))), - Selected = get_selected_items(LCtrl, dict:fetch(Module, TPs)), - Key = case Module of - 'Events' -> - SelectedEvents = [Event || #tpattern{fa=Event} <- Selected], - E1 = hd(SelectedEvents), - case lists:all(fun(E) when E==E1 -> true; (_) -> false end, - SelectedEvents) of - true -> E1; - false -> throw({error,"Can not set match specs for multiple event types"}) - end; - _ -> funcs - end, - Ms = observer_traceoptions_wx:select_matchspec(self(), Panel, Mss, Key), - Changed = [TP#tpattern{ms=Ms} || TP <- Selected], - {noreply, do_add_patterns({Module, Changed}, State)} + case get_selected_items(Mview, lists:sort(dict:fetch_keys(TPs))) of + [] -> + throw({error,"No module selected"}); + [Module] -> + Selected = get_selected_items(LCtrl, dict:fetch(Module, TPs)), + Key = case Module of + 'Events' -> + SelectedEvents = + [Event || #tpattern{fa=Event} <- Selected], + E1 = hd(SelectedEvents), + case lists:all(fun(E) when E==E1 -> true; + (_) -> false + end, + SelectedEvents) of + true -> E1; + false -> throw({error,"Can not set match specs for multiple event types"}) + end; + _ -> funcs + end, + Ms = observer_traceoptions_wx:select_matchspec(self(), Panel, + Mss, Key), + Changed = [TP#tpattern{ms=Ms} || TP <- Selected], + {noreply, do_add_patterns({Module, Changed}, State)} + end catch {error, Msg} -> observer_wx:create_txt_dialog(Panel, Msg, "Error", ?wxICON_ERROR), + {noreply, State}; + cancel -> {noreply, State} end; @@ -482,11 +567,20 @@ handle_call(Msg, From, _State) -> error({unhandled_call, Msg, From}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -handle_cast({add_processes, Pids}, State = #state{panel=Parent, def_trace_opts=TraceOpts}) -> +handle_cast({add_processes, Pids}, State = #state{panel=Parent, def_proc_flags=TraceOpts}) -> try Opts = observer_traceoptions_wx:process_trace(Parent, TraceOpts), - POpts = [#tpid{pid=Pid, opts=Opts} || Pid <- Pids], - S = do_add_processes(POpts, State#state{def_trace_opts=Opts}), + POpts = [#titem{id=Pid, opts=Opts} || Pid <- Pids], + S = do_add_processes(POpts, State#state{def_proc_flags=Opts}), + {noreply, S} + catch cancel -> + {noreply, State} + end; +handle_cast({add_ports, Ports}, State = #state{panel=Parent, def_port_flags=TraceOpts}) -> + try + Opts = observer_traceoptions_wx:port_trace(Parent, TraceOpts), + POpts = [#titem{id=Id, opts=Opts} || Id <- Ports], + S = do_add_ports(POpts, State#state{def_port_flags=Opts}), {noreply, S} catch cancel -> {noreply, State} @@ -534,13 +628,25 @@ do_add_patterns({Module, NewPs}, State=#state{tpatterns=TPs0, m_view=Mview, f_vi State#state{tpatterns=TPs} end. -do_add_processes(POpts, S0=#state{n_view=Nview, p_view=LCtrl, tpids=OldPids, nodes=Ns0}) -> - case merge_pids(POpts, OldPids) of - {OldPids, [], []} -> - S0; - {Pids, New, _Changed} -> - update_process_view(Pids, LCtrl), - Ns1 = lists:usort([node(Pid) || #tpid{pid=Pid} <- New, is_pid(Pid)]), +do_add_processes(POpts, S0=#state{n_view=Nview, proc_view=LCtrl, tpids=OldPids, nodes=OldNodes}) -> + CheckFun = fun(Pid) -> is_pid(Pid) end, + {Pids, Nodes} = do_add_pid_or_port(POpts, Nview, LCtrl, + OldPids, OldNodes, CheckFun), + S0#state{tpids=Pids, nodes=Nodes}. + +do_add_ports(POpts, S0=#state{n_view=Nview, port_view=LCtrl, tports=OldPorts, nodes=OldNodes}) -> + CheckFun = fun(Port) -> is_port(Port) end, + {Ports, Nodes} = do_add_pid_or_port(POpts, Nview, LCtrl, + OldPorts, OldNodes, CheckFun), + S0#state{tports=Ports, nodes=Nodes}. + +do_add_pid_or_port(POpts, Nview, LCtrl, OldPs, Ns0, Check) -> + case merge_trace_items(POpts, OldPs) of + {OldPs, [], []} -> + {OldPs,Ns0}; + {Ps, New, _Changed} -> + update_p_view(Ps, LCtrl), + Ns1 = lists:usort([node(Id) || #titem{id=Id} <- New, Check(Id)]), Nodes = case ordsets:subtract(Ns1, Ns0) of [] -> Ns0; %% No new Nodes NewNs -> @@ -549,20 +655,21 @@ do_add_processes(POpts, S0=#state{n_view=Nview, p_view=LCtrl, tpids=OldPids, nod update_nodes_view(All, Nview), All end, - S0#state{tpids=Pids, nodes=Nodes} + {Ps, Nodes} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -update_process_view(Pids, LCtrl) -> +update_p_view(PidsOrPorts, LCtrl) -> + %% pid- or port-view wxListCtrl:deleteAllItems(LCtrl), - wx:foldl(fun(#tpid{pid=Pid, opts=Opts}, Row) -> + wx:foldl(fun(#titem{id=Id, opts=Opts}, Row) -> _Item = wxListCtrl:insertItem(LCtrl, Row, ""), ?EVEN(Row) andalso wxListCtrl:setItemBackgroundColour(LCtrl, Row, ?BG_EVEN), - wxListCtrl:setItem(LCtrl, Row, 0, observer_lib:to_str(Pid)), + wxListCtrl:setItem(LCtrl, Row, 0, observer_lib:to_str(Id)), wxListCtrl:setItem(LCtrl, Row, 1, observer_lib:to_str(Opts)), Row+1 - end, 0, Pids). + end, 0, PidsOrPorts). update_nodes_view(Nodes, LCtrl) -> wxListCtrl:deleteAllItems(LCtrl), @@ -604,20 +711,24 @@ update_functions_view(Funcs, LCtrl) -> end, 0, Funcs). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -merge_pids([N1=#tpid{pid=new}|Ns], [N2=#tpid{pid=new}|Old]) -> - {Pids, New, Changed} = merge_pids_1(Ns,Old), - {[N1|Pids], New, [{N2,N2}|Changed]}; -merge_pids([N1=#tpid{pid=new}|Ns], Old) -> - {Pids, New, Changed} = merge_pids_1(Ns,Old), - {[N1|Pids], [N1|New], Changed}; -merge_pids(Ns, [N2=#tpid{pid=new}|Old]) -> - {Pids, New, Changed} = merge_pids_1(Ns,Old), - {[N2|Pids], New, Changed}; -merge_pids(New, Old) -> - merge_pids_1(New, Old). - -merge_pids_1(New, Old) -> - merge(lists:sort(New), Old, #tpid.pid, [], [], []). +%% Trace items are processes and ports +merge_trace_items([N1=#titem{id=NewP}|Ns], [N2=#titem{id=NewP}|Old]) + when NewP==new_processes; NewP==new_ports -> + {Ids, New, Changed} = merge_trace_items_1(Ns,Old), + {[N1|Ids], New, [{N2,N2}|Changed]}; +merge_trace_items([N1=#titem{id=NewP}|Ns], Old) + when NewP==new_processes; NewP==new_ports -> + {Ids, New, Changed} = merge_trace_items_1(Ns,Old), + {[N1|Ids], [N1|New], Changed}; +merge_trace_items(Ns, [N2=#titem{id=NewP}|Old]) + when NewP==new_processes; NewP==new_ports -> + {Ids, New, Changed} = merge_trace_items_1(Ns,Old), + {[N2|Ids], New, Changed}; +merge_trace_items(New, Old) -> + merge_trace_items_1(New, Old). + +merge_trace_items_1(New, Old) -> + merge(lists:sort(New), Old, #titem.id, [], [], []). merge_patterns(New, Old) -> merge(lists:sort(New), Old, #tpattern.fa, [], [], []). @@ -707,10 +818,12 @@ create_logwindow(Parent, true) -> wxFrame:show(LogWin), {LogWin, Text}. -setup_ttb(TPs, TPids) -> +setup_ttb(TPs, TPids, TPorts) -> _R1 = [setup_tps(FTP, []) || {_, FTP} <- TPs], - _R2 = [ttb:p(Pid, dbg_flags(Flags)) || #tpid{pid=Pid, opts=Flags} <- TPids], - [#tpid{pid=_Pid, opts=_Flags}|_] = TPids, + _R2 = [ttb:p(Pid, dbg_flags(proc,Flags)) || + #titem{id=Pid, opts=Flags} <- TPids], + _R3 = [ttb:p(Port, dbg_flags(port,Flags)) || + #titem{id=Port, opts=Flags} <- TPorts], ok. %% Sigh order is important @@ -731,17 +844,18 @@ setup_tp(#tpattern{m='Events',fa=Event, ms=#match_spec{term=Ms}}) -> setup_tp(#tpattern{m=M,fa={F,A}, ms=#match_spec{term=Ms}}) -> ttb:tpl(M,F,A,Ms). -dbg_flags(Flags) -> - [dbg_flag(Flag) || Flag <- Flags]. +dbg_flags(Type,Flags) -> + [dbg_flag(Type,Flag) || Flag <- Flags]. -dbg_flag(send) -> s; -dbg_flag('receive') -> r; -dbg_flag(functions) -> c; -dbg_flag(on_spawn) -> sos; -dbg_flag(on_link) -> sol; -dbg_flag(on_first_spawn) -> sofs; -dbg_flag(on_first_link) -> sofl; -dbg_flag(events) -> p. +dbg_flag(_,send) -> s; +dbg_flag(_,'receive') -> r; +dbg_flag(proc,functions) -> c; +dbg_flag(proc,on_spawn) -> sos; +dbg_flag(proc,on_link) -> sol; +dbg_flag(proc,on_first_spawn) -> sofs; +dbg_flag(proc,on_first_link) -> sofl; +dbg_flag(proc,events) -> p; +dbg_flag(port,events) -> ports. textformat(Trace) when element(1, Trace) == trace_ts, tuple_size(Trace) >= 4 -> format_trace(Trace, tuple_size(Trace)-1, element(tuple_size(Trace),Trace)); @@ -817,23 +931,28 @@ ftup(Trace, Index, Size) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -write_file(Frame, Filename, TraceOps, MatchSpecs, Output, TPs) -> - FormatMS = fun(#match_spec{name=Id, term=T, func=F}) -> - io_lib:format("[{name,\"~s\"}, {term, ~w}, {func, \"~s\"}]", - [Id, T, F]) - end, - FormatTP = fun({Module, FTPs}) -> - List = format_ftp(FTPs, FormatMS), - io_lib:format("{tp, ~w, [~s]}.~n",[Module, List]) +write_file(Frame, Filename, ProcFlags, PortFlags, MatchSpecs, Output, TPs) -> + MSToList = fun(#match_spec{name=Id, term=T, func=F}) -> + [{name,Id},{term,T},{func,F}] end, + MSTermList = [{ms,Key,[MSToList(MS) || MS <- MSs]} || + {Key,MSs} <- MatchSpecs], + TPToTuple = fun(#tpattern{fa={F,A}, ms=Ms}) -> + {F,A,MSToList(Ms)} + end, + ModuleTermList = [{tp, Module, [TPToTuple(FTP) || FTP <- FTPs]} || + {Module,FTPs} <- TPs], + Str = ["%%%\n%%% This file is generated by Observer\n", "%%%\n%%% DO NOT EDIT!\n%%%\n", - [["{ms, ", FormatMS(Ms), "}.\n"] || Ms <- MatchSpecs], - "{traceopts, ", io_lib:format("~w",[TraceOps]) ,"}.\n", - "{output, ", io_lib:format("~w",[Output]) ,"}.\n", - [FormatTP(TP) || TP <- TPs] + [io_lib:format("~p.~n",[MSTerm]) || MSTerm <- MSTermList], + io_lib:format("~p.~n",[{procflags,ProcFlags}]), + io_lib:format("~p.~n",[{portflags,PortFlags}]), + io_lib:format("~p.~n",[{output,Output}]), + [io_lib:format("~p.~n",[ModuleTerm]) || ModuleTerm <- ModuleTermList] ], + case file:write_file(Filename, list_to_binary(Str)) of ok -> success; @@ -842,38 +961,66 @@ write_file(Frame, Filename, TraceOps, MatchSpecs, Output, TPs) -> observer_wx:create_txt_dialog(Frame, FailMsg, "Error", ?wxICON_ERROR) end. -format_ftp([#tpattern{fa={F,A}, ms=Ms}], FormatMS) -> - io_lib:format("{~w, ~w, ~s}", [F,A,FormatMS(Ms)]); -format_ftp([#tpattern{fa={F,A}, ms=Ms}|Rest], FormatMS) -> - [io_lib:format("{~w, ~w, ~s},~n ", [F,A,FormatMS(Ms)]), - format_ftp(Rest, FormatMS)]. - -read_settings(Filename, #state{match_specs=Ms0, def_trace_opts=TO0} = State) -> +read_settings(Filename, #state{match_specs=Ms0, def_proc_flags=ProcFs0, def_port_flags=PortFs0} = State) -> case file:consult(Filename) of {ok, Terms} -> - Ms = lists:usort(Ms0 ++ [parse_ms(MsList) || {ms, MsList} <- Terms]), - TOs = lists:usort(TO0 ++ proplists:get_value(traceopts, Terms, [])), + Ms = parse_ms(Terms, Ms0), + ProcFs1 = proplists:get_value(procflags, Terms, []) ++ + proplists:get_value(traceopts, Terms, []), % for backwards comp. + ProcFs = lists:usort(ProcFs0 ++ ProcFs1), + PortFs = lists:usort(PortFs0 ++ + proplists:get_value(portflags, Terms, [])), Out = proplists:get_value(output, Terms, []), lists:foldl(fun parse_tp/2, - State#state{match_specs=Ms, def_trace_opts=TOs, output=Out}, + State#state{match_specs=Ms, def_proc_flags=ProcFs, + def_port_flags=PortFs, output=Out}, Terms); {error, _} -> - observer_wx:create_txt_dialog(State#state.panel, "Could not load settings", + observer_wx:create_txt_dialog(State#state.panel, + "Could not load settings", "Error", ?wxICON_ERROR), State end. -parse_ms(Opts) -> - Name = proplists:get_value(name, Opts, "TracePattern"), - Term = proplists:get_value(term, Opts, [{'_',[],[ok]}]), - FunStr = proplists:get_value(term, Opts, "fun(_) -> ok end"), - make_ms(Name, Term, FunStr). +parse_ms(Terms, OldMSs) -> + MSs = + case [{Key,[make_ms(MS) || MS <- MSs]} || {ms,Key,MSs} <- Terms] of + [] -> + case [make_ms(MS) || {ms,MS} <- Terms] of + [] -> + []; + FuncMSs -> % for backwards compatibility + [{funcs,FuncMSs}] + end; + KeyMSs -> + KeyMSs + end, + parse_ms_1(MSs, dict:from_list(OldMSs)). + +parse_ms_1([{Key,MSs} | T], Dict) -> + parse_ms_1(T, dict:append_list(Key,MSs,Dict)); +parse_ms_1([],Dict) -> + [{Key,rm_dups(MSs,[])} || {Key,MSs} <- dict:to_list(Dict)]. + +rm_dups([H|T],Acc) -> + case lists:member(H,Acc) of + true -> + rm_dups(T,Acc); + false -> + rm_dups(T,[H|Acc]) + end; +rm_dups([],Acc) -> + lists:reverse(Acc). + +make_ms(MS) -> + [{func,FunStr},{name,Name},{term,Term}] = lists:keysort(1,MS), + make_ms(Name,Term,FunStr). make_ms(Name, Term, FunStr) -> #match_spec{name=Name, term=Term, str=io_lib:format("~w", Term), func = FunStr}. parse_tp({tp, Mod, FAs}, State) -> - Patterns = [#tpattern{m=Mod,fa={F,A}, ms=parse_ms(List)} || + Patterns = [#tpattern{m=Mod,fa={F,A}, ms=make_ms(List)} || {F,A,List} <- FAs], do_add_patterns({Mod, Patterns}, State); parse_tp(_, State) -> diff --git a/lib/observer/src/observer_traceoptions_wx.erl b/lib/observer/src/observer_traceoptions_wx.erl index 8a6d1403a8..e623f90df3 100644 --- a/lib/observer/src/observer_traceoptions_wx.erl +++ b/lib/observer/src/observer_traceoptions_wx.erl @@ -22,7 +22,7 @@ -include_lib("wx/include/wx.hrl"). -include("observer_defs.hrl"). --export([process_trace/2, trace_pattern/4, select_nodes/2, +-export([process_trace/2, port_trace/2, trace_pattern/4, select_nodes/2, output/2, select_matchspec/4]). process_trace(Parent, Default) -> @@ -98,6 +98,42 @@ process_trace(Parent, Default) -> throw(cancel) end. +port_trace(Parent, Default) -> + Dialog = wxDialog:new(Parent, ?wxID_ANY, "Port Options", + [{style, ?wxDEFAULT_DIALOG_STYLE bor ?wxRESIZE_BORDER}]), + Panel = wxPanel:new(Dialog), + MainSz = wxBoxSizer:new(?wxVERTICAL), + OptsSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, "Tracing options"}]), + + SendBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace send message", []), + check_box(SendBox, lists:member(send, Default)), + RecBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace receive message", []), + check_box(RecBox, lists:member('receive', Default)), + EventBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace port events", []), + check_box(EventBox, lists:member(events, Default)), + + [wxSizer:add(OptsSz, CheckBox, []) || CheckBox <- [SendBox,RecBox,EventBox]], + wxSizer:add(OptsSz, 150, -1), + + wxPanel:setSizer(Panel, OptsSz), + wxSizer:add(MainSz, Panel, [{flag, ?wxEXPAND}, {proportion,1}]), + Buttons = wxDialog:createButtonSizer(Dialog, ?wxOK bor ?wxCANCEL), + wxSizer:add(MainSz, Buttons, [{flag, ?wxEXPAND bor ?wxALL}, {border, 5}]), + wxWindow:setSizerAndFit(Dialog, MainSz), + wxSizer:setSizeHints(MainSz, Dialog), + + case wxDialog:showModal(Dialog) of + ?wxID_OK -> + All = [{SendBox, send}, {RecBox, 'receive'}, + {EventBox, events}], + Opts = [Id || {Tick, Id} <- All, wxCheckBox:getValue(Tick)], + wxDialog:destroy(Dialog), + lists:reverse(Opts); + ?wxID_CANCEL -> + wxDialog:destroy(Dialog), + throw(cancel) + end. + trace_pattern(ParentPid, Parent, Node, MatchSpecs) -> try {Module,MFAs,MatchSpec} = -- cgit v1.2.3 From 6dfc55407493278e9875814df054de4df51e7527 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 28 Apr 2016 14:32:25 +0200 Subject: [observer] Add menu option to set default MS for send/receive --- lib/observer/src/observer_trace_wx.erl | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) (limited to 'lib/observer/src') diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl index 9f4a54c304..f5211c8a31 100644 --- a/lib/observer/src/observer_trace_wx.erl +++ b/lib/observer/src/observer_trace_wx.erl @@ -35,9 +35,11 @@ -define(ADD_NEW_PORTS, 309). -define(ADD_TP, 310). -define(TRACE_OUTPUT, 311). --define(TRACE_DEFMS, 312). --define(DEF_PROC_OPTS, 313). --define(DEF_PORT_OPTS, 314). +-define(DEF_MS_FUNCS, 312). +-define(DEF_MS_SEND, 313). +-define(DEF_MS_RECV, 314). +-define(DEF_PROC_OPTS, 315). +-define(DEF_PORT_OPTS, 316). -define(NODES_WIN, 330). -define(ADD_NODES, 331). @@ -241,7 +243,9 @@ create_menues(Parent) -> #create_menu{id = ?SAVE_TRACEOPTS, text = "Save settings"}]}, {"Options", [#create_menu{id = ?TRACE_OUTPUT, text = "Output"}, - #create_menu{id = ?TRACE_DEFMS, text = "Default Match Specifications for Functions"}, + #create_menu{id = ?DEF_MS_FUNCS, text = "Default Match Specifications for Functions"}, + #create_menu{id = ?DEF_MS_SEND, text = "Default Match Specifications for 'send'"}, + #create_menu{id = ?DEF_MS_RECV, text = "Default Match Specifications for 'receive'"}, #create_menu{id = ?DEF_PROC_OPTS, text = "Default Process Options"}, #create_menu{id = ?DEF_PORT_OPTS, text = "Default Port Options"}]} ], @@ -463,7 +467,7 @@ handle_event(#wx{id=?DEF_PORT_OPTS}, #state{panel=Panel, def_port_flags=PO} = St {noreply, State} end; -handle_event(#wx{id=?TRACE_DEFMS}, #state{panel=Panel, match_specs=Ms} = State) -> +handle_event(#wx{id=?DEF_MS_FUNCS}, #state{panel=Panel, match_specs=Ms} = State) -> try %% Return selected MS and sends new MS's to us observer_traceoptions_wx:select_matchspec(self(), Panel, Ms, funcs) catch _:_ -> @@ -471,6 +475,22 @@ handle_event(#wx{id=?TRACE_DEFMS}, #state{panel=Panel, match_specs=Ms} = State) end, {noreply, State}; +handle_event(#wx{id=?DEF_MS_SEND}, #state{panel=Panel, match_specs=Ms} = State) -> + try %% Return selected MS and sends new MS's to us + observer_traceoptions_wx:select_matchspec(self(), Panel, Ms, send) + catch _:_ -> + cancel + end, + {noreply, State}; + +handle_event(#wx{id=?DEF_MS_RECV}, #state{panel=Panel, match_specs=Ms} = State) -> + try %% Return selected MS and sends new MS's to us + observer_traceoptions_wx:select_matchspec(self(), Panel, Ms, 'receive') + catch _:_ -> + cancel + end, + {noreply, State}; + handle_event(#wx{id=?EDIT_FUNCS_MS}, #state{panel=Panel, tpatterns=TPs, f_view=LCtrl, m_view=Mview, match_specs=Mss -- cgit v1.2.3 From 5ddc361d6652fb5aa7a7ac6ad06be95f16f0030d Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 28 Apr 2016 14:46:02 +0200 Subject: [observer] Add right click menu in Table tab --- lib/observer/src/observer_tv_wx.erl | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) (limited to 'lib/observer/src') diff --git a/lib/observer/src/observer_tv_wx.erl b/lib/observer/src/observer_tv_wx.erl index 6acaf4fc73..59f6443551 100644 --- a/lib/observer/src/observer_tv_wx.erl +++ b/lib/observer/src/observer_tv_wx.erl @@ -37,7 +37,8 @@ -define(ID_UNREADABLE, 405). -define(ID_SYSTEM_TABLES, 406). -define(ID_TABLE_INFO, 407). - +-define(ID_SHOW_TABLE, 408). + -record(opt, {type=ets, sys_hidden=true, unread_hidden=true, @@ -49,6 +50,7 @@ { parent, grid, + panel, node=node(), opt=#opt{}, selected, @@ -86,12 +88,13 @@ init([Notebook, Parent]) -> wxListItem:destroy(Li), wxListCtrl:connect(Grid, command_list_item_activated), + wxListCtrl:connect(Grid, command_list_item_right_click), wxListCtrl:connect(Grid, command_list_item_selected), wxListCtrl:connect(Grid, command_list_col_click), wxListCtrl:connect(Grid, size, [{skip, true}]), wxWindow:setFocus(Grid), - {Panel, #state{grid=Grid, parent=Parent, timer={false, 10}}}. + {Panel, #state{grid=Grid, parent=Parent, panel=Panel, timer={false, 10}}}. handle_event(#wx{id=?ID_REFRESH}, State = #state{node=Node, grid=Grid, opt=Opt}) -> @@ -145,6 +148,16 @@ handle_event(#wx{event=#wxList{type=command_list_item_activated, end, {noreply, State}; +handle_event(#wx{event=#wxList{type=command_list_item_right_click}}, + State=#state{panel=Panel}) -> + + Menu = wxMenu:new(), + wxMenu:append(Menu, ?ID_TABLE_INFO, "Table info"), + wxMenu:append(Menu, ?ID_SHOW_TABLE, "Show Table Content"), + wxWindow:popupMenu(Panel, Menu), + wxMenu:destroy(Menu), + {noreply, State}; + handle_event(#wx{event=#wxList{type=command_list_item_selected, itemIndex=Index}}, State) -> {noreply, State#state{selected=Index}}; @@ -160,6 +173,22 @@ handle_event(#wx{id=?ID_TABLE_INFO}, {noreply, State} end; +handle_event(#wx{id=?ID_SHOW_TABLE}, + State=#state{grid=Grid, node=Node, opt=#opt{type=Type}, tabs=Tabs, selected=Sel}) -> + case Sel of + undefined -> + {noreply, State}; + R when is_integer(R) -> + Table = lists:nth(Sel+1, Tabs), + case Table#tab.protection of + private -> + self() ! {error, "Table has 'private' protection and can not be read"}; + _ -> + observer_tv_table:start_link(Grid, [{node,Node}, {type,Type}, {table,Table}]) + end, + {noreply, State} + end; + handle_event(#wx{id=?ID_REFRESH_INTERVAL}, State = #state{grid=Grid, timer=Timer0}) -> Timer = observer_lib:interval_dialog(Grid, Timer0, 10, 5*60), -- cgit v1.2.3 From cd14e151f84926cbdd1243b62ef5478681f56369 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 29 Apr 2016 08:58:27 +0200 Subject: [observer] In Trace tab, show procs/ports for selected node only Earlier, all traced processes and ports would be shown, independent of selected nodes in node view. This is now corrected. --- lib/observer/src/observer_trace_wx.erl | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'lib/observer/src') diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl index f5211c8a31..ac8c6cdc4f 100644 --- a/lib/observer/src/observer_trace_wx.erl +++ b/lib/observer/src/observer_trace_wx.erl @@ -198,6 +198,7 @@ create_proc_port_view(Parent) -> wxListCtrl:connect(Procs, command_list_item_right_click), wxListCtrl:connect(Ports, command_list_item_right_click), wxListCtrl:connect(Nodes, command_list_item_right_click), + wxListCtrl:connect(Nodes, command_list_item_selected), wxListCtrl:connect(Procs, size, [{skip, true}]), wxListCtrl:connect(Ports, size, [{skip, true}]), wxListCtrl:connect(Nodes, size, [{skip, true}]), @@ -295,6 +296,15 @@ handle_event(#wx{id=?MODULES_WIN, event=#wxList{type=command_list_item_selected, update_functions_view(dict:fetch(Module, TPs), Fview), {noreply, State}; +handle_event(#wx{id=?NODES_WIN, + event=#wxList{type=command_list_item_selected, itemIndex=Row}}, + State = #state{tpids=Tpids, tports=Tports, n_view=Nview, + proc_view=ProcView, port_view=PortView}) -> + Node = list_to_atom(wxListCtrl:getItemText(Nview, Row)), + update_p_view(Tpids, ProcView, Node), + update_p_view(Tports, PortView, Node), + {noreply, State}; + handle_event(#wx{event = #wxCommand{type = command_togglebutton_clicked, commandInt = 1}}, #state{panel = Panel, nodes = Nodes, @@ -679,6 +689,13 @@ do_add_pid_or_port(POpts, Nview, LCtrl, OldPs, Ns0, Check) -> end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +update_p_view(PidsOrPorts0, LCtrl, Node) -> + %% Show processes/ports belonging to the given node, + %% and processes/ports traced by name + PidsOrPorts = [P || P <- PidsOrPorts0, + is_atom(P#titem.id) orelse node(P#titem.id)==Node], + update_p_view(PidsOrPorts,LCtrl). + update_p_view(PidsOrPorts, LCtrl) -> %% pid- or port-view wxListCtrl:deleteAllItems(LCtrl), -- cgit v1.2.3 From 7b478eced25af1d3143d232e503723aaf86dfe8a Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 29 Apr 2016 10:02:37 +0200 Subject: [observer] Make right click menu act on the "expected pid" If multiple processes were selcted, "Process Info" and "Kill Process" menu choices would pick the first pid in the selection list, i.e. the last selected process. This is now changed so the process under the mouse pointer is used instead. --- lib/observer/src/observer_pro_wx.erl | 55 ++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 18 deletions(-) (limited to 'lib/observer/src') diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl index ca0ddab657..6296f89e7f 100644 --- a/lib/observer/src/observer_pro_wx.erl +++ b/lib/observer/src/observer_pro_wx.erl @@ -83,6 +83,7 @@ timer, procinfo_menu_pids=[], sel={[], []}, + right_clicked_pid, holder}). start_link(Notebook, Parent) -> @@ -303,13 +304,14 @@ handle_event(#wx{id=?ID_REFRESH_INTERVAL}, Timer = observer_lib:interval_dialog(Panel, Timer0, 1, 5*60), {noreply, State#state{timer=Timer}}; -handle_event(#wx{id=?ID_KILL}, #state{sel={[_|Ids], [ToKill|Pids]}}=State) -> - exit(ToKill, kill), - {noreply, State#state{sel={Ids,Pids}}}; +handle_event(#wx{id=?ID_KILL}, #state{right_clicked_pid=Pid, sel=Sel0}=State) -> + exit(Pid, kill), + Sel = rm_selected(Pid,Sel0), + {noreply, State#state{sel=Sel}}; handle_event(#wx{id=?ID_PROC}, - #state{panel=Panel, sel={_, [Pid|_]},procinfo_menu_pids=Opened}=State) -> + #state{panel=Panel, right_clicked_pid=Pid, procinfo_menu_pids=Opened}=State) -> Opened2 = start_procinfo(Pid, Panel, Opened), {noreply, State#state{procinfo_menu_pids=Opened2}}; @@ -347,20 +349,26 @@ handle_event(#wx{event=#wxList{type=command_list_item_right_click, itemIndex=Row}}, #state{panel=Panel, holder=Holder}=State) -> - case call(Holder, {get_row, self(), Row, pid}) of - {error, undefined} -> - undefined; - {ok, _} -> - Menu = wxMenu:new(), - wxMenu:append(Menu, ?ID_PROC, "Process info"), - wxMenu:append(Menu, ?ID_TRACE_PIDS, "Trace processes", [{help, ?TRACE_PIDS_STR}]), - wxMenu:append(Menu, ?ID_TRACE_NAMES, "Trace named processes (all nodes)", - [{help, ?TRACE_NAMES_STR}]), - wxMenu:append(Menu, ?ID_KILL, "Kill Process"), - wxWindow:popupMenu(Panel, Menu), - wxMenu:destroy(Menu) - end, - {noreply, State}; + Pid = + case call(Holder, {get_row, self(), Row, pid}) of + {error, undefined} -> + undefined; + {ok, P} -> + Menu = wxMenu:new(), + wxMenu:append(Menu, ?ID_PROC, + "Process info for " ++ pid_to_list(P)), + wxMenu:append(Menu, ?ID_TRACE_PIDS, + "Trace selected processes", + [{help, ?TRACE_PIDS_STR}]), + wxMenu:append(Menu, ?ID_TRACE_NAMES, + "Trace selected processes by name (all nodes)", + [{help, ?TRACE_NAMES_STR}]), + wxMenu:append(Menu, ?ID_KILL, "Kill process " ++ pid_to_list(P)), + wxWindow:popupMenu(Panel, Menu), + wxMenu:destroy(Menu), + P + end, + {noreply, State#state{right_clicked_pid=Pid}}; handle_event(#wx{event=#wxList{type=command_list_item_focused, itemIndex=Row}}, @@ -432,6 +440,17 @@ set_focus([Old|_], [New|_], Grid) -> wxListCtrl:setItemState(Grid, Old, 0, ?wxLIST_STATE_FOCUSED), wxListCtrl:setItemState(Grid, New, 16#FFFF, ?wxLIST_STATE_FOCUSED). +rm_selected(Pid, {Ids, Pids}) -> + rm_selected(Pid, Ids, Pids, [], []). + +rm_selected(Pid, [_Id|Ids], [Pid|Pids], AccIds, AccPids) -> + {lists:reverse(AccIds)++Ids,lists:reverse(AccPids)++Pids}; +rm_selected(Pid, [Id|Ids], [OtherPid|Pids], AccIds, AccPids) -> + rm_selected(Pid, Ids, Pids, [Id|AccIds], [OtherPid|AccPids]); +rm_selected(_, [], [], AccIds, AccPids) -> + {lists:reverse(AccIds), lists:reverse(AccPids)}. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%TABLE HOLDER%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init_table_holder(Parent, Attrs) -> -- cgit v1.2.3 From e4c25c20f548c068057547ad2095a4c65afc0d86 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 29 Apr 2016 12:00:23 +0200 Subject: [observer] Allow multiple select in Ports tab This is helpful when selecting ports to be traced. --- lib/observer/src/observer_app_wx.erl | 8 +- lib/observer/src/observer_port_wx.erl | 193 +++++++++++++++++++++------------ lib/observer/src/observer_pro_wx.erl | 6 +- lib/observer/src/observer_trace_wx.erl | 10 +- 4 files changed, 134 insertions(+), 83 deletions(-) (limited to 'lib/observer/src') diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl index cef83037d0..936b2783e2 100644 --- a/lib/observer/src/observer_app_wx.erl +++ b/lib/observer/src/observer_app_wx.erl @@ -221,21 +221,21 @@ handle_event(#wx{id=?ID_PROC_KILL, event=#wxCommand{type=command_menu_selected}} %%% Trace api handle_event(#wx{id=?ID_TRACE_PID, event=#wxCommand{type=command_menu_selected}}, State = #state{sel={Box,_}}) -> - observer_trace_wx:add_processes(observer_wx:get_tracer(), [box_to_pid(Box)]), + observer_trace_wx:add_processes([box_to_pid(Box)]), {noreply, State}; handle_event(#wx{id=?ID_TRACE_NAME, event=#wxCommand{type=command_menu_selected}}, State = #state{sel={Box,_}}) -> - observer_trace_wx:add_processes(observer_wx:get_tracer(), [box_to_reg(Box)]), + observer_trace_wx:add_processes([box_to_reg(Box)]), {noreply, State}; handle_event(#wx{id=?ID_TRACE_TREE_PIDS, event=#wxCommand{type=command_menu_selected}}, State = #state{sel=Sel}) -> Get = fun(Box) -> box_to_pid(Box) end, - observer_trace_wx:add_processes(observer_wx:get_tracer(), tree_map(Sel, Get)), + observer_trace_wx:add_processes(tree_map(Sel, Get)), {noreply, State}; handle_event(#wx{id=?ID_TRACE_TREE_NAMES, event=#wxCommand{type=command_menu_selected}}, State = #state{sel=Sel}) -> Get = fun(Box) -> box_to_reg(Box) end, - observer_trace_wx:add_processes(observer_wx:get_tracer(), tree_map(Sel, Get)), + observer_trace_wx:add_processes(tree_map(Sel, Get)), {noreply, State}; handle_event(Event, _State) -> diff --git a/lib/observer/src/observer_port_wx.erl b/lib/observer/src/observer_port_wx.erl index a09637161f..c8548e176e 100644 --- a/lib/observer/src/observer_port_wx.erl +++ b/lib/observer/src/observer_port_wx.erl @@ -32,11 +32,12 @@ -define(ID_REFRESH, 301). -define(ID_REFRESH_INTERVAL, 302). -define(ID_PORT_INFO, 303). --define(ID_TRACE_PORTS, 304). --define(ID_TRACE_NAMES, 305). --define(ID_TRACE_NEW, 306). --define(ID_TRACE_ALL, 307). --define(ID_CLOSE_PORT, 308). +-define(ID_PORT_INFO_SELECTED, 304). +-define(ID_TRACE_PORTS, 305). +-define(ID_TRACE_NAMES, 306). +-define(ID_TRACE_NEW, 307). +-define(ID_TRACE_ALL, 308). +-define(ID_CLOSE_PORT, 309). -define(TRACE_PORTS_STR, "Trace selected ports"). -define(TRACE_NAMES_STR, "Trace selected ports, " @@ -64,7 +65,7 @@ panel, node=node(), opt=#opt{}, - selected, + right_clicked_port, ports, timer, open_wins=[] @@ -76,7 +77,7 @@ start_link(Notebook, Parent) -> init([Notebook, Parent]) -> Panel = wxPanel:new(Notebook), Sizer = wxBoxSizer:new(?wxVERTICAL), - Style = ?wxLC_REPORT bor ?wxLC_SINGLE_SEL bor ?wxLC_HRULES, + Style = ?wxLC_REPORT bor ?wxLC_HRULES, Grid = wxListCtrl:new(Panel, [{winid, ?GRID}, {style, Style}]), wxSizer:add(Sizer, Grid, [{flag, ?wxEXPAND bor ?wxALL}, {proportion, 1}, {border, 5}]), @@ -99,7 +100,6 @@ init([Notebook, Parent]) -> wxListCtrl:connect(Grid, command_list_item_right_click), wxListCtrl:connect(Grid, command_list_item_activated), - wxListCtrl:connect(Grid, command_list_item_selected), wxListCtrl:connect(Grid, command_list_col_click), wxListCtrl:connect(Grid, size, [{skip, true}]), @@ -143,76 +143,92 @@ handle_event(#wx{event=#wxList{type=command_list_item_activated, NewOpened = display_port_info(Grid, Port, Opened), {noreply, State#state{open_wins=NewOpened}}; -handle_event(#wx{event=#wxList{type=command_list_item_right_click}}, - State=#state{panel=Panel}) -> - - Menu = wxMenu:new(), - wxMenu:append(Menu, ?ID_PORT_INFO, "Port info"), - wxMenu:append(Menu, ?ID_TRACE_PORTS, "Trace ports", - [{help, ?TRACE_PORTS_STR}]), - wxMenu:append(Menu, ?ID_TRACE_NAMES, "Trace named ports (all nodes)", - [{help, ?TRACE_NAMES_STR}]), - wxMenu:append(Menu, ?ID_CLOSE_PORT, "Close Port"), - wxWindow:popupMenu(Panel, Menu), - wxMenu:destroy(Menu), - {noreply, State}; - -handle_event(#wx{event=#wxList{type=command_list_item_selected, itemIndex=Index}}, - State) -> - {noreply, State#state{selected=Index}}; +handle_event(#wx{event=#wxList{type=command_list_item_right_click, + itemIndex=Index}}, + State=#state{panel=Panel, ports=Ports}) -> + case Index of + -1 -> + {noreply, State}; + _ -> + Port = lists:nth(Index+1, Ports), + Menu = wxMenu:new(), + wxMenu:append(Menu, ?ID_PORT_INFO, + "Port info for " ++ erlang:port_to_list(Port#port.id)), + wxMenu:append(Menu, ?ID_TRACE_PORTS, + "Trace selected ports", + [{help, ?TRACE_PORTS_STR}]), + wxMenu:append(Menu, ?ID_TRACE_NAMES, + "Trace selected ports by name (all nodes)", + [{help, ?TRACE_NAMES_STR}]), + wxMenu:append(Menu, ?ID_CLOSE_PORT, + "Close " ++ erlang:port_to_list(Port#port.id)), + wxWindow:popupMenu(Panel, Menu), + wxMenu:destroy(Menu), + {noreply, State#state{right_clicked_port=Port}} + end; handle_event(#wx{id=?ID_PORT_INFO}, - State = #state{grid=Grid, ports=Ports, - selected=Sel, open_wins=Opened}) -> - case Sel of + State = #state{grid=Grid, right_clicked_port=Port, + open_wins=Opened}) -> + case Port of undefined -> {noreply, State}; - R when is_integer(R) -> - Port = lists:nth(Sel+1, Ports), + _ -> NewOpened = display_port_info(Grid, Port, Opened), - {noreply, State#state{open_wins = NewOpened}} + {noreply, State#state{right_clicked_port=undefined, + open_wins=NewOpened}} end; -handle_event(#wx{id=?ID_CLOSE_PORT}, State = #state{selected=Sel, ports=Ports}) -> - case Sel of - undefined -> +handle_event(#wx{id=?ID_PORT_INFO_SELECTED}, + State = #state{grid=Grid, ports=Ports, open_wins=Opened}) -> + case get_selected_items(Grid,Ports) of + [] -> + observer_wx:create_txt_dialog(State#state.panel, "No selected ports", + "Port Info", ?wxICON_EXCLAMATION), {noreply, State}; - R when is_integer(R) -> - Port = lists:nth(Sel+1, Ports), - erlang:port_close(Port#port.id), - {noreply, State#state{selected=undefined}} + Selected -> + NewOpened = lists:foldl(fun(P,O) -> display_port_info(Grid, P, O) end, + Opened, Selected), + {noreply, State#state{open_wins = NewOpened}} end; -handle_event(#wx{id=?ID_TRACE_PORTS}, #state{selected=Sel, ports=Ports}=State) -> - case Sel of +handle_event(#wx{id=?ID_CLOSE_PORT}, State = #state{right_clicked_port=Port}) -> + case Port of undefined -> - observer_wx:create_txt_dialog(State#state.panel, "No selected port", - "Tracer", ?wxICON_EXCLAMATION), {noreply, State}; - R when is_integer(R) -> - Port = lists:nth(Sel+1, Ports), - observer_trace_wx:add_ports(observer_wx:get_tracer(), [Port#port.id]), - {noreply, State} - end; + _ -> + erlang:port_close(Port#port.id), + {noreply, State#state{right_clicked_port=undefined}} + end; + +handle_event(#wx{id=?ID_TRACE_PORTS}, #state{grid=Grid, ports=Ports}=State) -> + case get_selected_items(Grid, Ports) of + [] -> + observer_wx:create_txt_dialog(State#state.panel, "No selected ports", + "Tracer", ?wxICON_EXCLAMATION); + Selected -> + SelectedIds = [Port#port.id || Port <- Selected], + observer_trace_wx:add_ports(SelectedIds) + end, + {noreply, State}; -handle_event(#wx{id=?ID_TRACE_NAMES}, #state{selected=Sel, ports=Ports}=State) -> - case Sel of - undefined -> - observer_wx:create_txt_dialog(State#state.panel, "No selected port", - "Tracer", ?wxICON_EXCLAMATION), - {noreply, State}; - R when is_integer(R) -> - Port = lists:nth(Sel+1, Ports), - IdOrReg = case Port#port.name of - ignore -> Port#port.id; - Name -> Name - end, - observer_trace_wx:add_ports(observer_wx:get_tracer(), [IdOrReg]), - {noreply, State} - end; +handle_event(#wx{id=?ID_TRACE_NAMES}, #state{grid=Grid, ports=Ports}=State) -> + case get_selected_items(Grid, Ports) of + [] -> + observer_wx:create_txt_dialog(State#state.panel, "No selected ports", + "Tracer", ?wxICON_EXCLAMATION); + Selected -> + IdsOrRegs = + [case Port#port.name of + undefined -> Port#port.id; + Name -> Name + end || Port <- Selected], + observer_trace_wx:add_ports(IdsOrRegs) + end, + {noreply, State}; handle_event(#wx{id=?ID_TRACE_NEW, event=#wxCommand{type=command_menu_selected}}, State) -> - observer_trace_wx:add_ports(observer_wx:get_tracer(), [new_ports]), + observer_trace_wx:add_ports([new_ports]), {noreply, State}; handle_event(#wx{id=?ID_REFRESH_INTERVAL}, @@ -292,11 +308,18 @@ code_change(_, _, State) -> create_menus(Parent) -> MenuEntries = [{"View", - [#create_menu{id = ?ID_PORT_INFO, text = "Port information\tCtrl-I"}, + [#create_menu{id = ?ID_PORT_INFO_SELECTED, + text = "Port info for selected ports\tCtrl-I"}, separator, #create_menu{id = ?ID_REFRESH, text = "Refresh\tCtrl-R"}, #create_menu{id = ?ID_REFRESH_INTERVAL, text = "Refresh Interval..."} - ]}], + ]}, + {"Trace", + [#create_menu{id=?ID_TRACE_PORTS, text="Trace selected ports"}, + #create_menu{id=?ID_TRACE_NAMES, text="Trace selected ports by name (all nodes)"}, + #create_menu{id=?ID_TRACE_NEW, text="Trace new ports"} + ]} + ], observer_wx:create_menus(Parent, MenuEntries). get_ports(Node) -> @@ -332,9 +355,9 @@ list_to_portrec(PL) -> id_str = erlang:port_to_list(PortId), slot = proplists:get_value(id, PL), connected = proplists:get_value(connected, PL), - links = proplists:get_value(links, PL, ignore), - name = proplists:get_value(registered_name, PL, ignore), - monitors = proplists:get_value(monitors, PL, ignore), + links = proplists:get_value(links, PL, []), + name = proplists:get_value(registered_name, PL, []), + monitors = proplists:get_value(monitors, PL, []), controls = proplists:get_value(name, PL)}. portrec_to_list(#port{id = Id, @@ -388,9 +411,16 @@ port_info_fields(Port) -> {"Controls", controls}]}, {scroll_boxes, [{"Links",1,{click,links}}, - {"Monitors",1,{click,monitors}}]}], + {"Monitors",1,{click,filter_monitor_info()}}]}], observer_lib:fill_info(Struct, Port). +filter_monitor_info() -> + fun(Data) -> + Ms = proplists:get_value(monitors, Data), + [Pid || {process, Pid} <- Ms] + end. + + handle_error(Foo) -> Str = io_lib:format("ERROR: ~s~n",[Foo]), observer_lib:display_info_dialog(Str). @@ -412,8 +442,7 @@ update_grid2(Grid, #opt{sort_key=Sort,sort_incr=Dir}, Ports) -> true -> ignore end, - lists:foreach(fun({_, ignore}) -> ignore; - ({Col, Val}) -> + lists:foreach(fun({Col, Val}) -> wxListCtrl:setItem(Grid, Row, Col, observer_lib:to_str(Val)) end, @@ -426,3 +455,25 @@ update_grid2(Grid, #opt{sort_key=Sort,sort_incr=Dir}, Ports) -> end, lists:foldl(Update, 0, PortInfo), PortInfo. + + +get_selected_items(Grid, Data) -> + get_indecies(get_selected_items(Grid, -1, []), Data). +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 | ItemAcc]) + end. + +get_indecies(Items, Data) -> + get_indecies(Items, 0, Data). +get_indecies([I|Rest], I, [H|T]) -> + [H|get_indecies(Rest, I+1, T)]; +get_indecies(Rest = [_|_], I, [_|T]) -> + get_indecies(Rest, I+1, T); +get_indecies(_, _, _) -> + []. diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl index 6296f89e7f..ee6829b847 100644 --- a/lib/observer/src/observer_pro_wx.erl +++ b/lib/observer/src/observer_pro_wx.erl @@ -321,7 +321,7 @@ handle_event(#wx{id=?ID_TRACE_PIDS}, #state{sel={_, Pids}, panel=Panel}=State) observer_wx:create_txt_dialog(Panel, "No selected processes", "Tracer", ?wxICON_EXCLAMATION), {noreply, State}; Pids -> - observer_trace_wx:add_processes(observer_wx:get_tracer(), Pids), + observer_trace_wx:add_processes(Pids), {noreply, State} end; @@ -332,12 +332,12 @@ handle_event(#wx{id=?ID_TRACE_NAMES}, #state{sel={SelIds,_Pids}, holder=Holder, {noreply, State}; _ -> PidsOrReg = call(Holder, {get_name_or_pid, self(), SelIds}), - observer_trace_wx:add_processes(observer_wx:get_tracer(), PidsOrReg), + observer_trace_wx:add_processes(PidsOrReg), {noreply, State} end; handle_event(#wx{id=?ID_TRACE_NEW, event=#wxCommand{type=command_menu_selected}}, State) -> - observer_trace_wx:add_processes(observer_wx:get_tracer(), [new_processes]), + observer_trace_wx:add_processes([new_processes]), {noreply, State}; handle_event(#wx{event=#wxSize{size={W,_}}}, diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl index ac8c6cdc4f..dfb4857c0b 100644 --- a/lib/observer/src/observer_trace_wx.erl +++ b/lib/observer/src/observer_trace_wx.erl @@ -19,7 +19,7 @@ -module(observer_trace_wx). --export([start_link/2, add_processes/2, add_ports/2]). +-export([start_link/2, add_processes/1, add_ports/1]). -export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3, handle_event/2, handle_cast/2]). @@ -83,11 +83,11 @@ start_link(Notebook, ParentPid) -> wx_object:start_link(?MODULE, [Notebook, ParentPid], []). -add_processes(Tracer, Pids) when is_list(Pids) -> - wx_object:cast(Tracer, {add_processes, Pids}). +add_processes(Pids) when is_list(Pids) -> + wx_object:cast(observer_wx:get_tracer(), {add_processes, Pids}). -add_ports(Tracer, Ports) when is_list(Ports) -> - wx_object:cast(Tracer, {add_ports, Ports}). +add_ports(Ports) when is_list(Ports) -> + wx_object:cast(observer_wx:get_tracer(), {add_ports, Ports}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -- cgit v1.2.3 From ddbe6a2d2531abfc7c72b07442a585f2649faedd Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 4 May 2016 14:29:23 +0200 Subject: [observer] Improve appearance in Trace tab * Only allow single selection of modules * Add right click menu to remove module * Allow multiple selection of nodes, and show procs/ports for ALL selected nodes * On right click, only show menu items that make sense for the current content and selection * Add tooltips with help text --- lib/observer/src/observer_port_wx.erl | 2 +- lib/observer/src/observer_trace_wx.erl | 219 ++++++++++++++++++++++++++------- 2 files changed, 175 insertions(+), 46 deletions(-) (limited to 'lib/observer/src') diff --git a/lib/observer/src/observer_port_wx.erl b/lib/observer/src/observer_port_wx.erl index c8548e176e..3b788642cc 100644 --- a/lib/observer/src/observer_port_wx.erl +++ b/lib/observer/src/observer_port_wx.erl @@ -220,7 +220,7 @@ handle_event(#wx{id=?ID_TRACE_NAMES}, #state{grid=Grid, ports=Ports}=State) -> Selected -> IdsOrRegs = [case Port#port.name of - undefined -> Port#port.id; + [] -> Port#port.id; Name -> Name end || Port <- Selected], observer_trace_wx:add_ports(IdsOrRegs) diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl index dfb4857c0b..5c34961fc1 100644 --- a/lib/observer/src/observer_trace_wx.erl +++ b/lib/observer/src/observer_trace_wx.erl @@ -54,6 +54,7 @@ -define(REMOVE_PORTS, 352). -define(MODULES_WIN, 360). +-define(REMOVE_MOD_MS, 361). -define(FUNCS_WIN, 370). -define(EDIT_FUNCS_MS, 371). @@ -63,6 +64,13 @@ -define(LOG_SAVE, 381). -define(LOG_CLEAR, 382). +-define(NO_NODES_HELP,"Right click to add nodes"). +-define(NODES_HELP,"Select nodes to see traced processes and ports"). +-define(NO_P_HELP,"Add items from Processes/Ports tab"). +-define(P_HELP,"Select nodes to see traced processes and ports"). +-define(NO_TP_HELP,"Add trace pattern with button below"). +-define(TP_HELP,"Select module to see trace patterns"). + -record(state, {parent, panel, @@ -203,6 +211,10 @@ create_proc_port_view(Parent) -> wxListCtrl:connect(Ports, size, [{skip, true}]), wxListCtrl:connect(Nodes, size, [{skip, true}]), + wxListCtrl:setToolTip(Nodes, ?NO_NODES_HELP), + wxListCtrl:setToolTip(Procs, ?NO_P_HELP), + wxListCtrl:setToolTip(Ports, ?NO_P_HELP), + wxPanel:setSizer(Panel, MainSz), wxWindow:setFocus(Procs), {Panel, Nodes, Procs, Ports}. @@ -212,7 +224,8 @@ create_matchspec_view(Parent) -> MainSz = wxBoxSizer:new(?wxHORIZONTAL), Style = ?wxLC_REPORT bor ?wxLC_HRULES, Splitter = wxSplitterWindow:new(Panel, [{style, ?SASH_STYLE}]), - Modules = wxListCtrl:new(Splitter, [{winid, ?MODULES_WIN}, {style, Style}]), + Modules = wxListCtrl:new(Splitter, [{winid, ?MODULES_WIN}, + {style, Style bor ?wxLC_SINGLE_SEL}]), Funcs = wxListCtrl:new(Splitter, [{winid, ?FUNCS_WIN}, {style, Style}]), Li = wxListItem:new(), @@ -234,7 +247,9 @@ create_matchspec_view(Parent) -> wxListCtrl:connect(Modules, size, [{skip, true}]), wxListCtrl:connect(Funcs, size, [{skip, true}]), wxListCtrl:connect(Modules, command_list_item_selected), + wxListCtrl:connect(Modules, command_list_item_right_click), wxListCtrl:connect(Funcs, command_list_item_right_click), + wxListCtrl:setToolTip(Panel, ?NO_TP_HELP), wxPanel:setSizer(Panel, MainSz), {Panel, Modules, Funcs}. @@ -297,12 +312,12 @@ handle_event(#wx{id=?MODULES_WIN, event=#wxList{type=command_list_item_selected, {noreply, State}; handle_event(#wx{id=?NODES_WIN, - event=#wxList{type=command_list_item_selected, itemIndex=Row}}, + event=#wxList{type=command_list_item_selected}}, State = #state{tpids=Tpids, tports=Tports, n_view=Nview, - proc_view=ProcView, port_view=PortView}) -> - Node = list_to_atom(wxListCtrl:getItemText(Nview, Row)), - update_p_view(Tpids, ProcView, Node), - update_p_view(Tports, PortView, Node), + proc_view=ProcView, port_view=PortView, nodes=Ns}) -> + Nodes = get_selected_items(Nview, Ns), + update_p_view(Tpids, ProcView, Nodes), + update_p_view(Tports, PortView, Nodes), {noreply, State}; handle_event(#wx{event = #wxCommand{type = command_togglebutton_clicked, commandInt = 1}}, @@ -407,26 +422,86 @@ handle_event(#wx{id = ?LOAD_TRACEOPTS}, #state{panel = Panel} = State) -> wxDialog:destroy(Dialog), {noreply, State2}; -handle_event(#wx{id=Type, event=#wxList{type=command_list_item_right_click}}, - State = #state{panel=Panel}) -> - Menus = case Type of - ?PROC_WIN -> - [{?EDIT_PROCS, "Edit process options"}, - {?REMOVE_PROCS, "Remove processes"}]; - ?PORT_WIN -> - [{?EDIT_PORTS, "Edit port options"}, - {?REMOVE_PORTS, "Remove ports"}]; - ?FUNCS_WIN -> - [{?EDIT_FUNCS_MS, "Edit matchspecs"}, - {?REMOVE_FUNCS_MS, "Remove trace patterns"}]; - ?NODES_WIN -> - [{?ADD_NODES, "Trace other nodes"}, - {?REMOVE_NODES, "Remove nodes"}] - end, - Menu = wxMenu:new(), - [wxMenu:append(Menu,Id,Str) || {Id,Str} <- Menus], - wxWindow:popupMenu(Panel, Menu), - wxMenu:destroy(Menu), +handle_event(#wx{id=?PROC_WIN, event=#wxList{type=command_list_item_right_click}}, + State = #state{panel=Panel, proc_view=LCtrl, tpids=Tpids, + n_view=Nview, nodes=Nodes}) -> + case get_visible_ps(Tpids, Nodes, Nview) of + [] -> + ok; + Visible -> + case get_selected_items(LCtrl, Visible) of + [] -> + ok; + _ -> + create_right_click_menu( + Panel, + [{?EDIT_PROCS, "Edit process options"}, + {?REMOVE_PROCS, "Remove processes"}]) + end + end, + {noreply, State}; + +handle_event(#wx{id=?PORT_WIN, event=#wxList{type=command_list_item_right_click}}, + State = #state{panel=Panel, port_view=LCtrl, tports=Tports, + n_view=Nview, nodes=Nodes}) -> + case get_visible_ps(Tports, Nodes, Nview) of + [] -> + ok; + Visible -> + case get_selected_items(LCtrl, Visible) of + [] -> + ok; + _ -> + create_right_click_menu( + Panel, + [{?EDIT_PORTS, "Edit port options"}, + {?REMOVE_PORTS, "Remove ports"}]) + end + end, + {noreply, State}; + +handle_event(#wx{id=?MODULES_WIN,event=#wxList{type=command_list_item_right_click}}, + State = #state{panel=Panel, m_view=Mview, tpatterns=TPs}) -> + case get_selected_items(Mview, lists:sort(dict:fetch_keys(TPs))) of + [] -> + ok; + _ -> + create_right_click_menu( + Panel, + [{?REMOVE_MOD_MS, "Remove trace patterns"}]) + end, + {noreply,State}; + +handle_event(#wx{id=?FUNCS_WIN,event=#wxList{type=command_list_item_right_click}}, + State = #state{panel=Panel, m_view=Mview, f_view=Fview, + tpatterns=TPs}) -> + case get_selected_items(Mview, lists:sort(dict:fetch_keys(TPs))) of + [] -> + ok; + [Module] -> + case get_selected_items(Fview, dict:fetch(Module, TPs)) of + [] -> + ok; + _ -> + create_right_click_menu( + Panel, + [{?EDIT_FUNCS_MS, "Edit matchspecs"}, + {?REMOVE_FUNCS_MS, "Remove trace patterns"}]) + end + end, + {noreply,State}; + +handle_event(#wx{id=?NODES_WIN,event=#wxList{type=command_list_item_right_click}}, + State = #state{panel=Panel, n_view=Nview, nodes=Nodes}) -> + Menu = + case get_selected_items(Nview, Nodes) of + [] -> + [{?ADD_NODES, "Add nodes"}]; + _ -> + [{?ADD_NODES, "Add nodes"}, + {?REMOVE_NODES, "Remove nodes"}] + end, + create_right_click_menu(Panel,Menu), {noreply, State}; handle_event(#wx{id=?EDIT_PROCS}, #state{panel=Panel, tpids=Tpids, proc_view=Procs} = State) -> @@ -439,10 +514,12 @@ handle_event(#wx{id=?EDIT_PROCS}, #state{panel=Panel, tpids=Tpids, proc_view=Pro {noreply, State} end; -handle_event(#wx{id=?REMOVE_PROCS}, #state{tpids=Tpids, proc_view=LCtrl} = State) -> +handle_event(#wx{id=?REMOVE_PROCS}, + #state{tpids=Tpids, proc_view=LCtrl, + n_view=Nview, nodes=Nodes} = State) -> Selected = get_selected_items(LCtrl, Tpids), Pids = Tpids -- Selected, - update_p_view(Pids, LCtrl), + update_p_view(Pids, LCtrl, Nodes, Nview), {noreply, State#state{tpids=Pids}}; handle_event(#wx{id=?EDIT_PORTS}, #state{panel=Panel, tports=Tports, port_view=Ports} = State) -> @@ -455,10 +532,12 @@ handle_event(#wx{id=?EDIT_PORTS}, #state{panel=Panel, tports=Tports, port_view=P {noreply, State} end; -handle_event(#wx{id=?REMOVE_PORTS}, #state{tports=Tports, port_view=LCtrl} = State) -> +handle_event(#wx{id=?REMOVE_PORTS}, + #state{tports=Tports, port_view=LCtrl, + n_view=Nview, nodes=Nodes} = State) -> Selected = get_selected_items(LCtrl, Tports), Ports = Tports -- Selected, - update_p_view(Ports, LCtrl), + update_p_view(Ports, LCtrl, Nodes, Nview), {noreply, State#state{tports=Ports}}; handle_event(#wx{id=?DEF_PROC_OPTS}, #state{panel=Panel, def_proc_flags=PO} = State) -> @@ -556,6 +635,16 @@ handle_event(#wx{id=?REMOVE_FUNCS_MS}, #state{tpatterns=TPs0, f_view=LCtrl, m_vi {noreply, State#state{tpatterns=TPs}} end; +handle_event(#wx{id=?REMOVE_MOD_MS}, #state{tpatterns=TPs0, f_view=LCtrl, m_view=Mview} = State) -> + case get_selected_items(Mview, lists:sort(dict:fetch_keys(TPs0))) of + [] -> {noreply, State}; + [Module] -> + update_functions_view([], LCtrl), + TPs = dict:erase(Module, TPs0), + update_modules_view(lists:sort(dict:fetch_keys(TPs)), Module, Mview), + {noreply, State#state{tpatterns=TPs}} + end; + handle_event(#wx{id=?TRACE_OUTPUT}, #state{panel=Panel, output=Out0} = State) -> try Out = observer_traceoptions_wx:output(Panel, Out0), @@ -675,26 +764,33 @@ do_add_pid_or_port(POpts, Nview, LCtrl, OldPs, Ns0, Check) -> {OldPs, [], []} -> {OldPs,Ns0}; {Ps, New, _Changed} -> - update_p_view(Ps, LCtrl), Ns1 = lists:usort([node(Id) || #titem{id=Id} <- New, Check(Id)]), Nodes = case ordsets:subtract(Ns1, Ns0) of [] -> Ns0; %% No new Nodes - NewNs -> - %% if dynamicly updates add trace patterns for new nodes - All = ordsets:union(NewNs, Ns0), - update_nodes_view(All, Nview), - All + NewNs -> ordsets:union(NewNs, Ns0) end, + update_nodes_view(Nodes, Nview), + update_p_view(Ps, LCtrl, Nodes, Nview), {Ps, Nodes} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -update_p_view(PidsOrPorts0, LCtrl, Node) -> - %% Show processes/ports belonging to the given node, - %% and processes/ports traced by name - PidsOrPorts = [P || P <- PidsOrPorts0, - is_atom(P#titem.id) orelse node(P#titem.id)==Node], - update_p_view(PidsOrPorts,LCtrl). +get_visible_ps(PidsOrPorts, [Node], _Nview) -> + %% If only one node, treat this as selected + get_visible_ps(PidsOrPorts, [Node]); +get_visible_ps(PidsOrPorts, Nodes, Nview) -> + get_visible_ps(PidsOrPorts, get_selected_items(Nview, Nodes)). + +get_visible_ps(PidsOrPorts, Nodes) -> + %% Show pids/ports belonging to the selected nodes only (+ named pids/ports) + [P || P <- PidsOrPorts, + is_atom(P#titem.id) orelse + lists:member(node(P#titem.id),Nodes)]. + +update_p_view(PidsOrPorts, LCtrl, Nodes, Nview) -> + update_p_view(get_visible_ps(PidsOrPorts, Nodes, Nview), LCtrl). +update_p_view(PidsOrPorts, LCtrl, Nodes) -> + update_p_view(get_visible_ps(PidsOrPorts, Nodes), LCtrl). update_p_view(PidsOrPorts, LCtrl) -> %% pid- or port-view @@ -706,17 +802,37 @@ update_p_view(PidsOrPorts, LCtrl) -> wxListCtrl:setItem(LCtrl, Row, 0, observer_lib:to_str(Id)), wxListCtrl:setItem(LCtrl, Row, 1, observer_lib:to_str(Opts)), Row+1 - end, 0, PidsOrPorts). + end, 0, PidsOrPorts), + case PidsOrPorts of + [] -> + wxListCtrl:setToolTip(LCtrl,?NO_P_HELP); + _ -> + wxListCtrl:setToolTip(LCtrl,?P_HELP) + end. update_nodes_view(Nodes, LCtrl) -> + Selected = + case Nodes of + [_] -> Nodes; + _ -> get_selected_items(LCtrl, Nodes) + end, wxListCtrl:deleteAllItems(LCtrl), wx:foldl(fun(Node, Row) -> _Item = wxListCtrl:insertItem(LCtrl, Row, ""), ?EVEN(Row) andalso wxListCtrl:setItemBackgroundColour(LCtrl, Row, ?BG_EVEN), wxListCtrl:setItem(LCtrl, Row, 0, observer_lib:to_str(Node)), + lists:member(Node,Selected) andalso % keep selection + wxListCtrl:setItemState(LCtrl, Row, 16#FFFF, + ?wxLIST_STATE_SELECTED), Row+1 - end, 0, Nodes). + end, 0, Nodes), + case Nodes of + [] -> + wxListCtrl:setToolTip(LCtrl,?NO_NODES_HELP); + _ -> + wxListCtrl:setToolTip(LCtrl,?NODES_HELP) + end. update_modules_view(Mods, Module, LCtrl) -> wxListCtrl:deleteAllItems(LCtrl), @@ -728,7 +844,14 @@ update_modules_view(Mods, Module, LCtrl) -> (Mod =:= Module) andalso wxListCtrl:setItemState(LCtrl, Row, 16#FFFF, ?wxLIST_STATE_SELECTED), Row+1 - end, 0, Mods). + end, 0, Mods), + Parent = wxListCtrl:getParent(LCtrl), + case Mods of + [] -> + wxListCtrl:setToolTip(Parent,?NO_TP_HELP); + _ -> + wxListCtrl:setToolTip(Parent,?TP_HELP) + end. update_functions_view(Funcs, LCtrl) -> wxListCtrl:deleteAllItems(LCtrl), @@ -1083,3 +1206,9 @@ get_indecies(Rest = [_|_], I, [_|T]) -> get_indecies(Rest, I+1, T); get_indecies(_, _, _) -> []. + +create_right_click_menu(Panel,Menus) -> + Menu = wxMenu:new(), + [wxMenu:append(Menu,Id,Str) || {Id,Str} <- Menus], + wxWindow:popupMenu(Panel, Menu), + wxMenu:destroy(Menu). -- cgit v1.2.3 From d70f97d61f632b92b3e29e133487a082d2de04fc Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 12 May 2016 09:49:25 +0200 Subject: [observer] Add more default match specs for messages --- lib/observer/src/observer_trace_wx.erl | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'lib/observer/src') diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl index 5c34961fc1..f104f5bb19 100644 --- a/lib/observer/src/observer_trace_wx.erl +++ b/lib/observer/src/observer_trace_wx.erl @@ -143,16 +143,24 @@ default_matchspecs(Key) -> [make_ms(Name,Term,FunStr) || {Name,Term,FunStr} <- Ms]. get_default_matchspecs(funcs) -> - [{"Skeleton", [{'$1', [], [true]}], "fun(Args) -> true end"}, - {"Return Trace", [{'_', [], [{return_trace}]}], + [{"Return Trace", [{'_', [], [{return_trace}]}], "fun(_) -> return_trace() end"}, - {"Exception Trace", [{'_', [], [{exception_trace}]}], "fun(_) -> exception_trace() end"}, - {"Message Caller", [{'_', [], [{message,{caller}}]}], "fun(_) -> message(caller()) end"}, - {"Message Dump", [{'_', [], [{message,{process_dump}}]}], "fun(_) -> message(process_dump()) end"}]; + {"Exception Trace", [{'_', [], [{exception_trace}]}], + "fun(_) -> exception_trace() end"}, + {"Message Caller", [{'_', [], [{message,{caller}}]}], + "fun(_) -> message(caller()) end"}, + {"Message Dump", [{'_', [], [{message,{process_dump}}]}], + "fun(_) -> message(process_dump()) end"}]; get_default_matchspecs(send) -> - [{"Skeleton", [{['$1','$2'], [], [true]}], "fun([Pid,Msg]) -> true end"}]; + [{"To local node", [{['$1','_'], [{'==',{node,'$1'},{node}}], []}], + "fun([Pid,_]) when node(Pid)==node() ->\n true\nend"}, + {"To remote node", [{['$1','_'], [{'=/=',{node,'$1'},{node}}], []}], + "fun([Pid,_]) when node(Pid)=/=node() ->\n true\nend"}]; get_default_matchspecs('receive') -> - [{"Skeleton", [{['$1','$2','$3'], [], [true]}], "fun([Node,Pid,Msg]) -> true end"}]. + [{"From local node", [{['$1','_','_'], [{'==','$1',{node}}], []}], + "fun([Node,_,_]) when Node==node() ->\n true\nend"}, + {"From remote node", [{['$1','_','_'], [{'=/=','$1',{node}}], []}], + "fun([Node,_,_]) when Node=/=node() ->\n true\nend"}]. create_proc_port_view(Parent) -> -- cgit v1.2.3 From ebd440bfc9eb685606250a82cae352f1ee37dac6 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 12 May 2016 15:40:32 +0200 Subject: [observer] Allow more trace flags on procs/ports from GUI --- lib/observer/src/observer_trace_wx.erl | 3 ++- lib/observer/src/observer_traceoptions_wx.erl | 20 ++++++++++++++++---- lib/observer/src/ttb.erl | 3 +++ 3 files changed, 21 insertions(+), 5 deletions(-) (limited to 'lib/observer/src') diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl index f104f5bb19..7df599c602 100644 --- a/lib/observer/src/observer_trace_wx.erl +++ b/lib/observer/src/observer_trace_wx.erl @@ -1023,7 +1023,8 @@ dbg_flag(proc,on_link) -> sol; dbg_flag(proc,on_first_spawn) -> sofs; dbg_flag(proc,on_first_link) -> sofl; dbg_flag(proc,events) -> p; -dbg_flag(port,events) -> ports. +dbg_flag(port,events) -> ports; +dbg_flag(_,Flag) -> Flag. textformat(Trace) when element(1, Trace) == trace_ts, tuple_size(Trace) >= 4 -> format_trace(Trace, tuple_size(Trace)-1, element(tuple_size(Trace),Trace)); diff --git a/lib/observer/src/observer_traceoptions_wx.erl b/lib/observer/src/observer_traceoptions_wx.erl index e623f90df3..285c298c4b 100644 --- a/lib/observer/src/observer_traceoptions_wx.erl +++ b/lib/observer/src/observer_traceoptions_wx.erl @@ -36,12 +36,20 @@ process_trace(Parent, Default) -> FuncBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace function call", []), check_box(FuncBox, lists:member(functions, Default)), + ArityBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace arity instead of arguments", []), + check_box(ArityBox, lists:member(functions, Default)), SendBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace send message", []), check_box(SendBox, lists:member(send, Default)), RecBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace receive message", []), check_box(RecBox, lists:member('receive', Default)), EventBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace process events", []), check_box(EventBox, lists:member(events, Default)), + SchedBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace scheduling of processes", []), + check_box(SchedBox, lists:member(running_procs, Default)), + ExitBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace scheduling of exiting processes", []), + check_box(ExitBox, lists:member(exiting, Default)), + GCBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace garbage collections", []), + check_box(GCBox, lists:member(garbage_collection, Default)), {SpawnBox, SpwnAllRadio, SpwnFirstRadio} = optionpage_top_right(Panel, RightSz, [{flag, ?wxBOTTOM},{border, 5}], "spawn"), @@ -57,7 +65,7 @@ process_trace(Parent, Default) -> {Radio, Opt} <- [{SpwnAllRadio, on_spawn}, {SpwnFirstRadio, on_first_spawn}, {LinkAllRadio, on_link}, {LinkFirstRadio, on_first_link}]], - [wxSizer:add(LeftSz, CheckBox, []) || CheckBox <- [FuncBox,SendBox,RecBox,EventBox]], + [wxSizer:add(LeftSz, CheckBox, []) || CheckBox <- [FuncBox,ArityBox,SendBox,RecBox,EventBox,SchedBox,ExitBox,GCBox]], wxSizer:add(LeftSz, 150, -1), wxSizer:add(PanelSz, LeftSz, [{flag, ?wxEXPAND}, {proportion,1}]), @@ -80,7 +88,9 @@ process_trace(Parent, Default) -> case wxDialog:showModal(Dialog) of ?wxID_OK -> All = [{SendBox, send}, {RecBox, 'receive'}, - {FuncBox, functions}, {EventBox, events}, + {FuncBox, functions}, {ArityBox, arity}, + {EventBox, events}, {SchedBox, running_procs}, + {ExitBox, exiting}, {GCBox, garbage_collection}, {{SpawnBox, SpwnAllRadio}, on_spawn}, {{SpawnBox,SpwnFirstRadio}, on_first_spawn}, {{LinkBox, LinkAllRadio}, on_link}, @@ -111,8 +121,10 @@ port_trace(Parent, Default) -> check_box(RecBox, lists:member('receive', Default)), EventBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace port events", []), check_box(EventBox, lists:member(events, Default)), + SchedBox = wxCheckBox:new(Panel, ?wxID_ANY, "Trace scheduling of ports", []), + check_box(SchedBox, lists:member(running_ports, Default)), - [wxSizer:add(OptsSz, CheckBox, []) || CheckBox <- [SendBox,RecBox,EventBox]], + [wxSizer:add(OptsSz, CheckBox, []) || CheckBox <- [SendBox,RecBox,EventBox,SchedBox]], wxSizer:add(OptsSz, 150, -1), wxPanel:setSizer(Panel, OptsSz), @@ -125,7 +137,7 @@ port_trace(Parent, Default) -> case wxDialog:showModal(Dialog) of ?wxID_OK -> All = [{SendBox, send}, {RecBox, 'receive'}, - {EventBox, events}], + {EventBox, events}, {SchedBox, running_ports}], Opts = [Id || {Tick, Id} <- All, wxCheckBox:getValue(Tick)], wxDialog:destroy(Dialog), lists:reverse(Opts); diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index e41f2c36fc..ac6c4572eb 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -1310,6 +1310,9 @@ ip_to_file(Trace, {shell_only, Fun} = State) -> ip_to_file(Trace,{{file,File}, ShellOutput}) -> Fun = dbg:trace_port(file,File), %File can be a filename or a wrap spec Port = Fun(), + %% Just in case this is on the traced node, + %% make sure the port is not traced. + p(Port,clear), %% Store the port so it can be properly closed ?MODULE ! {ip_to_file_trace_port, Port, self()}, receive {?MODULE,ok} -> ok end, -- cgit v1.2.3 From 7db703f88a8665bf6fc3266619044da3c882b1e3 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Thu, 12 May 2016 15:50:19 +0200 Subject: [observer] Automatically add active node When setting trace flags on 'new_processes' or 'new_ports', the currently active node is now automatically added if no other node is traced. --- lib/observer/src/observer_trace_wx.erl | 1 + lib/observer/src/observer_wx.erl | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'lib/observer/src') diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl index 7df599c602..af90e2100c 100644 --- a/lib/observer/src/observer_trace_wx.erl +++ b/lib/observer/src/observer_trace_wx.erl @@ -774,6 +774,7 @@ do_add_pid_or_port(POpts, Nview, LCtrl, OldPs, Ns0, Check) -> {Ps, New, _Changed} -> Ns1 = lists:usort([node(Id) || #titem{id=Id} <- New, Check(Id)]), Nodes = case ordsets:subtract(Ns1, Ns0) of + [] when Ns0==[] -> [observer_wx:get_active_node()]; [] -> Ns0; %% No new Nodes NewNs -> ordsets:union(NewNs, Ns0) end, diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl index d896da1291..29778f6d13 100644 --- a/lib/observer/src/observer_wx.erl +++ b/lib/observer/src/observer_wx.erl @@ -21,8 +21,8 @@ -behaviour(wx_object). -export([start/0, stop/0]). --export([create_menus/2, get_attrib/1, get_tracer/0, set_status/1, - create_txt_dialog/4, try_rpc/4, return_to_localnode/2]). +-export([create_menus/2, get_attrib/1, get_tracer/0, get_active_node/0, + set_status/1, create_txt_dialog/4, try_rpc/4, return_to_localnode/2]). -export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3, handle_call/3, handle_info/2, check_page_title/1]). @@ -90,6 +90,9 @@ set_status(What) -> get_tracer() -> wx_object:call(observer, get_tracer). +get_active_node() -> + wx_object:call(observer, get_active_node). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init(_Args) -> @@ -385,6 +388,9 @@ handle_call({get_attrib, Attrib}, _From, State) -> handle_call(get_tracer, _From, State=#state{trace_panel=TraceP}) -> {reply, TraceP, State}; +handle_call(get_active_node, _From, State=#state{node=Node}) -> + {reply, Node, State}; + handle_call(stop, _, State = #state{frame = Frame}) -> wxFrame:destroy(Frame), {stop, normal, ok, State}; -- cgit v1.2.3