diff options
Diffstat (limited to 'lib/et/src/et_gs_contents_viewer.erl')
-rw-r--r-- | lib/et/src/et_gs_contents_viewer.erl | 602 |
1 files changed, 0 insertions, 602 deletions
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. |