From 39730743916b300eb3e229c4e6e8a2987487d797 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Fri, 18 Nov 2011 16:14:34 +0100 Subject: [observer] Implemented basic tracing functionality Use ttb which does most of the work already. --- lib/observer/src/observer_procinfo.erl | 2 +- lib/observer/src/observer_trace_wx.erl | 274 ++++++----- lib/observer/src/observer_traceoptions_wx.erl | 658 +------------------------- lib/observer/src/ttb.erl | 44 +- 4 files changed, 195 insertions(+), 783 deletions(-) (limited to 'lib/observer') diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl index 2600109161..127599a39e 100644 --- a/lib/observer/src/observer_procinfo.erl +++ b/lib/observer/src/observer_procinfo.erl @@ -137,7 +137,7 @@ init_process_page(Panel, Pid) -> init_text_page(Parent) -> Style = ?wxTE_MULTILINE bor ?wxTE_RICH2 bor ?wxTE_READONLY, Text = wxTextCtrl:new(Parent, ?wxID_ANY, [{style, Style}]), - Font = observer_wx:get_attrib({font, modern}), + Font = observer_wx:get_attrib({font, fixed}), Attr = wxTextAttr:new(?wxBLACK, [{font, Font}]), true = wxTextCtrl:setDefaultStyle(Text, Attr), wxTextAttr:destroy(Attr), diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl index 23b9b1fe6b..0ab7db121b 100644 --- a/lib/observer/src/observer_trace_wx.erl +++ b/lib/observer/src/observer_trace_wx.erl @@ -39,6 +39,7 @@ -define(PROCESSES, 350). -define(MODULES, 351). -define(FUNCTIONS, 352). +-define(TRACERWIN, 353). -record(state, {parent, @@ -91,6 +92,7 @@ create_window(Notebook, ParentPid) -> wxWindow:setSizer(Panel, Sizer), {Panel, #state{parent=ParentPid, panel=Panel, p_view=ProcessView, m_view=ModView, f_view=FuncView, + toggle_button = ToggleButton, match_specs=default_matchspecs()}}. default_matchspecs() -> @@ -204,6 +206,46 @@ handle_event(#wx{id=?MODULES, event=#wxList{type=command_list_item_selected, ite update_functions_view(dict:fetch(Module, TPs), Fview), {noreply, State}; + +handle_event(#wx{event = #wxCommand{type = command_togglebutton_clicked, commandInt = 1}}, + #state{panel = Panel, + nodes = Nodes, + tpids = TProcs, + tpatterns = TPs, + toggle_button = ToggleBtn} = State) -> + LogWin = wxFrame:new(Panel, ?TRACERWIN, "Trace Log", [{size, {750, 800}}]), + Text = wxTextCtrl:new(LogWin, ?wxID_ANY, + [{style, ?wxTE_MULTILINE bor ?wxTE_RICH2 bor + ?wxTE_DONTWRAP bor ?wxTE_READONLY}]), + Font = observer_wx:get_attrib({font, fixed}), + Attr = wxTextAttr:new(?wxBLACK, [{font, Font}]), + true = wxTextCtrl:setDefaultStyle(Text, Attr), + Env = wx:get_env(), + Write = fun(Trace) -> + wx:set_env(Env), + wxTextCtrl:appendText(Text, textformat(Trace)) + end, + {ok, _} = ttb:tracer(Nodes, [{file, {local,"/tmp/foo"}}, {shell, {only, Write}}]), + setup_ttb(dict:to_list(TPs), TProcs), + wxFrame:connect(LogWin, close_window, [{skip, true}]), + wxFrame:show(LogWin), + wxToggleButton:setLabel(ToggleBtn, "Stop Trace"), + {noreply, State}; + +handle_event(#wx{event = #wxCommand{type = command_togglebutton_clicked, commandInt = 0}}, + #state{toggle_button = ToggleBtn} = State) -> + %%Stop tracing + ttb:stop(nofetch), + wxToggleButton:setLabel(ToggleBtn, "Start Trace"), + {noreply, State}; + +handle_event(#wx{id=?TRACERWIN, event=#wxClose{}}, + #state{toggle_button = ToggleBtn} = State) -> + %%Stop tracing + ttb:stop(nofetch), + wxToggleButton:setLabel(ToggleBtn, "Start Trace"), + {noreply, State}; + %% handle_event(#wx{id = ?CLEAR, event = #wxCommand{type = command_menu_selected}}, %% #state{text_ctrl = TxtCtrl} = State) -> %% wxTextCtrl:clear(TxtCtrl), @@ -261,28 +303,6 @@ handle_event(#wx{id = ?LOAD_TRACEOPTS, wxDialog:destroy(Dialog), {noreply, State2}; - -%% handle_event(#wx{event = #wxCommand{type = command_togglebutton_clicked, commandInt = 1}}, -%% #state{node = Node, -%% traced_procs = TracedProcs, -%% traced_funcs = TracedDict, -%% trace_options = TraceOpts, -%% text_ctrl = TextCtrl, -%% toggle_button = ToggleBtn} = State) -> - -%% start_trace(Node, TracedProcs, TracedDict, TraceOpts), -%% wxTextCtrl:appendText(TextCtrl, "Start Trace:\n"), -%% wxToggleButton:setLabel(ToggleBtn, "Stop Trace"), -%% {noreply, State}; - -%% handle_event(#wx{event = #wxCommand{type = command_togglebutton_clicked, commandInt = 0}}, %%Stop tracing -%% #state{text_ctrl = TxtCtrl, -%% toggle_button = ToggleBtn} = State) -> -%% dbg:stop_clear(), -%% wxTextCtrl:appendText(TxtCtrl, "Stop Trace.\n"), -%% wxToggleButton:setLabel(ToggleBtn, "Start Trace"), -%% {noreply, State}; - handle_event(#wx{id=ID, event = What}, State) -> io:format("~p:~p: Unhandled event: ~p, ~p ~n", [?MODULE, self(), ID, What]), {noreply, State}. @@ -321,7 +341,7 @@ handle_info(Any, State) -> io:format("~p~p: received unexpected message: ~p\n", [?MODULE, self(), Any]), {noreply, State}. -terminate(Reason, #state{nodes=Nodes}) -> +terminate(_Reason, #state{nodes=_Nodes}) -> %% case observer_wx:try_rpc(Node, erlang, whereis, [dbg]) of %% undefined -> fine; %% Pid -> exit(Pid, kill) @@ -337,7 +357,7 @@ do_add_processes(POpts, S0=#state{p_view=LCtrl, tpids=OldPids, nodes=Ns0}) -> case merge_pids(POpts, OldPids) of {OldPids, [], []} -> S0; - {Pids, New, Changed} -> + {Pids, New, _Changed} -> update_process_view(Pids, LCtrl), Ns1 = lists:usort([node(Pid) || #tpid{pid=Pid} <- New, is_pid(Pid)]), Nodes = case ordsets:subtract(Ns1, Ns0) of @@ -369,7 +389,7 @@ do_add_patterns({Module, NewPs}, State=#state{tpatterns=TPs0, m_view=Mview, f_vi case merge_patterns(NewPs, Old) of {Old, [], []} -> State; - {MPatterns, New, Changed} -> + {MPatterns, _New, _Changed} -> TPs = dict:store(Module, MPatterns, TPs0), update_modules_view(lists:sort(dict:fetch_keys(TPs)), Module, Mview), update_functions_view(dict:fetch(Module, TPs), Fview), @@ -437,111 +457,115 @@ merge(Ns, [], _El, New, Ch, All) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -start_trace(Node, TracedProcs, TracedDict, - #trace_options{send = Send, treceive = Receive, functions = Functions, - events = Events, on_1st_spawn = On1Spawn, - on_all_spawn = AllSpawn, on_1st_link = On1Link, - on_all_link = AllLink}) -> - dbg:stop_clear(), - MyPid = self(), - HandlerFun = fun(NewMsg, _) -> - MyPid ! NewMsg - end, - dbg:tracer(process, {HandlerFun, []}), - - case Node =:= node() of - true -> - ok; - false -> - dbg:n(Node) - end, - - Recs = [{Send, send}, - {Receive, 'receive'}, - {Functions, call}, - {Events, procs}, - {On1Spawn, set_on_first_spawn}, - {AllSpawn, set_on_spawn}, - {On1Link, set_on_first_link}, - {AllLink, set_on_link}], - Flags = [Assoc || {true, Assoc} <- Recs], - - case TracedProcs of - all -> - dbg:p(all, Flags); - new -> - dbg:p(new, Flags); - _Pids -> - lists:foreach(fun(Pid) -> dbg:p(Pid, Flags) end, TracedProcs) - end, +setup_ttb(TPs, TPids) -> + _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, + %% io:format("ttb:p(pid(\"~w\", ~w).", [Pid, Flags]), + %% io:format("TTB ~w ~w~n",[R2, R1]), + ok. - case Functions of - true -> - trace_functions(TracedDict); - false -> - ok +%% Sigh order is important +setup_tps([First=#tpattern{fa={_,'_'}}|Rest], Prev) -> + setup_tp(First), + [setup_tp(TP) || TP <- lists:reverse(Prev)], + setup_tps(Rest, []); +setup_tps([First=#tpattern{fa={F,_}}|Rest], Prev = [#tpattern{fa={F,_}}|_]) -> + setup_tps(Rest, [First|Prev]); +setup_tps([First|Rest], Prev) -> + [setup_tp(TP) || TP <- lists:reverse(Prev)], + setup_tps(Rest, [First]); +setup_tps([], Prev) -> + [setup_tp(TP) || TP <- lists:reverse(Prev)]. + +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_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. + +textformat(Trace) when element(1, Trace) == trace_ts, tuple_size(Trace) >= 4 -> + format_trace(Trace, tuple_size(Trace)-1, element(tuple_size(Trace),Trace)); +textformat(Trace) when element(1, Trace) == drop, tuple_size(Trace) =:= 2 -> + io_lib:format("*** Dropped ~p messages.~n", [element(2,Trace)]); +textformat(Trace) when element(1, Trace) == seq_trace, tuple_size(Trace) >= 3 -> + io_lib:format("*** Seq trace not implmented.~n", []); +textformat(_) -> + "". + +format_trace(Trace, Size, TS0={_,_,MS}) -> + {_,{H,M,S}} = calendar:now_to_local_time(TS0), + TS = io_lib:format("~.2.0w:~.2.0w:~.2.0w:~.6.0w", [H,M,S,MS]), + From = element(2, Trace), + case element(3, Trace) of + 'receive' -> + case element(4, Trace) of + {dbg,ok} -> ""; + Message -> + io_lib:format("~s (~100p) << ~100p ~n", [TS,From,Message]) + end; + 'send' -> + Message = element(4, Trace), + To = element(5, Trace), + io_lib:format("~s (~100p) ~100p ! ~100p ~n", [TS,From,To,Message]); + call -> + case element(4, Trace) of + MFA when Size == 5 -> + Message = element(5, Trace), + io_lib:format("~s (~100p) call ~s (~100p) ~n", [TS,From,ffunc(MFA),Message]); + MFA -> + io_lib:format("~s (~100p) call ~s ~n", [TS,From,ffunc(MFA)]) + end; + return_from -> + MFA = element(4, Trace), + Ret = element(5, Trace), + io_lib:format("~s (~100p) returned from ~s -> ~100p ~n", [TS,From,ffunc(MFA),Ret]); + return_to -> + MFA = element(4, Trace), + io_lib:format("~s (~100p) returning to ~s ~n", [TS,From,ffunc(MFA)]); + spawn when Size == 5 -> + Pid = element(4, Trace), + MFA = element(5, Trace), + io_lib:format("~s (~100p) spawn ~100p as ~s ~n", [TS,From,Pid,ffunc(MFA)]); + Op -> + io_lib:format("~s (~100p) ~100p ~s ~n", [TS,From,Op,ftup(Trace,4,Size)]) end. -textformat({died, Pid}) -> - io_lib:format("~w Process died.~n",[Pid]); -textformat({shell_died, Old, New}) -> - io_lib:format("~w Shell Process died. Restarted as ~w~n~n",[Old,New]); -textformat({trace, From, 'receive', Msg}) -> - io_lib:format("~w: rec ~s~n", [From, - tuple_space(Msg)]); -textformat({trace, From, send, Msg, To}) -> - io_lib:format("~w: ! To: ~w Msg: ~s~n", [From, - To, - tuple_space(Msg)]); -textformat({trace, From, call, Func}) -> - io_lib:format("~w: call ~s~n",[From, ffunc(Func)]); -textformat({trace, From, spawn, Data}) -> - io_lib:format("~w: spawn ~p~n", [From, Data]); -textformat({trace, From, link, Data}) -> - io_lib:format("~w: link ~p~n", [From, Data]); -textformat({trace, From, unlink, Data}) -> - io_lib:format("~w: U-lnk ~p~n", [From, Data]); - -textformat({trace, From, Op, Data}) -> - io_lib:format("~w: ~w ~p~n", [From, Op, Data]); - -textformat({print, Format, Args}) -> - io_lib:format(Format, Args); -textformat(Other) -> - io_lib:format("~p~n",[Other]). - - -tuple_space(X) when is_tuple(X) -> print(tuple_size(X), X, "}"); -tuple_space(X) -> io_lib:format("~p",[X]). - - -ffunc({M,F, Argl}) -> - io_lib:format("~w:~w(~s)", [M, F, fargs(Argl)]); -ffunc(X) -> tuple_space(X). +%%% These f* functions returns non-flat strings + +%% {M,F,[A1, A2, ..., AN]} -> "M:F(A1, A2, ..., AN)" +%% {M,F,A} -> "M:F/A" +ffunc({M,F,Argl}) when is_list(Argl) -> + io_lib:format("~100p:~100p(~s)", [M, F, fargs(Argl)]); +ffunc({M,F,Arity}) -> + io_lib:format("~100p:~100p/~100p", [M,F,Arity]); +ffunc(X) -> io_lib:format("~100p", [X]). +%% Integer -> "Integer" +%% [A1, A2, ..., AN] -> "A1, A2, ..., AN" +fargs(Arity) when is_integer(Arity) -> integer_to_list(Arity); fargs([]) -> []; -fargs([A]) -> tuple_space(A); %% last arg -fargs([A|Args]) -> [tuple_space(A),", "|fargs(Args)]. - -print(0 , _X, Buff) -> ["{"|Buff]; -print(1 , X, Buff) -> - Str = tuple_space(element(1, X)), - ["{",Str|Buff]; -print(Num, X, Buff) -> - Str = tuple_space(element(Num, X)), - print(Num-1, X, [", ",Str|Buff]). - -trace_functions(TracedDict) -> - Trace = fun(KeyAtom, RecordList, acc_in) -> - lists:foreach(fun(#traced_func{func_name = Function, - arity = Arity, - match_spec = #match_spec{term = MS}}) -> - dbg:tpl({KeyAtom, Function, Arity}, MS) - end, - RecordList), - acc_in - end, - dict:fold(Trace, acc_in, TracedDict). +fargs([A]) -> io_lib:format("~100p", [A]); %% last arg +fargs([A|Args]) -> [io_lib:format("~100p,", [A]) | fargs(Args)]; +fargs(A) -> io_lib:format("~100p", [A]). % last or only arg + +%% {A_1, A_2, ..., A_N} -> "A_Index A_Index+1 ... A_Size" +ftup(Trace, Index, Index) -> + io_lib:format("~100p", [element(Index, Trace)]); +ftup(Trace, Index, Size) -> + [io_lib:format("~100p ", [element(Index, Trace)]) + | ftup(Trace, Index+1, Size)]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% write_file(Frame, Filename, TraceOps, MatchSpecs, TPs) -> FormatMS = fun(#match_spec{name=Id, term=T, func=F}) -> diff --git a/lib/observer/src/observer_traceoptions_wx.erl b/lib/observer/src/observer_traceoptions_wx.erl index 043126d85f..bad05ec016 100644 --- a/lib/observer/src/observer_traceoptions_wx.erl +++ b/lib/observer/src/observer_traceoptions_wx.erl @@ -145,14 +145,22 @@ module_selector(Parent, Node) -> wxListBox:connect(ListBox, command_listbox_doubleclicked, [{callback, fun(_, _) -> wxDialog:endModal(Dialog, ?wxID_OK) end}]), wxListBox:connect(ListBox, command_listbox_selected, - [{callback, fun(_, _) -> wxWindow:enable(OkButt) end}]), + [{callback, fun(#wx{event=#wxCommand{commandInt=Id}}, _) -> + Id >= 0 andalso wxWindow:enable(OkButt) + end}]), case wxDialog:showModal(Dialog) of ?wxID_OK -> SelId = wxListBox:getSelection(ListBox), - Module = wxListBox:getClientData(ListBox, SelId), - wxDialog:destroy(Dialog), - Module; + case SelId >= 0 of + true -> + Module = wxListBox:getClientData(ListBox, SelId), + wxDialog:destroy(Dialog), + Module; + false -> + wxDialog:destroy(Dialog), + throw(cancel) + end; ?wxID_CANCEL -> wxDialog:destroy(Dialog), throw(cancel) @@ -194,7 +202,7 @@ function_selector(Parent, Node, Module) -> filter_listbox_data("", ParsedChoices, ListBox, false), %% Setup Event handling wxTextCtrl:connect(TxtCtrl, command_text_updated, - [{callback, + [{callback, fun(#wx{event=#wxCommand{cmdString=Input}}, _) -> filter_listbox_data(Input, ParsedChoices, ListBox, false) end}]), @@ -208,7 +216,7 @@ function_selector(Parent, Node, Module) -> Data end, wxCheckListBox:connect(ListBox, command_checklistbox_toggled, - [{callback, + [{callback, fun(#wx{event=#wxCommand{commandInt=N}}, _) -> Self ! {ListBox, wxCheckListBox:isChecked(ListBox, N), GetClientData(ListBox, N)} @@ -443,11 +451,11 @@ filter_listbox_data(Input, Data, ListBox, AddClientData) -> FilteredData = [X || X = {Str, _} <- Data, re:run(Str, Input) =/= nomatch], wxListBox:clear(ListBox), wxListBox:appendStrings(ListBox, [Str || {Str,_} <- FilteredData]), - AddClientData andalso + AddClientData andalso wx:foldl(fun({_, Term}, N) -> wxListBox:setClientData(ListBox, N, Term), N+1 - end, 0, FilteredData), + end, 0, FilteredData), FilteredData. get_modules(Node) -> @@ -468,7 +476,7 @@ optionpage_top_right(Panel, TopRightSz, Options, Text) -> create_styled_txtctrl(Parent) -> - FixedFont = observer_wx:get_attrib({font, modern}), + FixedFont = observer_wx:get_attrib({font, fixed}), Ed = wxStyledTextCtrl:new(Parent), wxStyledTextCtrl:styleClearAll(Ed), wxStyledTextCtrl:styleSetFont(Ed, ?wxSTC_STYLE_DEFAULT, FixedFont), @@ -553,194 +561,6 @@ ms_names(MatchSpecList) -> end, [{MsOrAlias(X), X} || X <- MatchSpecList]. - -%% find_and_format_ms(Selection, [ #match_spec{str_ms = Spec, alias = Alias, fun2ms = Fun} | T ]) -> -%% case ((Selection =:= Spec) or (Selection =:= Alias)) or (Selection =:= Fun) of -%% true -> -%% if Selection =:= Alias -> -%% Spec; -%% true -> -%% Selection -%% end; -%% false -> -%% find_and_format_ms(Selection, T) -%% end. - -%% find_ms(_, []) -> -%% {nomatch, #match_spec{}}; -%% find_ms(Str, [ #match_spec{str_ms = Spec, alias = Alias, fun2ms = Fun} = MS | T ]) -> -%% case ((Str =:= Spec) or (Str =:= Alias)) or (Str =:= Fun) of -%% true -> -%% {match, MS}; -%% false -> -%% find_ms(Str, T) -%% end. - -%% apply_matchspec(MatchSpec, TracedDict, root) -> -%% UpdateMS = fun(_Key, RecordList) -> -%% [X#traced_func{match_spec = MatchSpec} || X <- RecordList] -%% end, -%% {ok, dict:map(UpdateMS, TracedDict)}; -%% apply_matchspec(MatchSpec, TracedDict, {module, Module}) -> -%% RecordList = dict:fetch(Module, TracedDict), -%% RecordList2 = [X#traced_func{match_spec = MatchSpec} || X <- RecordList], -%% {ok, dict:store(Module, RecordList2, TracedDict)}; -%% apply_matchspec(MatchSpec, TracedDict, {function, Module, TracedFuncRec}) -> -%% RecordList = dict:fetch(Module, TracedDict), -%% NewFunc = TracedFuncRec#traced_func{match_spec = MatchSpec}, -%% RecordList2 = [NewFunc | [X || X <- RecordList, X =/= TracedFuncRec]], -%% {NewFunc, dict:store(Module, RecordList2, TracedDict)}. - -%% create_matchspec_page(Parent, MatchSpecs, UserData) -> -%% Panel = wxPanel:new(Parent), -%% MainSz = wxBoxSizer:new(?wxVERTICAL), -%% TxtSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, "Match specification:"}]), -%% BtnSz = wxBoxSizer:new(?wxHORIZONTAL), -%% SavedSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, "Saved match specifications:"}]), - -%% TxtCtrl = create_styled_txtctrl(Panel), -%% wxSizer:add(TxtSz, TxtCtrl, [{flag, ?wxEXPAND}, {proportion, 1}]), - -%% AddMsBtn = wxButton:new(Panel, ?MATCHPAGE_ADDMS, [{label, "Add"}]), -%% AddMsAliasBtn = wxButton:new(Panel, ?MATCHPAGE_ADDMS_ALIAS, [{label, "Add with alias"}]), -%% Fun2MSBtn = wxButton:new(Panel, ?MATCHPAGE_ADDFUN, [{label, "Add fun"}]), -%% wxSizer:add(BtnSz, AddMsBtn), -%% wxSizer:add(BtnSz, AddMsAliasBtn), -%% wxSizer:add(BtnSz, Fun2MSBtn), - -%% Choices = show_ms_in_savedlistbox(MatchSpecs), -%% SavedMSListBox = wxListBox:new(Panel, ?MATCHPAGE_LISTBOX, [{choices, Choices}]), -%% wxSizer:add(SavedSz, SavedMSListBox, [{flag, ?wxEXPAND}, {proportion, 1}]), - -%% wxButton:connect(AddMsBtn, command_button_clicked, [{userData, UserData}]), -%% wxButton:connect(AddMsAliasBtn, command_button_clicked, [{userData, UserData}] ), -%% wxButton:connect(Fun2MSBtn, command_button_clicked, [{userData, UserData}] ), -%% wxListBox:connect(SavedMSListBox, command_listbox_selected, [{userData, UserData}] ), -%% wxSizer:add(MainSz, TxtSz, [{flag, ?wxEXPAND}, {proportion, 1}]), -%% wxSizer:add(MainSz, BtnSz), -%% wxSizer:add(MainSz, SavedSz, [{flag, ?wxEXPAND}, {proportion, 1}]), - -%% wxWindow:setSizer(Panel, MainSz), -%% {Panel, MainSz, TxtCtrl, SavedMSListBox}. - - - -%% update_tree(Tree, Dict) -> -%% RootId = wxTreeCtrl:getRootItem(Tree), -%% wxTreeCtrl:deleteChildren(Tree, RootId), - -%% FillTree = fun(KeyAtom, RecordList, acc_in) -> -%% ParsedList = parse_record_function_names(RecordList), -%% Module = wxTreeCtrl:appendItem(Tree, RootId, atom_to_list(KeyAtom)), -%% lists:foldl(fun(TracedFuncRecord, N) -> -%% FNameStr = lists:nth(N, ParsedList), -%% wxTreeCtrl:appendItem(Tree, Module, FNameStr, -%% [{data, TracedFuncRecord}]), -%% N+1 -%% end, -%% 1, RecordList), -%% wxTreeCtrl:sortChildren(Tree, Module), -%% acc_in -%% end, -%% dict:fold(FillTree, acc_in, Dict), -%% wxTreeCtrl:sortChildren(Tree, RootId), -%% wxTreeCtrl:expand(Tree, RootId). - - - - -%% create_module_popup(Parent, ModuleName, TracedDict) -> -%% Module = list_to_atom(ModuleName), -%% Value = dict:find(Module, TracedDict), -%% TracedModRecs = -%% case Value of -%% {ok, V} -> -%% V; -%% error -> -%% [] -%% end, -%% Functions = Module:module_info(functions), -%% Choices = lists:sort([{Name, Arity} || {Name, Arity} <- Functions, not(erl_internal:guard_bif(Name, Arity))]), -%% ParsedChoices = parse_function_names(Choices), - -%% Dialog = wxDialog:new(Parent, ?MODULEPOPUP_DIALOG, ModuleName, -%% [{style, ?wxDEFAULT_FRAME_STYLE}]), -%% Panel = wxPanel:new(Dialog), -%% MainSz = wxBoxSizer:new(?wxVERTICAL), - -%% SelBtnSz = wxBoxSizer:new(?wxHORIZONTAL), -%% TxtCtrl = wxTextCtrl:new(Panel, ?MODULEPOPUP_TXTCTRL), -%% SelBtn = wxButton:new(Panel, ?MODULEPOPUP_SELECT, [{label, "Select"}]), -%% DeSelBtn = wxButton:new(Panel, ?MODULEPOPUP_SELECT, [{label, "Deselect"}]), -%% SelAllBtn = wxButton:new(Panel, ?MODULEPOPUP_SELALL, [{label, "Select all"}]), -%% DeSelAllBtn = wxButton:new(Panel, ?MODULEPOPUP_SELALL, [{label, "Deselect all"}]), -%% CheckListBox = wxCheckListBox:new(Panel, ?MODULEPOPUP_CHECKLISTBOX, [{choices, ParsedChoices}, {style, ?wxLB_EXTENDED}]), -%% Indices = find_index(TracedModRecs, Choices), -%% lists:foreach(fun(X) -> wxCheckListBox:check(CheckListBox, X) end, Indices), -%% Selections = [wxControlWithItems:getString(CheckListBox, I) || I <- Indices], - -%% OKBtn = wxButton:new(Panel, ?wxID_OK, []), -%% CancelBtn = wxButton:new(Panel, ?wxID_CANCEL, []), -%% DialogBtnSz = wxStdDialogButtonSizer:new(), -%% wxStdDialogButtonSizer:addButton(DialogBtnSz, OKBtn), -%% wxStdDialogButtonSizer:addButton(DialogBtnSz, CancelBtn), -%% wxStdDialogButtonSizer:realize(DialogBtnSz), - -%% wxSizer:add(SelBtnSz, SelBtn), -%% wxSizer:add(SelBtnSz, DeSelBtn), -%% wxSizer:add(SelBtnSz, SelAllBtn), -%% wxSizer:add(SelBtnSz, DeSelAllBtn), -%% wxSizer:add(MainSz, TxtCtrl, [{flag, ?wxEXPAND}]), -%% wxSizer:add(MainSz, CheckListBox, [{flag, ?wxEXPAND}, {proportion, 1}]), -%% wxSizer:add(MainSz, SelBtnSz, [{flag, ?wxEXPAND}]), -%% wxSizer:add(MainSz, DialogBtnSz), -%% wxWindow:setSizer(Panel, MainSz), - -%% wxButton:connect(SelBtn, command_button_clicked, [{userData, true}]), -%% wxButton:connect(DeSelBtn, command_button_clicked, [{userData, false}]), -%% wxButton:connect(SelAllBtn, command_button_clicked, [{userData, true}]), -%% wxButton:connect(DeSelAllBtn, command_button_clicked, [{userData, false}]), -%% wxButton:connect(OKBtn, command_button_clicked, [{userData, {module_popup, Module, ParsedChoices, Choices}}]), -%% wxButton:connect(CancelBtn, command_button_clicked, [{userData, module_popup}]), -%% wxTextCtrl:connect(TxtCtrl, command_text_updated, [{userData, ParsedChoices}]), -%% wxCheckListBox:connect(CheckListBox, command_checklistbox_toggled), -%% wxDialog:connect(Dialog, close_window), -%% wxDialog:show(Dialog), -%% {Dialog, CheckListBox, Selections}. - -%% get_selections(Selections, FunctionList) -> -%% get_selections(Selections, FunctionList, []). -%% get_selections([], _, Acc) -> -%% Acc; -%% get_selections([Int|T], FuncList, Acc) -> -%% get_selections(T, FuncList, [lists:nth(Int, FuncList) | Acc]). - -%% find_index(Selections, FunctionList) -> -%% find_index(Selections, FunctionList, 1, []). -%% find_index(Selections, FunctionList, N, Acc) when N > length(FunctionList); Selections =:= [] -> -%% Acc; - -%% find_index([#traced_func{func_name = Name, arity = Arity} |STail] = Selections, -%% FunctionList, N, Acc) -> -%% {Fname, A} = lists:nth(N, FunctionList), -%% case (Fname =:= Name) and (A =:= Arity) of -%% true -> -%% find_index(STail, FunctionList, 1, [N-1|Acc]); -%% false -> -%% find_index(Selections, FunctionList, N+1, Acc) -%% end; - -%% find_index([Sel|STail] = Selections, FunctionList, N, Acc) when is_list(Sel) -> -%% case lists:nth(N, FunctionList) =:= Sel of -%% true -> -%% find_index(STail, FunctionList, 1, [N-1|Acc]); -%% false -> -%% find_index(Selections, FunctionList, N+1, Acc) -%% end. - -%% atomlist_to_stringlist(Modules) -> -%% [atom_to_list(X) || X <- Modules]. - ensure_last_is_dot([]) -> "."; ensure_last_is_dot(String) -> @@ -750,447 +570,3 @@ ensure_last_is_dot(String) -> false -> String ++ "." end. - - -%% dbg_from_string(Str0) -> -%% Str = unicode:characters_to_list(Str0), -%% case erl_scan:string(Str) of -%% {ok, Tokens,_} -> -%% case erl_parse:parse_exprs(Tokens) of -%% {ok,[{'fun',_,{clauses, Cl}}]} -> -%% case ms_transform: -%% transform_from_shell(dbg,Cl,orddict:new()) of -%% {error, [{_,[{Line,ms_transform,Info}]}],_} -> -%% {error,{Line,ms_transform,Info}}; -%% {error, _} = ET1 -> -%% ET1; -%% Else -> -%% {ok, Else, "[" ++ lists:flatten(io_lib:format("~p", Else)) ++ "]"} -%% end; -%% {ok,_} -> -%% {error, {1,ms_transform,1}}; -%% {error,Reason} -> -%% {error,Reason} -%% end; -%% {error,Reason2,_} -> -%% {error,Reason2} -%% end. - -%% get_correct_matchspec_components(From, State) -> -%% case From of -%% matchpage -> -%% {State#traceopts_state.matchpage_styled_txtctrl, -%% State#traceopts_state.frame}; -%% matchpopup -> -%% {State#traceopts_state.matchspec_popup_styled_txtctrl, -%% State#traceopts_state.matchspec_popup_dialog} -%% end. - - - -%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% %Trace option window - -%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% All pages - -%% handle_event(#wx{id = ?wxID_OK, -%% event = #wxCommand{type = command_button_clicked}, -%% userData = trace_options}, -%% #traceopts_state{boxes = Boxes, -%% trace_options = TraceOpts, -%% match_specs = MatchSpecs, -%% traced_funcs = TracedFuncs, -%% parent = Parent} = State) -> -%% UpdTraceOpts = wx:batch(fun() -> -%% read_trace_boxes(Boxes, TraceOpts) -%% end), -%% Parent ! {updated_traceopts, -%% UpdTraceOpts, -%% MatchSpecs, -%% TracedFuncs}, -%% {stop, shutdown, State}; - -%% handle_event(#wx{id = ?wxID_CANCEL, -%% event = #wxCommand{type = command_button_clicked}, -%% userData = trace_options}, -%% #traceopts_state{parent = Parent} = State) -> -%% Parent ! traceopts_closed, -%% {stop, shutdown, State}; - -%% handle_event(#wx{id = ?TRACEOPTS_FRAME, -%% event = #wxClose{type = close_window}}, -%% #traceopts_state{parent = Parent} = State) -> -%% Parent ! traceopts_closed, -%% {stop, shutdown, State}; - - -%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Page - Tracing - -%% handle_event(#wx{event = #wxCommand{type = command_checkbox_clicked}, userData = Boxgroup}, -%% State) -> -%% enable(Boxgroup), -%% {noreply, State}; - - -%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Page - Functions - -%% handle_event(#wx{id = ?FUNCTIONPAGE_LISTBOX, -%% event = #wxCommand{type = command_listbox_doubleclicked, -%% cmdString = ChosenModule}}, -%% #traceopts_state{frame = Frame, -%% traced_funcs = TracedDict, -%% popup_open = false} = State) -> -%% {Dialog, CheckListBox, CheckedFuncs} = create_module_popup(Frame, ChosenModule, TracedDict), -%% {noreply, State#traceopts_state{popup_open = true, -%% module_popup_dialog = Dialog, -%% module_popup_checklistbox = CheckListBox, -%% checked_funcs = CheckedFuncs}}; - -%% handle_event(#wx{id = ?FUNCTIONPAGE_TXTCTRL, -%% event = #wxCommand{type = command_text_updated, -%% cmdString = Input}, -%% userData = Data}, -%% #traceopts_state{functionpage_listbox = ListBox} = State) -> -%% filter_listbox_data(Input, Data, ListBox), -%% {noreply, State}; - -%% handle_event(#wx{event = #wxTree{type = command_tree_item_activated, -%% item = Item}}, -%% #traceopts_state{frame = Frame, -%% match_specs = MatchSpecs, -%% popup_open = false} = State) -> - -%% Dialog = wxDialog:new(Frame, ?MATCH_POPUP_DIALOG, "Match specification", -%% [{style, ?wxDEFAULT_FRAME_STYLE}]), -%% {MatchPanel, MatchSz, StyledTxtCtrl, ListBox} = create_matchspec_page(Dialog, MatchSpecs, matchpopup), -%% ApplyBtn = wxButton:new(MatchPanel, ?wxID_APPLY), -%% CancelBtn = wxButton:new(MatchPanel, ?wxID_CANCEL, []), -%% wxButton:connect(ApplyBtn, command_button_clicked, [{userData, Item}]), -%% wxButton:connect(CancelBtn, command_button_clicked, [{userData, matchspec_popup}]), -%% DialogBtnSz = wxStdDialogButtonSizer:new(), -%% wxStdDialogButtonSizer:addButton(DialogBtnSz, ApplyBtn), -%% wxStdDialogButtonSizer:addButton(DialogBtnSz, CancelBtn), -%% wxStdDialogButtonSizer:realize(DialogBtnSz), -%% wxSizer:add(MatchSz, DialogBtnSz), - -%% wxDialog:connect(Dialog, close_window), -%% wxDialog:show(Dialog), -%% {noreply, State#traceopts_state{matchspec_popup_dialog = Dialog, -%% matchspec_popup_listbox = ListBox, -%% matchspec_popup_styled_txtctrl = StyledTxtCtrl, -%% popup_open = true}}; - -%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Page - Match specs - -%% handle_event(#wx{event = #wxCommand{type = command_listbox_selected, -%% cmdString = Txt}}, -%% State) when Txt =:= [] -> -%% {noreply, State}; - -%% handle_event(#wx{id = ?MATCHPAGE_LISTBOX, -%% event = #wxCommand{type = command_listbox_selected, -%% cmdString = SavedTxt}, -%% userData = From}, -%% #traceopts_state{match_specs = MatchSpecs} = State) -> -%% {StyledTxtCtrl, _} = get_correct_matchspec_components(From, State), -%% MsOrFun = find_and_format_ms(SavedTxt, MatchSpecs), -%% wxStyledTextCtrl:setText(StyledTxtCtrl, MsOrFun), -%% {noreply, State}; - -%% handle_event(#wx{id = ?MATCHPAGE_ADDFUN, -%% event = #wxCommand{type = command_button_clicked}, -%% userData = From}, -%% #traceopts_state{match_specs = MatchSpecs, -%% matchpage_listbox = PageListBox, -%% matchspec_popup_listbox = PopupListBox} = State) -> - -%% {StyledTxtCtrl, Frame} = get_correct_matchspec_components(From, State), -%% StrFun = ensure_last_is_dot(wxStyledTextCtrl:getText(StyledTxtCtrl)), - -%% MatchSpecs2 = case dbg_from_string(StrFun) of -%% {ok, TermMS, StrMS} -> -%% FunMS = #match_spec{str_ms = StrMS, term_ms = TermMS, fun2ms = StrFun}, -%% case lists:member(FunMS, MatchSpecs) of -%% true -> -%% observer_wx:create_txt_dialog(Frame, StrFun ++ "\nalready exists", -%% "Error", ?wxICON_ERROR), -%% MatchSpecs; -%% false -> -%% wxStyledTextCtrl:setText(StyledTxtCtrl, StrMS), -%% update_matchspec_listbox(StrFun, {PopupListBox, PageListBox}, From), -%% lists:reverse([FunMS | MatchSpecs]) -%% end; -%% {error, {_, Module, What}} -> -%% FailMsg = Module:format_error(What), -%% observer_wx:create_txt_dialog(Frame, FailMsg, "Error", ?wxICON_ERROR), -%% MatchSpecs -%% end, -%% {noreply, State#traceopts_state{match_specs = MatchSpecs2}}; - -%% handle_event(#wx{id = ?MATCHPAGE_ADDMS, -%% event = #wxCommand{type = command_button_clicked}, -%% userData = From}, -%% #traceopts_state{match_specs = MatchSpecs, -%% matchpage_listbox = PageListBox, -%% matchspec_popup_listbox = PopupListBox} = State) -> - -%% {StyledTxtCtrl, Frame} = get_correct_matchspec_components(From, State), -%% StrMS = ensure_last_is_dot(wxStyledTextCtrl:getText(StyledTxtCtrl)), -%% MatchSpecs2 = case check_correct_MS(StrMS) of -%% {true, TermMS} -> -%% MS = #match_spec{str_ms = StrMS, term_ms = TermMS}, -%% case lists:member(MS, MatchSpecs) of -%% true -> -%% observer_wx:create_txt_dialog(Frame, StrMS ++ "\nalready exists", -%% "Error", ?wxICON_ERROR), -%% MatchSpecs; -%% false -> -%% update_matchspec_listbox(StrMS, {PopupListBox, PageListBox}, From), -%% lists:reverse([MS | MatchSpecs]) -%% end; -%% {false, Reason} -> -%% observer_wx:create_txt_dialog(Frame, Reason, "Error", ?wxICON_ERROR), -%% MatchSpecs -%% end, -%% {noreply, State#traceopts_state{match_specs = MatchSpecs2}}; - - -%% handle_event(#wx{id = ?MATCHPAGE_ADDMS_ALIAS, -%% event = #wxCommand{type = command_button_clicked}, -%% userData = From}, -%% #traceopts_state{match_specs = MatchSpecs, -%% matchpage_listbox = PageListBox, -%% matchspec_popup_listbox = PopupListBox} = State) -> - -%% {StyledTxtCtrl, Frame} = get_correct_matchspec_components(From, State), -%% StrMS = ensure_last_is_dot(wxStyledTextCtrl:getText(StyledTxtCtrl)), - -%% MatchSpecs2 = case check_correct_MS(StrMS) of -%% {true, TermMS} -> -%% Dialog = wxTextEntryDialog:new(Frame, "Enter ms alias: "), -%% Alias = case wxDialog:showModal(Dialog) of -%% ?wxID_OK -> -%% wxTextEntryDialog:getValue(Dialog); -%% ?wxID_CANCEL -> -%% "" -%% end, -%% wxDialog:destroy(Dialog), - -%% case Alias of -%% "" -> -%% observer_wx:create_txt_dialog(Frame, "Bad alias", "Syntax error", -%% ?wxICON_ERROR), -%% MatchSpecs; - -%% _ -> -%% MS = #match_spec{alias = Alias, str_ms = StrMS, -%% term_ms = TermMS}, -%% {OccupiedAlias, _} = find_ms(Alias, MatchSpecs), - -%% if -%% OccupiedAlias =:= match -> -%% observer_wx:create_txt_dialog(Frame, "Alias " ++ Alias ++ " already exists", -%% "Error", ?wxICON_ERROR), -%% MatchSpecs; -%% true -> -%% update_matchspec_listbox(Alias, {PopupListBox, PageListBox}, From), -%% lists:reverse([MS | MatchSpecs]) -%% end -%% end; -%% {false, Reason} -> -%% observer_wx:create_txt_dialog(Frame, Reason, "Error", ?wxICON_ERROR), -%% MatchSpecs -%% end, -%% {noreply, State#traceopts_state{match_specs = MatchSpecs2}}; - - -%% handle_event(#wx{id = ?wxID_APPLY, -%% event = #wxCommand{type = command_button_clicked}, -%% userData = Item}, -%% #traceopts_state{matchspec_popup_dialog = Dialog, -%% matchspec_popup_listbox = ListBox, -%% tree = Tree, -%% match_specs = MatchSpecs, -%% traced_funcs = TracedDict} = State) -> -%% IntSelection = wxListBox:getSelection(ListBox), -%% StrSelection = -%% case IntSelection >= 0 of -%% true -> -%% wxControlWithItems:getString(ListBox, IntSelection); -%% false -> -%% [] -%% end, -%% {_, MS} = find_ms(StrSelection, MatchSpecs), -%% RootId = wxTreeCtrl:getRootItem(Tree), -%% ItemParent = wxTreeCtrl:getItemParent(Tree, Item), - -%% TracedDict2 = -%% if (Item =:= RootId) -> -%% {ok, NewDict} = apply_matchspec(MS, TracedDict, root), -%% NewDict; -%% (ItemParent =:= RootId) -> -%% Module = list_to_atom(wxTreeCtrl:getItemText(Tree, Item)), -%% {ok, NewDict} = apply_matchspec(MS, TracedDict, {module, Module}), -%% NewDict; -%% true -> -%% TracedFuncRec = wxTreeCtrl:getItemData(Tree, Item), -%% Module = list_to_atom(wxTreeCtrl:getItemText(Tree, ItemParent)), -%% {NewTracedFuncRecord, NewDict} = -%% apply_matchspec(MS, -%% TracedDict, -%% {function, -%% Module, -%% TracedFuncRec}), -%% wxTreeCtrl:setItemData(Tree, Item, NewTracedFuncRecord), -%% NewDict -%% end, -%% wxDialog:destroy(Dialog), -%% {noreply, State#traceopts_state{traced_funcs = TracedDict2, -%% popup_open = false}}; - -%% handle_event(#wx{id = ?wxID_CANCEL, -%% event = #wxCommand{type = command_button_clicked}, -%% userData = matchspec_popup}, -%% #traceopts_state{matchspec_popup_dialog = Dialog} = State) -> -%% wxDialog:destroy(Dialog), -%% {noreply, State#traceopts_state{popup_open = false}}; - -%% handle_event(#wx{id = ?MATCH_POPUP_DIALOG, -%% event = #wxClose{type = close_window}}, -%% #traceopts_state{matchspec_popup_dialog = Dialog} = State) -> -%% wxDialog:destroy(Dialog), -%% {noreply, State#traceopts_state{popup_open = false}}; - -%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% %Module Popup - -%% handle_event(#wx{id = ?wxID_OK, -%% event = #wxCommand{type = command_button_clicked}, -%% userData = {module_popup, Module, -%% ParsedChoices, Choices}}, -%% #traceopts_state{ -%% module_popup_dialog = Dialog, -%% traced_funcs = TracedDict, -%% tree = Tree, -%% checked_funcs = CheckedFuncs} = State) -> - -%% Indices = [I+1 || I <- find_index(CheckedFuncs, ParsedChoices)], -%% Selections = get_selections(Indices, Choices), -%% TracedDict2 = case Selections of -%% [] -> -%% dict:erase(Module, TracedDict); -%% _ -> -%% Traced = [#traced_func{arity = Arity, -%% func_name = Function} -%% || {Function, Arity} <- Selections], -%% dict:store(Module, Traced, TracedDict) -%% end, - -%% update_tree(Tree, TracedDict2), -%% wxDialog:destroy(Dialog), -%% {noreply, State#traceopts_state{traced_funcs = TracedDict2, -%% checked_funcs = [], -%% popup_open = false}}; - -%% handle_event(#wx{id = ?wxID_CANCEL, -%% event = #wxCommand{type = command_button_clicked}, -%% userData = module_popup}, -%% #traceopts_state{module_popup_dialog = Dialog} = State) -> -%% wxDialog:destroy(Dialog), -%% {noreply, State#traceopts_state{popup_open = false, -%% checked_funcs = []}}; - -%% handle_event(#wx{id = ?MODULEPOPUP_SELECT, -%% event = #wxCommand{type = command_button_clicked}, -%% userData = Bool}, -%% #traceopts_state{module_popup_checklistbox = CheckListBox, -%% checked_funcs = CheckedFuncs} = State) -> -%% {_, Selections} = wxListBox:getSelections(CheckListBox), -%% lists:foreach(fun(Index) -> wxCheckListBox:check(CheckListBox, Index, [{check, Bool}]) end, Selections), -%% StrSelections = [wxControlWithItems:getString(CheckListBox, N) || N <- Selections], -%% CheckedFuncs2 = case Bool of -%% true -> -%% [X || X <- StrSelections, -%% not(lists:member(X, CheckedFuncs))] ++ CheckedFuncs; -%% false -> -%% CheckedFuncs -- StrSelections -%% end, -%% {noreply, State#traceopts_state{checked_funcs = CheckedFuncs2}}; - -%% handle_event(#wx{id = ?MODULEPOPUP_SELALL, -%% event = #wxCommand{type = command_button_clicked}, -%% userData = Bool}, -%% #traceopts_state{module_popup_checklistbox = CheckListBox} = State) -> -%% lists:foreach(fun(Index) -> -%% wxCheckListBox:check(CheckListBox, Index, [{check, Bool}]) -%% end, -%% lists:seq(0, wxControlWithItems:getCount(CheckListBox))), -%% CheckedFuncs = case Bool of -%% true -> -%% [wxControlWithItems:getString(CheckListBox, N) -%% || N <- lists:seq(0, wxControlWithItems:getCount(CheckListBox))]; -%% false -> -%% [] -%% end, -%% {noreply, State#traceopts_state{checked_funcs = CheckedFuncs}}; - -%% handle_event(#wx{id = ?MODULEPOPUP_CHECKLISTBOX, -%% obj = CheckListBox, -%% event = #wxCommand{type = command_checklistbox_toggled, -%% commandInt = Index}}, -%% #traceopts_state{checked_funcs = CheckedFuncs} = State) -> - -%% UpdCheckedFuncs = case -%% wxCheckListBox:isChecked(CheckListBox, Index) of -%% true -> -%% [wxControlWithItems:getString(CheckListBox, Index) | CheckedFuncs]; -%% false -> -%% lists:delete(wxControlWithItems:getString(CheckListBox, Index), CheckedFuncs) -%% end, -%% {noreply, State#traceopts_state{checked_funcs = UpdCheckedFuncs}}; - -%% handle_event(#wx{id = ?MODULEPOPUP_TXTCTRL, -%% event = #wxCommand{type = command_text_updated, -%% cmdString = Input}, -%% userData = Data}, -%% #traceopts_state{module_popup_checklistbox = CListBox, -%% checked_funcs = CheckedFuncs} = State) -> -%% FilteredData = filter_listbox_data(Input, Data, CListBox), -%% lists:foreach(fun(Index) -> -%% wxCheckListBox:check(CListBox, Index, [{check, true}]) -%% end, -%% [wxControlWithItems:findString(CListBox, X) || X <- CheckedFuncs, lists:member(X, FilteredData)]), -%% {noreply, State}; - -%% handle_event(#wx{id = ?MODULEPOPUP_DIALOG, -%% event = #wxClose{type = close_window}}, -%% #traceopts_state{module_popup_dialog = Dialog} = State) -> -%% wxDialog:destroy(Dialog), -%% {noreply, State#traceopts_state{popup_open = false, -%% checked_funcs = []}}; - -%% handle_event(#wx{event = What}, State) -> -%% io:format("~p~p: Unhandled event: ~p ~n", [?MODULE, self(), What]), -%% {noreply, State}. - - - -%% terminate(Reason, #traceopts_state{frame = Frame}) -> -%% io:format("~p terminating traceopts. Reason: ~p~n", [?MODULE, Reason]), -%% wxFrame:destroy(Frame), -%% ok. - -%% code_change(_, _, State) -> -%% {stop, not_yet_implemented, State}. - -%% handle_info(Any, State) -> -%% io:format("~p~p: received unexpected message: ~p\n", [?MODULE, self(), Any]), -%% {noreply, State}. - -%% handle_call(Msg, _From, State) -> -%% io:format("~p~p: Got Call ~p~n",[?MODULE, ?LINE, Msg]), -%% {reply, ok, State}. - -%% handle_cast(Msg, State) -> -%% io:format("~p ~p: Unhandled cast ~p~n", [?MODULE, ?LINE, Msg]), -%% {noreply, State}. diff --git a/lib/observer/src/ttb.erl b/lib/observer/src/ttb.erl index 1471be92e5..61fd6d1787 100644 --- a/lib/observer/src/ttb.erl +++ b/lib/observer/src/ttb.erl @@ -75,29 +75,41 @@ do_tracer(Nodes0,PI,Client,Traci) -> do_tracer(Clients,PI,Traci). do_tracer(Clients,PI,Traci) -> - ShellOutput = proplists:get_value(shell, Traci, false), - {ClientSucc,Succ} = + Shell = proplists:get_value(shell, Traci, false), + DefShell = fun(Trace) -> dbg:dhandler(Trace, standard_io) end, + {ClientSucc,Succ} = lists:foldl( - fun({N,{local,File},TF},{CS,S}) -> - TF2 = case ShellOutput of - only -> none; - _ -> TF - end, - [_Sname,Host] = string:tokens(atom_to_list(N),"@"), + fun({N,{local,File},TF},{CS,S}) -> + {TF2, FileInfo, ShellOutput} = + case Shell of + only -> {none, shell_only, DefShell}; + true -> {TF, {file,File}, DefShell}; + {only,Fun} -> {none, shell_only, Fun}; + Fun when is_function(Fun) -> {TF, {file,File}, Fun}; + _ -> {TF, {file,File}, false} + end, + Host = case N of + nonode@nohost -> + {ok, H} = inet:gethostname(), + H; + _ -> + [_,H] = string:tokens(atom_to_list(N),"@"), + H + end, case catch dbg:tracer(N,port,dbg:trace_port(ip,0)) of {ok,N} -> {ok,Port} = dbg:trace_port_control(N,get_listen_port), {ok,T} = dbg:get_tracer(N), rpc:call(N,seq_trace,set_system_tracer,[T]), dbg:trace_client(ip,{Host,Port}, - {fun ip_to_file/2,{{file,File}, ShellOutput}}), + {fun ip_to_file/2,{FileInfo, ShellOutput}}), {[{N,{local,File,Port},TF2}|CS], [N|S]}; Other -> display_warning(N,{cannot_open_ip_trace_port, Host, Other}), {CS, S} - end; + end; ({N,C,_}=Client,{CS,S}) -> case catch dbg:tracer(N,port,dbg:trace_port(file,C)) of {ok,N} -> @@ -620,7 +632,7 @@ stop_opts(Opts) -> case {FormatData, lists:member(return_fetch_dir, Opts)} of {false, true} -> {fetch, FetchDir}; % if we specify return_fetch_dir, the data should be fetched - {false, false} -> + {false, false} -> case lists:member(nofetch,Opts) of false -> {fetch, FetchDir}; true -> nofetch @@ -1275,10 +1287,10 @@ display_warning(Item,Warning) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Trace client which reads an IP port and puts data directly to a file. %%% This is used when tracing remote nodes with no file system. -ip_to_file({metadata,_,_},{_, only} = State) -> +ip_to_file({metadata,_,_},{shell_only, _} = State) -> State; -ip_to_file(Trace, {_, only} = State) -> - dbg:dhandler(Trace, standard_io), +ip_to_file(Trace, {shell_only, Fun} = State) -> + Fun(Trace), State; ip_to_file(Trace,{{file,File}, ShellOutput}) -> Fun = dbg:trace_port(file,File), %File can be a filename or a wrap spec @@ -1302,8 +1314,8 @@ ip_to_file(Trace,{Port, ShellOutput}) -> erlang:port_command(Port,B), {Port, ShellOutput}. -show_trace(Trace, true) -> - dbg:dhandler(Trace, standard_io); +show_trace(Trace, Fun) when is_function(Fun) -> + Fun(Trace); show_trace(_, _) -> ok. -- cgit v1.2.3