You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

257 line
7.7 KiB

  1. /*
  2. * %CopyrightBegin%
  3. *
  4. * Copyright Ericsson 2015-2016. All Rights Reserved.
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. *
  18. * %CopyrightEnd%
  19. */
  20. /*
  21. * Purpose: NIF library for process/port tracer
  22. *
  23. */
  24. #define STATIC_ERLANG_NIF 1
  25. #include "erl_nif.h"
  26. #include "config.h"
  27. #include "sys.h"
  28. #ifdef VALGRIND
  29. # include <valgrind/memcheck.h>
  30. #endif
  31. /* NIF interface declarations */
  32. static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info);
  33. static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info);
  34. static void unload(ErlNifEnv* env, void* priv_data);
  35. /* The NIFs: */
  36. static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
  37. static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
  38. static ErlNifFunc nif_funcs[] = {
  39. {"enabled", 3, enabled},
  40. {"trace", 5, trace}
  41. };
  42. ERL_NIF_INIT(erl_tracer, nif_funcs, load, NULL, upgrade, unload)
  43. #define ATOMS \
  44. ATOM_DECL(call); \
  45. ATOM_DECL(command); \
  46. ATOM_DECL(cpu_timestamp); \
  47. ATOM_DECL(discard); \
  48. ATOM_DECL(exception_from); \
  49. ATOM_DECL(extra); \
  50. ATOM_DECL(match_spec_result); \
  51. ATOM_DECL(monotonic); \
  52. ATOM_DECL(ok); \
  53. ATOM_DECL(remove); \
  54. ATOM_DECL(return_from); \
  55. ATOM_DECL(scheduler_id); \
  56. ATOM_DECL(send); \
  57. ATOM_DECL(send_to_non_existing_process); \
  58. ATOM_DECL(seq_trace); \
  59. ATOM_DECL(spawn); \
  60. ATOM_DECL(strict_monotonic); \
  61. ATOM_DECL(timestamp); \
  62. ATOM_DECL(trace); \
  63. ATOM_DECL(trace_status); \
  64. ATOM_DECL(trace_ts); \
  65. ATOM_DECL(true); \
  66. ATOM_DECL(gc_minor_start); \
  67. ATOM_DECL(gc_minor_end); \
  68. ATOM_DECL(gc_major_start); \
  69. ATOM_DECL(gc_major_end);
  70. #define ATOM_DECL(A) static ERL_NIF_TERM atom_##A
  71. ATOMS
  72. #undef ATOM_DECL
  73. static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
  74. {
  75. #define ATOM_DECL(A) atom_##A = enif_make_atom(env, #A)
  76. ATOMS
  77. #undef ATOM_DECL
  78. *priv_data = NULL;
  79. return 0;
  80. }
  81. static void unload(ErlNifEnv* env, void* priv_data)
  82. {
  83. }
  84. static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data,
  85. ERL_NIF_TERM load_info)
  86. {
  87. if (*old_priv_data != NULL) {
  88. return -1; /* Don't know how to do that */
  89. }
  90. if (*priv_data != NULL) {
  91. return -1; /* Don't know how to do that */
  92. }
  93. if (load(env, priv_data, load_info)) {
  94. return -1;
  95. }
  96. return 0;
  97. }
  98. static ERL_NIF_TERM enabled(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
  99. {
  100. ErlNifPid to_pid;
  101. ErlNifPort to_port;
  102. ERL_NIF_TERM ret = enif_is_identical(argv[0], atom_trace_status) ?
  103. atom_remove : atom_discard;
  104. ASSERT(argc == 3);
  105. if (enif_get_local_pid(env, argv[1], &to_pid)) {
  106. if (!enif_is_process_alive(env, &to_pid))
  107. /* tracer is dead so we should remove this trace point */
  108. return ret;
  109. } else if (enif_get_local_port(env, argv[1], &to_port)) {
  110. if (!enif_is_port_alive(env, &to_port))
  111. /* tracer is dead so we should remove this trace point */
  112. return ret;
  113. } else {
  114. /* The state was not a pid or a port */
  115. return ret;
  116. }
  117. /* Only generate trace for when tracer != tracee */
  118. if (enif_is_identical(argv[1], argv[2])) {
  119. return atom_discard;
  120. }
  121. return atom_trace;
  122. }
  123. /*
  124. -spec trace(seq_trace, TracerState :: pid() | port(),
  125. Label :: non_neg_integer(),
  126. Msg :: term(),
  127. Opts :: map()) -> ignored();
  128. trace(Tag :: atom(), TracerState :: pid() | port(),
  129. Tracee :: pid() || port() || undefined,
  130. Msg :: term(),
  131. Opts :: map()) -> ignored().
  132. */
  133. static ERL_NIF_TERM trace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
  134. {
  135. ERL_NIF_TERM value, msg, tt[8], opts;
  136. ErlNifPid to_pid;
  137. ErlNifPort to_port;
  138. size_t tt_sz = 0;
  139. int is_port = 0;
  140. size_t opts_sz = 0;
  141. ASSERT(argc == 5);
  142. if (!enif_get_local_pid(env, argv[1], &to_pid)) {
  143. if (!enif_get_local_port(env, argv[1], &to_port)) {
  144. /* This only fails if argv[1] is a not a local port/pid
  145. which should not happen as it is checked in enabled */
  146. ASSERT(0);
  147. return atom_ok;
  148. }
  149. is_port = 1;
  150. }
  151. opts = argv[4];
  152. if (!enif_get_map_size(env, opts, &opts_sz))
  153. opts_sz = 0;
  154. if (opts_sz && enif_get_map_value(env, opts, atom_extra, &value)) {
  155. tt[tt_sz++] = atom_trace;
  156. tt[tt_sz++] = argv[2];
  157. tt[tt_sz++] = argv[0];
  158. tt[tt_sz++] = argv[3];
  159. tt[tt_sz++] = value;
  160. } else {
  161. if (enif_is_identical(argv[0], atom_seq_trace)) {
  162. tt[tt_sz++] = atom_seq_trace;
  163. tt[tt_sz++] = argv[2];
  164. tt[tt_sz++] = argv[3];
  165. } else {
  166. tt[tt_sz++] = atom_trace;
  167. tt[tt_sz++] = argv[2];
  168. tt[tt_sz++] = argv[0];
  169. tt[tt_sz++] = argv[3];
  170. }
  171. }
  172. if (opts_sz && enif_get_map_value(env, opts, atom_match_spec_result, &value)) {
  173. tt[tt_sz++] = value;
  174. }
  175. if (opts_sz && enif_get_map_value(env, opts, atom_scheduler_id, &value)) {
  176. tt[tt_sz++] = value;
  177. }
  178. if (opts_sz && enif_get_map_value(env, opts, atom_timestamp, &value)) {
  179. ERL_NIF_TERM ts;
  180. if (enif_is_identical(value, atom_monotonic)) {
  181. ErlNifTime mon = enif_monotonic_time(ERL_NIF_NSEC);
  182. ts = enif_make_int64(env, mon);
  183. } else if (enif_is_identical(value, atom_strict_monotonic)) {
  184. ErlNifTime mon = enif_monotonic_time(ERL_NIF_NSEC);
  185. ERL_NIF_TERM unique = enif_make_unique_integer(
  186. env, ERL_NIF_UNIQUE_MONOTONIC);
  187. ts = enif_make_tuple2(env, enif_make_int64(env, mon), unique);
  188. } else if (enif_is_identical(value, atom_timestamp)) {
  189. ts = enif_now_time(env);
  190. } else if (enif_is_identical(value, atom_cpu_timestamp)) {
  191. ts = enif_cpu_time(env);
  192. } else {
  193. ASSERT(0);
  194. goto error;
  195. }
  196. tt[tt_sz++] = ts;
  197. if (tt[0] == atom_trace)
  198. tt[0] = atom_trace_ts;
  199. }
  200. msg = enif_make_tuple_from_array(env, tt, tt_sz);
  201. if (is_port) {
  202. ErlNifBinary bin;
  203. if (!enif_term_to_binary(env, msg, &bin))
  204. goto error;
  205. msg = enif_make_binary(env, &bin);
  206. (void) enif_port_command(env, &to_port, NULL, msg);
  207. /* if failure: port has probably died, enabled will clean up */
  208. enif_release_binary(&bin);
  209. } else {
  210. (void) enif_send(env, &to_pid, NULL, msg);
  211. /* if failure: process has probably died, enabled will clean up */
  212. }
  213. error:
  214. return atom_ok;
  215. }