%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2002-2010. 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%
%%
%%----------------------------------------------------------------------
%% et_demo.erl - Provide some event trace filter examples.
%%----------------------------------------------------------------------
-module(et_demo).
-export([
sim_trans/0, sim_trans/1,
live_trans/0, live_trans/1,
mgr_actors/1,
start/0, start/1,
filters/0,
trace_mnesia/0
]).
%% Test
-export([s/0, t/0, t/1, init/0, gen/3]).
-include_lib("et/include/et.hrl").
%%----------------------------------------------------------------------
%sim_trans
sim_trans() ->
sim_trans([]).
sim_trans(ExtraOptions) ->
Options = [{dict_insert, {filter, mgr_actors}, fun mgr_actors/1}],
{ok, Viewer} = et_viewer:start_link(Options ++ ExtraOptions),
Collector = et_viewer:get_collector_pid(Viewer),
et_collector:report_event(Collector, 60, my_shell, mnesia_tm, start_outer,
"Start outer transaction"),
et_collector:report_event(Collector, 40, mnesia_tm, my_shell, new_tid,
"New transaction id is 4711"),
et_collector:report_event(Collector, 20, my_shell, mnesia_locker, try_write_lock,
"Acquire write lock for {my_tab, key}"),
et_collector:report_event(Collector, 10, mnesia_locker, my_shell, granted,
"You got the write lock for {my_tab, key}"),
et_collector:report_event(Collector, 60, my_shell, do_commit,
"Perform transaction commit"),
et_collector:report_event(Collector, 40, my_shell, mnesia_locker, release_tid,
"Release all locks for transaction 4711"),
et_collector:report_event(Collector, 60, my_shell, mnesia_tm, delete_transaction,
"End of outer transaction"),
et_collector:report_event(Collector, 20, my_shell, end_outer,
"Transaction returned {atomic, ok}"),
{collector, Collector}.
%sim_trans
%mgr_actors
mgr_actors(E) when is_record(E, event) ->
Actor = fun(A) ->
case A of
mnesia_tm -> trans_mgr;
mnesia_locker -> lock_mgr;
_ -> A
end
end,
{true, E#event{from = Actor(E#event.from),
to = Actor(E#event.to),
contents = [{orig_from, E#event.from},
{orig_to, E#event.to},
{orig_contents, E#event.contents}]}}.
%mgr_actors
%%----------------------------------------------------------------------
%start
start() ->
start([]).
start(ExtraOptions) ->
Options = [{trace_global, true},
{parent_pid, undefined},
{max_actors, infinity},
{max_events, 1000},
{active_filter, module_as_actor}],
et_viewer:start_link(filters() ++ Options ++ ExtraOptions).
%start
%%----------------------------------------------------------------------
%live_trans
live_trans() ->
live_trans([]).
live_trans(ExtraOptions) ->
Options = [{title, "Mnesia tracer"},
{hide_actions, true},
{active_filter, named_process_info_nolink}],
et_demo:start(Options ++ ExtraOptions),
mnesia:start(),
mnesia:create_table(my_tab, [{ram_copies, [node()]}]),
et_demo:trace_mnesia(),
register(my_shell, self()),
mnesia:transaction(fun() -> mnesia:write({my_tab, key, val}) end).
%live_trans
%%----------------------------------------------------------------------
%trace_mnesia
trace_mnesia() ->
Modules = mnesia:ms(),
Spec = [{message, {caller}}, {return_trace}],
Flags = [send, 'receive', procs, timestamp],
dbg:p(all, [call, timestamp]),
[dbg:tpl(M, [{'_', [], Spec}]) || M <- Modules],
LocallyRunningServers = [M || M <- Modules, whereis(M) /= undefined],
[dbg:p(whereis(RS), Flags) || RS <- LocallyRunningServers],
dbg:p(self(), Flags),
LocallyRunningServers.
%trace_mnesia
%%----------------------------------------------------------------------
%% Filter funs returns false, true or {true, NewEvent}
%%----------------------------------------------------------------------
%filters
filters() ->
[{dict_insert, {filter, module_as_actor},
fun module_as_actor/1},
{dict_insert, {filter, plain_process_info},
fun plain_process_info/1},
{dict_insert, {filter, plain_process_info_nolink},
fun plain_process_info_nolink/1},
{dict_insert, {filter, named_process_info},
fun named_process_info/1},
{dict_insert, {filter, named_process_info_nolink},
fun named_process_info_nolink/1},
{dict_insert, {filter, node_process_info},
fun node_process_info/1},
{dict_insert, {filter, node_process_info_nolink},
fun node_process_info_nolink/1},
{dict_insert, {filter, application_as_actor},
fun application_as_actor/1}
].
%filters
%module_as_actor
module_as_actor(E) when is_record(E, event) ->
case lists:keysearch(mfa, 1, E#event.contents) of
{value, {mfa, {M, F, _A}}} ->
case lists:keysearch(pam_result, 1, E#event.contents) of
{value, {pam_result, {M2, _F2, _A2}}} ->
{true, E#event{label = F, from = M2, to = M}};
_ ->
{true, E#event{label = F, from = M, to = M}}
end;
_ ->
false
end.
%module_as_actor
%%----------------------------------------------------------------------
%plain_process_info
plain_process_info(E) when is_record(E, event) ->
case E#event.label of
send -> true;
send_to_non_existing_process -> true;
'receive' -> true;
spawn -> true;
exit -> true;
link -> true;
unlink -> true;
getting_linked -> true;
{seq_send, _Label} -> true;
{seq_receive, _Label} -> true;
{seq_print, _Label} -> true;
{drop, _N} -> true;
_ -> false
end.
%plain_process_info
%plain_process_info_nolink
plain_process_info_nolink(E) when is_record(E, event) ->
(E#event.label /= link) and
(E#event.label /= unlink) and
(E#event.label /= getting_linked) and
plain_process_info(E).
%plain_process_info_nolink
%%----------------------------------------------------------------------
named_process_info(E) when is_record(E, event) ->
case plain_process_info(E) of
true ->
{true, E#event{to = pid_to_name(E#event.to),
from = pid_to_name(E#event.from),
label = msg_to_label(E)}};
false ->
false
end.
named_process_info_nolink(E) when is_record(E, event) ->
case plain_process_info_nolink(E) of
true ->
{true, E#event{to = pid_to_name(E#event.to),
from = pid_to_name(E#event.from),
label = msg_to_label(E)}};
false ->
false
end.
pid_to_name(Pid) when is_pid(Pid) ->
case process_info(Pid, registered_name) of
{registered_name, Name} ->
Name;
_ ->
Pid
end;
pid_to_name({Name, Node}) when Node == node() ->
Name;
pid_to_name(Other) ->
Other.
%%----------------------------------------------------------------------
node_process_info(E) when is_record(E, event) ->
case plain_process_info(E) of
true ->
{true, E#event{to = pid_to_node(E#event.to),
from = pid_to_node(E#event.from),
label = msg_to_label(E)}};
false ->
false
end.
node_process_info_nolink(E) when is_record(E, event) ->
case plain_process_info_nolink(E) of
true ->
{true, E#event{to = pid_to_node(E#event.to),
from = pid_to_node(E#event.from),
label = msg_to_label(E)}};
false ->
false
end.
pid_to_node(Pid) when is_pid(Pid) ->
node(Pid);
pid_to_node(Name) when is_atom(Name) ->
node();
pid_to_node({_Name, Node}) when is_atom(Node) ->
Node.
%%----------------------------------------------------------------------
application_as_actor(E) when is_record(E, event) ->
{true, E#event{to = pid_to_application(E#event.to),
from = pid_to_application(E#event.from),
label = msg_to_label(E)}}.
pid_to_application(Pid) when is_pid(Pid) ->
case application:get_application(Pid) of
{ok, Name} ->
Name;
_ ->
"UNKNOWN"
end.
%%----------------------------------------------------------------------
msg_to_label(E) when is_record(E, event) ->
case lists:keysearch(msg, 1, E#event.contents) of
{value, {msg, Msg}} ->
mnesia_msg_to_label(Msg, E#event.label);
false ->
E#event.label
end.
mnesia_msg_to_label(Msg, Label) ->
case Msg of
{mnesia_tm, _Node, Reply} ->
case Reply of
ok -> ok;
store_erased -> store_erased;
unblocked -> unblocked;
{_From, _Ref, start_outer} -> start_outer;
{aborted, _Tid} -> aborted;
{committed, _Tid} -> committed;
{dirty_res, _Res} -> dirty_res;
{error, _Reason} -> error;
{info, _Part, _Coord} -> info;
{mnesia_tm, _Node, {'EXIT', _Reason}} -> 'EXIT';
{mnesia_tm, _Node, {dirty_res, _Reply}} -> dirty_res;
{new_store, _Etab} -> new_store;
{new_tid, _Tid, _Etab} -> new_tid;
{ok, _Name, _IgnoreNew, _Node} -> ok;
{restarted, _Tid} -> restarted;
{vote_yes, _Tid, _Self} -> vote_yes;
{vote_yes, _Tid} -> vote_yes;
{acc_pre_commit, _Tid, _Self, _Expect} -> acc_pre_commit;
{schema_commit, _Tid, _Self} -> schema_commit;
{vote_no, _Tid, _Reason} -> vote_no
end;
{'$gen_cast',allow_garb} -> allow_garb;
{'$gen_cast', {release_schema_commit_lock, _Pid}} -> release_schema_commit_lock;
{'$gen_call', _Ref, {mktab, _Name,_Args}} -> mktab;
{'$gen_call', _Ref, wait_for_schema_commit_lock} -> wait_for_schema_commit_lock;
{'$gen_call', _Ref, {set_lock, _Key}} -> set_global_lock;
{'$gen_call', _Ref, {del_lock, _Key}} -> del_global_lock;
{'$gen_call', _Ref, {what_happened, _Decision, _Tid}} -> what_happened;
{async_dump_log, _Reason} -> async_dump_log;
check_overload -> check_overload;
{dumper_done, _Pid, dumped} -> dumper_done;
garb_decisions -> garb_decisions;
timeout -> timeout;
{mnesia_locker, _Node, granted} -> granted;
{mnesia_locker, _Node, {granted, _Lookup}} -> granted;
{'EXIT', _Pid, _Reason} -> 'EXIT';
{_From, info} -> info;
{_From, start_outer} -> start_outer;
{_From, {add_store, _Tid}} -> add_store;
{_From, {ask_commit, _Prot, _Tid, _Commit, _DiscNs, _RamNs}} -> ask_commit;
{_From, {async_dirty, _Tid, _Commit, _Tab}} -> async_dirty;
{_From, {block_tab, _Tab}} -> block_tab;
{_From, {del_store, _Tid, _Current, _Obsolete, _Prop}} -> del_store;
{_From, {prepare_checkpoint, _Cp}} ->prepare_checkpoint;
{_From, {read, _Tid, _Oid}} -> try_read_lock;
{_From, {read_write, _Tid, _Oid}} -> try_read_write_lock;
{_From, {restart, _Tid, _Store}} -> restart;
{_From, {sync_dirty, _Tid, _Commit, _Tab}} -> sync_dirty;
{_From, {unblock_me, _Tab}} -> unblock_me;
{_From, {unblock_tab, _Tab}} -> unblock_tab;
{_From, {write, _Tid, _Oid}} -> try_write_lock;
{_Tid, committed} -> committed;
{_Tid, do_commit} -> do_commit;
{_Tid, pre_commit} -> pre_commit;
{_Tid, simple_commit} -> simple_commit;
{_Tid, {do_abort, _Reason}} -> do_abort;
{activity_ended, _, _Pid} -> activity_ended;
{ask_commit, _Protocol, _Tid, _Bin, _DiscNs, _RamNs} -> ask_commit;
{delete_transaction, _Tid} -> delete_transaction;
{mnesia_down, _Node} -> mnesia_down;
{release_tid, _Tid} -> release_tid;
{sync_trans_serial, _Tid} -> sync_trans_serial;
{system, _From, _Msg} -> system;
{transaction_done, _Res, _Pid} -> transaction_done;
{_Tid, {do_abort, _Reason}} -> do_abort;
{_Ref, granted} -> granted;
_ -> Label
end.
%%----------------------------------------------------------------------
s() ->
spawn(fun() -> t(), timer:sleep(infinity) end).
t() ->
t(500).
t(N) ->
Collector = init(),
gen(Collector, 1, N),
Collector.
init() ->
EvenFilter =
fun(#event{label = Label}) ->
case catch (list_to_integer(Label) div 10) rem 2 of
0 ->
false;
_ ->
true
end
end,
OddFilter = fun(E) -> not EvenFilter(E) end,
{ok, Viewer} = et_viewer:start_link([{dict_insert, {filter, odd_tens}, EvenFilter},
{dict_insert, {filter, even_tens}, OddFilter},
{active_filter, odd_tens}]),
et_viewer:get_collector_pid(Viewer).
gen(Collector, From, To) ->
[et_collector:report_event(Collector, 20, from, to, integer_to_list(I), [I]) || I <- lists:seq(From, To)],
ok.