A behaviour module for implementing the back end of the erlang
tracing system. The functions in this module will be called whenever
a trace probe is triggered. Both the
All functions in this behaviour have to be implemented as NIF's.
This is a limitation that may the lifted in the future.
There is an
Do not send messages or issue port commands to the
The different trace tags that the tracer will be called with.
Each trace tag is described in greater detail in
The process or port that the trace belongs to.
The options for the tracee.
The state which is given when calling
The following functions
should be exported from a
This callback will be called whenever a trace point is triggered. It
allows the tracer to decide whether a trace should be generated or not.
This check is made as early as possible in order to limit the amount of
overhead associated with tracing. If
This function may be called multiple times per trace point, so it is important that it is both fast and side effect free.
This callback will be called when a trace point is triggered and
the
The
In the example below a tracer module with a nif backend sends a message
for each
Here 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 {<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/6, 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", 6, 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]: Trace Tag * 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)) /* tracer is dead so we should remove this trace point */ return enif_make_atom(env, "remove"); /* 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]: Trace Tag, should only be 'send' * argv[1]: TracerState, process to send {argv[2], argv[4]} to * argv[2]: Tracee * argv[3]: Message, ignored * argv[4]: Recipient * argv[5]: Options, ignored */ static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ErlNifPid to_pid; if (enif_get_local_pid(env, argv[1], &to_pid)) { ERL_NIF_TERM msg = enif_make_tuple3(env, enif_make_atom(env, "trace"), argv[2], argv[4]); enif_send(env, &to_pid, NULL, msg); } return enif_make_atom(env, "ok"); }