diff options
Diffstat (limited to 'lib/et')
-rw-r--r-- | lib/et/src/Makefile | 5 | ||||
-rw-r--r-- | lib/et/src/et.app.src | 4 | ||||
-rw-r--r-- | lib/et/src/et_collector.erl | 2 | ||||
-rw-r--r-- | lib/et/src/et_gs_contents_viewer.erl | 602 | ||||
-rw-r--r-- | lib/et/src/et_gs_viewer.erl | 1498 | ||||
-rw-r--r-- | lib/et/src/et_viewer.erl | 19 | ||||
-rw-r--r-- | lib/et/src/modules.mk | 4 |
7 files changed, 7 insertions, 2127 deletions
diff --git a/lib/et/src/Makefile b/lib/et/src/Makefile index 386169fe95..377e593712 100644 --- a/lib/et/src/Makefile +++ b/lib/et/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2000-2012. All Rights Reserved. +# Copyright Ericsson AB 2000-2013. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -127,15 +127,12 @@ $(EBIN)/et_selector.$(EMULATOR): et_selector.erl ../include/et.hrl $(EBIN)/et_contents_viewer.$(EMULATOR): et_contents_viewer.erl ../include/et.hrl et_internal.hrl -$(EBIN)/et_gs_contents_viewer.$(EMULATOR): et_gs_contents_viewer.erl ../include/et.hrl et_internal.hrl $(EBIN)/et_wx_contents_viewer.$(EMULATOR): et_wx_contents_viewer.erl ../include/et.hrl et_internal.hrl $(EBIN)/et_collector.$(EMULATOR): et_collector.erl ../include/et.hrl et_internal.hrl $(EBIN)/et_viewer.$(EMULATOR): et_viewer.erl ../include/et.hrl et_internal.hrl -$(EBIN)/et_gs_viewer.$(EMULATOR): et_gs_viewer.erl ../include/et.hrl et_internal.hrl - $(EBIN)/et_wx_viewer.$(EMULATOR): et_wx_viewer.erl ../include/et.hrl et_internal.hrl diff --git a/lib/et/src/et.app.src b/lib/et/src/et.app.src index fd203e3e44..f7189a4197 100644 --- a/lib/et/src/et.app.src +++ b/lib/et/src/et.app.src @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2010. All Rights Reserved. +%% Copyright Ericsson AB 2002-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -24,8 +24,6 @@ [ et, et_collector, - et_gs_contents_viewer, - et_gs_viewer, et_selector, et_viewer, et_wx_contents_viewer, diff --git a/lib/et/src/et_collector.erl b/lib/et/src/et_collector.erl index a78b30c419..e05c67be60 100644 --- a/lib/et/src/et_collector.erl +++ b/lib/et/src/et_collector.erl @@ -409,8 +409,6 @@ report(TH, TraceOrEvent) when is_record(TH, table_handle) -> report(TH#table_handle.collector_pid, TraceOrEvent) end end; -report(TH, end_of_trace) when is_record(TH, table_handle) -> - {ok, TH}; report(_, Bad) -> exit({bad_event, Bad}). diff --git a/lib/et/src/et_gs_contents_viewer.erl b/lib/et/src/et_gs_contents_viewer.erl deleted file mode 100644 index 2d414f10b4..0000000000 --- a/lib/et/src/et_gs_contents_viewer.erl +++ /dev/null @@ -1,602 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2000-2012. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% -%%---------------------------------------------------------------------- -%% Purpose: Displays details of a trace event -%%---------------------------------------------------------------------- - --module(et_gs_contents_viewer). --compile([{nowarn_deprecated_function,{gs,config,2}}, - {nowarn_deprecated_function,{gs,destroy,1}}, - {nowarn_deprecated_function,{gs,editor,2}}, - {nowarn_deprecated_function,{gs,frame,2}}, - {nowarn_deprecated_function,{gs,menu,2}}, - {nowarn_deprecated_function,{gs,menubar,2}}, - {nowarn_deprecated_function,{gs,menubutton,2}}, - {nowarn_deprecated_function,{gs,menuitem,2}}, - {nowarn_deprecated_function,{gs,menuitem,3}}, - {nowarn_deprecated_function,{gs,start,0}}, - {nowarn_deprecated_function,{gs,window,2}}]). - --behaviour(gen_server). - -%% External exports --export([start_link/1, - stop/1]). - -%% gen_server callbacks --export([init/1, terminate/2, code_change/3, - handle_call/3, handle_cast/2, handle_info/2]). - --include("../include/et.hrl"). --include("et_internal.hrl"). - --record(state, {parent_pid, % Pid of parent process - viewer_pid, % Pid of viewer process - event_order, % Field to be used as primary key - event, % The original event - filtered_event, % Event processed by active filter - active_filter, % Name of the active filter - filters, % List of possible filters - win, % GUI: Window object - packer, % GUI: Packer object - width, % GUI: Window width - height}). % GUI: Window height - -%%%---------------------------------------------------------------------- -%%% Client side -%%%---------------------------------------------------------------------- - -%%---------------------------------------------------------------------- -%% start_link(Options) -> {ok, ContentsPid} | {error, Reason} -%% -%% Start an viewer for the event contents as window in GS -%% -%% Options = [option()] -%% -%% option() = -%% -%% {parent_pid, pid()} | % Pid of parent process -%% {viewer_pid, pid()} | % Pid of viewer process -%% {event_order, event_order()} | % Field to be used as primary key -%% {active_filter, atom()} | % Name of the active filter -%% {filter, atom(), fun()} % A named filter fun -%% -%% event_order() = 'trace_ts' | 'event_ts' -%% ContentsPid = pid() -%% Reason = term() -%%---------------------------------------------------------------------- - -start_link(Options) -> - case parse_opt(Options, default_state()) of - {ok, S} -> - case gen_server:start_link(?MODULE, [S], []) of - {ok, ContentsPid} when S#state.parent_pid =/= self() -> - unlink(ContentsPid), - {ok, ContentsPid}; - Other -> - Other - end; - {error, Reason} -> - {error, Reason} - end. - -default_state() -> - #state{parent_pid = self(), - viewer_pid = undefined, - active_filter = ?DEFAULT_FILTER_NAME, - filters = [?DEFAULT_FILTER], - width = 600, - height = 300}. - -parse_opt([], S) -> - Name = S#state.active_filter, - Filters = S#state.filters, - if - S#state.event =:= undefined -> - {error, {badarg, no_event}}; - is_atom(Name) -> - case lists:keysearch(Name, #filter.name, Filters) of - {value, F} when is_record(F, filter) -> - {ok, S#state{active_filter = Name}}; - false -> - {error, {badarg, {no_such_filter, Name, Filters}}} - end - end; -parse_opt([H | T], S) -> - case H of - {parent_pid, ParentPid} when is_pid(ParentPid) -> - parse_opt(T, S#state{parent_pid = ParentPid}); - {viewer_pid, ViewerPid} when is_pid(ViewerPid) -> - parse_opt(T, S#state{viewer_pid = ViewerPid}); - {event_order, trace_ts} -> - parse_opt(T, S#state{event_order = trace_ts}); - {event_order, event_ts} -> - parse_opt(T, S#state{event_order = event_ts}); - {event, Event} when is_record(Event, event) -> - parse_opt(T, S#state{event = Event}); - {active_filter, Name} when is_atom(Name) -> - parse_opt(T, S#state{active_filter = Name}); - F when is_record(F, filter), - is_atom(F#filter.name), - is_function(F#filter.function) -> - Filters = lists:keydelete(F#filter.name, #filter.name, S#state.filters), - Filters2 = lists:keysort(#filter.name, [F | Filters]), - parse_opt(T, S#state{filters = Filters2}); - {width, Width} when is_integer(Width), Width > 0 -> - parse_opt(T, S#state{width = Width}); - {height, Height} when is_integer(Height), Height > 0 -> - parse_opt(T, S#state{height = Height}); - Bad -> - {error, {bad_option, Bad}} - end; -parse_opt(BadList, _S) -> - {error, {bad_option_list, BadList}}. - -%%---------------------------------------------------------------------- -%% stop(ContentsPid) -> ok -%% -%% Stops a contents viewer process -%% -%% ContentsPid = pid() -%%---------------------------------------------------------------------- - -stop(ContentsPid) -> - unlink(ContentsPid), - call(ContentsPid, stop). - -call(ContentsPid, Request) -> - gen_server:call(ContentsPid, Request, infinity). - -%%%---------------------------------------------------------------------- -%%% Callback functions from gen_server -%%%---------------------------------------------------------------------- - -%%---------------------------------------------------------------------- -%% Func: init/1 -%% Returns: {ok, State} | -%% {ok, State, Timeout} | -%% ignore | -%% {stop, Reason} -%%---------------------------------------------------------------------- - -init([S]) when is_record(S, state) -> - process_flag(trap_exit, true), - S2 = create_window(S), - {ok, S2}. - -%%---------------------------------------------------------------------- -%% Func: handle_call/3 -%% Returns: {reply, Reply, State} | -%% {reply, Reply, State, Timeout} | -%% {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, Reply, State} | (terminate/2 is called) -%% {stop, Reason, State} (terminate/2 is called) -%%---------------------------------------------------------------------- - -handle_call(stop, _From, S) -> - unlink(S#state.parent_pid), - {stop, shutdown, ok, S}; -handle_call(Request, From, S) -> - ok = error_logger:format("~p(~p): handle_call(~p, ~p, ~p)~n", - [?MODULE, self(), Request, From, S]), - Reply = {error, {bad_request, Request}}, - {reply, Reply, S}. - -%%---------------------------------------------------------------------- -%% Func: handle_cast/2 -%% Returns: {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} (terminate/2 is called) -%%---------------------------------------------------------------------- - -handle_cast(Msg, S) -> - ok = error_logger:format("~p(~p): handle_cast(~p, ~p)~n", - [?MODULE, self(), Msg, S]), - {noreply, S}. - -%%---------------------------------------------------------------------- -%% Func: handle_info/2 -%% Returns: {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} (terminate/2 is called) -%%---------------------------------------------------------------------- - -handle_info({gs, Button, click, Data, _Other}, S) -> - case Button of - close -> - gs:destroy(S#state.win), - {stop, normal, S}; - save -> - Event = S#state.event, - Bin = list_to_binary(event_to_string(Event, S#state.event_order)), - TimeStamp = - case S#state.event_order of - trace_ts -> Event#event.trace_ts; - event_ts -> Event#event.event_ts - end, - FileName = ["et_contents_viewer_", now_to_string(TimeStamp), ".save"], - file:write_file(lists:flatten(FileName), Bin), - {noreply, S}; - _PopupMenuItem when is_record(Data, filter) -> - F = Data, - ChildState= S#state{active_filter = F#filter.name}, - case gen_server:start_link(?MODULE, [ChildState], []) of - {ok, Pid} when S#state.parent_pid =/= self() -> - unlink(Pid), - {noreply, S}; - _ -> - {noreply, S} - end; - {hide, Actors} -> - send_viewer_event(S, {delete_actors, Actors}), - {noreply, S}; - {show, Actors} -> - send_viewer_event(S, {insert_actors, Actors}), - {noreply, S}; - {mode, Mode} -> - send_viewer_event(S, {mode, Mode}), - {noreply, S}; - Nyi -> - ok = error_logger:format("~p: click ~p ignored (nyi)~n", - [?MODULE, Nyi]), - {noreply, S} - end; -handle_info({gs, _Obj, destroy,_, _}, S) -> - unlink(S#state.parent_pid), - gs:destroy(S#state.win), - {stop, normal, S}; -handle_info({gs, _Obj, keypress, _, [KeySym, _Keycode, _Shift, _Control | _]}, S) -> - case KeySym of - 'c' -> - gs:destroy(S#state.win), - {stop, normal, S}; - - 'f' -> - E = S#state.filtered_event, - From = E#event.from, - send_viewer_event(S, {delete_actors, [From]}), - {noreply, S}; - 't' -> - E = S#state.filtered_event, - To = E#event.to, - send_viewer_event(S, {delete_actors, [To]}), - {noreply, S}; - 'b' -> - E = S#state.filtered_event, - From = E#event.from, - To = E#event.to, - send_viewer_event(S, {delete_actors, [From, To]}), - {noreply, S}; - - 'F' -> - E = S#state.filtered_event, - From = E#event.from, - send_viewer_event(S, {insert_actors, [From]}), - {noreply, S}; - 'T' -> - E = S#state.filtered_event, - To = E#event.to, - send_viewer_event(S, {insert_actors, [To]}), - {noreply, S}; - 'B' -> - E = S#state.filtered_event, - From = E#event.from, - To = E#event.to, - send_viewer_event(S, {insert_actors, [From, To]}), - {noreply, S}; - - 's' -> - E = S#state.filtered_event, - From = E#event.from, - To = E#event.to, - First = et_collector:make_key(S#state.event_order, E), - Mode = {search_actors, forward, First, [From, To]}, - send_viewer_event(S, {mode, Mode}), - {noreply, S}; - 'r' -> - E = S#state.filtered_event, - From = E#event.from, - To = E#event.to, - First = et_collector:make_key(S#state.event_order, E), - Mode = {search_actors, reverse, First, [From, To]}, - send_viewer_event(S, {mode, Mode}), - {noreply, S}; - 'a' -> - send_viewer_event(S, {mode, all}), - {noreply, S}; - - 0 -> - case lists:keysearch(?DEFAULT_FILTER_NAME, #filter.name, S#state.filters) of - {value, F} when is_record(F, filter) -> - ChildState= S#state{active_filter = F#filter.name}, - case gen_server:start_link(?MODULE, [ChildState], []) of - {ok, Pid} when S#state.parent_pid =/= self() -> - unlink(Pid); - _ -> - ignore - end; - false -> - ignore - end, - {noreply, S}; - Int when is_integer(Int), Int > 0, Int =< 9 -> - case catch lists:nth(Int, S#state.filters) of - F when is_record(F, filter) -> - ChildState= S#state{active_filter = F#filter.name}, - case gen_server:start_link(?MODULE, [ChildState], []) of - {ok, Pid} when S#state.parent_pid =/= self() -> - unlink(Pid); - _ -> - ignore - end; - {'EXIT', _} -> - ignore - end, - {noreply, S}; - - 'Shift_L' -> - {noreply, S}; - 'Shift_R' -> - {noreply, S}; - 'Caps_Lock' -> - {noreply, S}; - _ -> - io:format("~p: ignored: ~p~n", [?MODULE, KeySym]), - {noreply, S} - end; -handle_info({gs, _Obj, configure, [], [W, H | _]}, S) -> - gs:config(S#state.packer, [{width, W},{height, H}]), - S2 = S#state{width = W, height = H}, - {noreply, S2}; -handle_info({'EXIT', Pid, Reason}, S) -> - if - Pid =:= S#state.parent_pid -> - unlink(Pid), - {stop, Reason, S}; - true -> - {noreply, S} - end; -handle_info(Info, S) -> - ok = error_logger:format("~p(~p): handle_info(~p, ~p)~n", - [?MODULE, self(), Info, S]), - {noreply, S}. - -%%---------------------------------------------------------------------- -%% Func: terminate/2 -%% Purpose: Shutdown the server -%% Returns: any (ignored by gen_server) -%%---------------------------------------------------------------------- - -terminate(_Reason, _S) -> - ignore. - -%%---------------------------------------------------------------------- -%% Func: code_change/3 -%% Purpose: Convert process state when code is changed -%% Returns: {ok, NewState} -%%---------------------------------------------------------------------- - -code_change(_OldVsn, S, _Extra) -> - {ok, S}. - -%%%---------------------------------------------------------------------- -%%% Handle graphics -%%%---------------------------------------------------------------------- - -create_window(S) -> - H = S#state.height, - W = S#state.width, - Name = S#state.active_filter, - Title = lists:concat([?MODULE, " (filter: ", Name, ")"]), - WinOpt = [{title, Title}, {configure, true}, - {width, W}, {height, H}], - GS = gs:start(), - Win = gs:window(GS, WinOpt), - Bar = gs:menubar(Win, []), - create_file_menu(Bar), - PackerOpt = [{packer_x, [{stretch, 1}]}, - {packer_y, [{stretch, 1}, {fixed, 25}]}, - {x, 0}, {y, 25}], - Packer = gs:frame(Win, PackerOpt), - EditorOpt = [{pack_xy, {1, 1}}, {vscroll, right}, {hscroll, bottom}, - {wrap, none}, - {bg, lightblue}, {font, {courier, 12}}], - Editor = gs:editor(Packer, EditorOpt), - FilteredEvent = config_editor(Editor, S), - S2 = S#state{win = Win, packer = Packer, filtered_event = FilteredEvent}, - create_hide_menu(Bar, S2), - create_search_menu(Bar, S2), - create_filter_menu(Bar, S#state.filters), - gs:config(Packer, [{width, W}, {height, H}]), - gs:config(Win, [{map,true}, {keypress, true}]), - S2. - -create_file_menu(Bar) -> - Button = gs:menubutton(Bar, [{label, {text, "File"}}]), - Menu = gs:menu(Button, []), - gs:menuitem(close, Menu, [{label, {text,"Close (c)"}}]), - gs:menuitem(save, Menu, [{label, {text,"Save"}}]). - -create_filter_menu(Bar, Filters) -> - Button = gs:menubutton(Bar, [{label, {text, "Filters"}}]), - Menu = gs:menu(Button, []), - gs:menuitem(Menu, [{label, {text, "Select Filter"}}, {bg, lightblue}, {enable, false}]), - gs:menuitem(Menu, [{itemtype, separator}]), - Item = fun(F, N) when F#filter.name =:= ?DEFAULT_FILTER_NAME-> - Label = lists:concat([pad_string(F#filter.name, 20), "(0)"]), - gs:menuitem(Menu, [{label, {text, Label}}, {data, F}]), - N + 1; - (F, N) -> - Name = F#filter.name, - Label = lists:concat([pad_string(Name, 20), "(", N, ")"]), - gs:menuitem(Menu, [{label, {text, Label}}, {data, F}]), - N + 1 - end, - Filters2 = lists:keysort(#filter.name, Filters), - lists:foldl(Item, 1, Filters2), - Menu. - -create_hide_menu(Bar, S) -> - Button = gs:menubutton(Bar, [{label, {text, "Hide"}}]), - Menu = gs:menu(Button, []), - E = S#state.filtered_event, - From = E#event.from, - To = E#event.to, - if - S#state.viewer_pid =:= undefined -> - ignore; - From =:= To -> - gs:menuitem(Menu, [{label, {text, "Hide actor in Viewer "}}, {bg, lightblue}, {enable, false}]), - gs:menuitem(Menu, [{itemtype, separator}]), - gs:menuitem({hide, [From]}, Menu, [{label, {text,"From=To (f|t|b)"}}]), - gs:menuitem(Menu, [{itemtype, separator}]), - gs:menuitem(Menu, [{label, {text, "Show actor in Viewer "}}, {bg, lightblue}, {enable, false}]), - gs:menuitem(Menu, [{itemtype, separator}]), - gs:menuitem({show, [From]}, Menu, [{label, {text,"From=To (F|T|B)"}}]); - true -> - gs:menuitem(Menu, [{label, {text, "Hide actor in Viewer "}}, {bg, lightblue}, {enable, false}]), - gs:menuitem(Menu, [{itemtype, separator}]), - gs:menuitem({hide, [From]}, Menu, [{label, {text,"From (f)"}}]), - gs:menuitem({hide, [To]}, Menu, [{label, {text,"To (t)"}}]), - gs:menuitem({hide, [From, To]}, Menu, [{label, {text,"Both (b)"}}]), - gs:menuitem(Menu, [{itemtype, separator}]), - gs:menuitem(Menu, [{label, {text, "Show actor in Viewer "}}, {bg, lightblue}, {enable, false}]), - gs:menuitem(Menu, [{itemtype, separator}]), - gs:menuitem({show, [From]}, Menu, [{label, {text,"From (F)"}}]), - gs:menuitem({show, [To]}, Menu, [{label, {text,"To (T)"}}]), - gs:menuitem({show, [From, To]}, Menu, [{label, {text,"Both (B)"}}]) - end. - -create_search_menu(Bar, S) -> - Button = gs:menubutton(Bar, [{label, {text, "Search"}}]), - Menu = gs:menu(Button, []), - E = S#state.filtered_event, - From = E#event.from, - To = E#event.to, - gs:menuitem(Menu, [{label, {text, "Search in Viewer "}}, - {bg, lightblue}, {enable, false}]), - gs:menuitem(Menu, [{itemtype, separator}]), - if - S#state.viewer_pid =:= undefined -> - S; - From =:= To -> - Key = et_collector:make_key(S#state.event_order, E), - ModeS = {search_actors, forward, Key, [From]}, - ModeR = {search_actors, reverse, Key, [From]}, - gs:menuitem({mode, ModeS}, Menu, [{label, {text,"Forward from this event (s)"}}]), - gs:menuitem({mode, ModeR}, Menu, [{label, {text,"Reverse from this event (r)"}}]); - true -> - Key = et_collector:make_key(S#state.event_order, E), - ModeS = {search_actors, forward, Key, [From, To]}, - ModeR = {search_actors, reverse, Key, [From, To]}, - gs:menuitem({mode, ModeS}, Menu, [{label, {text,"Forward from this event (s)"}}]), - gs:menuitem({mode, ModeR}, Menu, [{label, {text,"Reverse from this event (r)"}}]) - end, - gs:menuitem({mode, all}, Menu, [{label, {text,"Abort search. Display all (a)"}}]). - -config_editor(Editor, S) -> - Event = S#state.event, - Name = S#state.active_filter, - {value, F} = lists:keysearch(Name, #filter.name, S#state.filters), - FilterFun = F#filter.function, - case catch FilterFun(Event) of - true -> - do_config_editor(Editor, Event, lightblue, S#state.event_order); - {true, Event2} when is_record(Event2, event) -> - do_config_editor(Editor, Event2, lightblue, S#state.event_order); - false -> - do_config_editor(Editor, Event, red, S#state.event_order); - Bad -> - Contents = {bad_filter, Name, Bad}, - BadEvent = Event#event{contents = Contents}, - do_config_editor(Editor, BadEvent, red, S#state.event_order) - end. - -do_config_editor(Editor, Event, Colour, TsKey) -> - String = event_to_string(Event, TsKey), - gs:config(Editor, {insert, {'end', String}}), - gs:config(Editor, {enable, false}), - gs:config(Editor, {bg, Colour}), - Event. - -%%%---------------------------------------------------------------------- -%%% String handling -%%%---------------------------------------------------------------------- - -term_to_string(Term) -> - case catch io_lib:format("~s", [Term]) of - {'EXIT', _} -> io_lib:format("~p", [Term]); - GoodString -> GoodString - end. - -now_to_string({Mega, Sec, Micro} = Now) - when is_integer(Mega), is_integer(Sec), is_integer(Micro) -> - {{Y, Mo, D}, {H, Mi, S}} = calendar:now_to_universal_time(Now), - lists:concat([Y, "-", Mo, "-", D, " ", H, ".", Mi, ".", S, ".", Micro]); -now_to_string(Other) -> - term_to_string(Other). - -event_to_string(Event, TsKey) -> - ReportedTs = Event#event.trace_ts, - ParsedTs = Event#event.event_ts, - Deep = - ["DETAIL LEVEL: ", term_to_string(Event#event.detail_level), - "\nLABEL: ", term_to_string(Event#event.label), - case Event#event.from =:= Event#event.to of - true -> - ["\nACTOR: ", term_to_string(Event#event.from)]; - false -> - ["\nFROM: ", term_to_string(Event#event.from), - "\nTO: ", term_to_string(Event#event.to)] - end, - case ReportedTs =:= ParsedTs of - true -> - ["\nPARSED: ", now_to_string(ParsedTs)]; - false -> - case TsKey of - trace_ts -> - ["\nTRACE_TS: ", now_to_string(ReportedTs), - "\nEVENT_TS: ", now_to_string(ParsedTs)]; - event_ts -> - ["\nEVENT_TS: ", now_to_string(ParsedTs), - "\nTRACE_TS: ", now_to_string(ReportedTs)] - end - end, - "\nCONTENTS:\n\n", term_to_string(Event#event.contents)], - lists:flatten(Deep). - -pad_string(Atom, MinLen) when is_atom(Atom) -> - pad_string(atom_to_list(Atom), MinLen); -pad_string(String, MinLen) when is_integer(MinLen), MinLen >= 0 -> - Len = length(String), - case Len >= MinLen of - true -> - String; - false -> - String ++ lists:duplicate(MinLen - Len, $ ) - end. - -send_viewer_event(S, Event) -> - case S#state.viewer_pid of - ViewerPid when is_pid(ViewerPid) -> - ViewerPid ! {et, Event}; - undefined -> - ignore - end. diff --git a/lib/et/src/et_gs_viewer.erl b/lib/et/src/et_gs_viewer.erl deleted file mode 100644 index ce8634d09f..0000000000 --- a/lib/et/src/et_gs_viewer.erl +++ /dev/null @@ -1,1498 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2009-2012. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% -%%---------------------------------------------------------------------- -%% Purpose: Displays a sequence chart for trace events (messages/actions) -%%---------------------------------------------------------------------- - --module(et_gs_viewer). --compile([{nowarn_deprecated_function,{gs,canvas,2}}, - {nowarn_deprecated_function,{gs,checkbutton,3}}, - {nowarn_deprecated_function,{gs,config,2}}, - {nowarn_deprecated_function,{gs,destroy,1}}, - {nowarn_deprecated_function,{gs,frame,2}}, - {nowarn_deprecated_function,{gs,line,2}}, - {nowarn_deprecated_function,{gs,menu,2}}, - {nowarn_deprecated_function,{gs,menu,3}}, - {nowarn_deprecated_function,{gs,menubar,2}}, - {nowarn_deprecated_function,{gs,menubutton,2}}, - {nowarn_deprecated_function,{gs,menubutton,3}}, - {nowarn_deprecated_function,{gs,menuitem,2}}, - {nowarn_deprecated_function,{gs,menuitem,3}}, - {nowarn_deprecated_function,{gs,scale,2}}, - {nowarn_deprecated_function,{gs,start,0}}, - {nowarn_deprecated_function,{gs,text,2}}, - {nowarn_deprecated_function,{gs,window,2}}]). - --behaviour(gen_server). - -%% External exports --export([start_link/1]). - -%% gen_server callbacks --export([init/1, terminate/2, code_change/3, - handle_call/3, handle_cast/2, handle_info/2]). - --include("../include/et.hrl"). --include("et_internal.hrl"). - --define(unknown, "UNKNOWN"). - --record(state, - {parent_pid, % Pid of parent process - collector_pid, % Pid of collector process - event_order, % Field to be used as primary key - trace_pattern, % Collector trace pattern - active_filter, % Name of the active filter - filters, % List of possible filters - selected_actor, % Actor selected by user - first_event, % Key of first event (regardless of visibility) - last_event, % Key of last event (regardless of visibility) - max_events, % Maximum number of shown events - events, % Queue containg all event keys (regardless of visibility) - max_actors, % Maximum number of shown actors - actors, % List of known actors - refresh_needed, % Refresh is needed in order to show all actors - display_mode, % Display all or only matching actors - detail_level, % Show only events with lesser detail level - hide_actions, % Hide/show events where to == from actor (bool) - hide_unknown, % Hide/show events with unknown actor (bool) - is_suspended, % Suspend viewer updates (bool) - title, % GUI: Window title - win, % GUI: Window object - menubar, % GUI: Menu bar object - packer, % GUI: Packer object - width, % GUI: Window width - height, % GUI: Window height - scale, % GUI: Scaling factor on canvas - font, % GUI: Font to be used on text labels - canvas_width, % GUI: Canvas width - canvas_height, % GUI: Canvas height - canvas, % GUI: Canvas object - y_pos}). % GUI: Current y position on canvas - --record(actor, {name, string}). - --define(initial_x, 10). --define(incr_x, 60). --define(initial_y, 15). --define(incr_y, 15). - -%%%---------------------------------------------------------------------- -%%% Client side -%%%---------------------------------------------------------------------- - -start_link(Options) -> - case parse_opt(Options, default_state(), []) of - {ok, S, CollectorOpt} -> - case S#state.collector_pid of - CollectorPid when is_pid(CollectorPid) -> - case gen_server:start_link(?MODULE, [S], []) of - {ok, Pid} when S#state.parent_pid =/= self() -> - unlink(Pid), - {ok, Pid}; - Other -> - Other - end; - undefined -> - case et_collector:start_link(CollectorOpt) of - {ok, CollectorPid} -> - S2 = S#state{collector_pid = CollectorPid}, - case gen_server:start_link(?MODULE, [S2], []) of - {ok, Pid} when S#state.parent_pid =/= self() -> - unlink(Pid), - {ok, Pid}; - Other -> - Other - end; - {error, Reason} -> - {error, {et_collector, Reason}} - end - end; - {error, Reason} -> - {error, Reason} - end. - -default_state() -> - #state{parent_pid = self(), - collector_pid = undefined, - detail_level = ?detail_level_max, - active_filter = ?DEFAULT_FILTER_NAME, - filters = [?DEFAULT_FILTER], - event_order = trace_ts, - is_suspended = false, - max_events = 100, - first_event = first, - last_event = first, - events = queue_new(), - max_actors = 5, - actors = [create_actor(?unknown)], - selected_actor = ?unknown, - hide_actions = false, - hide_unknown = false, - refresh_needed = false, - display_mode = all, - scale = 2, - canvas_height = 0, - canvas_width = 0, - width = 800, - height = 600}. - -parse_opt([], S, CollectorOpt) -> - {ok, S, [{parent_pid, S#state.parent_pid} | CollectorOpt]}; -parse_opt([H | T], S, CollectorOpt) -> - case H of - {parent_pid, Parent} when Parent =:= undefined -> - CollectorOpt2 = [H | CollectorOpt], - parse_opt(T, S#state{parent_pid = Parent}, CollectorOpt2); - {parent_pid, Parent} when is_pid(Parent) -> - CollectorOpt2 = [H | CollectorOpt], - parse_opt(T, S#state{parent_pid = Parent}, CollectorOpt2); - {title, Title} -> - parse_opt(T, S#state{title = name_to_string(Title)}, CollectorOpt); - {detail_level, Level} when is_integer(Level), - Level >= ?detail_level_min, - Level =< ?detail_level_max -> - parse_opt(T, S#state{detail_level = Level}, CollectorOpt); - {detail_level, max} -> - parse_opt(T, S#state{detail_level = ?detail_level_max}, CollectorOpt); - {detail_level, min} -> - parse_opt(T, S#state{detail_level = ?detail_level_min}, CollectorOpt); - {is_suspended, true} -> - parse_opt(T, S#state{is_suspended = true}, CollectorOpt); - {is_suspended, false} -> - parse_opt(T, S#state{is_suspended = false}, CollectorOpt); - {scale, Scale} when is_integer(Scale), Scale > 0 -> - parse_opt(T, S#state{scale = Scale}, CollectorOpt); - {width, W} when is_integer(W), W > 0 -> - parse_opt(T, S#state{width = W, canvas_width = W}, CollectorOpt); - {height, WH} when is_integer(WH), WH > 0 -> - parse_opt(T, S#state{height = WH, canvas_height = WH}, CollectorOpt); - {collector_pid, Pid} when is_pid(Pid) -> - parse_opt(T, S#state{collector_pid = Pid}, CollectorOpt); - {collector_pid, undefined} -> - parse_opt(T, S#state{collector_pid = undefined}, CollectorOpt); - {active_filter, Name} when is_atom(Name) -> - parse_opt(T, S#state{active_filter = Name}, CollectorOpt); - {event_order, trace_ts} -> %% BUGBUG: Verify event_order with collector - CollectorOpt2 = [H | CollectorOpt], - parse_opt(T, S#state{event_order = trace_ts}, CollectorOpt2); - {event_order, event_ts} -> %% BUGBUG: Verify event_order with collector - CollectorOpt2 = [H | CollectorOpt], - parse_opt(T, S#state{event_order = event_ts}, CollectorOpt2); - {trace_port, _Port} -> - CollectorOpt2 = [H | CollectorOpt], - parse_opt(T, S, CollectorOpt2); - {trace_max_queue, _Queue} -> - CollectorOpt2 = [H | CollectorOpt], - parse_opt(T, S, CollectorOpt2); - {trace_pattern, _Pattern} -> - CollectorOpt2 = [H | CollectorOpt], - parse_opt(T, S, CollectorOpt2); - {trace_global, _Boolean} -> - CollectorOpt2 = [H | CollectorOpt], - parse_opt(T, S, CollectorOpt2); - {trace_client, _Client} -> - CollectorOpt2 = [H | CollectorOpt], - parse_opt(T, S, CollectorOpt2); - {dict_insert, {filter, Name}, Fun} -> - if - is_atom(Name), is_function(Fun) -> - F = #filter{name = Name, function = Fun}, - Filters = lists:keydelete(Name, #filter.name, S#state.filters), - CollectorOpt2 = [H | CollectorOpt], - parse_opt(T, S#state{filters = Filters ++ [F]}, CollectorOpt2); - true -> - {error, {bad_option, H}} - end; - {dict_insert, {subscriber, Pid}, _Val} -> - if - is_pid(Pid) -> - CollectorOpt2 = [H | CollectorOpt], - parse_opt(T, S, CollectorOpt2); - true -> - {error, {bad_option, H}} - end; - {dict_insert, _Key, _Val} -> - CollectorOpt2 = [H | CollectorOpt], - parse_opt(T, S, CollectorOpt2); - {dict_delete, {filter, Name}} -> - Filters = lists:keydelete(Name, #filter.name, S#state.filters), - CollectorOpt2 = [H | CollectorOpt], - parse_opt(T, S#state{filters = Filters}, CollectorOpt2); - {dict_delete, _Key} -> - CollectorOpt2 = [H | CollectorOpt], - parse_opt(T, S, CollectorOpt2); - {max_events, Max} when is_integer(Max), Max > 0-> - parse_opt(T, S#state{max_events = Max}, CollectorOpt); - {max_events, Max} when Max =:= infinity -> - parse_opt(T, S#state{max_events = Max}, CollectorOpt); - {max_actors, Max} when is_integer(Max), Max >= 0-> - parse_opt(T, S#state{max_actors = Max}, CollectorOpt); - {max_actors, Max} when Max =:= infinity -> - parse_opt(T, S#state{max_actors = Max}, CollectorOpt); - {actors, ActorNames} when is_list(ActorNames) -> - ActorNames2 = - case lists:member(?unknown, ActorNames) of - false -> [?unknown | ActorNames]; - true -> ActorNames - end, - Actors = [create_actor(Name) || Name <- ActorNames2], - parse_opt(T, S#state{actors = Actors}, CollectorOpt); - {first_event, First} -> - parse_opt(T, S#state{first_event = First}, CollectorOpt); - {hide_unknown, Bool} when Bool =:= false -> - parse_opt(T, S#state{hide_unknown = Bool}, CollectorOpt); - {hide_unknown, Bool} when Bool =:= true -> - parse_opt(T, S#state{hide_unknown = Bool}, CollectorOpt); - {hide_actions, Bool} when Bool =:= false -> - parse_opt(T, S#state{hide_actions = Bool}, CollectorOpt); - {hide_actions, Bool} when Bool =:= true -> - parse_opt(T, S#state{hide_actions = Bool}, CollectorOpt); - {display_mode, Mode = all} -> - parse_opt(T, S#state{display_mode = Mode}, CollectorOpt); - {display_mode, Mode = {search_actors, Dir, _Key, Actors}} when is_list(Actors), Dir =:= forward -> - parse_opt(T, S#state{display_mode = Mode}, CollectorOpt); - {display_mode, Mode = {search_actors, Dir, _Key, Actors}} when is_list(Actors), Dir =:= reverse -> - parse_opt(T, S#state{display_mode = Mode}, CollectorOpt); - - Bad -> - {error, {bad_option, Bad}} - end; -parse_opt(BadList, _S, _CollectorOpt) -> - {error, {bad_option_list, BadList}}. - -do_dict_insert({filter, Name}, Fun, S) when is_atom(Name), is_function(Fun) -> - F = #filter{name = Name, function = Fun}, - Filters = lists:keydelete(Name, #filter.name, S#state.filters), - Filters2 = lists:keysort(#filter.name, [F | Filters]), - gs:destroy(filter_menu), - create_filter_menu(S#state.active_filter, Filters2), - S#state{filters = Filters2}; -do_dict_insert(_Key, _Val, S) -> - %% ok = error_logger:format("~p(~p): handle_info({et, {dict_insert, ~p, ~p}})~n", - %% [?MODULE, self(), Key, Val]), - S. - -do_dict_delete({filter, Name}, S) when is_atom(Name), Name =/= S#state.active_filter -> - Filters = lists:keydelete(Name, #filter.name, S#state.filters), - gs:destroy(filter_menu), - create_filter_menu(S#state.active_filter, Filters), - S#state{filters = Filters}; -do_dict_delete(_Key, S) -> - %% ok = error_logger:format("~p(~p): handle_info({et, {dict_delete, ~p}})~n", - %% [?MODULE, self(), Key]), - S. - -%%%---------------------------------------------------------------------- -%%% Callback functions from gen_server -%%%---------------------------------------------------------------------- - -%%---------------------------------------------------------------------- -%% Func: init/1 -%% Returns: {ok, State} | -%% {ok, State, Timeout} | -%% ignore | -%% {stop, Reason} -%%---------------------------------------------------------------------- - -init([S]) when is_record(S, state) -> - process_flag(trap_exit, true), - InitialTimeout = 0, - case S#state.parent_pid of - undefined -> - ignore; - Pid when is_pid(Pid) -> - link(Pid) - end, - et_collector:dict_insert(S#state.collector_pid, - {subscriber, self()}, - ?MODULE), - {ok, create_main_window(S), InitialTimeout}. - -%%---------------------------------------------------------------------- -%% Func: handle_call/3 -%% Returns: {reply, Reply, State} | -%% {reply, Reply, State, Timeout} | -%% {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, Reply, State} | (terminate/2 is called) -%% {stop, Reason, State} (terminate/2 is called) -%%---------------------------------------------------------------------- - -handle_call(get_collector_pid, _From, S) -> - Reply = S#state.collector_pid, - reply(Reply, S); -handle_call(stop, _From, S) -> - gs:destroy(S#state.win), - {stop, shutdown, ok, S}; -handle_call(Request, From, S) -> - ok = error_logger:format("~p(~p): handle_call(~p, ~p, ~p)~n", - [?MODULE, self(), Request, From, S]), - Reply = {error, {bad_request, Request}}, - reply(Reply, S). - -%%---------------------------------------------------------------------- -%% Func: handle_cast/2 -%% Returns: {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} (terminate/2 is called) -%%---------------------------------------------------------------------- - -handle_cast(Msg, S) -> - ok = error_logger:format("~p(~p): handle_cast(~p, ~p)~n", - [?MODULE, self(), Msg, S]), - noreply(S). - -%%---------------------------------------------------------------------- -%% Func: handle_info/2 -%% Returns: {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} (terminate/2 is called) -%%---------------------------------------------------------------------- - -handle_info({et, {more_events, _Size}}, S) -> - noreply(S); -handle_info({et, {insert_actors, ActorNames}}, S) when is_list(ActorNames) -> - Fun = fun(N, Actors) -> - case lists:keymember(N, #actor.name, Actors) of - true -> Actors; - false -> Actors ++ [create_actor(N)] - end - end, - Actors = lists:foldl(Fun, S#state.actors, ActorNames), - S2 = refresh_main_window(S#state{actors = Actors}), - noreply(S2); -handle_info({et, {delete_actors, ActorNames}}, S) when is_list(ActorNames)-> - Fun = fun(N, Actors) when N =:= ?unknown -> - Actors; - (N, Actors) -> - lists:keydelete(N, #actor.name, Actors) - end, - New = lists:foldl(Fun, S#state.actors, ActorNames), - S2 = refresh_main_window(S#state{actors = New}), - noreply(S2); -handle_info({et, {dict_insert, Key, Val}}, S) -> - S2 = do_dict_insert(Key, Val, S), - noreply(S2); -handle_info({et, {dict_delete, Key}}, S) -> - S2 = do_dict_delete(Key, S), - noreply(S2); -handle_info({et, first}, S) -> - S2 = scroll_first(S), - noreply(S2); -handle_info({et, prev}, S) -> - S2 = scroll_prev(S), - noreply(S2); -handle_info({et, next}, S) -> - S2 = scroll_next(S), - noreply(S2); -handle_info({et, last}, S) -> - S2 = scroll_last(S), - noreply(S2); -handle_info({et, refresh}, S) -> - S2 = refresh_main_window(S), - noreply(S2); -handle_info({et, {display_mode, Mode}}, S) -> - S2 = change_display_mode(Mode, S), - noreply(S2); -handle_info({et, close}, S) -> - gs:destroy(S#state.win), - {stop, shutdown, S}; -handle_info({gs, Button, click, Data, Other} = Click, S) -> - CollectorPid = S#state.collector_pid, - case Button of - close -> - gs:destroy(S#state.win), - {stop, shutdown, S}; - suspended -> - case Other of - [_Text, _Group, Bool | _] when Bool =:= true -> - S2 = do_suspend(S), - noreply(S2); - [_Text, _Group, Bool | _] when Bool =:= false -> - S2 = do_resume(S), - noreply(S2); - _ -> - click_error(Click, S), - noreply(S) - end; - hide_actions -> - case Other of - [_Text, _Group, Bool | _] when Bool =:= true -> - S2 = refresh_main_window(S#state{hide_actions = Bool}), - noreply(S2); - [_Text, _Group, Bool | _] when Bool =:= false -> - S2 = refresh_main_window(S#state{hide_actions = Bool}), - noreply(S2); - _ -> - click_error(Click, S), - noreply(S) - end; - hide_unknown -> - case Other of - [_Text, _Group, Bool | _] when Bool =:= true -> - S2 = refresh_main_window(S#state{hide_unknown = Bool}), - noreply(S2); - [_Text, _Group, Bool | _] when Bool =:= false -> - S2 = refresh_main_window(S#state{hide_unknown = Bool}), - noreply(S2); - _ -> - click_error(Click, S), - noreply(S) - end; - up -> - S2 = scroll_up(S), - noreply(S2); - down -> - S2 = scroll_down(S), - noreply(S2); - first -> - S2 = scroll_first(S), - noreply(S2); - prev -> - S2 = scroll_prev(S), - noreply(S2); - next -> - S2 = scroll_next(S), - noreply(S2); - last -> - S2 = scroll_last(S), - noreply(S2); - refresh -> - S2 = refresh_main_window(S), - noreply(S2); - {display_mode, Mode} -> - S2 = change_display_mode(Mode, S), - noreply(S2); - close_all -> - close_all(S); - close_all_others -> - close_all_others(S); - first_all -> - et_collector:multicast(CollectorPid, first), - noreply(S); - prev_all -> - et_collector:multicast(CollectorPid, prev), - noreply(S); - next_all -> - et_collector:multicast(CollectorPid, next), - noreply(S); - last_all -> - et_collector:multicast(CollectorPid, last), - noreply(S); - refresh_all -> - et_collector:multicast(CollectorPid, refresh), - noreply(S); - clear_all -> - et_collector:clear_table(CollectorPid), - et_collector:multicast(CollectorPid, refresh), - noreply(S); - load_all -> - et_collector:start_trace_client(CollectorPid, event_file, "et_viewer.log"), - noreply(S); - save_all -> - et_collector:save_event_file(CollectorPid, - "et_viewer.log", - [existing, write, keep]), - noreply(S); - {open_viewer, Scale} -> - Actors = [A#actor.name || A <- S#state.actors], - open_viewer(Scale, S#state.active_filter, Actors, S), - noreply(S); - _Level when Data =:= detail_level, is_integer(hd(Other)), - hd(Other) >= ?detail_level_min, - hd(Other) =< ?detail_level_max -> - S2 = S#state{detail_level = hd(Other)}, - noreply(S2); - _PopupMenuItem when is_record(Data, filter) -> - open_viewer(S#state.scale, Data#filter.name, [?unknown], S), - noreply(S); - _ -> - click_error(Click, S), - noreply(S) - end; -handle_info({gs, _Obj, destroy,_, _}, S) -> - gs:destroy(S#state.win), - {stop, shutdown, S}; -handle_info({gs, _Obj, buttonpress, _, [_Button, X, Y | _]}, S) -> - S3 = - case y_to_n(Y, S) of - actor -> - %% Actor click - case S#state.actors of - [] -> - S; - _ -> - N = x_to_n(X, S), - A = lists:nth(N, S#state.actors), - S#state{selected_actor = A} - end; - {event, N} -> - %% Event click - List = queue_to_list(S#state.events), - S2 = S#state{events = list_to_queue(List)}, - - Key = lists:nth(N, List), - Pid = S#state.collector_pid, - Fun = fun create_contents_window/2, - case et_collector:iterate(Pid, Key, -1) of - Prev when Prev =:= Key -> - et_collector:iterate(Pid, first, 1, Fun, S2); - Prev -> - et_collector:iterate(Pid, Prev, 1, Fun, S2) - end - end, - noreply(S3); -handle_info({gs, _Obj, buttonrelease, _, [_Button, X, Y | _]}, S) -> - S2 = - case y_to_n(Y, S) of - actor -> - %% Actor click - case S#state.actors of - [] -> - S; - Actors -> - N = x_to_n(X, S), - New = lists:nth(N, S#state.actors), - Old = S#state.selected_actor, - case New#actor.name =:= Old#actor.name of - true -> - A = S#state.selected_actor, - toggle_search_for_actor(A#actor.name, S); - false -> - move_actor(Old, New, Actors, S) - end - end; - {event, _N} -> - %% Event click ignored - S - end, - noreply(S2); -handle_info({gs, _Obj, keypress, _, [KeySym, _Keycode, _Shift, _Control | _]} = Key, S) -> - case KeySym of - 'c' -> - close_all_others(S); - 'C' -> - close_all(S); - 'Up' -> - S2 = scroll_up(S), - noreply(S2); - 'Down' -> - S2 = scroll_down(S), - noreply(S2); - 'f' -> - S2 = scroll_first(S), - noreply(S2); - 'p' -> - S2 = scroll_prev(S), - noreply(S2); - 'Prior' -> - S2 = scroll_prev(S), - noreply(S2); - 'n' -> - S2 = scroll_next(S), - noreply(S2); - 'Next' -> - S2 = scroll_next(S), - noreply(S2); - 'l' -> - S2 = scroll_last(S), - noreply(S2); - 'r' -> - S2 = refresh_main_window(S), - noreply(S2); - 'F' -> - et_collector:multicast(S#state.collector_pid, first), - noreply(S); - 'P' -> - et_collector:multicast(S#state.collector_pid, prev), - noreply(S); - 'N' -> - et_collector:multicast(S#state.collector_pid, next), - noreply(S); - 'L' -> - et_collector:multicast(S#state.collector_pid, last), - noreply(S); - 'R' -> - et_collector:multicast(S#state.collector_pid, refresh), - noreply(S); - - 'a' -> - S2 = S#state{display_mode = all}, - S3 = refresh_main_window(S2), - noreply(S3); - - 'equal' -> - Scale = S#state.scale, - Actors = [A#actor.name || A <- S#state.actors], - open_viewer(Scale, S#state.active_filter, Actors, S), - noreply(S); - 'plus' -> - Scale = S#state.scale + 1, - Actors = [A#actor.name || A <- S#state.actors], - open_viewer(Scale, S#state.active_filter, Actors, S), - noreply(S); - 'minus' -> - case S#state.scale of - 1 -> - gs:config(S#state.canvas, beep); - Scale -> - Actors = [A#actor.name || A <- S#state.actors], - open_viewer(Scale - 1, S#state.active_filter, Actors, S) - end, - noreply(S); - 0 -> - case lists:keysearch(?DEFAULT_FILTER_NAME, #filter.name, S#state.filters) of - {value, F} when is_record(F, filter) -> - open_viewer(S#state.scale, F#filter.name, [?unknown], S); - false -> - gs:config(S#state.canvas, beep) - end, - noreply(S); - Int when is_integer(Int), Int > 0, Int =< 9 -> - case catch lists:nth(Int, S#state.filters) of - F when is_record(F, filter) -> - open_viewer(S#state.scale, F#filter.name, [?unknown], S); - {'EXIT', _} -> - gs:config(S#state.canvas, beep) - end, - noreply(S); - - 'Shift_L' -> - noreply(S); - 'Shift_R' -> - noreply(S); - 'Caps_Lock' -> - noreply(S); - - _ -> - click_error(Key, S), - noreply(S) - end; -handle_info({gs, _Obj,configure, [], [W, H | _]}, S) -> - gs:config(S#state.packer, [{width, W}, {height, H}]), - S2 = S#state{width = W, height = H}, - noreply(S2); -handle_info(timeout, S) -> - Try = - case S#state.display_mode of - {search_actors, reverse, _, _} -> - -10; - _ -> - 10 - end, - if - S#state.is_suspended =:= true -> - {noreply, S, infinity}; - S#state.max_events =:= infinity -> - display_more_events(Try, S); - true -> - Needed = S#state.max_events - queue_length(S#state.events), - if - Needed =< 0 -> {noreply, S, infinity}; - Needed > 10 -> display_more_events(Try, S); - Needed =< 10 -> display_more_events(Needed, S) - end - end; - -handle_info({'EXIT', Pid, Reason}, S) -> - if - Pid =:= S#state.collector_pid -> - unlink(Pid), - gs:destroy(S#state.win), - {stop, Reason, S}; - Pid =:= S#state.parent_pid -> - unlink(Pid), - gs:destroy(S#state.win), - {stop, Reason, S}; - true -> - noreply(S) - end; -handle_info(Info, S) -> - ok = error_logger:format("~p(~p): handle_info(~p, ~p)~n", - [?MODULE, self(), Info, S]), - noreply(S). - -%%---------------------------------------------------------------------- -%% Func: terminate/2 -%% Purpose: Shutdown the server -%% Returns: any (ignored by gen_server) -%%---------------------------------------------------------------------- - -terminate(_Reason, _S) -> - ignore. - -%%---------------------------------------------------------------------- -%% Func: code_change/3 -%% Purpose: Convert process state when code is changed -%% Returns: {ok, NewState} -%%---------------------------------------------------------------------- - -code_change(_OldVsn, S, _Extra) -> - {ok, S}. - -%%%---------------------------------------------------------------------- -%%% Handle suspend/resume -%%%---------------------------------------------------------------------- - -reply(Reply, S) -> - case queue_length(S#state.events) of - _ when S#state.is_suspended =:= true -> - {reply, Reply, S, infinity}; - _ when S#state.max_events =:= infinity -> - {reply, Reply, S, 500}; - N when N >= S#state.max_events -> - {reply, Reply, S, infinity}; - _ -> - {reply, Reply, S, 0} - end. - -noreply(S) -> - case queue_length(S#state.events) of - _ when S#state.is_suspended =:= true -> - {noreply, S, infinity}; - _ when S#state.max_events =:= infinity -> - {noreply, S, 500}; - N when N >= S#state.max_events -> - {noreply, S, infinity}; - _ -> - {noreply, S, 0} - end. - -do_suspend(S) -> - config_suspend(S#state{is_suspended = true}). - -do_resume(S) -> - config_suspend(S#state{is_suspended = false}). - -config_suspend(S) -> - Suspended = S#state.is_suspended, - gs:config(refresh, [{enable, not Suspended}]), - gs:config(refresh_all, [{enable, not Suspended}]), - gs:config(clear_all, [{enable, not Suspended}]), - S. - -refresh_main_window(S) -> - Pid = S#state.collector_pid, - Key = S#state.first_event, - case et_collector:iterate(Pid, Key, -1) of - Prev when Prev =:= Key -> - scroll_first(S); - _Prev -> - S2 = S#state{last_event = S#state.first_event}, - clear_canvas(S2) - end. - -scroll_first(S) -> - S2 = S#state{first_event = first, last_event = first}, - clear_canvas(S2). - -scroll_prev(S) -> - Try = - case S#state.max_events of - infinity -> -10; - Max -> -Max - end, - Key = et_collector:iterate(S#state.collector_pid, S#state.first_event, Try), - S2 = S#state{first_event = Key, last_event = Key}, - clear_canvas(S2). - -scroll_next(S) -> - S2 = S#state{first_event = S#state.last_event}, - clear_canvas(S2). - -scroll_up(S) -> - Key = et_collector:iterate(S#state.collector_pid, S#state.first_event, -5), - S2 = S#state{first_event = Key, last_event = Key}, - clear_canvas(S2). - -scroll_down(S) -> - Key = et_collector:iterate(S#state.collector_pid, S#state.first_event, 5), - S2 = S#state{first_event = Key, last_event = Key}, - clear_canvas(S2). - -scroll_last(S) -> - S2 = S#state{first_event = last, last_event = last}, - clear_canvas(S2). - -change_display_mode(Mode, S) -> - case Mode of - all -> - S2 = S#state{display_mode = Mode}, - refresh_main_window(S2); - {search_actors, _Dir, _Key, []} -> - S2 = S#state{display_mode = all}, - refresh_main_window(S2); - {search_actors, _Dir, Key, Actors} when is_list(Actors) -> - Pid = S#state.collector_pid, - Prev = et_collector:iterate(Pid, Key, -1), - S2 = S#state{first_event = Prev, - last_event = Prev, - display_mode = Mode}, - clear_canvas(S2) - end. - -close_all(S) -> - et_collector:multicast(S#state.collector_pid, close), - timer:sleep(timer:seconds(1)), - spawn(et_collector, stop, [S#state.collector_pid]), - gs:destroy(S#state.win), - {stop, shutdown, S}. - -close_all_others(S) -> - Fun = - fun({{subscriber, Pid}, _}) -> - if - Pid =:= self() -> - ignore; - true -> - unlink(Pid), - Pid ! {et, close} - end - end, - All = et_collector:dict_match(S#state.collector_pid, - {{subscriber, '_'}, '_'}), - lists:foreach(Fun, All), - noreply(S). - -click_error(Click, S) -> - gs:config(S#state.canvas, beep), - io:format("~p: ignored: ~p~n", [?MODULE, Click]). - -%%%---------------------------------------------------------------------- -%%% Clone viewer -%%%---------------------------------------------------------------------- - -open_viewer(Scale, FilterName, Actors, S) -> - Filters = [{dict_insert, {filter, F#filter.name}, F#filter.function} - || F <- S#state.filters], - Options = - [{parent_pid, S#state.parent_pid}, - {title, S#state.title}, - {collector_pid, S#state.collector_pid}, - {is_suspended, S#state.is_suspended}, - {detail_level, S#state.detail_level}, - {active_filter, FilterName}, - {event_order, S#state.event_order}, - {first_event, S#state.first_event}, - {max_events, S#state.max_events}, - {max_actors, S#state.max_actors}, - {hide_actions, S#state.hide_actions}, - {hide_unknown, S#state.hide_unknown}, - {is_suspended, S#state.is_suspended}, - {actors, Actors}, - {scale, Scale}, - {width, S#state.width}, - {height, S#state.height} | Filters], - case start_link(Options) of - {ok, ViewerPid} -> - unlink(ViewerPid), - ok; - {error, Reason} -> - ok = error_logger:format("~p: Failed to start a new window: ~p~n", - [?MODULE, Reason]) - end. - -%%%---------------------------------------------------------------------- -%%% Handle graphics -%%%---------------------------------------------------------------------- - -create_main_window(S) -> - Font = select_font(S#state.scale), - GS = gs:start(), - Name = name_to_string(S#state.active_filter), - Title = case S#state.title of - undefined -> atom_to_list(?MODULE); - Explicit -> name_to_string(Explicit) - end, - WinOpt = [{title, Title ++ " (filter: " ++ Name ++ ")"}, - {configure, true}, - {width, S#state.width}, - {height, S#state.height}], - Win = gs:window(GS, WinOpt), - Bar = gs:menubar(Win, []), - - create_file_menu(Bar), - create_viewer_menu(Bar), - create_collector_menu(Bar), - gs:menubutton(filter_button, Bar, [{label, {text, "Filter"}}]), - create_filter_menu(S#state.active_filter, S#state.filters), - create_help_menu(Bar), - - config_suspend(S), - - PackerOpt = [{packer_x, [{fixed, 5}, {fixed, 40}, {fixed, 40}, - {stretch, 1}, {fixed, 5}]}, - {packer_y, [{fixed, 30}, {fixed, 30}, - {stretch, 1}, {fixed, 30}]}, - {x, 0}, {y, 30}], - Packer = gs:frame(Win, PackerOpt), - gs:checkbutton(suspended, Packer, [{label,{text,"Freeze"}}, - {x, 10}, {y, 0}, - {width, 120}, {align, w}, - {select, S#state.is_suspended}]), - gs:checkbutton(hide_actions, Packer, [{label,{text,"Hide From=To"}}, - {x, 10}, {y, 20}, - {width, 120}, {align, w}, - {select, S#state.hide_actions}]), - gs:checkbutton(hide_unknown, Packer, [{label,{text,"Hide Unknown"}}, - {x, 10}, {y, 40}, - {width, 120}, {align, w}, - {select, S#state.hide_unknown}]), - gs:scale(Packer, [{text,"Detail Level"}, - {range, {?detail_level_min, ?detail_level_max}}, - {orient, horizontal}, - {x, 150}, {y, 0}, {height, 65}, {width, 200}, - {pos, S#state.detail_level}, {data, detail_level}]), - CanvasW = calc_canvas_width(S), - CanvasH = calc_canvas_height(S), - CanOpt = [{pack_xy, {{2, 4}, 3}}, {vscroll, right}, {hscroll, bottom}, - {scrollregion, {2, 2, CanvasW, CanvasH}}], - Canvas = gs:canvas(Packer, CanOpt), - gs:config(Canvas, [{buttonpress, true}, {buttonrelease, true}]), - gs:config(Packer, [{width, S#state.width}, {height, S#state.height}]), - gs:config(Win, [{map, true}, {keypress, true}]), - S2 = S#state{title = Title, - win = Win, font = Font, packer = Packer, - canvas_width = CanvasW, canvas_height = CanvasH, - canvas = Canvas, - y_pos = ?initial_y * S#state.scale}, - draw_all_actors(S2). - -select_font(Scale) when is_integer(Scale) -> - case Scale of - 1 -> {courier, 7}; - 2 -> {courier, 10}; - 3 -> {courier, 12}; - 4 -> {courier, 14}; - S -> {courier, S * 4} - end. - -create_file_menu(Bar) -> - Button = gs:menubutton(Bar, [{label, {text, "File"}}]), - Menu = gs:menu(Button, []), - gs:menuitem(close_all, Menu, [{label, {text, "Close Collector and all Viewers (C) "}}]), - gs:menuitem(close_all_others, Menu, [{label, {text, "Close other Viewers, but keep Collector (c)"}}]), - gs:menuitem(close, Menu, [{label, {text, "Close this Viewer, but keep Collector"}}]), - gs:menuitem(Menu, [{itemtype, separator}]), - - gs:menuitem(clear_all, Menu, [{label, {text, "Clear Collector"}}]), - gs:menuitem(load_all, Menu, [{label, {text, "Load Collector from the file \"et_viewer.log\""}}]), - gs:menuitem(save_all, Menu, [{label, {text, "Save Collector to the file \"et_viewer.log\""}}]). - -create_viewer_menu(Bar) -> - Button = gs:menubutton(Bar, [{label, {text, "Viewer"}}]), - Menu = gs:menu(Button, []), - gs:menuitem(Menu, [{label, {text, "Scroll this Viewer"}}, {bg, lightblue}, {enable,false}]), - gs:menuitem(Menu, [{itemtype, separator}]), - gs:menuitem(first, Menu, [{label, {text, "First (f)"}}]), - gs:menuitem(prev, Menu, [{label, {text, "Prev (p)"}}]), - gs:menuitem(next, Menu, [{label, {text, "Next (n)"}}]), - gs:menuitem(last, Menu, [{label, {text, "Last (l)"}}]), - gs:menuitem(refresh, Menu, [{label, {text, "Refresh (r)"}}]), - gs:menuitem(Menu, [{itemtype, separator}]), - gs:menuitem(up, Menu, [{label, {text, "Up 5 (Up)"}}]), - gs:menuitem(down, Menu, [{label, {text, "Down 5 (Down)"}}]), - gs:menuitem(Menu, [{itemtype, separator}]), - gs:menuitem(Menu, [{label, {text, "Search in this Viewer"}}, {bg, lightblue}, {enable,false}]), - gs:menuitem(Menu, [{itemtype, separator}]), - gs:menuitem({mode, all}, Menu, [{label, {text, "Abort search. Display all (a)"}}]). - -create_collector_menu(Bar) -> - Button = gs:menubutton(Bar, [{label, {text, "Collector"}}]), - Menu = gs:menu(Button, []), - gs:menuitem(Menu, [{label, {text, "Scroll all Viewers"}}, {bg, lightblue}, {enable,false}]), - gs:menuitem(Menu, [{itemtype, separator}]), - gs:menuitem(first_all, Menu, [{label, {text, "First (F)"}}]), - gs:menuitem(prev_all, Menu, [{label, {text, "Prev (P)"}}]), - gs:menuitem(next_all, Menu, [{label, {text, "Next (N)"}}]), - gs:menuitem(last_all, Menu, [{label, {text, "Last (L)"}}]), - gs:menuitem(refresh_all, Menu, [{label, {text, "Refresh (R)"}}]). - -create_filter_menu(ActiveFilterName, Filters) -> - Menu = gs:menu(filter_menu, filter_button, []), - Item = fun(F, N) when F#filter.name =:= collector -> - Label = lists:concat([pad_string(F#filter.name, 20), "(0)"]), - gs:menuitem(Menu, [{label, {text, Label}}, {data, F}]), - N + 1; - (F, N) -> - Label = lists:concat([pad_string(F#filter.name, 20), "(", N, ")"]), - gs:menuitem(Menu, [{label, {text, Label}}, {data, F}]), - N + 1 - end, - gs:menuitem(Menu, [{label, {text, "Same Filter New Scale"}}, {bg, lightblue}, {enable,false}]), - gs:menuitem(Menu, [{itemtype, separator}]), - {value, Filter} = lists:keysearch(ActiveFilterName, #filter.name, Filters), - Same = lists:concat([pad_string(ActiveFilterName, 20), "(=)"]), - Larger = lists:concat([pad_string(ActiveFilterName, 20), "(+)"]), - Smaller = lists:concat([pad_string(ActiveFilterName, 20), "(-)"]), - gs:menuitem(Menu, [{label, {text, Same}}, {data, Filter}]), - gs:menuitem(Menu, [{label, {text, Smaller}}, {data, Filter}]), - gs:menuitem(Menu, [{label, {text, Larger}}, {data, Filter}]), - gs:menuitem(Menu, [{itemtype, separator}]), - gs:menuitem(Menu, [{label, {text, "New Filter Same Scale"}}, {bg, lightblue}, {enable,false}]), - gs:menuitem(Menu, [{itemtype, separator}]), - lists:foldl(Item, 1, Filters). - -create_help_menu(Bar) -> - Button = gs:menubutton(Bar, [{label, {text, "Help"}}]), - Menu = gs:menu(Button, []), - gs:menuitem(Menu, [{label, {text, "Display details of an event"}}, - {bg, lightblue}, {enable,false}]), - gs:menuitem(Menu, [{label, {text, " Single click on the name tag or the arrow (Mouse-1)"}}, - {enable,false}]), - gs:menuitem(Menu, [{itemtype, separator}]), - gs:menuitem(Menu, [{label, {text, "Toggle actor search"}}, - {bg, lightblue}, {enable,false}]), - gs:menuitem(Menu, [{label, {text, " Single click on the name tag (Mouse-1)"}}, - {enable,false}]), - gs:menuitem(Menu, [{itemtype, separator}]), - gs:menuitem(Menu, [{label, {text, "Move actor"}}, - {bg, lightblue}, {enable,false}]), - gs:menuitem(Menu, [{label, {text, " se drag and drop on name tag (Mouse-1)"}}, - {enable,false}]). - -clear_canvas(S) -> - gs:destroy(S#state.canvas), - CanvasW = calc_canvas_width(S), - CanvasH = calc_canvas_height(S), - CanOpt = [{pack_xy, {{2, 4}, 3}}, {vscroll, right}, {hscroll, bottom}, - {scrollregion, {2, 2, CanvasW, CanvasH}}], - Canvas = gs:canvas(S#state.packer, CanOpt), - gs:config(S#state.packer, [{width, S#state.width}, {height, S#state.height}]), - gs:config(Canvas, [{buttonpress, true}, {buttonrelease, true}]), - S2 = S#state{refresh_needed = false, - y_pos = ?initial_y * S#state.scale, - canvas = Canvas, - canvas_width = CanvasW, - canvas_height = CanvasH, - events = queue_new()}, - draw_all_actors(S2). - -calc_canvas_width(S) -> - Min = calc_min_actors(S), - CanvasW = ((2 * ?initial_x) + (Min * ?incr_x)) * S#state.scale, - lists:max([CanvasW, S#state.width - (15 * S#state.scale), S#state.canvas_width]). - -calc_canvas_height(S) -> - Min = calc_min_events(S), - CanvasH = ((2 * ?initial_y) + (Min * ?incr_y)) * S#state.scale, - lists:max([CanvasH, S#state.height - (4 * 30), S#state.canvas_height]). - -calc_min_actors(S) -> - Max = S#state.max_actors, - N = length(S#state.actors), - if - Max =:= infinity -> - N * 2; - Max < N -> - N; - true -> - Max - end. - -calc_min_events(S) -> - Max = S#state.max_events, - N = queue_length(S#state.events), - if - Max =:= infinity -> - N * 2; - Max < N -> - N; - true -> - Max - end. - -display_more_events(Try, S) -> - Name = S#state.active_filter, - {value, F} = lists:keysearch(Name, #filter.name, S#state.filters), - FilterFun = F#filter.function, - Fun = fun(Event, State) -> - case catch FilterFun(Event) of - true -> - State2 = ensure_key(Event, State), - opt_display_event(Event, State2); - {true, Event2} -> - State2 = ensure_key(Event2, State), - opt_display_event(Event2, State2); - false -> - ensure_key(Event, State); - Bad -> - Contents = {bad_filter, Name, Bad, Event}, - Event2 = Event#event{contents = Contents, - from = bad_filter, - to = bad_filter}, - State2 = ensure_key(Event2, State), - opt_display_event(Event2, State2) - end - end, - Pid = S#state.collector_pid, - S2 = et_collector:iterate(Pid, S#state.last_event, Try, Fun, S), - case queue_length(S2#state.events) - queue_length(S#state.events) of - Diff when Diff =:= Try -> - %% Got as much as requested, look for more - %% io:format("Done: ~p~n", [{Try, Diff}]), - {noreply, S2, 0}; - _Diff when S2#state.first_event =:= S#state.first_event, - S2#state.last_event =:= S#state.last_event -> - %% Got lesser than requested, wait a while before looking for more - %% io:format("More: ~p~n", [{Try, Diff}]), - {noreply, S2, 500}; - _Diff -> - %% Got lesser than requested, look for more - %% io:format("More2: ~p~n", [{Try, Diff}]), - {noreply, S2, 0} - end. - -ensure_key(E, S) when is_record(E, event), is_record(S, state) -> - Key = et_collector:make_key(S#state.event_order, E), - case S#state.first_event of - first -> - S#state{first_event = Key, last_event = Key}; - last -> - S#state{first_event = Key, last_event = Key}; - _ -> - S#state{last_event = Key} - end. - -opt_display_event(E, S) -> - case S#state.display_mode of - all -> - display_event(E, S); - {search_actors, _Dir, _FirstKey, Actors} -> - %% Key = S#state.last_event, - From = select_actor_name(E#event.from, S), - case lists:member(From, Actors) of - true -> - display_event(E, S); - false -> - To = select_actor_name(E#event.to, S), - case lists:member(To, Actors) of - true -> - display_event(E, S); - false -> - S - end - end - end. - -select_actor_name(Name, S) -> - case lists:keymember(Name, #actor.name, S#state.actors) of - true -> Name; - false -> ?unknown - end. - -display_event(E, S) when E#event.detail_level < S#state.detail_level -> - {FromRefresh, From} = ensure_actor(E#event.from, S), - {FromName, FromPos, S2} = From, - {ToRefresh, To} = ensure_actor(E#event.to, S2), - {ToName, ToPos, S3} = To, - if - FromRefresh =/= false, ToRefresh =/= false -> - Key = S#state.last_event, - refresh_beep(S), - S3#state{refresh_needed = true, - events = queue_in(Key, S3#state.events)}; - FromName =:= ToName -> - case S#state.hide_actions of - true -> - S3; - false -> - Label = name_to_string(E#event.label), - draw_named_arrow(Label, FromName, FromPos, ToName, ToPos, S3) - end; - true -> - Label = name_to_string(E#event.label), - draw_named_arrow(Label, FromName, FromPos, ToName, ToPos, S3) - end; -display_event(_, S) -> - S. - -draw_named_arrow(Label, FromName, FromPos, ToName, ToPos, S) -> - Key = S#state.last_event, - case S#state.y_pos + (?incr_y * S#state.scale) of - _ when S#state.hide_unknown =:= true, FromName =:= ?unknown -> - S; - _ when S#state.hide_unknown =:= true, ToName =:= ?unknown -> - S; - Y when Y > S#state.canvas_height -> - refresh_beep(S), - S#state{refresh_needed = true, - events = queue_in(Key, S#state.events)}; - Y -> - S2 = S#state{y_pos = Y, events = queue_in(Key, S#state.events)}, - S3 = draw_arrow(FromPos, ToPos, S2), - draw_label(Label, FromName, ToName, FromPos, ToPos, S3) - end. - -refresh_beep(S) -> - case S#state.refresh_needed of - false -> - gs:config(S#state.canvas, beep), - gs:config(S#state.canvas, beep), - gs:config(S#state.canvas, beep); - true -> - ignore - end. - -draw_arrow(Pos, Pos, S) -> - S; -draw_arrow(FromPos, ToPos, S) -> - Y = S#state.y_pos, - CanOpts = [{coords, [{FromPos , Y}, {ToPos, Y}]}, - {arrow, last},{width, 1}, {fg, black}], - gs:line(S#state.canvas, CanOpts), - S. - -draw_label(Label, FromName, ToName, FromPos, ToPos, S) -> - Colour = - if - FromName =:= ?unknown, - ToName =:= ?unknown -> blue; %turquoise; - FromName =:= ?unknown -> orange; - ToName =:= ?unknown -> orange; - FromPos =:= ToPos -> blue; - true -> red - end, - Scale = S#state.scale, - X = lists:min([FromPos, ToPos]) + (6 * Scale), - Y = S#state.y_pos, - write_text(Label, X, Y, Colour, S), - S. - -draw_all_actors(State) -> - Scale = State#state.scale, - Fun = fun(A, X) -> - draw_actor(A, X, State), - X + (?incr_x * Scale) - end, - lists:foldl(Fun, ?initial_x * Scale, State#state.actors), - State. - -%% Returns: {NeedsRefreshBool, {ActorPos, NewsS, NewActors}} -ensure_actor(Name, S) -> - do_ensure_actor(Name, S, S#state.actors, 0). - -do_ensure_actor(Name, S, [H | _], N) when H#actor.name =:= Name -> - Pos = (?initial_x + (N * ?incr_x)) * S#state.scale, - {false, {Name, Pos, S}}; -do_ensure_actor(Name, S, [_ | T], N) -> - do_ensure_actor(Name, S, T, N + 1); -do_ensure_actor(Name, S, [], N) -> - %% A brand new actor, let's see if it does fit - Pos = (?initial_x + (N * ?incr_x)) * S#state.scale, - MaxActors = S#state.max_actors, - if - is_integer(MaxActors), N > MaxActors -> - %% Failed on max_actors limit, put into unknown - %% Assume that unknown always is in actor list - ensure_actor(?unknown, S); - Pos > (S#state.canvas_width - ((?initial_x - 15) * S#state.scale)) -> - %% New actor does not fit in canvas, refresh needed - A = create_actor(Name), - draw_actor(A, Pos, S), - {true, {Name, Pos, S#state{actors = S#state.actors ++ [A]}}}; - true -> - %% New actor fits in canvas. Draw the new actor. - A = create_actor(Name), - draw_actor(A, Pos, S), - {false, {Name, Pos, S#state{actors = S#state.actors ++ [A]}}} - end. - -draw_actor(A, LineX, S) -> - Scale = S#state.scale, - TextX = LineX - (5 * Scale), - TextY = ?initial_y * Scale, - LineTopY = TextY + ((?incr_y / 2) * Scale), - LineBotY = S#state.canvas_height - ((?incr_y / 2) * Scale), - Colour = case A#actor.name of - ?unknown -> orange; - _ -> red - end, - write_text(A#actor.string, TextX, TextY, Colour, S), - LineOpt = [{coords, [{LineX, LineTopY}, {LineX, LineBotY}]}, - {width, 1}, {fg, Colour}], - gs:line(S#state.canvas, LineOpt). - -toggle_search_for_actor(ActorName,S) -> - case S#state.display_mode of - all -> - io:format("~p: search for: ~p ++ ~p~n", [?MODULE, [], [ActorName]]), - %% Search for this actor - Key = S#state.first_event, - Actors = [ActorName], - Mode = {search_actors, forward, Key, Actors}, - change_display_mode(Mode, S); - {search_actors, Dir, Key, Actors}-> - Actors2 = - case lists:member(ActorName, Actors) of - true -> - io:format("~p: search for: ~p -- ~p~n", [?MODULE, Actors, [ActorName]]), - %% Remove actor from search list - Actors -- [ActorName]; - false -> - io:format("~p: search for: ~p ++ ~p~n", [?MODULE, Actors, [ActorName]]), - %% Add actor from search list - [ActorName | Actors] - end, - Mode2 = {search_actors, Dir, Key, Actors2}, - change_display_mode(Mode2, S) - end. - -move_actor(From, To, Actors, S) -> - Pos = #actor.name, - ToName = To#actor.name, - FromName = From#actor.name, - ToIx = actor_index(ToName, Pos, Actors), - FromIx = actor_index(FromName, Pos, Actors), - if - FromIx =/= 0, ToIx =/= 0, ToIx > FromIx -> - Actors2 = lists:keydelete(FromName, Pos, Actors), - Actors3 = insert_actor_after(From, To, Actors2), - S2 = S#state{actors = Actors3}, - refresh_main_window(S2); - FromIx =/= 0, ToIx =/= 0 -> - Actors2 = lists:keydelete(FromName, Pos, Actors), - Actors3 = insert_actor_before(From, To, Actors2), - S2 = S#state{actors = Actors3}, - refresh_main_window(S2); - true -> - %% Ignore - S - end. - -insert_actor_after(From, To, [H | T]) -> - case To#actor.name =:= H#actor.name of - true -> [H, From | T]; - false -> [H | insert_actor_after(From, To, T)] - end; -insert_actor_after(_From, _To, []) -> - []. - -insert_actor_before(From, To, [H | T]) -> - case To#actor.name =:= H#actor.name of - true -> [From, H | T]; - false -> [H | insert_actor_before(From, To, T)] - end; -insert_actor_before(_From, _To, []) -> - []. - -actor_index(_Key, _Pos, []) -> - 0; -actor_index(Key, Pos, [H | T]) -> - case Key =:= element(Pos, H) of - false -> actor_index(Key, Pos, T) + 1; - true -> 1 - end. - -y_to_n(Y, S) -> - Y2 = ((Y / S#state.scale) - ?initial_y + (?incr_y / 2)), - N = round(Y2 / ?incr_y - 0.2), - MaxN = queue_length(S#state.events), - if - N =< 0 -> actor; - N > MaxN -> actor; - true -> {event, N} - end. - -x_to_n(X, S) -> - Scale = S#state.scale, - Len = length(S#state.actors), - X2 = X - (?initial_x * Scale), - N = X2 / (?incr_x * Scale), - N2 = trunc(N + 1.5), - if - N2 > Len -> Len; - N2 < 1 -> 1; - true -> N2 - end. - -write_text(Text, X, Y, Colour, S) -> - Opt = [{coords, [{X, Y - (?incr_y * S#state.scale / 2)}]}, - {font, S#state.font}, {fg, Colour}, {text, Text}], - gs:text(S#state.canvas, Opt). - -create_contents_window(Event, S) -> - Options = [{viewer_pid, self()}, - {event, Event}, - {event_order, S#state.event_order}, - {active_filter, S#state.active_filter} - | S#state.filters], - case et_gs_contents_viewer:start_link(Options) of - {ok, _Pid} -> - S; - {error, Reason} -> - ok = error_logger:format("~p(~p): create_contents_window(~p) ->~n ~p~n", - [?MODULE, self(), Options, Reason]), - S - end. - -%%%---------------------------------------------------------------------- -%%% String padding of actors -%%%---------------------------------------------------------------------- - -create_actor(Name) -> - String = name_to_string(Name), - PaddedString = pad_string(String, 8), - #actor{name = Name, string = PaddedString}. - -name_to_string(Name) -> - case catch io_lib:format("~s", [Name]) of - {'EXIT', _} -> lists:flatten(io_lib:format("~w", [Name])); - GoodString -> lists:flatten(GoodString) - end. - -pad_string(Atom, MinLen) when is_atom(Atom) -> - pad_string(atom_to_list(Atom), MinLen); -pad_string(String, MinLen) when is_integer(MinLen), MinLen >= 0 -> - Len = length(String), - case Len >= MinLen of - true -> - String; - false -> - String ++ lists:duplicate(MinLen - Len, $ ) - end. - -%%%---------------------------------------------------------------------- -%%% Queue management -%%%---------------------------------------------------------------------- - -queue_new() -> - {0, [], []}. - -queue_in(X, {Size, In, Out}) -> - {Size + 1, [X | In], Out}. - -%% queue_out(Q) -> -%% case Q of -%% {Size, In, [H | Out]} -> {{value, H}, {Size - 1, In, Out}}; -%% {Size, [], []} -> {empty, {Size, [], []}}; -%% {Size, In, _} -> queue_out({Size, [], lists:reverse(In)}) -%% end. - -queue_to_list({_Size, [], Out}) -> - Out; -queue_to_list({_Size, In, Out}) -> - Out ++ lists:reverse(In). - -queue_length({Size, _In, _Out}) -> - Size. - -list_to_queue(List) when is_list(List) -> - {length(List), [], List}. diff --git a/lib/et/src/et_viewer.erl b/lib/et/src/et_viewer.erl index d9bd01f8d0..ead5639da6 100644 --- a/lib/et/src/et_viewer.erl +++ b/lib/et/src/et_viewer.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2010. All Rights Reserved. +%% Copyright Ericsson AB 2000-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -76,7 +76,7 @@ start() -> %% start(Options) -> {ok, ViewerPid} | {error, Reason} %%---------------------------------------------------------------------- -start(GUI) when GUI =:= wx; GUI =:= gs; GUI =:= default -> +start(GUI) when GUI =:= wx; GUI =:= default -> start_link([{trace_global, true}], GUI); start(Options) -> start_link([{parent_pid, undefined} | Options], default). @@ -139,7 +139,7 @@ start(Options, GUI) -> %% and returns false | true | {true, NewEvent}. %%---------------------------------------------------------------------- -start_link(GUI) when GUI =:= wx; GUI =:= gs; GUI =:= default -> +start_link(GUI) when GUI =:= wx; GUI =:= default -> start_link([{trace_global, true}], GUI); start_link(Options) -> start_link(Options, default). @@ -148,22 +148,11 @@ start_link(Options, GUI) -> case GUI of wx -> et_wx_viewer:start_link(Options); - gs -> - et_gs_viewer:start_link(Options); default -> start_link(Options, which_gui()) end. - -which_gui() -> - try - wx:new(), - wx:destroy(), - wx - catch _:_ -> - gs - end. - +which_gui() -> wx. get_collector_pid(ViewerPid) -> call(ViewerPid, get_collector_pid). diff --git a/lib/et/src/modules.mk b/lib/et/src/modules.mk index 8d6c0902fb..b2d2f0b481 100644 --- a/lib/et/src/modules.mk +++ b/lib/et/src/modules.mk @@ -1,7 +1,7 @@ #-*-makefile-*- ; force emacs to enter makefile-mode # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2010. All Rights Reserved. +# Copyright Ericsson AB 2001-2013. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -19,8 +19,6 @@ MODULES = \ et \ et_collector \ - et_gs_contents_viewer \ - et_gs_viewer \ et_selector \ et_viewer \ et_wx_contents_viewer \ |