Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

231 linhas
6.8 KiB

  1. #include "erl_nif.h"
  2. #define NIF_ATOM_DECL(a) ERL_NIF_TERM atom_ ## a;
  3. // #define NIF_ATOM_H_DECL(a) extern ERL_NIF_TERM atom_ ## a;
  4. #define NIF_ATOM_INIT(a) atom_ ## a = enif_make_atom(env, #a);
  5. #define NIF_ATOMS(A) \
  6. A(_nif_thread_ret_) \
  7. A(call) \
  8. A(closed) \
  9. A(cpu_timestamp) \
  10. A(discard) \
  11. A(exception_from) \
  12. A(exit) \
  13. A(extra) \
  14. A(gc_major_end) \
  15. A(gc_major_start) \
  16. A(gc_minor_end) \
  17. A(gc_minor_start) \
  18. A(getting_linked) \
  19. A(getting_unlinked) \
  20. A(in) \
  21. A(in_exiting) \
  22. A(link) \
  23. A(match_spec_result) \
  24. A(mode) \
  25. A(monotonic) \
  26. A(ok) \
  27. A(open) \
  28. A(out) \
  29. A(out_exited) \
  30. A(out_exiting) \
  31. A(percent) \
  32. A(profile) \
  33. A(receive) \
  34. A(register) \
  35. A(remove) \
  36. A(return_from) \
  37. A(return_to) \
  38. A(scheduler_id) \
  39. A(send) \
  40. A(send_to_non_existing_process) \
  41. A(spawn) \
  42. A(spawned) \
  43. A(strict_monotonic) \
  44. A(timestamp) \
  45. A(trace) \
  46. A(trace_status) \
  47. A(tracers) \
  48. A(unlink) \
  49. A(unregister)
  50. NIF_ATOMS(NIF_ATOM_DECL)
  51. static int load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info) {
  52. NIF_ATOMS(NIF_ATOM_INIT)
  53. *priv_data = NULL;
  54. return 0;
  55. }
  56. static int upgrade(ErlNifEnv *env, void **priv_data, void **old_priv_data, ERL_NIF_TERM load_info) {
  57. *priv_data = *old_priv_data;
  58. return 0;
  59. }
  60. static void unload(ErlNifEnv *env, void *priv_data) {
  61. }
  62. // enabled(TraceTag, TracerState, Tracee)
  63. static ERL_NIF_TERM enabled(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  64. int arity;
  65. const ERL_NIF_TERM *tuple;
  66. ErlNifPid tracer_pid;
  67. if (!enif_get_tuple(env, argv[1], &arity, &tuple)) {
  68. return atom_remove;
  69. }
  70. // Disable the trace when one of the tracers is not a local process.
  71. if (!enif_get_local_pid(env, tuple[0], &tracer_pid))
  72. return atom_remove;
  73. // Disable the trace when one of the tracers is not alive.
  74. if (!enif_is_process_alive(env, &tracer_pid))
  75. return atom_remove;
  76. return atom_discard;
  77. }
  78. static ERL_NIF_TERM enabled_call(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  79. // We always want both call and return_to.
  80. return atom_trace;
  81. }
  82. static ERL_NIF_TERM enabled_procs(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  83. int arity;
  84. const ERL_NIF_TERM *tuple;
  85. if (!enif_get_tuple(env, argv[1], &arity, &tuple)) {
  86. return atom_remove;
  87. }
  88. // We only want the spawn and exit events when 'profile' mode
  89. // is enabled. Technically we only care about exits for callgrind,
  90. // but spawn is cheap to keep and useful for message profilers.
  91. if (enif_is_identical(atom_profile, tuple[1]) && !(enif_is_identical(atom_spawn, argv[0]) || enif_is_identical(atom_exit, argv[0]))) {
  92. return atom_discard;
  93. }
  94. return atom_trace;
  95. }
  96. static ERL_NIF_TERM enabled_send(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  97. // We always want both send and send_to_non_existing_process.
  98. return atom_trace;
  99. }
  100. static ERL_NIF_TERM enabled_receive(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  101. // We always want receive.
  102. return atom_trace;
  103. }
  104. static ERL_NIF_TERM enabled_running_procs(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  105. // We always want both in and out.
  106. return atom_trace;
  107. }
  108. static ERL_NIF_TERM enabled_garbage_collection(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  109. // We always want both gc_minor_start, gc_max_heap_size, and gc_minor_end.
  110. return atom_trace;
  111. }
  112. // trace(TraceTag, TracerState, Tracee, TraceTerm, Opts)
  113. static ERL_NIF_TERM trace(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  114. enif_fprintf(stdout, "IMY************trace:");
  115. for (int i = 0; i <= argc - 1; i++) {
  116. if (i != 1) {
  117. enif_fprintf(stdout, " %d %T", i, argv[i]);
  118. }
  119. }
  120. enif_fprintf(stdout, "\n");
  121. ERL_NIF_TERM tracers, head, ts, extra, mspec, msg;
  122. ErlNifPid tracer;
  123. unsigned int nth;
  124. size_t len;
  125. int has_extra, has_mspec;
  126. if (!enif_get_map_value(env, argv[1], atom_tracers, &tracers))
  127. return atom_ok;
  128. // We know for a fact that the argument is a map. And if not,
  129. // no problem because we will return when trying to get a value from it.
  130. enif_get_map_size(env, tracers, &len);
  131. #if (ERL_NIF_MAJOR_VERSION >= 2) && (ERL_NIF_MINOR_VERSION >= 12)
  132. nth = enif_hash(ERL_NIF_INTERNAL_HASH, argv[2], 0) % len;
  133. #else
  134. // Select the correct tracer for this process.
  135. //
  136. // The pid value is detailed in:
  137. // 5b6dd0e84cf0f1dc19ddd05f86cf04b2695d8a9e/erts/emulator/beam/erl_term.h#L498
  138. //
  139. // As can be seen there, the first four bits of the pid value
  140. // are always the same. We therefore shift them out.
  141. ErlNifPid tracee;
  142. if (!enif_get_local_pid(env, argv[2], &tracee))
  143. return atom_ok;
  144. nth = (tracee.pid >> 4) % len;
  145. #endif
  146. if (!enif_get_map_value(env, tracers, enif_make_int(env, nth), &head))
  147. return atom_ok;
  148. if (!enif_get_local_pid(env, head, &tracer))
  149. return atom_ok;
  150. // Everything good. Generate a timestamp to include in the message.
  151. ts = enif_make_int64(env, enif_monotonic_time(ERL_NIF_USEC));
  152. // Build the message. There can be two different messages
  153. // depending on whether the extra option was set:
  154. //
  155. // - {Tag, Tracee, Ts, Term}
  156. // - {Tag, Tracee, Ts, Term, Extra}
  157. //
  158. // On top of that when match specs are enabled we may have
  159. // one additional term at the end of the tuple containing
  160. // the result of the match spec function.
  161. //
  162. // - {Tag, Tracee, Ts, Term, Result}
  163. // - {Tag, Tracee, Ts, Term, Extra, Result}
  164. has_extra = enif_get_map_value(env, argv[4], atom_extra, &extra);
  165. has_mspec = enif_get_map_value(env, argv[4], atom_match_spec_result, &mspec);
  166. if (has_extra && has_mspec)
  167. msg = enif_make_tuple6(env, argv[0], argv[2], ts, argv[3], extra, mspec);
  168. else if (has_extra)
  169. msg = enif_make_tuple5(env, argv[0], argv[2], ts, argv[3], extra);
  170. else if (has_mspec)
  171. msg = enif_make_tuple5(env, argv[0], argv[2], ts, argv[3], mspec);
  172. else
  173. msg = enif_make_tuple4(env, argv[0], argv[2], ts, argv[3]);
  174. // Send the message to the selected tracer.
  175. enif_send(env, &tracer, NULL, msg);
  176. return atom_ok;
  177. }
  178. static ErlNifFunc nifFuns[] = {
  179. {"enabled", 3, enabled},
  180. {"enabled_call", 3, enabled_call},
  181. {"enabled_procs", 3, enabled_procs},
  182. {"enabled_send", 3, enabled_send},
  183. {"enabled_receive", 3, enabled_receive},
  184. {"enabled_running_procs", 3, enabled_running_procs},
  185. {"enabled_garbage_collection", 3, enabled_garbage_collection},
  186. {"trace", 5, trace},
  187. };
  188. ERL_NIF_INIT(tpTracerNif, nifFuns, load, NULL, upgrade, unload
  189. )