查看源代码 高级示例
模拟 Mnesia 事务
前一章中运行模拟 Mnesia
事务示例的 Erlang 代码包含在 et/examples/et_demo.erl
文件中
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}.
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}]}}.
如果您调用 et_demo:sim_trans()
函数,将会弹出一个 Viewer
窗口,并且序列跟踪将几乎与运行以下 Mnesia
事务相同
mnesia:transaction(fun() -> mnesia:write({my_tab, key, val}) end).
查看器窗口将如下所示
Erlang R13B03 (erts-5.7.4) [64-bit] [smp:4:4] [rq:4] [async-threads:0] [kernel-poll:false]
Eshell V5.7.4 (abort with ^G)
1> {ok, Viewer} = et_viewer:start([]).
{ok,<0.40.0>;}
2> et_demo:sim_trans().
{ok,{table_handle,<0.45.0>,24596,trace_ts,
#Fun<et_collector.0.62831470>}}
Mnesia 事务示例中使用的一些便利函数
module_as_actor
过滤器转换 事件记录
,使模块名称成为参与者,被调用的函数成为标签。如果有关调用者的信息存在,将显示从调用者到被调用者的箭头。dbg:tpl/2
函数的 [{message, {caller}}, {return_trace}]
选项将包含 Erlang 跟踪中必要的信息。以下是 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.
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_nolink
过滤器不会更改 事件记录
。它使用 plain_process_info
,但也确保跳过与链接和取消链接相关的进程信息
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).
为了简化启动带有上述过滤器以及其他一些过滤器(也在 et/examples/et_demo.erl
src/et_collector.erl 中找到)的 et_viewer
进程,可以使用 et_demo:start/0,1
函数
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).
一个简单的单行代码即可启动该工具
erl -pa ../examples -s et_demo
过滤器通过以下参数包含
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}
].
真实 Mnesia 事务的 Erlang 跟踪
以下代码段 et_demo:trace_mnesia/0
激活 Mnesia
应用程序中所有模块的本地和外部函数调用的调用跟踪。调用跟踪配置为覆盖所有进程(包括现有进程和将来生成的进程),并包含跟踪数据的时间戳。它还会为 Mnesia
的静态进程以及调用进程(即您的 shell)激活进程相关事件的跟踪。请注意,以下代码中的 whereis/1
调用要求被跟踪的 Mnesia
应用程序和 et_viewer
都在同一节点上运行
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.
et_demo:live_trans/0
函数启动全局 Collector
,启动 Viewer
,启动 Mnesia
,创建本地表,激活跟踪(如上所述),并将 shell 进程注册为 'my_shell' 以便清晰。最后,运行一个简单的 Mnesia
事务,该事务写入单个记录
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).
现在我们运行 et_demo:live_trans/0
函数
erl -pa ../examples
Erlang R13B03 (erts-5.7.4) [64-bit] [smp:4:4] [rq:4]
[async-threads:0] [kernel-poll:false]
Eshell V5.7.4 (abort with ^G)
1> et_demo:live_trans().
{atomic,ok}
请探索不同的过滤器,以便从不同的角度查看被跟踪的事务
Megaco 启动的 Erlang 跟踪
事件跟踪器 (ET)
工具最初是为了演示如何通过 Megaco
协议发送消息而编写的。那是早些时候,在 IETF
和 ITU
的标准机构批准 Megaco
(也称为 H.248
) 作为国际标准之前。
在 Erlang/OTP 的 Megaco
应用程序中,代码经过精心设计,使用对 et:trace_me/5
的调用。为了简单地实现对跟踪级别的动态控制,为每个调用都给出了详细级别。
megaco_filter
模块实现了 Megaco
消息的自定义过滤器。它还结合使用 trace_global
和 trace_pattern
-module(megaco_filter).
-export([start/0]).
start() ->
Options =
[{event_order, event_ts},
{scale, 3},
{max_actors, infinity},
{trace_pattern, {megaco, max}},
{trace_global, true},
{dict_insert, {filter, megaco_filter}, fun filter/1},
{active_filter, megaco_filter},
{title, "Megaco tracer - Erlang/OTP"}],
et_viewer:start(Options).
首先,我们启动一个带有全局 Collector
及其 Viewer
的 Erlang 节点。
erl -sname observer
Erlang R13B03 (erts-5.7.4) [64-bit] [smp:4:4] [rq:4] [async-threads:0] [kernel-poll:false]
Eshell V5.7.4 (abort with ^G)
(observer@falco)1> megaco_filter:start().
{ok,<0.48.0>}
其次,我们启动另一个 Erlang 节点,我们将其连接到观察者节点,然后启动我们要跟踪的应用程序。在这种情况下,我们启动一个媒体网关控制器,该控制器在 Megaco 的文本和二进制端口上侦听 TCP 和 UDP
erl -sname mgc -pa ../../megaco/examples/simple
Erlang R13B03 (erts-5.7.4) [64-bit] [smp:4:4] [rq:4] [async-threads:0] [kernel-poll:false]
Eshell V5.7.4 (abort with ^G)
(mgc@falco)1> net:ping(observer@falco).
pong
(mgc@falco)2> megaco:start().
ok
(mgc@falco)3> megaco_simple_mgc:start().
{ok,[{ok,2944,
{megaco_receive_handle,{deviceName,"controller"},
megaco_pretty_text_encoder,[],megaco_tcp,dynamic}},
{ok,2944,
{megaco_receive_handle,{deviceName,"controller"},
megaco_pretty_text_encoder,[],megaco_udp,dynamic}},
{ok,2945,
{megaco_receive_handle,{deviceName,"controller"},
megaco_binary_encoder,[],megaco_tcp,dynamic}},
{ok,2945,
{megaco_receive_handle,{deviceName,"controller"},
megaco_binary_encoder,[],megaco_udp,dynamic}}]}
最后,我们启动一个用于媒体网关的 Erlang 节点,并连接到观察者节点。每个媒体网关都连接到控制器,并发送初始服务更改消息。控制器接受网关,并根据每个网关的偏好,使用相同的传输机制和消息编码向每个网关发送回复。这就是 TCP/IP 传输、UDP/IP 传输、文本编码和 ASN.1 BER 编码的所有组合
Erlang R13B03 (erts-5.7.4) [64-bit] [smp:4:4] [rq:4] [async-threads:0] [kernel-poll:false]
Eshell V5.7.4 (abort with ^G)
(mg@falco)1> net:ping(observer@falco).
pong
(mg@falco)2> megaco_simple_mg:start().
[{{deviceName,"gateway_tt"},
{error,{start_user,megaco_not_started}}},
{{deviceName,"gateway_tb"},
{error,{start_user,megaco_not_started}}},
{{deviceName,"gateway_ut"},
{error,{start_user,megaco_not_started}}},
{{deviceName,"gateway_ub"},
{error,{start_user,megaco_not_started}}}]
(mg@falco)3> megaco:start().
ok
(mg@falco)4> megaco_simple_mg:start().
[{{deviceName,"gateway_tt"},
{1,
{ok,[{'ActionReply',0,asn1_NOVALUE,asn1_NOVALUE,
[{serviceChangeReply,
{'ServiceChangeReply',
[{megaco_term_id,false,["root"]}],
{serviceChangeResParms,
{'ServiceChangeResParm',
{deviceName,"controller"},
asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,
asn1_NOVALUE}}}}]}]}}},
{{deviceName,"gateway_tb"},
{1,
{ok,[{'ActionReply',0,asn1_NOVALUE,asn1_NOVALUE,
[{serviceChangeReply,
{'ServiceChangeReply',
[{megaco_term_id,false,["root"]}],
{serviceChangeResParms,
{'ServiceChangeResParm',
{deviceName,"controller"},
asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,
asn1_NOVALUE}}}}]}]}}},
{{deviceName,"gateway_ut"},
{1,
{ok,[{'ActionReply',0,asn1_NOVALUE,asn1_NOVALUE,
[{serviceChangeReply,
{'ServiceChangeReply',
[{megaco_term_id,false,["root"]}],
{serviceChangeResParms,
{'ServiceChangeResParm',
{deviceName,"controller"},
asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,
asn1_NOVALUE}}}}]}]}}},
{{deviceName,"gateway_ub"},
{1,
{ok,[{'ActionReply',0,asn1_NOVALUE,asn1_NOVALUE,
[{serviceChangeReply,
{'ServiceChangeReply',
[{megaco_term_id,false,["root"]}],
{serviceChangeResParms,
{'ServiceChangeResParm',
{deviceName,"controller"},
asn1_NOVALUE,asn1_NOVALUE,
asn1_NOVALUE,...}}}}]}]}}}]
当我们单击 [gateway_tt] 参与者名称以便仅显示有关该参与者的事件时,采用的 Megaco
查看器如下所示
格式化的 Megaco
消息如下所示
同一 Megaco
消息的相应内部形式如下所示