25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

235 lines
6.9 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. /* Only generate trace for when tracer != tracee */
  65. // enif_fprintf(stdout, "IMY************enabled 000:");
  66. // for (int i = 0; i <= argc - 1; i++) {
  67. // if (i != 10) {
  68. // enif_fprintf(stdout, " %d %T", i, argv[i]);
  69. // }
  70. // }
  71. // enif_fprintf(stdout, "\n");
  72. if (enif_is_identical(argv[1], argv[2])) {
  73. return atom_discard;
  74. }
  75. ErlNifPid tracer_pid;
  76. // Disable the trace when one of the tracers is not a local process.
  77. if (!enif_get_local_pid(env, argv[1], &tracer_pid))
  78. return atom_remove;
  79. // Disable the trace when one of the tracers is not alive.
  80. if (!enif_is_process_alive(env, &tracer_pid))
  81. return atom_remove;
  82. return atom_discard;
  83. }
  84. static ERL_NIF_TERM enabled_call(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  85. // We want both call and return_to when tracer != tracee.
  86. // enif_fprintf(stdout, "IMY************enabled_call 000:");
  87. // for (int i = 0; i <= argc - 1; i++) {
  88. // if (i != 10) {
  89. // enif_fprintf(stdout, " %d %T", i, argv[i]);
  90. // }
  91. // }
  92. // enif_fprintf(stdout, "\n");
  93. if (enif_is_identical(argv[1], argv[2])) {
  94. return atom_discard;
  95. }
  96. return atom_trace;
  97. }
  98. static ERL_NIF_TERM enabled_procs(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  99. // int arity;
  100. // const ERL_NIF_TERM *tuple;
  101. // if (!enif_get_tuple(env, argv[1], &arity, &tuple)) {
  102. // return atom_remove;
  103. // }
  104. // // We only want the spawn and exit events when 'profile' mode
  105. // // is enabled. Technically we only care about exits for callgrind,
  106. // // but spawn is cheap to keep and useful for message profilers.
  107. // if (enif_is_identical(atom_profile, tuple[1]) && !(enif_is_identical(atom_spawn, argv[0]) || enif_is_identical(atom_exit, argv[0]))) {
  108. // return atom_discard;
  109. // }
  110. if (enif_is_identical(argv[1], argv[2])) {
  111. return atom_discard;
  112. }
  113. return atom_trace;
  114. }
  115. static ERL_NIF_TERM enabled_send(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  116. // We want both send and send_to_non_existing_process when tracer != tracee.
  117. if (enif_is_identical(argv[1], argv[2])) {
  118. return atom_discard;
  119. }
  120. return atom_trace;
  121. }
  122. static ERL_NIF_TERM enabled_receive(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  123. // We want receive when tracer != tracee.
  124. if (enif_is_identical(argv[1], argv[2])) {
  125. return atom_discard;
  126. }
  127. return atom_trace;
  128. }
  129. static ERL_NIF_TERM enabled_running_procs(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  130. // We want both in and out tracer != tracee.
  131. if (enif_is_identical(argv[1], argv[2])) {
  132. return atom_discard;
  133. }
  134. return atom_trace;
  135. }
  136. static ERL_NIF_TERM enabled_garbage_collection(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  137. // We want both gc_minor_start, gc_max_heap_size, and gc_minor_end tracer != tracee.
  138. if (enif_is_identical(argv[1], argv[2])) {
  139. return atom_discard;
  140. }
  141. return atom_trace;
  142. }
  143. // trace(TraceTag, TracerState, Tracee, TraceTerm, Opts)
  144. static ERL_NIF_TERM trace(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
  145. // enif_fprintf(stdout, "IMY************trace:");
  146. // for (int i = 0; i <= argc - 1; i++) {
  147. // if (i != 1) {
  148. // enif_fprintf(stdout, " %d %T", i, argv[i]);
  149. // }
  150. // }
  151. // enif_fprintf(stdout, "\n");
  152. int has_extra, has_mspec;
  153. ErlNifPid tracer_pid;
  154. // Disable the trace when one of the tracers is not a local process.
  155. if (!enif_get_local_pid(env, argv[1], &tracer_pid))
  156. return atom_ok;
  157. ERL_NIF_TERM ts, extra, mspec, msg;
  158. // Build the message. There can be two different messages
  159. // depending on whether the extra option was set:
  160. //
  161. // - {Tag, Tracee, Ts, Term}
  162. // - {Tag, Tracee, Ts, Term, Extra}
  163. //
  164. // On top of that when match specs are enabled we may have
  165. // one additional term at the end of the tuple containing
  166. // the result of the match spec function.
  167. //
  168. // - {Tag, Tracee, Ts, Term, Result}
  169. // - {Tag, Tracee, Ts, Term, Extra, Result}
  170. has_extra = enif_get_map_value(env, argv[4], atom_extra, &extra);
  171. has_mspec = enif_get_map_value(env, argv[4], atom_match_spec_result, &mspec);
  172. ErlNifTime mon = enif_monotonic_time(ERL_NIF_NSEC);
  173. ts = enif_make_int64(env, mon);
  174. //ts = enif_cpu_time(env);
  175. if (has_extra && has_mspec)
  176. msg = enif_make_tuple6(env, argv[0], argv[2], ts, argv[3], extra, mspec);
  177. else if (has_extra)
  178. msg = enif_make_tuple5(env, argv[0], argv[2], ts, argv[3], extra);
  179. else if (has_mspec)
  180. msg = enif_make_tuple5(env, argv[0], argv[2], ts, argv[3], mspec);
  181. else
  182. msg = enif_make_tuple4(env, argv[0], argv[2], ts, argv[3]);
  183. // Send the message to the tracer_pid.
  184. enif_send(env, &tracer_pid, NULL, msg);
  185. return atom_ok;
  186. }
  187. static ErlNifFunc nif_funs[] = {
  188. {"enabled", 3, enabled},
  189. {"enabled_call", 3, enabled_call},
  190. {"enabled_procs", 3, enabled_procs},
  191. {"enabled_send", 3, enabled_send},
  192. {"enabled_receive", 3, enabled_receive},
  193. {"enabled_running_procs", 3, enabled_running_procs},
  194. {"enabled_garbage_collection", 3, enabled_garbage_collection},
  195. {"trace", 5, trace},
  196. };
  197. ERL_NIF_INIT(tpTracerNif, nif_funs, load, NULL, upgrade, unload)