|
|
- /*
- * %CopyrightBegin%
- *
- * Copyright Ericsson 2015-2016. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * %CopyrightEnd%
- */
-
- /*
- * Purpose: NIF library for process/port tracer
- *
- */
-
-
- #define STATIC_ERLANG_NIF 1
-
- #include "erl_nif.h"
- #include "config.h"
- #include "sys.h"
-
- #ifdef VALGRIND
- # include <valgrind/memcheck.h>
- #endif
-
- /* 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_tracer, nif_funcs, load, NULL, upgrade, unload)
-
- #define ATOMS \
- ATOM_DECL(call); \
- ATOM_DECL(command); \
- ATOM_DECL(cpu_timestamp); \
- ATOM_DECL(discard); \
- ATOM_DECL(exception_from); \
- ATOM_DECL(extra); \
- ATOM_DECL(match_spec_result); \
- ATOM_DECL(monotonic); \
- ATOM_DECL(ok); \
- ATOM_DECL(remove); \
- ATOM_DECL(return_from); \
- ATOM_DECL(scheduler_id); \
- ATOM_DECL(send); \
- ATOM_DECL(send_to_non_existing_process); \
- ATOM_DECL(seq_trace); \
- ATOM_DECL(spawn); \
- ATOM_DECL(strict_monotonic); \
- ATOM_DECL(timestamp); \
- ATOM_DECL(trace); \
- ATOM_DECL(trace_status); \
- ATOM_DECL(trace_ts); \
- ATOM_DECL(true); \
- ATOM_DECL(gc_minor_start); \
- ATOM_DECL(gc_minor_end); \
- ATOM_DECL(gc_major_start); \
- ATOM_DECL(gc_major_end);
-
- #define ATOM_DECL(A) static ERL_NIF_TERM atom_##A
- ATOMS
- #undef ATOM_DECL
-
- static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
- {
-
- #define ATOM_DECL(A) atom_##A = enif_make_atom(env, #A)
- ATOMS
- #undef ATOM_DECL
-
- *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) {
- return -1; /* Don't know how to do that */
- }
- if (*priv_data != NULL) {
- return -1; /* Don't know how to do that */
- }
- if (load(env, priv_data, load_info)) {
- return -1;
- }
- return 0;
- }
-
- static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
- {
- ErlNifPid to_pid;
- ErlNifPort to_port;
- ERL_NIF_TERM ret = enif_is_identical(argv[0], atom_trace_status) ?
- atom_remove : atom_discard;
-
- ASSERT(argc == 3);
-
- 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 ret;
- } else if (enif_get_local_port(env, argv[1], &to_port)) {
- if (!enif_is_port_alive(env, &to_port))
- /* tracer is dead so we should remove this trace point */
- return ret;
- } else {
- /* The state was not a pid or a port */
- return ret;
- }
-
- /* Only generate trace for when tracer != tracee */
- if (enif_is_identical(argv[1], argv[2])) {
- return atom_discard;
- }
-
- return atom_trace;
- }
-
- /*
- -spec trace(seq_trace, TracerState :: pid() | port(),
- Label :: non_neg_integer(),
- Msg :: term(),
- Opts :: map()) -> ignored();
- trace(Tag :: atom(), TracerState :: pid() | port(),
- Tracee :: pid() || port() || undefined,
- Msg :: term(),
- Opts :: map()) -> ignored().
- */
- static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
- {
- ERL_NIF_TERM value, msg, tt[8], opts;
- ErlNifPid to_pid;
- ErlNifPort to_port;
- size_t tt_sz = 0;
- int is_port = 0;
- size_t opts_sz = 0;
- ASSERT(argc == 5);
-
- if (!enif_get_local_pid(env, argv[1], &to_pid)) {
- if (!enif_get_local_port(env, argv[1], &to_port)) {
- /* This only fails if argv[1] is a not a local port/pid
- which should not happen as it is checked in enabled */
- ASSERT(0);
- return atom_ok;
- }
- is_port = 1;
- }
-
- opts = argv[4];
-
- if (!enif_get_map_size(env, opts, &opts_sz))
- opts_sz = 0;
-
- if (opts_sz && enif_get_map_value(env, opts, atom_extra, &value)) {
- tt[tt_sz++] = atom_trace;
- tt[tt_sz++] = argv[2];
- tt[tt_sz++] = argv[0];
- tt[tt_sz++] = argv[3];
- tt[tt_sz++] = value;
- } else {
- if (enif_is_identical(argv[0], atom_seq_trace)) {
- tt[tt_sz++] = atom_seq_trace;
- tt[tt_sz++] = argv[2];
- tt[tt_sz++] = argv[3];
- } else {
- tt[tt_sz++] = atom_trace;
- tt[tt_sz++] = argv[2];
- tt[tt_sz++] = argv[0];
- tt[tt_sz++] = argv[3];
- }
- }
-
-
- if (opts_sz && enif_get_map_value(env, opts, atom_match_spec_result, &value)) {
- tt[tt_sz++] = value;
- }
-
- if (opts_sz && enif_get_map_value(env, opts, atom_scheduler_id, &value)) {
- tt[tt_sz++] = value;
- }
-
- if (opts_sz && enif_get_map_value(env, opts, atom_timestamp, &value)) {
- ERL_NIF_TERM ts;
- if (enif_is_identical(value, atom_monotonic)) {
- ErlNifTime mon = enif_monotonic_time(ERL_NIF_NSEC);
- ts = enif_make_int64(env, mon);
- } else if (enif_is_identical(value, atom_strict_monotonic)) {
- ErlNifTime mon = enif_monotonic_time(ERL_NIF_NSEC);
- ERL_NIF_TERM unique = enif_make_unique_integer(
- env, ERL_NIF_UNIQUE_MONOTONIC);
- ts = enif_make_tuple2(env, enif_make_int64(env, mon), unique);
- } else if (enif_is_identical(value, atom_timestamp)) {
- ts = enif_now_time(env);
- } else if (enif_is_identical(value, atom_cpu_timestamp)) {
- ts = enif_cpu_time(env);
- } else {
- ASSERT(0);
- goto error;
- }
- tt[tt_sz++] = ts;
- if (tt[0] == atom_trace)
- tt[0] = atom_trace_ts;
- }
-
- msg = enif_make_tuple_from_array(env, tt, tt_sz);
-
- if (is_port) {
- ErlNifBinary bin;
-
- if (!enif_term_to_binary(env, msg, &bin))
- goto error;
-
- msg = enif_make_binary(env, &bin);
-
- (void) enif_port_command(env, &to_port, NULL, msg);
- /* if failure: port has probably died, enabled will clean up */
-
- enif_release_binary(&bin);
- } else {
-
- (void) enif_send(env, &to_pid, NULL, msg);
- /* if failure: process has probably died, enabled will clean up */
- }
-
- error:
-
- return atom_ok;
- }
|