📄 et_viewer.erl
字号:
%% ``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 via the world wide web 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.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %% $Id$%%%%----------------------------------------------------------------------%% Purpose: Displays a sequence chart for trace events (messages/actions)%%-----------------------------------------------------------------------module(et_viewer).-behaviour(gen_server).%% External exports-export([file/1, start/0, start/1, start_link/1, stop/1, get_collector_pid/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).-define(detail_level_min, 0).-define(detail_level_max, 100).%%%----------------------------------------------------------------------%%% Client side%%%----------------------------------------------------------------------%%----------------------------------------------------------------------%% file(FileName) -> {ok, ViewerPid} | {error, Reason}%%%% Start a new event viewer and a corresponding collector%% and load them with trace events from a trace file.%%%% FileName() = string()%% ViewerPid = pid()%% Reason = term()%%----------------------------------------------------------------------file(FileName) -> start_link([{trace_client, {file, FileName}}]).%%----------------------------------------------------------------------%% start() -> ok%% %% Simplified start of a sequence chart viewer with%% global tracing activated.%%%% Convenient to be used from the command line%% (erl -s et_viewer) as both the viewer and collector%% processes are unlinked from the calling process.%%----------------------------------------------------------------------start() -> start([{trace_global, true}]).%%----------------------------------------------------------------------%% start(Options) -> {ok, ViewerPid} | {error, Reason}%%----------------------------------------------------------------------start(Options) -> start_link([{parent_pid, undefined} | Options]).%%----------------------------------------------------------------------%% start_link(Options) -> {ok, ViewerPid} | {error, Reason}%%%% Start a sequence chart viewer for trace events (messages/actions)%% %% Options = [option() | collector_option()]%%%% option() =%% {parent_pid, extended_pid()} |%% {title, term()} |%% {detail_level, detail_level()} |%% {is_suspended, boolean()} |%% {scale, integer()} |%% {width, integer()} |%% {height, integer()} |%% {collector_pid, extended_pid()} |%% {event_order, event_order()} |%% {active_filter, atom()} |%% {max_events, extended_integer()} |%% {max_actors, extended_integer()} |%% {trace_global, et_collector_trace_global()} |%% {trace_pattern, et_collector_trace_pattern()} |%% {trace_port, et_collector_trace_port()} |%% {trace_max_queue, et_collector_trace_max_queue()} |%% {trace_client, et_collector_trace_client()} |%% {dict_insert, {filter, filter_name()}, event_filter_fun()} |%% {dict_insert, et_collector_dict_key(), et_collector_dict_val()} |%% {dict_delete, {filter, filter_name()}} |%% {dict_delete, et_collector_dict_key()} |%% {actors, actors()} |%% {first_event, first_key()} |%% {hide_unknown, boolean()} |%% {hide_actions, boolean()} |%% {display_mode, display_mode()}%% %% extended_pid() = pid() | undefined%% detail_level() = min | max | integer(X) when X >=0, X =< 100%% event_order() = trace_ts | event_ts%% extended_integer() = integer() | infinity%% display_mode() = all | {search_actors, direction(), first_key(), actors()}%% direction() = forward | reverse%% first_key() = event_key()%% actors() = [term()]%% %% filter_name() = atom()%% filter_fun() = fun(Event) -> false | true | {true, NewEvent}%% Event = NewEvent = record(event)%%%% ViewerPid = pid()%% Reason = term()%%%% A filter_fun() takes an event record as sole argument%% and returns false | true | {true, NewEvent}.%%----------------------------------------------------------------------start_link(Options) -> case parse_opt(Options, default_state(), []) of {ok, S, CollectorOpt} -> case S#state.collector_pid of CollectorPid when 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 = max, active_filter = collector, filters = [#filter{name = collector, function = fun(E) -> E end}], 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) -> case S#state.detail_level of max -> {ok, S#state{detail_level = ?detail_level_max}, CollectorOpt}; min -> {ok, S#state{detail_level = ?detail_level_min}, CollectorOpt}; Int when integer(Int) -> {ok, S, [{parent_pid, S#state.parent_pid} | CollectorOpt]} end;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 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 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 integer(Scale), Scale > 0 -> parse_opt(T, S#state{scale = Scale}, CollectorOpt); {width, W} when integer(W), W > 0 -> parse_opt(T, S#state{width = W, canvas_width = W}, CollectorOpt); {height, WH} when integer(WH), WH > 0 -> parse_opt(T, S#state{height = WH, canvas_height = WH}, CollectorOpt); {collector_pid, Pid} when 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 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 atom(Name), 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 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 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 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 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 list(Actors), Dir == forward -> parse_opt(T, S#state{display_mode = Mode}, CollectorOpt); {display_mode, Mode = {search_actors, Dir, _Key, Actors}} when 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 atom(Name), 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 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.%%----------------------------------------------------------------------%% get_collector_pid(ViewerPid) -> CollectorPid%%%% Returns the identifier of the collector process%%%% ViewerPid = pid()%% CollectorPid = pid()%%----------------------------------------------------------------------get_collector_pid(ViewerPid) -> call(ViewerPid, get_collector_pid).%%----------------------------------------------------------------------%% stop(ViewerPid) -> ok%% %% Stops a viewer
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -