Provided by: erlang-manpages_16.b.3-dfsg-1ubuntu2.2_all bug

NAME

       seq_trace - Sequential Tracing of Messages

DESCRIPTION

       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
       erlang:trace/3 BIF. See the chapter What is Sequential Tracing below  for  more  information  about  what
       sequential tracing is and how it can be used.

       seq_trace  provides  functions  which  control all aspects of sequential tracing. There are functions for
       activation, deactivation, inspection and for collection of the trace output.

   Note:
       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.

DATA TYPES

       token() = {integer(), boolean(), term(), term(), term()}

              An opaque term (a tuple) representing a trace token.

EXPORTS

       set_token(Token) -> PreviousToken | ok

              Types:

                 Token = PreviousToken = [] | token()

              Sets  the  trace  token for the calling process to Token. If Token == [] then tracing is disabled,
              otherwise Token should be an Erlang term returned from get_token/0 or set_token/1. set_token/1 can
              be used to temporarily exclude message passing from the trace by setting the trace token to  empty
              like this:

              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.

       set_token(Component, Val) -> {Component, OldVal}

              Types:

                 Component = component()
                 Val = OldVal = value()
                 component() = label | serial | flag()
                 flag() = send | 'receive' | print | timestamp
                 value() = (Integer :: integer() >= 0)
                         | {Previous :: integer() >= 0,
                            Current :: integer() >= 0}
                         | (Bool :: boolean())

              Sets  the  individual  Component  of  the  trace  token  to Val. Returns the previous value of the
              component.

                set_token(label, Integer):
                  The label component is an integer which identifies all events belonging to the same sequential
                  trace. If several sequential traces can be active simultaneously, label is  used  to  identify
                  the separate traces. Default is 0.

                set_token(serial, SerialValue):
                  SerialValue  =  {Previous,  Current}. The serial component contains counters which enables the
                  traced messages to be sorted, should never be set explicitly by the user as these counters are
                  updated automatically. Default is {0, 0}.

                set_token(send, Bool):
                  A trace token flag (true | false) which enables/disables tracing on message  sending.  Default
                  is false.

                set_token('receive', Bool):
                  A trace token flag (true | false) which enables/disables tracing on message reception. Default
                  is false.

                set_token(print, Bool):
                  A  trace  token  flag  (true  |  false)  which  enables/disables  tracing on explicit calls to
                  seq_trace:print/1. Default is false.

                set_token(timestamp, Bool):
                  A trace token flag (true | false) which enables/disables a timestamp to be generated for  each
                  traced event. Default is false.

       get_token() -> [] | token()

              Returns  the  value  of  the trace token for the calling process. If [] is returned, it means that
              tracing is not active. Any other value returned is the value of an active trace token.  The  value
              returned can be used as input to the set_token/1 function.

       get_token(Component) -> {Component, Val}

              Types:

                 Component = component()
                 Val = value()
                 component() = label | serial | flag()
                 flag() = send | 'receive' | print | timestamp
                 value() = (Integer :: integer() >= 0)
                         | {Previous :: integer() >= 0,
                            Current :: integer() >= 0}
                         | (Bool :: boolean())

              Returns  the  value of the trace token component Component. See set_token/2 for possible values of
              Component and Val.

       print(TraceInfo) -> ok

              Types:

                 TraceInfo = term()

              Puts the Erlang term TraceInfo into the sequential trace output if the calling  process  currently
              is executing within a sequential trace and the print flag of the trace token is set.

       print(Label, TraceInfo) -> ok

              Types:

                 Label = integer()
                 TraceInfo = term()

              Same  as  print/1 with the additional condition that TraceInfo is output only if Label is equal to
              the label component of the trace token.

       reset_trace() -> true

              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.

       set_system_tracer(Tracer) -> OldTracer

              Types:

                 Tracer = OldTracer = tracer()
                 tracer() = (Pid :: pid()) | port() | false

              Sets  the  system  tracer.  The  system  tracer can be either a process or port denoted by Tracer.
              Returns the previous value (which can be false if no system tracer is active).

              Failure: {badarg, Info}} if Pid is not an existing local pid.

       get_system_tracer() -> Tracer

              Types:

                 Tracer = tracer()
                 tracer() = (Pid :: pid()) | port() | false

              Returns the pid or port identifier of the current system tracer or false if no  system  tracer  is
              activated.

TRACE MESSAGES SENT TO THE SYSTEM TRACER

       The format of the messages are:

       {seq_trace, Label, SeqTraceInfo, TimeStamp}

       or

       {seq_trace, Label, SeqTraceInfo}

       depending on whether the timestamp flag of the trace token is set to true or false. Where:

       Label = int()
       TimeStamp = {Seconds, Milliseconds, Microseconds}
         Seconds = Milliseconds = Microseconds = int()

       The SeqTraceInfo can have the following formats:

         {send, Serial, From, To, Message}:
           Used when a process From with its trace token flag print set to true has sent a message.

         {'receive', Serial, From, To, Message}:
           Used when a process To receives a message with a trace token that has the 'receive' flag set to true.

         {print, Serial, From, _, Info}:
           Used  when a process From has called seq_trace:print(Label, TraceInfo) and has a trace token with the
           print flag set to true and label set to Label.

       Serial is a tuple {PreviousSerial, ThisSerial}, where the first integer PreviousSerial denotes the serial
       counter passed in the last received message which carried a trace token. If the process is the first  one
       in  a new sequential trace, PreviousSerial is set to the value of the process internal "trace clock". The
       second integer ThisSerial is the serial counter that a process sets on outgoing messages and it is  based
       on  the  process  internal  "trace  clock" which is incremented by one before it is attached to the trace
       token in the message.

WHAT IS SEQUENTIAL TRACING

       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 {Label, Flags, Serial, From}. The trace token is passed invisibly with each message.

       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 send or 'receive' is
       set). The system tracer can then print each trace event, write it to a file or whatever suitable.

   Note:
       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.

TRACE TOKEN

       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 seq_trace:set_token, or

         * 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.

SERIAL

       The trace token contains a component which is called serial. It consists of  two  integers  Previous  and
       Current.  The  purpose is to uniquely identify each traced event within a trace sequence and to order the
       messages chronologically and in the different branches if any.

       The algorithm for updating Serial can be described as follows:

       Let each process have two counters prev_cnt and curr_cnt which both are  set  to  0  when  a  process  is
       created. The counters are updated at the following occasions:

         * When the process is about to send a message and the trace token is not empty.

           Let the serial of the trace token be tprev and tcurr.
           curr_cnt := curr_cnt + 1
           tprev := prev_cnt
           tcurr := curr_cnt

           The trace token with tprev and tcurr is then passed along with the message.

         * When  the  process callsseq_trace:print(Label, Info), Label matches the label part of the trace token
           and the trace token print flag is true.

           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 tprev and tcurr.
           if (curr_cnt < tcurr )
           curr_cnt := tcurr
           prev_cnt := tcurr

       The curr_cnt of a process is incremented each time the process is involved in  a  sequential  trace.  The
       counter  can reach its limit (27 bits) if a process is very long-lived and is involved in much sequential
       tracing. If the counter overflows it will not be possible to use the serial for  ordering  of  the  trace
       events.  To  prevent  the  counter  from  overflowing  in  the  middle of a sequential trace the function
       seq_trace:reset_trace/0 can be called to reset the prev_cnt and curr_cnt of all processes in  the  Erlang
       node.  This  function  will  also set all trace tokens in processes and their message queues to empty and
       will thus stop all ongoing sequential tracing.

PERFORMANCE CONSIDERATIONS

       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.

PORTS

       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 seq_trace:get_token/1 and include trace information in the
       message data sent to their respective ports.

       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 seq_trace:set_token/2.

DISTRIBUTION

       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.

EXAMPLE OF USAGE

       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 Pid == <0.30.0> like this:

       -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 call_server with Pid == <0.31.0> like this:

       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/0 below the whole example can be started.

       test() ->
           P = spawn(?MODULE, loop, [port]),
           register(call_server, spawn(?MODULE, loop, [])),
           start(),
           P ! {port,message}.

Ericsson AB                                       kernel 2.16.4                                  seq_trace(3erl)