Provided by: erlang-manpages_24.3.4.1+dfsg-1_all bug

NAME

       erl_tracer - Erlang tracer behavior.

DESCRIPTION

       This  behavior  module implements the back end of the Erlang tracing system. The functions
       in this module are called whenever a trace probe is triggered. Both the enabled and  trace
       functions  are  called  in  the context of the entity that triggered the trace probe. This
       means that the overhead by having the tracing enabled is greatly effected by how much time
       is spent in these functions. So, do as little work as possible in these functions.

   Note:
       All functions in this behavior must be implemented as NIFs. This limitation can be removed
       in a future releases. An example tracer module NIF implementation is provided at  the  end
       of this page.

   Warning:
       Do not send messages or issue port commands to the Tracee in any of the callbacks. This is
       not allowed and can cause all sorts of strange behavior, including, but  not  limited  to,
       infinite recursions.

DATA TYPES

       trace_tag_call() =
           call | return_to | return_from | exception_from

       trace_tag_gc() =
           gc_minor_start | gc_minor_end | gc_major_start | gc_major_end

       trace_tag_ports() =
           open | closed | link | unlink | getting_linked |
           getting_unlinked

       trace_tag_procs() =
           spawn | spawned | exit | link | unlink | getting_linked |
           getting_unlinked | register | unregister

       trace_tag_receive() = 'receive'

       trace_tag_running_ports() =
           in | out | in_exiting | out_exiting | out_exited

       trace_tag_running_procs() =
           in | out | in_exiting | out_exiting | out_exited

       trace_tag_send() = send | send_to_non_existing_process

       trace_tag() =
           trace_tag_send() |
           trace_tag_receive() |
           trace_tag_call() |
           trace_tag_procs() |
           trace_tag_ports() |
           trace_tag_running_procs() |
           trace_tag_running_ports() |
           trace_tag_gc()

              The  different  trace  tags  that  the  tracer  is  called  with. Each trace tag is
              described in detail in Module:trace/5.

       tracee() = port() | pid() | undefined

              The process or port that the trace belongs to.

       trace_opts() =
           #{extra => term(),
             match_spec_result => term(),
             scheduler_id => integer() >= 0,
             timestamp =>
                 timestamp | cpu_timestamp | monotonic | strict_monotonic}

              The options for the tracee:

                timestamp:
                  If set the tracer has been requested to include a time stamp.

                extra:
                  If set the tracepoint has included additional data about the trace event.  What
                  the  additional data is depends on which TraceTag has been triggered. The extra
                  trace data corresponds to the fifth element in the trace  tuples  described  in
                  erlang:trace/3.

                match_spec_result:
                  If  set  the  tracer  has  been  requested  to  include  the  output of a match
                  specification that was run.

                scheduler_id:
                  If set the scheduler id is to be included by the tracer.

       tracer_state() = term()

              The             state              specified              when              calling
              erlang:trace(PidPortSpec,true,[{tracer,Module,TracerState}]).  The  tracer state is
              an immutable value that is passed to erl_tracer callbacks and is to contain all the
              data that is needed to generate the trace event.

CALLBACK FUNCTIONS

       The following functions are to be exported from an erl_tracer callback module:

         Module:enabled/3:
           Mandatory

         Module:trace/5:
           Mandatory

         Module:enabled_call/3:
           Optional

         Module:trace_call/5:
           Optional

         Module:enabled_garbage_collection/3:
           Optional

         Module:trace_garbage_collection/5:
           Optional

         Module:enabled_ports/3:
           Optional

         Module:trace_ports/5:
           Optional

         Module:enabled_procs/3:
           Optional

         Module:trace_procs/5:
           Optional

         Module:enabled_receive/3:
           Optional

         Module:trace_receive/5:
           Optional

         Module:enabled_running_ports/3:
           Optional

         Module:trace_running_ports/5:
           Optional

         Module:enabled_running_procs/3:
           Optional

         Module:trace_running_procs/5:
           Optional

         Module:enabled_send/3:
           Optional

         Module:trace_send/5:
           Optional

EXPORTS

       Module:enabled(TraceTag, TracerState, Tracee) -> Result

              Types:

                 TraceTag =  trace_tag() | trace_status
                 TracerState = term()
                 Tracee = tracee()
                 Result = trace | discard | remove

              This callback is called whenever a tracepoint is triggered. It allows the tracer to
              decide whether a trace is to be generated or not. This check is made  as  early  as
              possible  to  limit  the  amount  of  overhead associated with tracing. If trace is
              returned, the necessary trace data is created and the trace callback of the  tracer
              is  called.  If  discard  is  returned, this trace call is discarded and no call to
              trace is done.

              trace_status is a special type of TraceTag, which is used to check if the tracer is
              still  to  be active. It is called in multiple scenarios, but most significantly it
              is used when tracing is started using this tracer. If remove is returned  when  the
              trace_status is checked, the tracer is removed from the tracee.

              This  function can be called multiple times per tracepoint, so it is important that
              it is both fast and without side effects.

       Module:enabled_call(TraceTag, TracerState, Tracee) -> Result

              Types:

                 TraceTag =  trace_tag_call()
                 TracerState = term()
                 Tracee = tracee()
                 Result = trace | discard | remove

              This callback is called whenever a tracepoint with trace flag call |  return_to  is
              triggered.

              If enabled_call/3 is undefined, Module:enabled/3 is called instead.

       Module:enabled_garbage_collection(TraceTag, TracerState, Tracee) -> Result

              Types:

                 TraceTag =  trace_tag_gc()
                 TracerState = term()
                 Tracee = tracee()
                 Result = trace | discard | remove

              This callback is called whenever a tracepoint with trace flag garbage_collection is
              triggered.

              If enabled_garbage_collection/3 is undefined, Module:enabled/3 is called instead.

       Module:enabled_ports(TraceTag, TracerState, Tracee) -> Result

              Types:

                 TraceTag =  trace_tag_ports()
                 TracerState = term()
                 Tracee = tracee()
                 Result = trace | discard | remove

              This callback is called whenever a tracepoint with trace flag ports is triggered.

              If enabled_ports/3 is undefined, Module:enabled/3 is called instead.

       Module:enabled_procs(TraceTag, TracerState, Tracee) -> Result

              Types:

                 TraceTag =  trace_tag_procs()
                 TracerState = term()
                 Tracee = tracee()
                 Result = trace | discard | remove

              This callback is called whenever a tracepoint with trace flag procs is triggered.

              If enabled_procs/3 is undefined, Module:enabled/3 is called instead.

       Module:enabled_receive(TraceTag, TracerState, Tracee) -> Result

              Types:

                 TraceTag =  trace_tag_receive()
                 TracerState = term()
                 Tracee = tracee()
                 Result = trace | discard | remove

              This callback is  called  whenever  a  tracepoint  with  trace  flag  'receive'  is
              triggered.

              If enabled_receive/3 is undefined, Module:enabled/3 is called instead.

       Module:enabled_running_ports(TraceTag, TracerState, Tracee) -> Result

              Types:

                 TraceTag =  trace_tag_running_ports()
                 TracerState = term()
                 Tracee = tracee()
                 Result = trace | discard | remove

              This  callback  is  called  whenever  a tracepoint with trace flag running_ports is
              triggered.

              If enabled_running_ports/3 is undefined, Module:enabled/3 is called instead.

       Module:enabled_running_procs(TraceTag, TracerState, Tracee) -> Result

              Types:

                 TraceTag =  trace_tag_running_procs()
                 TracerState = term()
                 Tracee = tracee()
                 Result = trace | discard | remove

              This callback is called whenever a  tracepoint  with  trace  flag  running_procs  |
              running is triggered.

              If enabled_running_procs/3 is undefined, Module:enabled/3 is called instead.

       Module:enabled_send(TraceTag, TracerState, Tracee) -> Result

              Types:

                 TraceTag =  trace_tag_send()
                 TracerState = term()
                 Tracee = tracee()
                 Result = trace | discard | remove

              This callback is called whenever a tracepoint with trace flag send is triggered.

              If enabled_send/3 is undefined, Module:enabled/3 is called instead.

       Module:trace(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result

              Types:

                 TraceTag =  trace_tag()
                 TracerState = term()
                 Tracee = tracee()
                 TraceTerm = term()
                 Opts = trace_opts()
                 Result = ok

              This  callback  is  called  when a tracepoint is triggered and the Module:enabled/3
              callback returned trace. In it any side effects needed by  the  tracer  are  to  be
              done.  The  tracepoint  payload  is  located  in  the TraceTerm. The content of the
              TraceTerm depends on which TraceTag is  triggered.  TraceTerm  corresponds  to  the
              fourth element in the trace tuples described in erlang:trace/3.

              If  the  trace tuple has five elements, the fifth element will be sent as the extra
              value in the Opts maps.

       Module:trace(seq_trace, TracerState, Label, SeqTraceInfo, Opts) -> Result

              Types:

                 TracerState = term()
                 Label = term()
                 SeqTraceInfo = term()
                 Opts = trace_opts()
                 Result = ok

              The TraceTag seq_trace is handled slightly differently.  There  is  no  Tracee  for
              seq_trace, instead the Label associated with the seq_trace event is specified.

              For more information on what Label and SeqTraceInfo can be, see seq_trace(3erl).

       Module:trace_call(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result

              Types:

                 TraceTag =  trace_tag_call()
                 TracerState = term()
                 Tracee = tracee()
                 TraceTerm = term()
                 Opts = trace_opts()
                 Result = ok

              This    callback   is   called   when   a   tracepoint   is   triggered   and   the
              Module:enabled_call/3 callback returned trace.

              If trace_call/5 is undefined, Module:trace/5 is called instead.

       Module:trace_garbage_collection(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result

              Types:

                 TraceTag =  trace_tag_gc()
                 TracerState = term()
                 Tracee = tracee()
                 TraceTerm = term()
                 Opts = trace_opts()
                 Result = ok

              This   callback   is   called   when   a   tracepoint   is   triggered   and    the
              Module:enabled_garbage_collection/3 callback returned trace.

              If trace_garbage_collection/5 is undefined, Module:trace/5 is called instead.

       Module:trace_ports(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result

              Types:

                 TraceTag =  trace_tag()
                 TracerState = term()
                 Tracee = tracee()
                 TraceTerm = term()
                 Opts = trace_opts()
                 Result = ok

              This    callback   is   called   when   a   tracepoint   is   triggered   and   the
              Module:enabled_ports/3 callback returned trace.

              If trace_ports/5 is undefined, Module:trace/5 is called instead.

       Module:trace_procs(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result

              Types:

                 TraceTag =  trace_tag()
                 TracerState = term()
                 Tracee = tracee()
                 TraceTerm = term()
                 Opts = trace_opts()
                 Result = ok

              This   callback   is   called   when   a   tracepoint   is   triggered   and    the
              Module:enabled_procs/3 callback returned trace.

              If trace_procs/5 is undefined, Module:trace/5 is called instead.

       Module:trace_receive(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result

              Types:

                 TraceTag =  trace_tag_receive()
                 TracerState = term()
                 Tracee = tracee()
                 TraceTerm = term()
                 Opts = trace_opts()
                 Result = ok

              This    callback   is   called   when   a   tracepoint   is   triggered   and   the
              Module:enabled_receive/3 callback returned trace.

              If trace_receive/5 is undefined, Module:trace/5 is called instead.

       Module:trace_running_ports(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result

              Types:

                 TraceTag =  trace_tag_running_ports()
                 TracerState = term()
                 Tracee = tracee()
                 TraceTerm = term()
                 Opts = trace_opts()
                 Result = ok

              This   callback   is   called   when   a   tracepoint   is   triggered   and    the
              Module:enabled_running_ports/3 callback returned trace.

              If trace_running_ports/5 is undefined, Module:trace/5 is called instead.

       Module:trace_running_procs(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result

              Types:

                 TraceTag =  trace_tag_running_procs()
                 TracerState = term()
                 Tracee = tracee()
                 TraceTerm = term()
                 Opts = trace_opts()
                 Result = ok

              This    callback   is   called   when   a   tracepoint   is   triggered   and   the
              Module:enabled_running_procs/3 callback returned trace.

              If trace_running_procs/5 is undefined, Module:trace/5 is called instead.

       Module:trace_send(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result

              Types:

                 TraceTag =  trace_tag_send()
                 TracerState = term()
                 Tracee = tracee()
                 TraceTerm = term()
                 Opts = trace_opts()
                 Result = ok

              This   callback   is   called   when   a   tracepoint   is   triggered   and    the
              Module:enabled_send/3 callback returned trace.

              If trace_send/5 is undefined, Module:trace/5 is called instead.

ERL TRACER MODULE EXAMPLE

       In  this  example, a tracer module with a NIF back end sends a message for each send trace
       tag containing only the sender and  receiver.  Using  this  tracer  module,  a  much  more
       lightweight message tracer is used, which only records who sent messages to who.

       The following is an example session using it on Linux:

       $ gcc -I erts-8.0/include/ -fPIC -shared -o erl_msg_tracer.so erl_msg_tracer.c
       $ erl
       Erlang/OTP 19 [DEVELOPMENT] [erts-8.0] [source-ed2b56b] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false]

       Eshell V8.0  (abort with ^G)
       1> c(erl_msg_tracer), erl_msg_tracer:load().
       ok
       2> Tracer = spawn(fun F() -> receive M -> io:format("~p~n",[M]), F() end end).
       <0.37.0>
       3> erlang:trace(new, true, [send,{tracer, erl_msg_tracer, Tracer}]).
       0
       {trace,<0.39.0>,<0.27.0>}
       4> {ok, D} = file:open("/tmp/tmp.data",[write]).
       {trace,#Port<0.486>,<0.40.0>}
       {trace,<0.40.0>,<0.21.0>}
       {trace,#Port<0.487>,<0.4.0>}
       {trace,#Port<0.488>,<0.4.0>}
       {trace,#Port<0.489>,<0.4.0>}
       {trace,#Port<0.490>,<0.4.0>}
       {ok,<0.40.0>}
       {trace,<0.41.0>,<0.27.0>}
       5>

       erl_msg_tracer.erl:

       -module(erl_msg_tracer).

       -export([enabled/3, trace/5, load/0]).

       load() ->
           erlang:load_nif("erl_msg_tracer", []).

       enabled(_, _, _) ->
           error.

       trace(_, _, _, _, _) ->
           error.

       erl_msg_tracer.c:

       #include <erl_nif.h>

       /* NIF interface declarations */
       static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info);
       static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info);
       static void unload(ErlNifEnv* env, void* priv_data);

       /* The NIFs: */
       static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
       static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);

       static ErlNifFunc nif_funcs[] = {
           {"enabled", 3, enabled},
           {"trace", 5, trace}
       };

       ERL_NIF_INIT(erl_msg_tracer, nif_funcs, load, NULL, upgrade, unload)

       static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
       {
           *priv_data = NULL;
           return 0;
       }

       static void unload(ErlNifEnv* env, void* priv_data)
       {

       }

       static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data,
                    ERL_NIF_TERM load_info)
       {
           if (*old_priv_data != NULL || *priv_data != NULL) {
            return -1; /* Don't know how to do that */
           }
           if (load(env, priv_data, load_info)) {
            return -1;
           }
           return 0;
       }

       /*
        * argv[0]: TraceTag
        * argv[1]: TracerState
        * argv[2]: Tracee
        */
       static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
       {
           ErlNifPid to_pid;
           if (enif_get_local_pid(env, argv[1], &to_pid))
               if (!enif_is_process_alive(env, &to_pid))
                   if (enif_is_identical(enif_make_atom(env, "trace_status"), argv[0]))
                       /* tracer is dead so we should remove this tracepoint */
                       return enif_make_atom(env, "remove");
                   else
                       return enif_make_atom(env, "discard");

           /* Only generate trace for when tracer != tracee */
           if (enif_is_identical(argv[1], argv[2]))
               return enif_make_atom(env, "discard");

           /* Only trigger trace messages on 'send' */
           if (enif_is_identical(enif_make_atom(env, "send"), argv[0]))
               return enif_make_atom(env, "trace");

           /* Have to answer trace_status */
           if (enif_is_identical(enif_make_atom(env, "trace_status"), argv[0]))
               return enif_make_atom(env, "trace");

           return enif_make_atom(env, "discard");
       }

       /*
        * argv[0]: TraceTag, should only be 'send'
        * argv[1]: TracerState, process to send {Tracee, Recipient} to
        * argv[2]: Tracee
        * argv[3]: Message
        * argv[4]: Options, map containing Recipient
        */
       static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
       {
           ErlNifPid to_pid;
           ERL_NIF_TERM recipient, msg;

           if (enif_get_local_pid(env, argv[1], &to_pid)) {
             if (enif_get_map_value(env, argv[4], enif_make_atom(env, "extra"), &recipient)) {
               msg = enif_make_tuple3(env, enif_make_atom(env, "trace"), argv[2], recipient);
               enif_send(env, &to_pid, NULL, msg);
             }
           }

           return enif_make_atom(env, "ok");
       }