bionic (3) seq_trace.3erl.gz

Provided by: erlang-manpages_20.2.2+dfsg-1ubuntu2_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 independent of the ordinary tracing in Erlang, which is controlled by the erlang:trace/3  BIF.
       For  more  information  about  what  sequential tracing is and how it can be used, see section Sequential
       Tracing.

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

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 |
                     monotonic_timestamp |
                     strict_monotonic_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.

                set_token(strict_monotonic_timestamp, Bool):
                  A  trace  token  flag (true | false) which enables/disables a strict monotonic timestamp to be
                  generated for each traced event. Default is false. Timestamps will consist of Erlang monotonic
                  time  and  a monotonically increasing integer. The time-stamp has the same format and value as
                  produced by {erlang:monotonic_time(nanosecond), erlang:unique_integer([monotonic])}.

                set_token(monotonic_timestamp, Bool):
                  A trace token flag (true | false) which enables/disables a strict monotonic  timestamp  to  be
                  generated  for each traced event. Default is false. Timestamps will use Erlang monotonic time.
                  The time-stamp has the same format and value as produced by erlang:monotonic_time(nanosecond).

              If multiple timestamp flags are passed, timestamp has precedence  over  strict_monotonic_timestamp
              which  in  turn has precedence over monotonic_timestamp. All timestamp flags are remembered, so if
              two are passed and the one with highest precedence later is disabled the  other  one  will  become
              active.

       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 |
                     monotonic_timestamp |
                     strict_monotonic_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() |
                     (TracerModule :: {module(), term()}) |
                     false

              Sets  the  system tracer. The system tracer can be either a process, port or tracer module 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() |
                     (TracerModule :: {module(), term()}) |
                     false

              Returns the pid, port identifier or tracer module 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 is one of the following, depending on if flag timestamp of the trace token is
       set to true or false:

       {seq_trace, Label, SeqTraceInfo, TimeStamp}

       or

       {seq_trace, Label, SeqTraceInfo}

       Where:

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

       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 flag 'receive' 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 flag
           print set to true, and label set to Label.

       Serial is a tuple {PreviousSerial, ThisSerial}, where:

         * Integer  PreviousSerial denotes the serial counter passed in the last received message that carried a
           trace token. If the process is the first in a new sequential trace,  PreviousSerial  is  set  to  the
           value of the process internal "trace clock".

         * Integer ThisSerial is the serial counter that a process sets on outgoing messages. 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.

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 a single message. In short, it works as follows:

       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.

       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 only receives those trace events that occur locally within the Erlang node. To get the
       whole picture of a sequential trace, involving processes on many Erlang nodes, the output from the system
       tracer on each involved node must be merged (offline).

       The following sections describe sequential tracing and its most fundamental concepts.

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 is sent "invisibly" along with the message.

       The current token of a process is set in one of the following two ways:

         * Explicitly by the process itself, through a call to seq_trace:set_token/1,2

         * When a message is received

       In both cases, the current token is set. In particular, if the token of a received message 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 both
       alternatives above.

SERIAL

       The trace token contains a component called serial. It consists of two integers,  Previous  and  Current.
       The  purpose  is  to uniquely identify each traced event within a trace sequence, as well as 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, 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 calls seq_trace:print(Label, Info), Label matches the label part of the trace token
           and the trace token print flag is true.

           The algorithm is the same as for send above.

         * When a message is received and contains a non-empty 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

       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, the serial for ordering of the trace events cannot  be  used.  To  prevent  the
       counter  from  overflowing  in  the middle of a sequential trace, function seq_trace:reset_trace/0 can be
       called to reset prev_cnt and curr_cnt of all processes in the Erlang node. This function  also  sets  all
       trace  tokens  in  processes  and  their  message  queues to empty, and thus stops all ongoing sequential
       tracing.

PERFORMANCE CONSIDERATIONS

       The performance degradation for a system that is enabled for sequential tracing is negligible as long  as
       no  tracing  is activated. When tracing is activated, there is an extra cost for each traced message, but
       all other messages are 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 must 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 appears as one process from the sequential tracing point of view.

EXAMPLE OF USE

       This example gives a rough idea of how the new primitives  can  be  used  and  what  kind  of  output  it
       produces.

       Assume that you 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 can be like this:

       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 this printout can 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 this tracer function and sets that process as the system tracer
       can 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, the whole example can be started:

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