seq_trace
seq_trace
模块
seq_trace
模块摘要
消息的顺序跟踪。
描述
顺序跟踪使得跟踪从一个初始消息得到的所有消息成为可能。顺序跟踪与Erlang中的普通跟踪无关,它由erlang:trace/3
BIF 控制。有关什么是顺序跟踪以及如何使用顺序跟踪的更多信息,请参见部分Sequential Tracing
。
seq_trace
提供了控制顺序跟踪的各个方面的功能。有用于激活,停用,检查和收集跟踪输出的功能。
数据类型
token() = {integer(), boolean(), term(), term(), term()}
表示跟踪令牌的不透明术语(元组)。
出口
set_token(Token) -> PreviousToken | ok
类型
将调用进程的跟踪令牌设置为Token
。如果Token == []
跟踪被禁用,否则Token
应该是从get_token/0
or 返回的Erlang术语set_token/1
。set_token/1
可用于通过将跟踪标记设置为空来暂时排除从跟踪传递的消息,如下所示:
OldToken = seq_trace:set_token([]), % set to empty and save
% old value
% do something that should not be part of the trace
io:format("Exclude the signalling caused by this~n"),
seq_trace:set_token(OldToken), % activate the trace token again
...
返回跟踪令牌的前一个值。
set_token(Component, Val) -> {Component, OldVal}
类型
Component
将跟踪令牌的个人设置为Val
。返回组件的前一个值。
set_token(label, Integer)
label
组件是一个整数,用于标识属于同一顺序跟踪的所有事件。如果几条连续的迹线可以同时激活,label
则用于识别单独的迹线。缺省值是0。
set_token(serial, SerialValue)
SerialValue = {Previous, Current}
。该serial
组件包含计数器,可以对跟踪的消息进行排序,决不应该由用户明确设置,因为这些计数器会自动更新。默认是{0, 0}
。
set_token(send, Bool)
跟踪令牌标志(true | false
),用于启用/禁用消息发送跟踪。默认是false
。
set_token('receive', Bool)
跟踪令牌标志(true | false
),用于启用/禁用跟踪消息接收。默认是false
。
set_token(print, Bool)
追踪标记标记(true | false
),用于启用/禁用对显式调用的追踪seq_trace:print/1
。默认是false
。
set_token(timestamp, Bool)
跟踪令牌标记(true | false
),用于启用/禁用每个跟踪事件的时间戳。默认是false
。
set_token(strict_monotonic_timestamp, Bool)
跟踪令牌标记(true | false
),用于为每个跟踪事件启用/禁用严格的单调时间戳。默认是false
。时间戳将由Erlang monotonic time
单调递增的整数组成。时间戳具有与生成的相同的格式和值{erlang:monotonic_time(nanosecond), erlang:unique_integer([monotonic])}
。
set_token(monotonic_timestamp, Bool)
跟踪令牌标记(true | false
),用于为每个跟踪事件启用/禁用严格的单调时间戳。默认是false
。时间戳将使用Erlang monotonic time
。时间戳具有与生成的相同的格式和值erlang:monotonic_time(nanosecond)
。
如果传递多个时间戳标志,timestamp
则优先于strict_monotonic_timestamp
哪个优先级monotonic_timestamp
。所有时间戳记都会被记住,所以如果两个时间戳被传递并且以后具有最高优先级的那个被禁用,另一个将变为活动状态。
get_token() -> [] | token()
返回调用进程的跟踪令牌的值。如果[]
返回,则表示跟踪未激活。返回的任何其他值都是活动跟踪令牌的值。返回的值可以用作set_token/1
函数的输入。
get_token(Component) -> {Component, Val}
类型
返回跟踪令牌组件的值Component
。查看set_token/2
可能的值Component
和Val
。
print(TraceInfo) -> ok
类型
TraceInfo
如果调用进程当前正在顺序跟踪内执行且print
设置了跟踪令牌的标志,则将Erlang术语放入顺序跟踪输出中。
print(Label, TraceInfo) -> ok
类型
与仅在跟踪令牌的标签组件相同时才输出print/1
的附加条件相同。TraceInfoLabel
reset_trace() -> true
将本地节点上的所有进程的跟踪标记设置为空。用于创建跟踪令牌串行的进程内部计数器设置为0.对于消息队列中的所有消息,跟踪令牌都设置为空。这将一起有效地停止本地节点中的所有正在进行的顺序跟踪。
set_system_tracer(Tracer) -> OldTracer
类型
设置系统跟踪器。系统跟踪器可以是一个进程,端口或tracer module
标记Tracer
。返回前一个值(可能是false
没有系统跟踪器处于活动状态)。
失败:{badarg, Info}}
如果Pid
不是现有的本地pid。
get_system_tracer() -> Tracer
类型
返回当前系统跟踪器的pid,端口标识符或跟踪器模块,或者false
没有激活系统跟踪器。
发送到系统跟踪程序的跟踪消息
消息的格式是以下之一,具体取决于timestamp
跟踪标记的标记是否设置为true
或false
:
{seq_trace, Label, SeqTraceInfo, TimeStamp}
或
{seq_trace, Label, SeqTraceInfo}
其中:
Label = int()
TimeStamp = {Seconds, Milliseconds, Microseconds}
Seconds = Milliseconds = Microseconds = int()
SeqTraceInfo
可以具有下列格式:
{send, Serial, From, To, Message}
当进程From
使用它的跟踪令牌标志print
设为true
已经发出了信息。
{'receive', Serial, From, To, Message}
当进程To
收到带有标志'receive'
设置为的跟踪令牌的消息时使用true
。
{print, Serial, From, _, Info}
用于进程From
已调用seq_trace:print(Label, TraceInfo)
且具有标记print
设置为true
并label
设置为的跟踪标记Label
。
Serial
是一个元组{PreviousSerial, ThisSerial}
,其中:
- 整数
PreviousSerial
表示在最后收到的消息中传递的串行计数器中携带跟踪令牌。如果进程是新顺序跟踪中的第一个,PreviousSerial
则将其设置为进程内部“跟踪时钟”的值。
- 整数
ThisSerial
是进程在传出消息上设置的串行计数器。它基于进程内部的“跟踪时钟”,在附加到消息中的跟踪令牌之前,它会加1。
顺序追踪
顺序跟踪是一种跟踪在不同本地或远程进程之间发送的消息序列的方法,其中序列由单个消息启动。简而言之,它的工作如下:
每个进程都有一个跟踪令牌
,可以为空或不为空。当不为空时,跟踪令牌
可以被看作是元组{Label, Flags, Serial, From}
。跟踪令牌
不可见地传递给每条消息。
若要启动顺序跟踪,用户必须在进程中显式设置跟踪令牌,以发送序列中的第一条消息。
每当进程匹配接收语句中的消息时,根据接收消息携带的跟踪令牌(空或非)设置进程的跟踪令牌。
在每个Erlang节点上,可以将进程设置为系统跟踪程序
。这个过程将接收跟踪消息每一个与跟踪令牌的消息被发送或接收的时间(如果跟踪令牌标志send
或'receive'
设定)。系统跟踪器可以打印每个跟踪事件,将其写入文件或任何适合的文件。
注
系统跟踪器只接收在Erlang节点内本地发生的那些跟踪事件。要了解包含许多Erlang节点上的进程的顺序跟踪的全貌,必须合并每个涉及节点上的系统跟踪器的输出(脱机)。
以下各节介绍顺序跟踪及其最基本的概念。
跟踪令牌
每个进程都有一个当前跟踪令牌。最初,令牌是空的。当进程向另一个进程发送消息时,当前令牌的副本将与消息一起“以不可见的方式”发送。
进程的当前令牌以下列两种方式之一设置:
- 通过调用
seq_trace:set_token/1,2
- 当收到消息时
在这两种情况下,都会设置当前令牌。特别是,如果收到的消息的标记为空,则该进程的当前标记被设置为空。
跟踪令牌包含一个标签和一组标志。标签和标志都是在上面的两个选项中设置的。
连载
跟踪令牌包含一个名为的组件serial
。它由两个整数组成,Previous
而且Current
。目的是唯一标识跟踪序列中的每个跟踪事件,以及按时间顺序排列消息并在不同分支中排序(如果有)。
更新算法Serial
可描述如下:
让每个进程都有两个计数器,prev_cnt
并且在创建进程时curr_cnt
都设置为0
。柜台在以下场合更新:
当进程即将发送消息并且跟踪令牌不为空时。
让跟踪令牌的序列为tprev
和tcurr
。curr_cnt:= curr_cnt + 1tprev
:= prev_cnttcurr
:= curr_cnt带有tprev
and 的跟踪标记tcurr
随后与消息一起传递。
进程调用时
seq_trace:print(Label, Info)
,Label
匹配跟踪令牌的标签部分,跟踪令牌打印标志为
true**
。**
该算法与上述发送算法相同。
当接收到消息并包含非空跟踪令牌时。
进程跟踪令牌设置为消息中的跟踪令牌。
让跟踪令牌的序列为tprev
和tcurr
。
if(curr_cnt <tcurr)curr_cnt:= tcurr prev_cnt:= tcurr
curr_cnt
每次进程涉及顺序跟踪时,都会增加一个进程。如果一个进程非常长寿且涉及大量顺序跟踪,计数器可以达到极限(27位)。如果计数器溢出,则不能使用用于排序跟踪事件的串行。为防止计数器在连续迹线的中间溢出,函数seq_trace:reset_trace/0
可以被称为复位prev_cnt
并curr_cnt
用Erlang节点的所有进程。该函数还将进程中的所有跟踪令牌以及它们的消息队列设置为空,从而停止所有正在进行的顺序跟踪。
业绩考虑
只要没有跟踪被激活,启用了顺序跟踪的系统的性能下降可以忽略不计。当跟踪被激活时,每个跟踪消息都会产生额外的成本,但所有其他消息都不受影响。
端口
不跨端口执行顺序跟踪。
如果用户出于某种原因想要将跟踪令牌传递给端口,则必须在端口控制进程的代码中手动完成。端口控制进程必须检查适当的顺序跟踪设置(从中获得seq_trace:get_token/1
),并将消息数据中的跟踪信息包含在发送到它们各自的端口中。
同样,对于从端口接收到的消息,端口控制器必须检索特定于跟踪的信息,并通过调用来设置适当的顺序跟踪标志seq_trace:set_token/2
。
分布
节点之间的顺序跟踪是透明地执行的。这也适用于使用C编译的C节点Erl_Interface
。构建的C节点Erl_Interface
只维护一个跟踪令牌,这意味着从连续跟踪角度看,C节点显示为一个进程。
使用实例
这个例子给出了如何使用新的原语以及它产生什么样的输出的大致概念。
假设您有一个启动过程Pid == <0.30.0>就像这样:
-module(seqex).
-compile(export_all).
loop(Port) ->
receive
{Port,Message} ->
seq_trace:set_token(label,17),
seq_trace:set_token('receive',true),
seq_trace:set_token(print,true),
seq_trace:print(17,"**** Trace Started ****"),
call_server ! {self(),the_message};
{ack,Ack} ->
ok
end,
loop(Port).
而注册过程call_server有Pid == <0.31.0>这样的:
loop() ->
receive
{PortController,Message} ->
Ack = {received, Message},
seq_trace:print(17,"We are here now"),
PortController ! {ack,Ack}
end,
loop().
系统可能的输出sequential_tracer
可能是这样的:
17:<0.30.0> Info {0,1} WITH
"**** Trace Started ****"
17:<0.31.0> Received {0,2} FROM <0.30.0> WITH
{<0.30.0>,the_message}
17:<0.31.0> Info {2,3} WITH
"We are here now"
17:<0.30.0> Received {2,4} FROM <0.31.0> WITH
{ack,{received,the_message}}
生成此打印输出的系统跟踪程序流程的实现如下所示:
tracer() ->
receive
{seq_trace,Label,TraceInfo} ->
print_trace(Label,TraceInfo,false
{seq_trace,Label,TraceInfo,Ts} ->
print_trace(Label,TraceInfo,Ts
Other -> ignore
end,
tracer().
print_trace(Label,TraceInfo,false) ->
io:format("~p:",[Label]),
print_trace(TraceInfo
print_trace(Label,TraceInfo,Ts) ->
io:format("~p ~p:",[Label,Ts]),
print_trace(TraceInfo).
print_trace{print,Serial,From,_,Info}) ->
io:format("~p Info ~p WITH~n~p~n", [From,Serial,Info]
print_trace{'receive',Serial,From,To,Message}) ->
io:format("~p Received ~p FROM ~p WITH~n~p~n",
[To,Serial,From,Message]
print_trace{send,Serial,From,To,Message}) ->
io:format("~p Sent ~p TO ~p WITH~n~p~n",
[From,Serial,To,Message]).
创建运行此跟踪器功能并将该过程设置为系统跟踪器的过程的代码如下所示:
start() ->
Pid = spawn(?MODULE,tracer,[]),
seq_trace:set_system_tracer(Pid), % set Pid as the system tracer
ok.
具有类似于test/0
,可以启动整个示例:
test() ->
P = spawn(?MODULE, loop, [port]),
register(call_server, spawn(?MODULE, loop, [])),
start(),
P ! {port,message}.