|
|
- #include "erl_nif.h"
-
- #define NIF_ATOM_DECL(a) ERL_NIF_TERM atom_ ## a;
- // #define NIF_ATOM_H_DECL(a) extern ERL_NIF_TERM atom_ ## a;
- #define NIF_ATOM_INIT(a) atom_ ## a = enif_make_atom(env, #a);
-
- #define NIF_ATOMS(A) \
- A(_nif_thread_ret_) \
- A(call) \
- A(closed) \
- A(cpu_timestamp) \
- A(discard) \
- A(exception_from) \
- A(exit) \
- A(extra) \
- A(gc_major_end) \
- A(gc_major_start) \
- A(gc_minor_end) \
- A(gc_minor_start) \
- A(getting_linked) \
- A(getting_unlinked) \
- A(in) \
- A(in_exiting) \
- A(link) \
- A(match_spec_result) \
- A(mode) \
- A(monotonic) \
- A(ok) \
- A(open) \
- A(out) \
- A(out_exited) \
- A(out_exiting) \
- A(percent) \
- A(profile) \
- A(receive) \
- A(register) \
- A(remove) \
- A(return_from) \
- A(return_to) \
- A(scheduler_id) \
- A(send) \
- A(send_to_non_existing_process) \
- A(spawn) \
- A(spawned) \
- A(strict_monotonic) \
- A(timestamp) \
- A(trace) \
- A(trace_status) \
- A(tracers) \
- A(unlink) \
- A(unregister)
-
- NIF_ATOMS(NIF_ATOM_DECL)
-
- static int load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info) {
- NIF_ATOMS(NIF_ATOM_INIT)
- *priv_data = NULL;
-
- return 0;
- }
-
- static int upgrade(ErlNifEnv *env, void **priv_data, void **old_priv_data, ERL_NIF_TERM load_info) {
- *priv_data = *old_priv_data;
-
- return 0;
- }
-
- static void unload(ErlNifEnv *env, void *priv_data) {
-
- }
-
- // enabled(TraceTag, TracerState, Tracee)
- static ERL_NIF_TERM enabled(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
- int arity;
- const ERL_NIF_TERM *tuple;
- ErlNifPid tracer_pid;
-
- if (!enif_get_tuple(env, argv[1], &arity, &tuple)) {
- return atom_remove;
- }
-
- // Disable the trace when one of the tracers is not a local process.
- if (!enif_get_local_pid(env, tuple[0], &tracer_pid))
- return atom_remove;
-
- // Disable the trace when one of the tracers is not alive.
- if (!enif_is_process_alive(env, &tracer_pid))
- return atom_remove;
-
- return atom_discard;
- }
-
- static ERL_NIF_TERM enabled_call(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
- // We always want both call and return_to.
- return atom_trace;
- }
-
- static ERL_NIF_TERM enabled_procs(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
- int arity;
- const ERL_NIF_TERM *tuple;
-
- if (!enif_get_tuple(env, argv[1], &arity, &tuple)) {
- return atom_remove;
- }
-
- // We only want the spawn and exit events when 'profile' mode
- // is enabled. Technically we only care about exits for callgrind,
- // but spawn is cheap to keep and useful for message profilers.
- if (enif_is_identical(atom_profile, tuple[1]) && !(enif_is_identical(atom_spawn, argv[0]) || enif_is_identical(atom_exit, argv[0]))) {
- return atom_discard;
- }
-
- return atom_trace;
- }
-
- static ERL_NIF_TERM enabled_send(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
- // We always want both send and send_to_non_existing_process.
- return atom_trace;
- }
-
- static ERL_NIF_TERM enabled_receive(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
- // We always want receive.
- return atom_trace;
- }
-
- static ERL_NIF_TERM enabled_running_procs(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
- // We always want both in and out.
- return atom_trace;
- }
-
- static ERL_NIF_TERM enabled_garbage_collection(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
- // We always want both gc_minor_start, gc_max_heap_size, and gc_minor_end.
- return atom_trace;
- }
-
- // trace(TraceTag, TracerState, Tracee, TraceTerm, Opts)
- static ERL_NIF_TERM trace(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
- enif_fprintf(stdout, "IMY************trace:");
- for (int i = 0; i <= argc - 1; i++) {
- if (i != 1) {
- enif_fprintf(stdout, " %d %T", i, argv[i]);
- }
- }
- enif_fprintf(stdout, "\n");
-
- ERL_NIF_TERM tracers, head, ts, extra, mspec, msg;
- ErlNifPid tracer;
- unsigned int nth;
- size_t len;
- int has_extra, has_mspec;
-
- if (!enif_get_map_value(env, argv[1], atom_tracers, &tracers))
- return atom_ok;
-
- // We know for a fact that the argument is a map. And if not,
- // no problem because we will return when trying to get a value from it.
- enif_get_map_size(env, tracers, &len);
-
- #if (ERL_NIF_MAJOR_VERSION >= 2) && (ERL_NIF_MINOR_VERSION >= 12)
- nth = enif_hash(ERL_NIF_INTERNAL_HASH, argv[2], 0) % len;
- #else
- // Select the correct tracer for this process.
- //
- // The pid value is detailed in:
- // 5b6dd0e84cf0f1dc19ddd05f86cf04b2695d8a9e/erts/emulator/beam/erl_term.h#L498
- //
- // As can be seen there, the first four bits of the pid value
- // are always the same. We therefore shift them out.
-
- ErlNifPid tracee;
-
- if (!enif_get_local_pid(env, argv[2], &tracee))
- return atom_ok;
-
- nth = (tracee.pid >> 4) % len;
- #endif
-
- if (!enif_get_map_value(env, tracers, enif_make_int(env, nth), &head))
- return atom_ok;
-
- if (!enif_get_local_pid(env, head, &tracer))
- return atom_ok;
-
- // Everything good. Generate a timestamp to include in the message.
-
- ts = enif_make_int64(env, enif_monotonic_time(ERL_NIF_USEC));
-
- // Build the message. There can be two different messages
- // depending on whether the extra option was set:
- //
- // - {Tag, Tracee, Ts, Term}
- // - {Tag, Tracee, Ts, Term, Extra}
- //
- // On top of that when match specs are enabled we may have
- // one additional term at the end of the tuple containing
- // the result of the match spec function.
- //
- // - {Tag, Tracee, Ts, Term, Result}
- // - {Tag, Tracee, Ts, Term, Extra, Result}
-
- has_extra = enif_get_map_value(env, argv[4], atom_extra, &extra);
- has_mspec = enif_get_map_value(env, argv[4], atom_match_spec_result, &mspec);
-
- if (has_extra && has_mspec)
- msg = enif_make_tuple6(env, argv[0], argv[2], ts, argv[3], extra, mspec);
- else if (has_extra)
- msg = enif_make_tuple5(env, argv[0], argv[2], ts, argv[3], extra);
- else if (has_mspec)
- msg = enif_make_tuple5(env, argv[0], argv[2], ts, argv[3], mspec);
- else
- msg = enif_make_tuple4(env, argv[0], argv[2], ts, argv[3]);
-
- // Send the message to the selected tracer.
-
- enif_send(env, &tracer, NULL, msg);
-
- return atom_ok;
- }
-
- static ErlNifFunc nifFuns[] = {
- {"enabled", 3, enabled},
- {"enabled_call", 3, enabled_call},
- {"enabled_procs", 3, enabled_procs},
- {"enabled_send", 3, enabled_send},
- {"enabled_receive", 3, enabled_receive},
- {"enabled_running_procs", 3, enabled_running_procs},
- {"enabled_garbage_collection", 3, enabled_garbage_collection},
- {"trace", 5, trace},
- };
-
- ERL_NIF_INIT(tpTracerNif, nifFuns, load, NULL, upgrade, unload
- )
|