aboutsummaryrefslogtreecommitdiffstats
path: root/lib/observer/src/observer_trace_wx.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/observer/src/observer_trace_wx.erl')
-rw-r--r--lib/observer/src/observer_trace_wx.erl672
1 files changed, 514 insertions, 158 deletions
diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl
index 9c0243e4a7..af90e2100c 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/1, add_ports/1]).
-export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3,
handle_event/2, handle_cast/2]).
@@ -31,11 +31,15 @@
-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(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).
@@ -45,36 +49,53 @@
-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(REMOVE_MOD_MS, 361).
--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).
+
+-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,
- 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], []).
-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(Ports) when is_list(Ports) ->
+ wx_object:cast(observer_wx:get_tracer(), {add_ports, Ports}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -87,11 +108,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 +122,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,24 +131,47 @@ 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()}}.
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].
-create_process_view(Parent) ->
+get_default_matchspecs(funcs) ->
+ [{"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) ->
+ [{"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') ->
+ [{"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) ->
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),
@@ -136,31 +183,57 @@ 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(Nodes, command_list_item_selected),
wxListCtrl:connect(Procs, size, [{skip, true}]),
+ 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}.
+ {Panel, Nodes, Procs, Ports}.
create_matchspec_view(Parent) ->
Panel = wxPanel:new(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(),
@@ -182,7 +255,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}.
@@ -192,8 +267,11 @@ 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_DEFPS, text = "Default Process Options"}]}
+ #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"}]}
],
observer_wx:create_menus(Parent, Menus).
@@ -206,11 +284,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;
@@ -233,26 +319,36 @@ 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}},
+ State = #state{tpids=Tpids, tports=Tports, n_view=Nview,
+ 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}},
#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} ->
@@ -302,7 +398,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
@@ -312,7 +409,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)
);
_ ->
@@ -333,52 +430,159 @@ 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"}];
- ?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, 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,
+ n_view=Nview, nodes=Nodes} = State) ->
Selected = get_selected_items(LCtrl, Tpids),
Pids = Tpids -- Selected,
- update_process_view(Pids, LCtrl),
+ update_p_view(Pids, LCtrl, Nodes, Nview),
{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,
+ n_view=Nview, nodes=Nodes} = State) ->
+ Selected = get_selected_items(LCtrl, Tports),
+ Ports = Tports -- Selected,
+ 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) ->
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=?TRACE_DEFMS}, #state{panel=Panel, match_specs=Ms} = State) ->
+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;
+
+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)
+ observer_traceoptions_wx:select_matchspec(self(), Panel, Ms, funcs)
+ catch _:_ ->
+ cancel
+ 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,
@@ -389,12 +593,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)),
- Ms = observer_traceoptions_wx:select_matchspec(self(), Panel, Mss),
- Changed = [TP#tpattern{ms=Ms} || TP <- Selected],
- {noreply, do_add_patterns({Module, Changed}, State)}
- catch _:_ ->
+ 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;
@@ -417,6 +643,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),
@@ -458,11 +694,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}
@@ -510,45 +755,93 @@ 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} ->
+ 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 ->
- %% 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,
- S0#state{tpids=Pids, nodes=Nodes}
+ update_nodes_view(Nodes, Nview),
+ update_p_view(Ps, LCtrl, Nodes, Nview),
+ {Ps, Nodes}
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-update_process_view(Pids, 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
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),
+ 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),
@@ -560,33 +853,51 @@ 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),
- 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).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-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, [], [], []).
@@ -676,10 +987,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
@@ -695,20 +1008,24 @@ 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).
-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;
+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));
@@ -784,23 +1101,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;
@@ -809,38 +1131,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) ->
@@ -866,3 +1216,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).