Sequential tracing makes it possible to trace all messages
resulting from one initial message. Sequential tracing is
completely independent of the ordinary tracing in Erlang, which
is controlled by the
The implementation of sequential tracing is in beta status. This means that the programming interface still might undergo minor adjustments (possibly incompatible) based on feedback from users.
An opaque term (a tuple) representing a trace token.
Sets the trace token for the calling process to
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
...
Returns the previous value of the trace token.
Sets the individual
The
A trace token flag (
A trace token flag (
A trace token flag (
A trace token flag (
Returns the value of the trace token for the calling process.
If
Returns the value of the trace token component
Puts the Erlang term
Same as
Sets the trace token to empty for all processes on the local node. The process internal counters used to create the serial of the trace token is set to 0. The trace token is set to empty for all messages in message queues. Together this will effectively stop all ongoing sequential tracing in the local node.
Sets the system tracer. The system tracer can be either a
process or port denoted by
Failure:
Returns the pid or port identifier of the current system
tracer or
The format of the messages are:
{seq_trace, Label, SeqTraceInfo, TimeStamp}
or
{seq_trace, Label, SeqTraceInfo}
depending on whether the
Label = int()
TimeStamp = {Seconds, Milliseconds, Microseconds}
Seconds = Milliseconds = Microseconds = int()
The
Used when a process
Used when a process
Used when a process
Sequential tracing is a way to trace a sequence of messages sent between different local or remote processes, where the sequence is initiated by one single message. In short it works like this:
Each process has a trace token, which can be empty or
not empty. When not empty the trace token can be seen as
the tuple
In order to start a sequential trace the user must explicitly set the trace token in the process that will send the first message in a sequence.
The trace token of a process is set each time the process matches a message in a receive statement, according to the trace token carried by the received message, empty or not.
On each Erlang node a process can be set as the system tracer. This process will receive trace messages each time
a message with a trace token is sent or received (if the trace
token flag
The system tracer will only receive those trace events that occur locally within the Erlang node. To get the whole picture of a sequential trace that involves processes on several Erlang nodes, the output from the system tracer on each involved node must be merged (off line).
In the following sections Sequential Tracing and its most fundamental concepts are described.
Each process has a current trace token. Initially the token is empty. When the process sends a message to another process, a copy of the current token will be sent "invisibly" along with the message.
The current token of a process is set in two ways, either
explicitly by the process itself, through a call to
when a message is received.
In both cases the current token will be set. In particular, if the token of a message received is empty, the current token of the process is set to empty.
A trace token contains a label, and a set of flags. Both the label and the flags are set in 1 and 2 above.
The trace token contains a component which is called
The algorithm for updating
Let each process have two counters
When the process is about to send a message and the trace token is not empty.
Let the serial of the trace token be
The trace token with
When the process calls
The same algorithm as for send above.
When a message is received and contains a nonempty trace token.
The process trace token is set to the trace token from the message.
Let the serial of the trace token be
The
The performance degradation for a system which is enabled for Sequential Tracing is negligible as long as no tracing is activated. When tracing is activated there will of course be an extra cost for each traced message but all other messages will be unaffected.
Sequential tracing is not performed across ports.
If the user for some reason wants to pass the trace token to a
port this has to be done manually in the code of the port
controlling process. The port controlling processes have to check
the appropriate sequential trace settings (as obtained from
Similarly, for messages received from a port, a port controller
has to retrieve trace specific information, and set appropriate
sequential trace flags through calls to
Sequential tracing between nodes is performed transparently. This applies to C-nodes built with Erl_Interface too. A C-node built with Erl_Interface only maintains one trace token, which means that the C-node will appear as one process from the sequential tracing point of view.
In order to be able to perform sequential tracing between distributed Erlang nodes, the distribution protocol has been extended (in a backward compatible way). An Erlang node which supports sequential tracing can communicate with an older (OTP R3B) node but messages passed within that node can of course not be traced.
The example shown here will give rough idea of how the new primitives can be used and what kind of output it will produce.
Assume that we have an initiating process with
-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).
And a registered process
loop() ->
receive
{PortController,Message} ->
Ack = {received, Message},
seq_trace:print(17,"We are here now"),
PortController ! {ack,Ack}
end,
loop().
A possible output from the system's sequential_tracer (inspired by AXE-10 and MD-110) could look like:
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}}
The implementation of a system tracer process that produces the printout above could look like this:
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]).
The code that creates a process that runs the tracer function above and sets that process as the system tracer could look like this:
start() ->
Pid = spawn(?MODULE,tracer,[]),
seq_trace:set_system_tracer(Pid), % set Pid as the system tracer
ok.
With a function like
test() ->
P = spawn(?MODULE, loop, [port]),
register(call_server, spawn(?MODULE, loop, [])),
start(),
P ! {port,message}.