aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/beam
diff options
context:
space:
mode:
authorPatrik Nyblom <[email protected]>2012-03-22 18:29:34 +0100
committerPatrik Nyblom <[email protected]>2012-03-22 18:30:07 +0100
commit5957a8338fe1f4e79a39277174094bbd9e978896 (patch)
tree4abcf47850e2a577b401a61bb1693d85cb137ffe /erts/emulator/beam
parentb5f6596f49db47e6dacddb2b10292a7294e13993 (diff)
parent1e653c3539423e4cfdeab1d232483bcac0e8b073 (diff)
downloadotp-5957a8338fe1f4e79a39277174094bbd9e978896.tar.gz
otp-5957a8338fe1f4e79a39277174094bbd9e978896.tar.bz2
otp-5957a8338fe1f4e79a39277174094bbd9e978896.zip
Merge branch 'pan/dtrace' into maint
* pan/dtrace: (22 commits) Use distinct function-entry probes for local and global calls Correct calculation of stack depth in call/return probes Add probes for all kind of calls Don't try to "clean up" generated fun names erl_process.c: Fix probe for process exit Remove code causing dialyzer warning from prim_file Add documentation for dyntrace and system_info changes Update slogan and add system_info for dynamic trace Update README's for dtrace and systemtap Rename dyntrace BIFs to more suiting names If VM probes are not enabled, short-circuit calls to probe BIFs beam_makeops: Add a simple preprocessor Ifdef all dynamic trace code Move dtrace erlang code and NIF into runtime_tools Correct some errors in the user tag spreading Change to more specific configure options for dtrace Add user tag spreading functionality to VM and use in file Update dtrace for changes in R15 Add DTrace support for OS X, Solaris, and Linux (via SystemTap), 4/4 Add DTrace support for OS X, Solaris, and Linux (via SystemTap), 3/4 ... OTP-10017
Diffstat (limited to 'erts/emulator/beam')
-rw-r--r--erts/emulator/beam/atom.names1
-rw-r--r--erts/emulator/beam/beam_emu.c276
-rw-r--r--erts/emulator/beam/bif.c210
-rw-r--r--erts/emulator/beam/bif.tab17
-rw-r--r--erts/emulator/beam/copy.c11
-rw-r--r--erts/emulator/beam/dist.c189
-rw-r--r--erts/emulator/beam/dtrace-wrapper.h109
-rw-r--r--erts/emulator/beam/erl_alloc.c6
-rw-r--r--erts/emulator/beam/erl_async.c32
-rw-r--r--erts/emulator/beam/erl_bif_ddll.c8
-rw-r--r--erts/emulator/beam/erl_bif_info.c24
-rw-r--r--erts/emulator/beam/erl_bif_port.c32
-rw-r--r--erts/emulator/beam/erl_bif_timer.c6
-rw-r--r--erts/emulator/beam/erl_bif_trace.c34
-rw-r--r--erts/emulator/beam/erl_db_util.c12
-rw-r--r--erts/emulator/beam/erl_driver.h2
-rw-r--r--erts/emulator/beam/erl_gc.c76
-rw-r--r--erts/emulator/beam/erl_lock_check.c3
-rw-r--r--erts/emulator/beam/erl_message.c254
-rw-r--r--erts/emulator/beam/erl_message.h13
-rw-r--r--erts/emulator/beam/erl_nif.c16
-rw-r--r--erts/emulator/beam/erl_node_tables.c9
-rw-r--r--erts/emulator/beam/erl_node_tables.h1
-rw-r--r--erts/emulator/beam/erl_port_task.c22
-rw-r--r--erts/emulator/beam/erl_process.c80
-rw-r--r--erts/emulator/beam/erl_process.h12
-rw-r--r--erts/emulator/beam/erl_trace.c51
-rw-r--r--erts/emulator/beam/erlang_dtrace.d726
-rw-r--r--erts/emulator/beam/global.h42
-rw-r--r--erts/emulator/beam/io.c185
-rw-r--r--erts/emulator/beam/ops.tab89
-rw-r--r--erts/emulator/beam/utils.c6
32 files changed, 2475 insertions, 79 deletions
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index 7be40976f6..9ce1068b23 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -248,6 +248,7 @@ atom global_heaps_size
atom Gt='>'
atom grun
atom group_leader
+atom have_dt_utag
atom heap_block_size
atom heap_size
atom heap_sizes
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index c65b2be106..8b4f067b98 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -41,6 +41,7 @@
#include "hipe_mode_switch.h"
#include "hipe_bif1.h"
#endif
+#include "dtrace-wrapper.h"
/* #define HARDDEBUG 1 */
@@ -1050,6 +1051,101 @@ init_emulator(void)
# define REG_tmp_arg2
#endif
+#ifdef USE_VM_PROBES
+# define USE_VM_CALL_PROBES
+#endif
+
+#ifdef USE_VM_CALL_PROBES
+
+#define DTRACE_LOCAL_CALL(p, m, f, a) \
+ if (DTRACE_ENABLED(local_function_entry)) { \
+ DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); \
+ DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); \
+ int depth = STACK_START(p) - STACK_TOP(p); \
+ dtrace_fun_decode(p, m, f, a, \
+ process_name, mfa); \
+ DTRACE3(local_function_entry, process_name, mfa, depth); \
+ }
+
+#define DTRACE_GLOBAL_CALL(p, m, f, a) \
+ if (DTRACE_ENABLED(global_function_entry)) { \
+ DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); \
+ DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); \
+ int depth = STACK_START(p) - STACK_TOP(p); \
+ dtrace_fun_decode(p, m, f, a, \
+ process_name, mfa); \
+ DTRACE3(global_function_entry, process_name, mfa, depth); \
+ }
+
+#define DTRACE_RETURN(p, m, f, a) \
+ if (DTRACE_ENABLED(function_return)) { \
+ DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); \
+ DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); \
+ int depth = STACK_START(p) - STACK_TOP(p); \
+ dtrace_fun_decode(p, m, f, a, \
+ process_name, mfa); \
+ DTRACE3(function_return, process_name, mfa, depth); \
+ }
+
+#define DTRACE_BIF_ENTRY(p, m, f, a) \
+ if (DTRACE_ENABLED(bif_entry)) { \
+ DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); \
+ DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); \
+ dtrace_fun_decode(p, m, f, a, \
+ process_name, mfa); \
+ DTRACE2(bif_entry, process_name, mfa); \
+ }
+
+#define DTRACE_BIF_RETURN(p, m, f, a) \
+ if (DTRACE_ENABLED(bif_return)) { \
+ DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); \
+ DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); \
+ dtrace_fun_decode(p, m, f, a, \
+ process_name, mfa); \
+ DTRACE2(bif_return, process_name, mfa); \
+ }
+
+#define DTRACE_NIF_ENTRY(p, m, f, a) \
+ if (DTRACE_ENABLED(nif_entry)) { \
+ DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); \
+ DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); \
+ dtrace_fun_decode(p, m, f, a, \
+ process_name, mfa); \
+ DTRACE2(nif_entry, process_name, mfa); \
+ }
+
+#define DTRACE_NIF_RETURN(p, m, f, a) \
+ if (DTRACE_ENABLED(nif_return)) { \
+ DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE); \
+ DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE); \
+ dtrace_fun_decode(p, m, f, a, \
+ process_name, mfa); \
+ DTRACE2(nif_return, process_name, mfa); \
+ }
+
+#else /* USE_VM_PROBES */
+
+#define DTRACE_LOCAL_CALL(p, m, f, a) do {} while (0)
+#define DTRACE_GLOBAL_CALL(p, m, f, a) do {} while (0)
+#define DTRACE_RETURN(p, m, f, a) do {} while (0)
+#define DTRACE_BIF_ENTRY(p, m, f, a) do {} while (0)
+#define DTRACE_BIF_RETURN(p, m, f, a) do {} while (0)
+#define DTRACE_NIF_ENTRY(p, m, f, a) do {} while (0)
+#define DTRACE_NIF_RETURN(p, m, f, a) do {} while (0)
+
+#endif /* USE_VM_PROBES */
+
+#ifdef USE_VM_PROBES
+void
+dtrace_drvport_str(ErlDrvPort drvport, char *port_buf)
+{
+ Port *port = erts_drvport2port(drvport);
+
+ erts_snprintf(port_buf, DTRACE_TERM_BUF_SIZE, "#Port<%lu.%lu>",
+ port_channel_no(port->id),
+ port_number(port->id));
+}
+#endif
/*
* process_main() is called twice:
* The first call performs some initialisation, including exporting
@@ -1221,6 +1317,30 @@ void process_main(void)
#endif
SWAPIN;
ASSERT(VALID_INSTR(next));
+
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(process_scheduled)) {
+ DTRACE_CHARBUF(process_buf, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(fun_buf, DTRACE_TERM_BUF_SIZE);
+ dtrace_proc_str(c_p, process_buf);
+
+ if (ERTS_PROC_IS_EXITING(c_p)) {
+ strcpy(fun_buf, "<exiting>");
+ } else {
+ BeamInstr *fptr = find_function_from_pc(c_p->i);
+ if (fptr) {
+ dtrace_fun_decode(c_p, (Eterm)fptr[0],
+ (Eterm)fptr[1], (Uint)fptr[2],
+ NULL, fun_buf);
+ } else {
+ erts_snprintf(fun_buf, sizeof(fun_buf),
+ "<unknown/%p>", next);
+ }
+ }
+
+ DTRACE2(process_scheduled, process_buf, fun_buf);
+ }
+#endif
Goto(next);
}
@@ -1397,6 +1517,7 @@ void process_main(void)
/* FALL THROUGH */
OpCase(i_call_only_f): {
SET_I((BeamInstr *) Arg(0));
+ DTRACE_LOCAL_CALL(c_p, (Eterm)I[-3], (Eterm)I[-2], I[-1]);
Dispatch();
}
@@ -1408,6 +1529,7 @@ void process_main(void)
RESTORE_CP(E);
E = ADD_BYTE_OFFSET(E, Arg(1));
SET_I((BeamInstr *) Arg(0));
+ DTRACE_LOCAL_CALL(c_p, (Eterm)I[-3], (Eterm)I[-2], I[-1]);
Dispatch();
}
@@ -1419,6 +1541,7 @@ void process_main(void)
OpCase(i_call_f): {
SET_CP(c_p, I+2);
SET_I((BeamInstr *) Arg(0));
+ DTRACE_LOCAL_CALL(c_p, (Eterm)I[-3], (Eterm)I[-2], I[-1]);
Dispatch();
}
@@ -1435,6 +1558,12 @@ void process_main(void)
* is not loaded, it points to code which will invoke the error handler
* (see lb_call_error_handler below).
*/
+#ifdef USE_VM_CALL_PROBES
+ if (DTRACE_ENABLED(global_function_entry)) {
+ BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->address);
+ DTRACE_GLOBAL_CALL(c_p, (Eterm)fp[-3], (Eterm)fp[-2], fp[-1]);
+ }
+#endif
Dispatchx();
OpCase(i_move_call_ext_cre): {
@@ -1444,6 +1573,12 @@ void process_main(void)
/* FALL THROUGH */
OpCase(i_call_ext_e):
SET_CP(c_p, I+2);
+#ifdef USE_VM_CALL_PROBES
+ if (DTRACE_ENABLED(global_function_entry)) {
+ BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->address);
+ DTRACE_GLOBAL_CALL(c_p, (Eterm)fp[-3], (Eterm)fp[-2], fp[-1]);
+ }
+#endif
Dispatchx();
OpCase(i_move_call_ext_only_ecr): {
@@ -1451,6 +1586,12 @@ void process_main(void)
}
/* FALL THROUGH */
OpCase(i_call_ext_only_e):
+#ifdef USE_VM_CALL_PROBES
+ if (DTRACE_ENABLED(global_function_entry)) {
+ BeamInstr* fp = (BeamInstr *) (((Export *) Arg(0))->address);
+ DTRACE_GLOBAL_CALL(c_p, (Eterm)fp[-3], (Eterm)fp[-2], fp[-1]);
+ }
+#endif
Dispatchx();
OpCase(init_y): {
@@ -1486,7 +1627,16 @@ void process_main(void)
OpCase(return): {
+#ifdef USE_VM_CALL_PROBES
+ BeamInstr* fptr;
+#endif
SET_I(c_p->cp);
+
+#ifdef USE_VM_CALL_PROBES
+ if (DTRACE_ENABLED(function_return) && (fptr = find_function_from_pc(c_p->cp))) {
+ DTRACE_RETURN(c_p, (Eterm)fptr[0], (Eterm)fptr[1], (Uint)fptr[2]);
+ }
+#endif
/*
* We must clear the CP to make sure that a stale value do not
* create a false module dependcy preventing code upgrading.
@@ -1755,6 +1905,7 @@ void process_main(void)
* remove it...
*/
ASSERT(!msgp->data.attached);
+ /* TODO: Add DTrace probe for this bad message situation? */
UNLINK_MESSAGE(c_p, msgp);
free_message(msgp);
goto loop_rec__;
@@ -1780,24 +1931,88 @@ void process_main(void)
save_calls(c_p, &exp_receive);
}
if (ERL_MESSAGE_TOKEN(msgp) == NIL) {
- SEQ_TRACE_TOKEN(c_p) = NIL;
+#ifdef USE_VM_PROBES
+ if (DT_UTAG(c_p) != NIL) {
+ if (DT_UTAG_FLAGS(c_p) & DT_UTAG_PERMANENT) {
+ SEQ_TRACE_TOKEN(c_p) = am_have_dt_utag;
+#ifdef DTRACE_TAG_HARDDEBUG
+ if (DT_UTAG_FLAGS(c_p) & DT_UTAG_SPREADING)
+ erts_fprintf(stderr,
+ "Dtrace -> (%T) stop spreading "
+ "tag %T with message %T\r\n",
+ c_p->id,DT_UTAG(c_p),ERL_MESSAGE_TERM(msgp));
+#endif
+ } else {
+#ifdef DTRACE_TAG_HARDDEBUG
+ erts_fprintf(stderr,
+ "Dtrace -> (%T) kill tag %T with "
+ "message %T\r\n",
+ c_p->id,DT_UTAG(c_p),ERL_MESSAGE_TERM(msgp));
+#endif
+ DT_UTAG(c_p) = NIL;
+ SEQ_TRACE_TOKEN(c_p) = NIL;
+ }
+ } else {
+#endif
+ SEQ_TRACE_TOKEN(c_p) = NIL;
+#ifdef USE_VM_PROBES
+ }
+ DT_UTAG_FLAGS(c_p) &= ~DT_UTAG_SPREADING;
+#endif
} else if (ERL_MESSAGE_TOKEN(msgp) != am_undefined) {
Eterm msg;
SEQ_TRACE_TOKEN(c_p) = ERL_MESSAGE_TOKEN(msgp);
- ASSERT(is_tuple(SEQ_TRACE_TOKEN(c_p)));
- ASSERT(SEQ_TRACE_TOKEN_ARITY(c_p) == 5);
- ASSERT(is_small(SEQ_TRACE_TOKEN_SERIAL(c_p)));
- ASSERT(is_small(SEQ_TRACE_TOKEN_LASTCNT(c_p)));
- ASSERT(is_small(SEQ_TRACE_TOKEN_FLAGS(c_p)));
- ASSERT(is_pid(SEQ_TRACE_TOKEN_SENDER(c_p)));
- c_p->seq_trace_lastcnt = unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p));
- if (c_p->seq_trace_clock < unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p))) {
- c_p->seq_trace_clock = unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p));
+#ifdef USE_VM_PROBES
+ if (ERL_MESSAGE_TOKEN(msgp) == am_have_dt_utag) {
+ if (DT_UTAG(c_p) == NIL) {
+ DT_UTAG(c_p) = ERL_MESSAGE_DT_UTAG(msgp);
+ }
+ DT_UTAG_FLAGS(c_p) |= DT_UTAG_SPREADING;
+#ifdef DTRACE_TAG_HARDDEBUG
+ erts_fprintf(stderr,
+ "Dtrace -> (%T) receive tag (%T) "
+ "with message %T\r\n",
+ c_p->id, DT_UTAG(c_p), ERL_MESSAGE_TERM(msgp));
+#endif
+ } else {
+#endif
+ ASSERT(is_tuple(SEQ_TRACE_TOKEN(c_p)));
+ ASSERT(SEQ_TRACE_TOKEN_ARITY(c_p) == 5);
+ ASSERT(is_small(SEQ_TRACE_TOKEN_SERIAL(c_p)));
+ ASSERT(is_small(SEQ_TRACE_TOKEN_LASTCNT(c_p)));
+ ASSERT(is_small(SEQ_TRACE_TOKEN_FLAGS(c_p)));
+ ASSERT(is_pid(SEQ_TRACE_TOKEN_SENDER(c_p)));
+ c_p->seq_trace_lastcnt = unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p));
+ if (c_p->seq_trace_clock < unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p))) {
+ c_p->seq_trace_clock = unsigned_val(SEQ_TRACE_TOKEN_SERIAL(c_p));
+ }
+ msg = ERL_MESSAGE_TERM(msgp);
+ seq_trace_output(SEQ_TRACE_TOKEN(c_p), msg, SEQ_TRACE_RECEIVE,
+ c_p->id, c_p);
+#ifdef USE_VM_PROBES
}
- msg = ERL_MESSAGE_TERM(msgp);
- seq_trace_output(SEQ_TRACE_TOKEN(c_p), msg, SEQ_TRACE_RECEIVE,
- c_p->id, c_p);
+#endif
+ }
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(message_receive)) {
+ Eterm token2 = NIL;
+ DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE);
+ Sint tok_label = 0;
+ Sint tok_lastcnt = 0;
+ Sint tok_serial = 0;
+
+ dtrace_proc_str(c_p, receiver_name);
+ token2 = SEQ_TRACE_TOKEN(c_p);
+ if (token2 != NIL && token2 != am_have_dt_utag) {
+ tok_label = signed_val(SEQ_TRACE_T_LABEL(token2));
+ tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token2));
+ tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token2));
+ }
+ DTRACE6(message_receive,
+ receiver_name, size_object(ERL_MESSAGE_TERM(msgp)),
+ c_p->msg.len - 1, tok_label, tok_lastcnt, tok_serial);
}
+#endif
UNLINK_MESSAGE(c_p, msgp);
JOIN_MESSAGE(c_p);
CANCEL_TIMER(c_p);
@@ -3157,6 +3372,7 @@ void process_main(void)
*/
BifFunction vbf;
+ DTRACE_NIF_ENTRY(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]);
c_p->current = I-3; /* current and vbf set to please handle_error */
SWAPOUT;
c_p->fcalls = FCALLS - 1;
@@ -3178,6 +3394,8 @@ void process_main(void)
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(nif_bif_result));
PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
+
+ DTRACE_NIF_RETURN(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]);
goto apply_bif_or_nif_epilogue;
OpCase(apply_bif):
@@ -3197,6 +3415,8 @@ void process_main(void)
c_p->arity = 0; /* To allow garbage collection on ourselves
* (check_process_code/2).
*/
+ DTRACE_BIF_ENTRY(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]);
+
SWAPOUT;
c_p->fcalls = FCALLS - 1;
vbf = (BifFunction) Arg(0);
@@ -3216,6 +3436,8 @@ void process_main(void)
PROCESS_MAIN_CHK_LOCKS(c_p);
}
+ DTRACE_BIF_RETURN(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]);
+
apply_bif_or_nif_epilogue:
ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
ERTS_HOLE_CHECK(c_p);
@@ -5899,6 +6121,12 @@ apply(Process* p, Eterm module, Eterm function, Eterm args, Eterm* reg)
save_calls(p, ep);
}
+#ifdef USE_VM_CALL_PROBES
+ if (DTRACE_ENABLED(global_function_entry)) {
+ BeamInstr *fptr = (BeamInstr *) ep->address;
+ DTRACE_GLOBAL_CALL(p, (Eterm)fptr[-3], (Eterm)fptr[-2], (Uint)fptr[-1]);
+ }
+#endif
return ep->address;
}
@@ -5948,6 +6176,12 @@ fixed_apply(Process* p, Eterm* reg, Uint arity)
save_calls(p, ep);
}
+#ifdef USE_VM_CALL_PROBES
+ if (DTRACE_ENABLED(global_function_entry)) {
+ BeamInstr *fptr = (BeamInstr *) ep->address;
+ DTRACE_GLOBAL_CALL(p, (Eterm)fptr[-3], (Eterm)fptr[-2], (Uint)fptr[-1]);
+ }
+#endif
return ep->address;
}
@@ -5997,6 +6231,15 @@ erts_hibernate(Process* c_p, Eterm module, Eterm function, Eterm args, Eterm* re
c_p->max_arg_reg = sizeof(c_p->def_arg_reg)/sizeof(c_p->def_arg_reg[0]);
}
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(process_hibernate)) {
+ DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE);
+ dtrace_fun_decode(c_p, module, function, arity,
+ process_name, mfa);
+ DTRACE2(process_hibernate, process_name, mfa);
+ }
+#endif
/*
* Arrange for the process to be resumed at the given MFA with
* the stack cleared.
@@ -6072,6 +6315,9 @@ call_fun(Process* p, /* Current process. */
actual_arity = (int) code_ptr[-1];
if (actual_arity == arity+num_free) {
+ DTRACE_LOCAL_CALL(p, (Eterm)code_ptr[-3],
+ (Eterm)code_ptr[-2],
+ code_ptr[-1]);
if (num_free == 0) {
return code_ptr;
} else {
@@ -6089,7 +6335,7 @@ call_fun(Process* p, /* Current process. */
} else {
/*
* Something wrong here. First build a list of the arguments.
- */
+ */
if (is_non_value(args)) {
Uint sz = 2 * arity;
@@ -6164,6 +6410,7 @@ call_fun(Process* p, /* Current process. */
actual_arity = (int) ep->code[2];
if (arity == actual_arity) {
+ DTRACE_GLOBAL_CALL(p, ep->code[0], ep->code[1], (Uint)ep->code[2]);
return ep->address;
} else {
/*
@@ -6239,6 +6486,7 @@ call_fun(Process* p, /* Current process. */
reg[1] = function;
reg[2] = args;
}
+ DTRACE_GLOBAL_CALL(p, module, function, arity);
return ep->address;
} else {
badfun:
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index ebdca87f4a..39d4582435 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -563,7 +563,11 @@ erts_queue_monitor_message(Process *p,
ref_copy = copy_struct(ref, ref_size, &hp, ohp);
tup = TUPLE5(hp, am_DOWN, ref_copy, type, item_copy, reason_copy);
- erts_queue_message(p, p_locksp, bp, tup, NIL);
+ erts_queue_message(p, p_locksp, bp, tup, NIL
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
}
static BIF_RETTYPE
@@ -1944,7 +1948,11 @@ do_send(Process *p, Eterm to, Eterm msg, int suspend) {
if (ERTS_PROC_GET_SAVED_CALLS_BUF(p))
save_calls(p, &exp_send);
- if (SEQ_TRACE_TOKEN(p) != NIL) {
+ if (SEQ_TRACE_TOKEN(p) != NIL
+#ifdef USE_VM_PROBES
+ && SEQ_TRACE_TOKEN(p) != am_have_dt_utag
+#endif
+ ) {
seq_trace_update_send(p);
seq_trace_output(SEQ_TRACE_TOKEN(p), msg,
SEQ_TRACE_SEND, portid, p);
@@ -4226,13 +4234,21 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2)
for (i = 0; i < erts_max_processes; i++) {
if (process_tab[i] != (Process*) 0) {
Process* p = process_tab[i];
+#ifdef USE_VM_PROBES
+ p->seq_trace_token = (p->dt_utag != NIL) ? am_have_dt_utag : NIL;
+#else
p->seq_trace_token = NIL;
+#endif
p->seq_trace_clock = 0;
p->seq_trace_lastcnt = 0;
ERTS_SMP_MSGQ_MV_INQ2PRIVQ(p);
mp = p->msg.first;
while(mp != NULL) {
+#ifdef USE_VM_PROBES
+ ERL_MESSAGE_TOKEN(mp) = (ERL_MESSAGE_DT_UTAG(mp) != NIL) ? am_have_dt_utag : NIL;
+#else
ERL_MESSAGE_TOKEN(mp) = NIL;
+#endif
mp = mp->next;
}
}
@@ -4630,3 +4646,193 @@ BIF_RETTYPE get_module_info_2(BIF_ALIST_2)
}
BIF_RET(ret);
}
+
+BIF_RETTYPE dt_put_tag_1(BIF_ALIST_1)
+{
+#ifdef USE_VM_PROBES
+ Eterm otag;
+ if (BIF_ARG_1 == am_undefined) {
+ otag = (DT_UTAG(BIF_P) == NIL) ? am_undefined : DT_UTAG(BIF_P);
+ DT_UTAG(BIF_P) = NIL;
+ DT_UTAG_FLAGS(BIF_P) = 0;
+ if (SEQ_TRACE_TOKEN(BIF_P) == am_have_dt_utag) {
+ SEQ_TRACE_TOKEN(BIF_P) = NIL;
+ }
+ BIF_RET(otag);
+ }
+ if (!is_binary(BIF_ARG_1)) {
+ BIF_ERROR(BIF_P,BADARG);
+ }
+ otag = (DT_UTAG(BIF_P) == NIL) ? am_undefined : DT_UTAG(BIF_P);
+ DT_UTAG(BIF_P) = BIF_ARG_1;
+ DT_UTAG_FLAGS(BIF_P) |= DT_UTAG_PERMANENT;
+ if (SEQ_TRACE_TOKEN(BIF_P) == NIL) {
+ SEQ_TRACE_TOKEN(BIF_P) = am_have_dt_utag;
+ }
+ BIF_RET(otag);
+#else
+ BIF_RET(am_undefined);
+#endif
+}
+
+BIF_RETTYPE dt_get_tag_0(BIF_ALIST_0)
+{
+#ifdef USE_VM_PROBES
+ BIF_RET((DT_UTAG(BIF_P) == NIL || !(DT_UTAG_FLAGS(BIF_P) & DT_UTAG_PERMANENT)) ? am_undefined : DT_UTAG(BIF_P));
+#else
+ BIF_RET(am_undefined);
+#endif
+}
+BIF_RETTYPE dt_get_tag_data_0(BIF_ALIST_0)
+{
+#ifdef USE_VM_PROBES
+ BIF_RET((DT_UTAG(BIF_P) == NIL) ? am_undefined : DT_UTAG(BIF_P));
+#else
+ BIF_RET(am_undefined);
+#endif
+}
+BIF_RETTYPE dt_prepend_vm_tag_data_1(BIF_ALIST_1)
+{
+#ifdef USE_VM_PROBES
+ Eterm b;
+ Eterm *hp;
+ hp = HAlloc(BIF_P,2);
+ if (is_binary((DT_UTAG(BIF_P)))) {
+ Uint sz = binary_size(DT_UTAG(BIF_P));
+ int i;
+ unsigned char *p,*q;
+ byte *temp_alloc = NULL;
+ b = new_binary(BIF_P,NULL,sz+1);
+ q = binary_bytes(b);
+ p = erts_get_aligned_binary_bytes(DT_UTAG(BIF_P),&temp_alloc);
+ for(i=0;i<sz;++i) {
+ q[i] = p[i];
+ }
+ erts_free_aligned_binary_bytes(temp_alloc);
+ q[sz] = '\0';
+ } else {
+ b = new_binary(BIF_P,(byte *)"\0",1);
+ }
+ BIF_RET(CONS(hp,b,BIF_ARG_1));
+#else
+ BIF_RET(BIF_ARG_1);
+#endif
+}
+BIF_RETTYPE dt_append_vm_tag_data_1(BIF_ALIST_1)
+{
+#ifdef USE_VM_PROBES
+ Eterm b;
+ Eterm *hp;
+ hp = HAlloc(BIF_P,2);
+ if (is_binary((DT_UTAG(BIF_P)))) {
+ Uint sz = binary_size(DT_UTAG(BIF_P));
+ int i;
+ unsigned char *p,*q;
+ byte *temp_alloc = NULL;
+ b = new_binary(BIF_P,NULL,sz+1);
+ q = binary_bytes(b);
+ p = erts_get_aligned_binary_bytes(DT_UTAG(BIF_P),&temp_alloc);
+ for(i=0;i<sz;++i) {
+ q[i] = p[i];
+ }
+ erts_free_aligned_binary_bytes(temp_alloc);
+ q[sz] = '\0';
+ } else {
+ b = new_binary(BIF_P,(byte *)"\0",1);
+ }
+ BIF_RET(CONS(hp,BIF_ARG_1,b));
+#else
+ BIF_RET(BIF_ARG_1);
+#endif
+}
+BIF_RETTYPE dt_spread_tag_1(BIF_ALIST_1)
+{
+#ifdef USE_VM_PROBES
+ Eterm ret;
+ Eterm *hp;
+#endif
+ if (BIF_ARG_1 != am_true && BIF_ARG_1 != am_false) {
+ BIF_ERROR(BIF_P,BADARG);
+ }
+#ifdef USE_VM_PROBES
+ hp = HAlloc(BIF_P,3);
+ ret = TUPLE2(hp,make_small(DT_UTAG_FLAGS(BIF_P)),DT_UTAG(BIF_P));
+ if (DT_UTAG(BIF_P) != NIL) {
+ if (BIF_ARG_1 == am_true) {
+ DT_UTAG_FLAGS(BIF_P) |= DT_UTAG_SPREADING;
+#ifdef DTRACE_TAG_HARDDEBUG
+ erts_fprintf(stderr,
+ "Dtrace -> (%T) start spreading tag %T\r\n",
+ BIF_P->id,DT_UTAG(BIF_P));
+#endif
+ } else {
+ DT_UTAG_FLAGS(BIF_P) &= ~DT_UTAG_SPREADING;
+#ifdef DTRACE_TAG_HARDDEBUG
+ erts_fprintf(stderr,
+ "Dtrace -> (%T) stop spreading tag %T\r\n",
+ BIF_P->id,DT_UTAG(BIF_P));
+#endif
+ }
+ }
+ BIF_RET(ret);
+#else
+ BIF_RET(am_true);
+#endif
+}
+BIF_RETTYPE dt_restore_tag_1(BIF_ALIST_1)
+{
+#ifdef USE_VM_PROBES
+ Eterm *tpl;
+ Uint x;
+ if (is_not_tuple(BIF_ARG_1)) {
+ BIF_ERROR(BIF_P,BADARG);
+ }
+ tpl = tuple_val(BIF_ARG_1);
+ if(arityval(*tpl) != 2 || is_not_small(tpl[1]) || (is_not_binary(tpl[2]) && tpl[2] != NIL)) {
+ BIF_ERROR(BIF_P,BADARG);
+ }
+ if (tpl[2] == NIL) {
+ if (DT_UTAG(BIF_P) != NIL) {
+#ifdef DTRACE_TAG_HARDDEBUG
+ erts_fprintf(stderr,
+ "Dtrace -> (%T) restore Killing tag!\r\n",
+ BIF_P->id);
+#endif
+ }
+ DT_UTAG(BIF_P) = NIL;
+ if (SEQ_TRACE_TOKEN(BIF_P) == am_have_dt_utag) {
+ SEQ_TRACE_TOKEN(BIF_P) = NIL;
+ }
+ DT_UTAG_FLAGS(BIF_P) = 0;
+ } else {
+ x = unsigned_val(tpl[1]) & (DT_UTAG_SPREADING | DT_UTAG_PERMANENT);
+#ifdef DTRACE_TAG_HARDDEBUG
+
+ if (!(x & DT_UTAG_SPREADING) && (DT_UTAG_FLAGS(BIF_P) &
+ DT_UTAG_SPREADING)) {
+ erts_fprintf(stderr,
+ "Dtrace -> (%T) restore stop spreading "
+ "tag %T\r\n",
+ BIF_P->id, tpl[2]);
+ } else if ((x & DT_UTAG_SPREADING) &&
+ !(DT_UTAG_FLAGS(BIF_P) & DT_UTAG_SPREADING)) {
+ erts_fprintf(stderr,
+ "Dtrace -> (%T) restore start spreading "
+ "tag %T\r\n",BIF_P->id,tpl[2]);
+ }
+#endif
+ DT_UTAG_FLAGS(BIF_P) = x;
+ DT_UTAG(BIF_P) = tpl[2];
+ if (SEQ_TRACE_TOKEN(BIF_P) == NIL) {
+ SEQ_TRACE_TOKEN(BIF_P) = am_have_dt_utag;
+ }
+ }
+#else
+ if (BIF_ARG_1 != am_true) {
+ BIF_ERROR(BIF_P,BADARG);
+ }
+#endif
+ BIF_RET(am_true);
+}
+
+
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 007c884a6a..8a85e102d1 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -814,6 +814,23 @@ bif erlang:check_old_code/1
#
bif erlang:universaltime_to_posixtime/1
bif erlang:posixtime_to_universaltime/1
+
+#
+# New in R15B01
+#
+
+# The dtrace BIF's are always present, but give dummy results if dynamic trace is not enabled in the build
+bif erlang:dt_put_tag/1
+bif erlang:dt_get_tag/0
+bif erlang:dt_get_tag_data/0
+bif erlang:dt_spread_tag/1
+bif erlang:dt_restore_tag/1
+
+# These are dummies even with enabled dynamic trace unless vm probes are enabled.
+# They are also internal, for dtrace tags sent to the VM's own drivers (efile)
+bif erlang:dt_prepend_vm_tag_data/1
+bif erlang:dt_append_vm_tag_data/1
+
#
# Obsolete
#
diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c
index 1d968fb147..2c355fadfa 100644
--- a/erts/emulator/beam/copy.c
+++ b/erts/emulator/beam/copy.c
@@ -30,6 +30,7 @@
#include "big.h"
#include "erl_binary.h"
#include "erl_bits.h"
+#include "dtrace-wrapper.h"
#ifdef HYBRID
MA_STACK_DECLARE(src);
@@ -59,6 +60,14 @@ copy_object(Eterm obj, Process* to)
Eterm* hp = HAlloc(to, size);
Eterm res;
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(copy_object)) {
+ DTRACE_CHARBUF(proc_name, 64);
+
+ erts_snprintf(proc_name, sizeof(proc_name), "%T", to->id);
+ DTRACE2(copy_object, proc_name, size);
+ }
+#endif
res = copy_struct(obj, size, &hp, &to->off_heap);
#ifdef DEBUG
if (eq(obj, res) == 0) {
@@ -213,6 +222,8 @@ Eterm copy_struct(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap)
if (IS_CONST(obj))
return obj;
+ DTRACE1(copy_struct, (int32_t)sz);
+
hp = htop = *hpp;
hbot = htop + sz;
hstart = (char *)htop;
diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c
index bee61e7273..802feaeb1c 100644
--- a/erts/emulator/beam/dist.c
+++ b/erts/emulator/beam/dist.c
@@ -42,6 +42,7 @@
#include "external.h"
#include "erl_binary.h"
#include "erl_thr_progress.h"
+#include "dtrace-wrapper.h"
/* Turn this on to get printouts of all distribution messages
* which go on the line
@@ -381,7 +382,11 @@ static void doit_node_link_net_exits(ErtsLink *lnk, void *vnecp)
Eterm tup;
Eterm *hp = erts_alloc_message_heap(3,&bp,&ohp,rp,&rp_locks);
tup = TUPLE2(hp, am_nodedown, name);
- erts_queue_message(rp, &rp_locks, bp, tup, NIL);
+ erts_queue_message(rp, &rp_locks, bp, tup, NIL
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
}
erts_smp_proc_unlock(rp, rp_locks);
}
@@ -740,19 +745,50 @@ erts_dsig_send_msg(ErtsDSigData *dsdp, Eterm remote, Eterm message)
Eterm token = NIL;
Process *sender = dsdp->proc;
int res;
+#ifdef USE_VM_PROBES
+ Sint tok_label = 0;
+ Sint tok_lastcnt = 0;
+ Sint tok_serial = 0;
+ Uint msize = 0;
+ DTRACE_CHARBUF(node_name, 64);
+ DTRACE_CHARBUF(sender_name, 64);
+ DTRACE_CHARBUF(receiver_name, 64);
+#endif
UseTmpHeapNoproc(5);
- if (SEQ_TRACE_TOKEN(sender) != NIL) {
+ if (SEQ_TRACE_TOKEN(sender) != NIL
+#ifdef USE_VM_PROBES
+ && SEQ_TRACE_TOKEN(sender) != am_have_dt_utag
+#endif
+ ) {
seq_trace_update_send(sender);
token = SEQ_TRACE_TOKEN(sender);
seq_trace_output(token, message, SEQ_TRACE_SEND, remote, sender);
}
+#ifdef USE_VM_PROBES
+ *node_name = *sender_name = *receiver_name = '\0';
+ if (DTRACE_ENABLED(message_send) || DTRACE_ENABLED(message_send_remote)) {
+ erts_snprintf(node_name, sizeof(node_name), "%T", dsdp->dep->sysname);
+ erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->id);
+ erts_snprintf(receiver_name, sizeof(receiver_name), "%T", remote);
+ msize = size_object(message);
+ if (token != NIL && token != am_have_dt_utag) {
+ tok_label = signed_val(SEQ_TRACE_T_LABEL(token));
+ tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token));
+ tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token));
+ }
+ }
+#endif
if (token != NIL)
ctl = TUPLE4(&ctl_heap[0],
make_small(DOP_SEND_TT), am_Cookie, remote, token);
else
ctl = TUPLE3(&ctl_heap[0], make_small(DOP_SEND), am_Cookie, remote);
+ DTRACE6(message_send, sender_name, receiver_name,
+ msize, tok_label, tok_lastcnt, tok_serial);
+ DTRACE7(message_send_remote, sender_name, node_name, receiver_name,
+ msize, tok_label, tok_lastcnt, tok_serial);
res = dsig_send(dsdp, ctl, message, 0);
UnUseTmpHeapNoproc(5);
return res;
@@ -766,13 +802,41 @@ erts_dsig_send_reg_msg(ErtsDSigData *dsdp, Eterm remote_name, Eterm message)
Eterm token = NIL;
Process *sender = dsdp->proc;
int res;
+#ifdef USE_VM_PROBES
+ Sint tok_label = 0;
+ Sint tok_lastcnt = 0;
+ Sint tok_serial = 0;
+ Uint32 msize = 0;
+ DTRACE_CHARBUF(node_name, 64);
+ DTRACE_CHARBUF(sender_name, 64);
+ DTRACE_CHARBUF(receiver_name, 128);
+#endif
UseTmpHeapNoproc(6);
- if (SEQ_TRACE_TOKEN(sender) != NIL) {
+ if (SEQ_TRACE_TOKEN(sender) != NIL
+#ifdef USE_VM_PROBES
+ && SEQ_TRACE_TOKEN(sender) != am_have_dt_utag
+#endif
+ ) {
seq_trace_update_send(sender);
token = SEQ_TRACE_TOKEN(sender);
seq_trace_output(token, message, SEQ_TRACE_SEND, remote_name, sender);
}
+#ifdef USE_VM_PROBES
+ *node_name = *sender_name = *receiver_name = '\0';
+ if (DTRACE_ENABLED(message_send) || DTRACE_ENABLED(message_send_remote)) {
+ erts_snprintf(node_name, sizeof(node_name), "%T", dsdp->dep->sysname);
+ erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->id);
+ erts_snprintf(receiver_name, sizeof(receiver_name),
+ "{%T,%s}", remote_name, node_name);
+ msize = size_object(message);
+ if (token != NIL && token != am_have_dt_utag) {
+ tok_label = signed_val(SEQ_TRACE_T_LABEL(token));
+ tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token));
+ tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token));
+ }
+ }
+#endif
if (token != NIL)
ctl = TUPLE5(&ctl_heap[0], make_small(DOP_REG_SEND_TT),
@@ -780,6 +844,10 @@ erts_dsig_send_reg_msg(ErtsDSigData *dsdp, Eterm remote_name, Eterm message)
else
ctl = TUPLE4(&ctl_heap[0], make_small(DOP_REG_SEND),
sender->id, am_Cookie, remote_name);
+ DTRACE6(message_send, sender_name, receiver_name,
+ msize, tok_label, tok_lastcnt, tok_serial);
+ DTRACE7(message_send_remote, sender_name, node_name, receiver_name,
+ msize, tok_label, tok_lastcnt, tok_serial);
res = dsig_send(dsdp, ctl, message, 0);
UnUseTmpHeapNoproc(6);
return res;
@@ -793,9 +861,23 @@ erts_dsig_send_exit_tt(ErtsDSigData *dsdp, Eterm local, Eterm remote,
Eterm ctl;
DeclareTmpHeapNoproc(ctl_heap,6);
int res;
+#ifdef USE_VM_PROBES
+ Process *sender = dsdp->proc;
+ Sint tok_label = 0;
+ Sint tok_lastcnt = 0;
+ Sint tok_serial = 0;
+ DTRACE_CHARBUF(node_name, 64);
+ DTRACE_CHARBUF(sender_name, 64);
+ DTRACE_CHARBUF(remote_name, 128);
+ DTRACE_CHARBUF(reason_str, 128);
+#endif
UseTmpHeapNoproc(6);
- if (token != NIL) {
+ if (token != NIL
+#ifdef USE_VM_PROBES
+ && token != am_have_dt_utag
+#endif
+ ) {
seq_trace_update_send(dsdp->proc);
seq_trace_output_exit(token, reason, SEQ_TRACE_SEND, remote, local);
ctl = TUPLE5(&ctl_heap[0],
@@ -803,6 +885,23 @@ erts_dsig_send_exit_tt(ErtsDSigData *dsdp, Eterm local, Eterm remote,
} else {
ctl = TUPLE4(&ctl_heap[0], make_small(DOP_EXIT), local, remote, reason);
}
+#ifdef USE_VM_PROBES
+ *node_name = *sender_name = *remote_name = '\0';
+ if (DTRACE_ENABLED(process_exit_signal_remote)) {
+ erts_snprintf(node_name, sizeof(node_name), "%T", dsdp->dep->sysname);
+ erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->id);
+ erts_snprintf(remote_name, sizeof(remote_name),
+ "{%T,%s}", remote, node_name);
+ erts_snprintf(reason_str, sizeof(reason), "%T", reason);
+ if (token != NIL && token != am_have_dt_utag) {
+ tok_label = signed_val(SEQ_TRACE_T_LABEL(token));
+ tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token));
+ tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token));
+ }
+ }
+#endif
+ DTRACE7(process_exit_signal_remote, sender_name, node_name,
+ remote_name, reason_str, tok_label, tok_lastcnt, tok_serial);
/* forced, i.e ignore busy */
res = dsig_send(dsdp, ctl, THE_NON_VALUE, 1);
UnUseTmpHeapNoproc(6);
@@ -1619,6 +1718,18 @@ dsig_send(ErtsDSigData *dsdp, Eterm ctl, Eterm msg, int force_busy)
if (!(dep->qflgs & ERTS_DE_QFLG_BUSY)) {
if (suspended)
resume = 1; /* was busy when we started, but isn't now */
+#ifdef USE_VM_PROBES
+ if (resume && DTRACE_ENABLED(dist_port_not_busy)) {
+ DTRACE_CHARBUF(port_str, 64);
+ DTRACE_CHARBUF(remote_str, 64);
+
+ erts_snprintf(port_str, sizeof(port_str), "%T", cid);
+ erts_snprintf(remote_str, sizeof(remote_str),
+ "%T", dep->sysname);
+ DTRACE3(dist_port_not_busy, erts_this_node_sysname,
+ port_str, remote_str);
+ }
+#endif
}
else {
/* Enqueue suspended process on dist entry */
@@ -1668,6 +1779,19 @@ dsig_send(ErtsDSigData *dsdp, Eterm ctl, Eterm msg, int force_busy)
}
if (suspended) {
+#ifdef USE_VM_PROBES
+ if (!resume && DTRACE_ENABLED(dist_port_busy)) {
+ DTRACE_CHARBUF(port_str, 64);
+ DTRACE_CHARBUF(remote_str, 64);
+ DTRACE_CHARBUF(pid_str, 16);
+
+ erts_snprintf(port_str, sizeof(port_str), "%T", cid);
+ erts_snprintf(remote_str, sizeof(remote_str), "%T", dep->sysname);
+ erts_snprintf(pid_str, sizeof(pid_str), "%T", c_p->id);
+ DTRACE4(dist_port_busy, erts_this_node_sysname,
+ port_str, remote_str, pid_str);
+ }
+#endif
if (!resume && erts_system_monitor_flags.busy_dist_port)
monitor_generic(c_p, am_busy_dist_port, cid);
return ERTS_DSIG_SEND_YIELD;
@@ -1691,6 +1815,18 @@ dist_port_command(Port *prt, ErtsDistOutputBuf *obuf)
"(%beu bytes) passed.\n",
size);
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(dist_output)) {
+ DTRACE_CHARBUF(port_str, 64);
+ DTRACE_CHARBUF(remote_str, 64);
+
+ erts_snprintf(port_str, sizeof(port_str), "%T", prt->id);
+ erts_snprintf(remote_str, sizeof(remote_str),
+ "%T", prt->dist_entry->sysname);
+ DTRACE4(dist_output, erts_this_node_sysname, port_str,
+ remote_str, size);
+ }
+#endif
prt->caller = NIL;
fpe_was_unmasked = erts_block_fpe();
(*prt->drv_ptr->output)((ErlDrvData) prt->drv_data,
@@ -1733,6 +1869,18 @@ dist_port_commandv(Port *prt, ErtsDistOutputBuf *obuf)
ASSERT(prt->drv_ptr->outputv);
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(dist_outputv)) {
+ DTRACE_CHARBUF(port_str, 64);
+ DTRACE_CHARBUF(remote_str, 64);
+
+ erts_snprintf(port_str, sizeof(port_str), "%T", prt->id);
+ erts_snprintf(remote_str, sizeof(remote_str),
+ "%T", prt->dist_entry->sysname);
+ DTRACE4(dist_outputv, erts_this_node_sysname, port_str,
+ remote_str, size);
+ }
+#endif
prt->caller = NIL;
fpe_was_unmasked = erts_block_fpe();
(*prt->drv_ptr->outputv)((ErlDrvData) prt->drv_data, &eiov);
@@ -2052,6 +2200,18 @@ erts_dist_command(Port *prt, int reds_limit)
void
erts_dist_port_not_busy(Port *prt)
{
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(dist_port_not_busy)) {
+ DTRACE_CHARBUF(port_str, 64);
+ DTRACE_CHARBUF(remote_str, 64);
+
+ erts_snprintf(port_str, sizeof(port_str), "%T", prt->id);
+ erts_snprintf(remote_str, sizeof(remote_str),
+ "%T", prt->dist_entry->sysname);
+ DTRACE3(dist_port_not_busy, erts_this_node_sysname,
+ port_str, remote_str);
+ }
+#endif
erts_schedule_dist_command(prt, NULL);
}
@@ -2972,7 +3132,11 @@ send_nodes_mon_msg(Process *rp,
}
ASSERT(hend == hp);
- erts_queue_message(rp, rp_locksp, bp, msg, NIL);
+ erts_queue_message(rp, rp_locksp, bp, msg, NIL
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
}
static void
@@ -2985,6 +3149,21 @@ send_nodes_mon_msgs(Process *c_p, Eterm what, Eterm node, Eterm type, Eterm reas
ASSERT(is_immed(what));
ASSERT(is_immed(node));
ASSERT(is_immed(type));
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(dist_monitor)) {
+ DTRACE_CHARBUF(what_str, 12);
+ DTRACE_CHARBUF(node_str, 64);
+ DTRACE_CHARBUF(type_str, 12);
+ DTRACE_CHARBUF(reason_str, 64);
+
+ erts_snprintf(what_str, sizeof(what_str), "%T", what);
+ erts_snprintf(node_str, sizeof(node_str), "%T", node);
+ erts_snprintf(type_str, sizeof(type_str), "%T", type);
+ erts_snprintf(reason_str, sizeof(reason_str), "%T", reason);
+ DTRACE5(dist_monitor, erts_this_node_sysname,
+ what_str, node_str, type_str, reason_str);
+ }
+#endif
ERTS_SMP_LC_ASSERT(!c_p
|| (erts_proc_lc_my_proc_locks(c_p)
diff --git a/erts/emulator/beam/dtrace-wrapper.h b/erts/emulator/beam/dtrace-wrapper.h
new file mode 100644
index 0000000000..9d1e55fc43
--- /dev/null
+++ b/erts/emulator/beam/dtrace-wrapper.h
@@ -0,0 +1,109 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Dustin Sallings, Michal Ptaszek, Scott Lystig Fritchie 2011.
+ * All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#ifndef __DTRACE_WRAPPER_H
+#define __DTRACE_WRAPPER_H
+
+#define DTRACE_TERM_BUF_SIZE 256
+
+/*
+ * Some varieties of SystemTap macros do not like statically-sized
+ * char[N] buffers. (For example, CentOS 6's macros.)
+ * So, we'll play a game to humor them.
+ *
+ * The code necessary to play nice with CentOS 6's SystemTap looks
+ * stupid to a C programmer's eyes, so we hide the ugliness with this
+ * macro, which expands:
+ *
+ * DTRACE_CHARBUF(proc_name, 64);
+ *
+ * to become:
+ *
+ * char proc_name_BUFFER[64], *proc_name = proc_name_BUFFER;
+ */
+
+#define DTRACE_CHARBUF(name, size) \
+ char name##_BUFFER[size], *name = name##_BUFFER
+
+#if defined(USE_DYNAMIC_TRACE) && defined(USE_VM_PROBES)
+
+#include "erlang_dtrace.h"
+
+#define DTRACE_ENABLED(name) \
+ erlang_##name##_enabled()
+#define DTRACE0(name) \
+ erlang_##name()
+#define DTRACE1(name, a0) \
+ erlang_##name(a0)
+#define DTRACE2(name, a0, a1) \
+ erlang_##name((a0), (a1))
+#define DTRACE3(name, a0, a1, a2) \
+ erlang_##name((a0), (a1), (a2))
+#define DTRACE4(name, a0, a1, a2, a3) \
+ erlang_##name((a0), (a1), (a2), (a3))
+#define DTRACE5(name, a0, a1, a2, a3, a4) \
+ erlang_##name((a0), (a1), (a2), (a3), (a4))
+#define DTRACE6(name, a0, a1, a2, a3, a4, a5) \
+ erlang_##name((a0), (a1), (a2), (a3), (a4), (a5))
+#define DTRACE7(name, a0, a1, a2, a3, a4, a5, a6) \
+ erlang_##name((a0), (a1), (a2), (a3), (a4), (a5), (a6))
+#define DTRACE10(name, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) \
+ erlang_##name((a0), (a1), (a2), (a3), (a4), (a5), (a6), (a7), (a8), (a9))
+#define DTRACE11(name, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) \
+ erlang_##name((a0), (a1), (a2), (a3), (a4), (a5), (a6), (a7), (a8), (a9), (a10))
+
+#if defined(_SDT_PROBE) && !defined(STAP_PROBE11)
+/* SLF: This is Ubuntu 11-style SystemTap hackery */
+/* work arround for missing STAP macro */
+#define STAP_PROBE11(provider,name,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11) \
+ _SDT_PROBE(provider, name, 11, \
+ (arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11))
+#define _SDT_ASM_OPERANDS_11(arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11) \
+ _SDT_ASM_OPERANDS_10(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9,arg10), \
+ _SDT_ARG(11, arg11)
+#endif
+
+#ifdef STAP_PROBE_ADDR
+/* SLF: This is CentOS 5-style SystemTap hackery */
+/* SystemTap compat mode cannot support 11 args. We'll ignore the 11th */
+#define STAP_PROBE11(provider,probe,parm1,parm2,parm3,parm4,parm5,parm6,parm7,parm8,parm9,parm10,parm11) \
+ STAP_PROBE10(provider,probe,(parm1),(parm2),(parm3),(parm4),(parm5),(parm6),(parm7),(parm8),(parm9),(parm10))
+#endif /* STAP_PROBE_ADDR */
+
+#else /* USE_DYNAMIC_TRACE && USE_VM_PROBES */
+
+/* Render all macros to do nothing */
+#define DTRACE_ENABLED(name) 0
+#define DTRACE0(name) do {} while (0)
+#define DTRACE1(name, a0) do {} while (0)
+#define DTRACE2(name, a0, a1) do {} while (0)
+#define DTRACE3(name, a0, a1, a2) do {} while (0)
+#define DTRACE4(name, a0, a1, a2, a3) do {} while (0)
+#define DTRACE5(name, a0, a1, a2, a3, a4) do {} while (0)
+#define DTRACE6(name, a0, a1, a2, a3, a4, a5) do {} while (0)
+#define DTRACE7(name, a0, a1, a2, a3, a4, a5, a6) do {} while (0)
+#define DTRACE10(name, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) \
+ do {} while (0)
+#define DTRACE11(name, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) \
+ do {} while (0)
+
+#endif /* USE_DYNAMIC_TRACE && USE_VM_PROBES */
+
+#endif /* __DTRACE_WRAPPER_H */
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index df27186680..8130d5c576 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -3001,7 +3001,11 @@ reply_alloc_info(void *vair)
HRelease(rp, hp_end, hp);
}
- erts_queue_message(rp, &rp_locks, bp, msg, NIL);
+ erts_queue_message(rp, &rp_locks, bp, msg, NIL
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
if (air->req_sched == sched_id)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c
index 8bca9ae582..f0e98b33a5 100644
--- a/erts/emulator/beam/erl_async.c
+++ b/erts/emulator/beam/erl_async.c
@@ -26,6 +26,7 @@
#include "erl_threads.h"
#include "erl_thr_queue.h"
#include "erl_async.h"
+#include "dtrace-wrapper.h"
#define ERTS_MAX_ASYNC_READY_CALLS_IN_SEQ 20
@@ -121,6 +122,14 @@ typedef struct {
#endif
} ErtsAsyncData;
+/*
+ * Some compilers, e.g. GCC 4.2.1 and -O3, will optimize away DTrace
+ * calls if they're the last thing in the function. :-(
+ * Many thanks to Trond Norbye, via:
+ * https://github.com/memcached/memcached/commit/6298b3978687530bc9d219b6ac707a1b681b2a46
+ */
+static unsigned gcc_optimizer_hack = 0;
+
int erts_async_max_threads; /* Initialized by erl_init.c */
int erts_async_thread_suggested_stack_size; /* Initialized by erl_init.c */
@@ -244,6 +253,8 @@ erts_get_async_ready_queue(Uint sched_id)
static ERTS_INLINE void async_add(ErtsAsync *a, ErtsAsyncQ* q)
{
+ int len;
+
if (is_internal_port(a->port)) {
#if ERTS_USE_ASYNC_READY_Q
ErtsAsyncReadyQ *arq = async_ready_q(a->sched_id);
@@ -259,6 +270,17 @@ static ERTS_INLINE void async_add(ErtsAsync *a, ErtsAsyncQ* q)
#endif
erts_thr_q_enqueue(&q->thr_q, a);
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(aio_pool_add)) {
+ DTRACE_CHARBUF(port_str, 16);
+
+ erts_snprintf(port_str, sizeof(port_str), "%T", a->port);
+ /* DTRACE TODO: Get the queue length from erts_thr_q_enqueue() ? */
+ len = -1;
+ DTRACE2(aio_pool_add, port_str, len);
+ }
+#endif
+ gcc_optimizer_hack++;
}
static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q,
@@ -269,6 +291,7 @@ static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q,
int saved_fin_deq = 0;
ErtsThrQFinDeQ_t fin_deq;
#endif
+ int len;
while (1) {
ErtsAsync *a = (ErtsAsync *) erts_thr_q_dequeue(q);
@@ -280,7 +303,16 @@ static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q,
if (saved_fin_deq)
erts_thr_q_append_finalize_dequeue_data(&a->q.fin_deq, &fin_deq);
#endif
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(aio_pool_get)) {
+ DTRACE_CHARBUF(port_str, 16);
+ erts_snprintf(port_str, sizeof(port_str), "%T", a->port);
+ /* DTRACE TODO: Get the length from erts_thr_q_dequeue() ? */
+ len = -1;
+ DTRACE2(aio_pool_get, port_str, len);
+ }
+#endif
return a;
}
diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c
index 37d540b41b..bcfdacb91c 100644
--- a/erts/emulator/beam/erl_bif_ddll.c
+++ b/erts/emulator/beam/erl_bif_ddll.c
@@ -45,6 +45,7 @@
#include "big.h"
#include "dist.h"
#include "erl_version.h"
+#include "dtrace-wrapper.h"
#ifdef ERTS_SMP
#define DDLL_SMP 1
@@ -1647,6 +1648,7 @@ static int do_unload_driver_entry(DE_Handle *dh, Eterm *save_name)
diver_list lock here!*/
if (q->finish) {
int fpe_was_unmasked = erts_block_fpe();
+ DTRACE1(driver_finish, q->name);
(*(q->finish))();
erts_unblock_fpe(fpe_was_unmasked);
}
@@ -1760,7 +1762,11 @@ static void notify_proc(Process *proc, Eterm ref, Eterm driver_name, Eterm type,
hp += REF_THING_SIZE;
mess = TUPLE5(hp,type,r,am_driver,driver_name,tag);
}
- erts_queue_message(proc, &rp_locks, bp, mess, am_undefined);
+ erts_queue_message(proc, &rp_locks, bp, mess, am_undefined
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
erts_smp_proc_unlock(proc, rp_locks);
ERTS_SMP_CHK_NO_PROC_LOCKS;
}
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index ebd475f73a..041eac240d 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -115,6 +115,12 @@ static char erts_system_version[] = ("Erlang " ERLANG_OTP_RELEASE
#ifdef VALGRIND
" [valgrind-compiled]"
#endif
+#ifdef USE_DTRACE
+ " [dtrace]"
+#endif
+#ifdef USE_SYSTEMTAP
+ " [systemtap]"
+#endif
"\n");
#define ASIZE(a) (sizeof(a)/sizeof(a[0]))
@@ -2720,6 +2726,24 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
#endif
BIF_RET(am_true);
}
+ else if (ERTS_IS_ATOM_STR("dynamic_trace", BIF_ARG_1)) {
+#if defined(USE_DTRACE)
+ DECL_AM(dtrace);
+ BIF_RET(AM_dtrace);
+#elif defined(USE_SYSTEMTAP)
+ DECL_AM(systemtap);
+ BIF_RET(AM_systemtap);
+#else
+ BIF_RET(am_none);
+#endif
+ }
+ else if (ERTS_IS_ATOM_STR("dynamic_trace_probes", BIF_ARG_1)) {
+#if defined(USE_VM_PROBES)
+ BIF_RET(am_true);
+#else
+ BIF_RET(am_false);
+#endif
+ }
#ifdef ERTS_SMP
else if (ERTS_IS_ATOM_STR("thread_progress", BIF_ARG_1)) {
erts_thr_progress_dbg_print_state();
diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c
index cd423eb200..3056319809 100644
--- a/erts/emulator/beam/erl_bif_port.c
+++ b/erts/emulator/beam/erl_bif_port.c
@@ -40,6 +40,7 @@
#include "external.h"
#include "packet_parser.h"
#include "erl_bits.h"
+#include "dtrace-wrapper.h"
static int open_port(Process* p, Eterm name, Eterm settings, int *err_nump);
static byte* convert_environment(Process* p, Eterm env);
@@ -343,6 +344,16 @@ port_call(Process* c_p, Eterm arg1, Eterm arg2, Eterm arg3)
__FILE__, __LINE__, endp - (bytes + size));
}
erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(driver_call)) {
+ DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE);
+
+ dtrace_pid_str(p->connected, process_str);
+ dtrace_port_str(p, port_str);
+ DTRACE5(driver_call, process_str, port_str, p->name, op, real_size);
+ }
+#endif
prc = (char *) port_resp;
fpe_was_unmasked = erts_block_fpe();
ret = drv->call((ErlDrvData)p->drv_data,
@@ -539,6 +550,18 @@ BIF_RETTYPE port_connect_2(BIF_ALIST_2)
prt->connected = pid; /* internal pid */
erts_smp_port_unlock(prt);
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(port_connect)) {
+ DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(newprocess_str, DTRACE_TERM_BUF_SIZE);
+
+ dtrace_pid_str(prt->connected, process_str);
+ erts_snprintf(port_str, sizeof(port_str), "%T", prt->id);
+ dtrace_proc_str(rp, newprocess_str);
+ DTRACE4(port_connect, process_str, port_str, prt->name, newprocess_str);
+ }
+#endif
BIF_RET(am_true);
}
@@ -904,7 +927,16 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_nump)
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
port_num = erts_open_driver(driver, p->id, name_buf, &opts, err_nump);
+#ifdef USE_VM_PROBES
+ if (port_num >= 0 && DTRACE_ENABLED(port_open)) {
+ DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE);
+ dtrace_proc_str(p, process_str);
+ erts_snprintf(port_str, sizeof(port_str), "%T", erts_port[port_num].id);
+ DTRACE3(port_open, process_str, name_buf, port_str);
+ }
+#endif
erts_smp_proc_lock(p, ERTS_PROC_LOCK_MAIN);
if (port_num < 0) {
diff --git a/erts/emulator/beam/erl_bif_timer.c b/erts/emulator/beam/erl_bif_timer.c
index a922a33da3..0002f8374f 100644
--- a/erts/emulator/beam/erl_bif_timer.c
+++ b/erts/emulator/beam/erl_bif_timer.c
@@ -373,7 +373,11 @@ bif_timer_timeout(ErtsBifTimer* btm)
message = TUPLE3(hp, am_timeout, ref, message);
}
- erts_queue_message(rp, &rp_locks, bp, message, NIL);
+ erts_queue_message(rp, &rp_locks, bp, message, NIL
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
erts_smp_proc_unlock(rp, rp_locks);
erts_smp_proc_dec_refc(rp);
}
diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c
index b0a58c80ea..1ef4b07c24 100644
--- a/erts/emulator/beam/erl_bif_trace.c
+++ b/erts/emulator/beam/erl_bif_trace.c
@@ -1744,9 +1744,17 @@ Eterm erts_seq_trace(Process *p, Eterm arg1, Eterm arg2,
return THE_NON_VALUE;
}
if (build_result) {
+#ifdef USE_VM_PROBES
+ old_value = (SEQ_TRACE_TOKEN(p) == am_have_dt_utag) ? NIL : SEQ_TRACE_TOKEN(p);
+#else
old_value = SEQ_TRACE_TOKEN(p);
+#endif
}
+#ifdef USE_VM_PROBES
+ SEQ_TRACE_TOKEN(p) = (DT_UTAG(p) != NIL) ? am_have_dt_utag : NIL;
+#else
SEQ_TRACE_TOKEN(p) = NIL;
+#endif
return old_value;
}
else {
@@ -1759,7 +1767,11 @@ new_seq_trace_token(Process* p)
{
Eterm* hp;
- if (SEQ_TRACE_TOKEN(p) == NIL) {
+ if (SEQ_TRACE_TOKEN(p) == NIL
+#ifdef USE_VM_PROBES
+ || SEQ_TRACE_TOKEN(p) == am_have_dt_utag
+#endif
+ ) {
hp = HAlloc(p, 6);
SEQ_TRACE_TOKEN(p) = TUPLE5(hp, make_small(0), /* Flags */
make_small(0), /* Label */
@@ -1779,7 +1791,11 @@ BIF_RETTYPE erl_seq_trace_info(Process *p, Eterm item)
BIF_ERROR(p, BADARG);
}
- if (SEQ_TRACE_TOKEN(p) == NIL) {
+ if (SEQ_TRACE_TOKEN(p) == NIL
+#ifdef USE_VM_PROBES
+ || SEQ_TRACE_TOKEN(p) == am_have_dt_utag
+#endif
+ ) {
if ((item == am_send) || (item == am_receive) ||
(item == am_print) || (item == am_timestamp)) {
hp = HAlloc(p,3);
@@ -1836,8 +1852,13 @@ BIF_RETTYPE seq_trace_info_1(BIF_ALIST_1)
*/
BIF_RETTYPE seq_trace_print_1(BIF_ALIST_1)
{
- if (SEQ_TRACE_TOKEN(BIF_P) == NIL)
+ if (SEQ_TRACE_TOKEN(BIF_P) == NIL
+#ifdef USE_VM_PROBES
+ || SEQ_TRACE_TOKEN(BIF_P) == am_have_dt_utag
+#endif
+ ) {
BIF_RET(am_false);
+ }
seq_trace_update_send(BIF_P);
seq_trace_output(SEQ_TRACE_TOKEN(BIF_P), BIF_ARG_1,
SEQ_TRACE_PRINT, NIL, BIF_P);
@@ -1854,8 +1875,13 @@ BIF_RETTYPE seq_trace_print_1(BIF_ALIST_1)
*/
BIF_RETTYPE seq_trace_print_2(BIF_ALIST_2)
{
- if (SEQ_TRACE_TOKEN(BIF_P) == NIL)
+ if (SEQ_TRACE_TOKEN(BIF_P) == NIL
+#ifdef USE_VM_PROBES
+ || SEQ_TRACE_TOKEN(BIF_P) == am_have_dt_utag
+#endif
+ ) {
BIF_RET(am_false);
+ }
if (!(is_atom(BIF_ARG_1) || is_small(BIF_ARG_1))) {
BIF_ERROR(BIF_P, BADARG);
}
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index 4821a7d9fb..be345e7c9b 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -2203,7 +2203,11 @@ restart:
*esp++ = am_true;
break;
case matchIsSeqTrace:
- if (SEQ_TRACE_TOKEN(c_p) != NIL)
+ if (SEQ_TRACE_TOKEN(c_p) != NIL
+#ifdef USE_VM_PROBES
+ && SEQ_TRACE_TOKEN(c_p) != am_have_dt_utag
+#endif
+ )
*esp++ = am_true;
else
*esp++ = am_false;
@@ -2227,7 +2231,11 @@ restart:
--esp;
break;
case matchGetSeqToken:
- if (SEQ_TRACE_TOKEN(c_p) == NIL)
+ if (SEQ_TRACE_TOKEN(c_p) == NIL
+#ifdef USE_VM_PROBES
+ || SEQ_TRACE_TOKEN(c_p) == am_have_dt_utag
+#endif
+ )
*esp++ = NIL;
else {
Eterm sender = SEQ_TRACE_TOKEN_SENDER(c_p);
diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h
index 7510f6b724..1ae9a211d7 100644
--- a/erts/emulator/beam/erl_driver.h
+++ b/erts/emulator/beam/erl_driver.h
@@ -649,6 +649,8 @@ EXTERN int erl_drv_getenv(char *key, char *value, size_t *value_size);
#endif
+/* also in global.h, but driver's can't include global.h */
+void dtrace_drvport_str(ErlDrvPort port, char *port_buf);
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index bde87b8346..82f2dc6091 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -35,6 +35,7 @@
#include "hipe_stack.h"
#include "hipe_mode_switch.h"
#endif
+#include "dtrace-wrapper.h"
#define ERTS_INACT_WR_PB_LEAVE_MUCH_LIMIT 1
#define ERTS_INACT_WR_PB_LEAVE_MUCH_PERCENTAGE 20
@@ -349,7 +350,9 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
Uint reclaimed_now = 0;
int done = 0;
Uint ms1, s1, us1;
-
+#ifdef USE_VM_PROBES
+ DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE);
+#endif
if (IS_TRACED_FL(p, F_TRACE_GC)) {
trace_gc(p, am_gc_start);
}
@@ -369,15 +372,27 @@ erts_garbage_collect(Process* p, int need, Eterm* objv, int nobj)
if (GEN_GCS(p) >= MAX_GEN_GCS(p)) {
FLAGS(p) |= F_NEED_FULLSWEEP;
}
-
+#ifdef USE_VM_PROBES
+ *pidbuf = '\0';
+ if (DTRACE_ENABLED(gc_major_start)
+ || DTRACE_ENABLED(gc_major_end)
+ || DTRACE_ENABLED(gc_minor_start)
+ || DTRACE_ENABLED(gc_minor_end)) {
+ dtrace_proc_str(p, pidbuf);
+ }
+#endif
/*
* Test which type of GC to do.
*/
while (!done) {
if ((FLAGS(p) & F_NEED_FULLSWEEP) != 0) {
+ DTRACE2(gc_major_start, pidbuf, need);
done = major_collection(p, need, objv, nobj, &reclaimed_now);
+ DTRACE2(gc_major_end, pidbuf, reclaimed_now);
} else {
+ DTRACE2(gc_minor_start, pidbuf, need);
done = minor_collection(p, need, objv, nobj, &reclaimed_now);
+ DTRACE2(gc_minor_end, pidbuf, reclaimed_now);
}
}
@@ -1118,6 +1133,15 @@ do_minor(Process *p, Uint new_sz, Eterm* objv, int nobj)
sys_memcpy(n_heap + new_sz - n, p->stop, n * sizeof(Eterm));
p->stop = n_heap + new_sz - n;
+#ifdef USE_VM_PROBES
+ if (HEAP_SIZE(p) != new_sz && DTRACE_ENABLED(process_heap_grow)) {
+ DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE);
+
+ dtrace_proc_str(p, pidbuf);
+ DTRACE3(process_heap_grow, pidbuf, HEAP_SIZE(p), new_sz);
+ }
+#endif
+
ERTS_HEAP_FREE(ERTS_ALC_T_HEAP,
(void*)HEAP_START(p),
HEAP_SIZE(p) * sizeof(Eterm));
@@ -1339,6 +1363,15 @@ major_collection(Process* p, int need, Eterm* objv, int nobj, Uint *recl)
sys_memcpy(n_heap + new_sz - n, p->stop, n * sizeof(Eterm));
p->stop = n_heap + new_sz - n;
+#ifdef USE_VM_PROBES
+ if (HEAP_SIZE(p) != new_sz && DTRACE_ENABLED(process_heap_grow)) {
+ DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE);
+
+ dtrace_proc_str(p, pidbuf);
+ DTRACE3(process_heap_grow, pidbuf, HEAP_SIZE(p), new_sz);
+ }
+#endif
+
ERTS_HEAP_FREE(ERTS_ALC_T_HEAP,
(void *) HEAP_START(p),
(HEAP_END(p) - HEAP_START(p)) * sizeof(Eterm));
@@ -1907,7 +1940,13 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset)
roots[n].sz = 1;
n++;
}
-
+#ifdef USE_VM_PROBES
+ if (is_not_immed(p->dt_utag)) {
+ roots[n].v = &p->dt_utag;
+ roots[n].sz = 1;
+ n++;
+ }
+#endif
ASSERT(is_nil(p->tracer_proc) ||
is_internal_pid(p->tracer_proc) ||
is_internal_port(p->tracer_proc));
@@ -2009,6 +2048,16 @@ grow_new_heap(Process *p, Uint new_sz, Eterm* objv, int nobj)
HEAP_TOP(p) = new_heap + heap_size;
HEAP_START(p) = new_heap;
}
+
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(process_heap_grow)) {
+ DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE);
+
+ dtrace_proc_str(p, pidbuf);
+ DTRACE3(process_heap_grow, pidbuf, HEAP_SIZE(p), new_sz);
+ }
+#endif
+
HEAP_SIZE(p) = new_sz;
}
@@ -2018,7 +2067,6 @@ shrink_new_heap(Process *p, Uint new_sz, Eterm *objv, int nobj)
Eterm* new_heap;
Uint heap_size = HEAP_TOP(p) - HEAP_START(p);
Sint offs;
-
Uint stack_size = p->hend - p->stop;
ASSERT(new_sz < p->heap_sz);
@@ -2047,6 +2095,16 @@ shrink_new_heap(Process *p, Uint new_sz, Eterm *objv, int nobj)
HEAP_TOP(p) = new_heap + heap_size;
HEAP_START(p) = new_heap;
}
+
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(process_heap_shrink)) {
+ DTRACE_CHARBUF(pidbuf, DTRACE_TERM_BUF_SIZE);
+
+ dtrace_proc_str(p, pidbuf);
+ DTRACE3(process_heap_shrink, pidbuf, HEAP_SIZE(p), new_sz);
+ }
+#endif
+
HEAP_SIZE(p) = new_sz;
}
@@ -2429,6 +2487,13 @@ offset_mqueue(Process *p, Sint offs, char* area, Uint area_size)
if (is_boxed(mesg) && in_area(ptr_val(mesg), area, area_size)) {
ERL_MESSAGE_TOKEN(mp) = offset_ptr(mesg, offs);
}
+#ifdef USE_VM_PROBES
+ mesg = ERL_MESSAGE_DT_UTAG(mp);
+ if (is_boxed(mesg) && in_area(ptr_val(mesg), area, area_size)) {
+ ERL_MESSAGE_DT_UTAG(mp) = offset_ptr(mesg, offs);
+ }
+#endif
+
ASSERT((is_nil(ERL_MESSAGE_TOKEN(mp)) ||
is_tuple(ERL_MESSAGE_TOKEN(mp)) ||
is_atom(ERL_MESSAGE_TOKEN(mp))));
@@ -2448,6 +2513,9 @@ offset_one_rootset(Process *p, Sint offs, char* area, Uint area_size,
offset_heap_ptr(&p->fvalue, 1, offs, area, area_size);
offset_heap_ptr(&p->ftrace, 1, offs, area, area_size);
offset_heap_ptr(&p->seq_trace_token, 1, offs, area, area_size);
+#ifdef USE_VM_PROBES
+ offset_heap_ptr(&p->dt_utag, 1, offs, area, area_size);
+#endif
offset_heap_ptr(&p->group_leader, 1, offs, area, area_size);
offset_mqueue(p, offs, area, area_size);
offset_heap_ptr(p->stop, (STACK_START(p) - p->stop), offs, area, area_size);
diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c
index 09e85893c3..5eb2a69242 100644
--- a/erts/emulator/beam/erl_lock_check.c
+++ b/erts/emulator/beam/erl_lock_check.c
@@ -183,6 +183,9 @@ static erts_lc_lock_order_t erts_lock_order[] = {
{ "save_ops_lock", NULL },
#endif
#endif
+#ifdef USE_VM_PROBES
+ { "efile_drv dtrace mutex", NULL },
+#endif
{ "mtrace_buf", NULL },
{ "erts_alloc_hard_debug", NULL }
};
diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c
index ab1ab7b1ea..4cdf2d7d09 100644
--- a/erts/emulator/beam/erl_message.c
+++ b/erts/emulator/beam/erl_message.c
@@ -31,6 +31,7 @@
#include "erl_process.h"
#include "erl_nmgc.h"
#include "erl_binary.h"
+#include "dtrace-wrapper.h"
ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(message,
ErlMessage,
@@ -335,6 +336,11 @@ erts_queue_dist_message(Process *rcvr,
Eterm token)
{
ErlMessage* mp;
+#ifdef USE_VM_PROBES
+ Sint tok_label = 0;
+ Sint tok_lastcnt = 0;
+ Sint tok_serial = 0;
+#endif
#ifdef ERTS_SMP
ErtsProcLocks need_locks;
#endif
@@ -376,15 +382,61 @@ erts_queue_dist_message(Process *rcvr,
message_free(mp);
msg = erts_msg_distext2heap(rcvr, rcvr_locks, &mbuf, &token, dist_ext);
if (is_value(msg))
- erts_queue_message(rcvr, rcvr_locks, mbuf, msg, token);
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(message_queued)) {
+ DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE);
+
+ dtrace_proc_str(rcvr, receiver_name);
+ if (token != NIL && token != am_have_dt_utag) {
+ tok_label = signed_val(SEQ_TRACE_T_LABEL(token));
+ tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token));
+ tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token));
+ }
+ DTRACE6(message_queued,
+ receiver_name, size_object(msg), rcvr->msg.len,
+ tok_label, tok_lastcnt, tok_serial);
+ }
+#endif
+ erts_queue_message(rcvr, rcvr_locks, mbuf, msg, token
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
}
else {
/* Enqueue message on external format */
ERL_MESSAGE_TERM(mp) = THE_NON_VALUE;
- ERL_MESSAGE_TOKEN(mp) = token;
+#ifdef USE_VM_PROBES
+ ERL_MESSAGE_DT_UTAG(mp) = NIL;
+ if (token == am_have_dt_utag) {
+ ERL_MESSAGE_TOKEN(mp) = NIL;
+ } else {
+#endif
+ ERL_MESSAGE_TOKEN(mp) = token;
+#ifdef USE_VM_PROBES
+ }
+#endif
mp->next = NULL;
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(message_queued)) {
+ DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE);
+
+ dtrace_proc_str(rcvr, receiver_name);
+ if (token != NIL && token != am_have_dt_utag) {
+ tok_label = signed_val(SEQ_TRACE_T_LABEL(token));
+ tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token));
+ tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token));
+ }
+ /*
+ * TODO: We don't know the real size of the external message here.
+ * -1 will appear to a D script as 4294967295.
+ */
+ DTRACE6(message_queued, receiver_name, -1, rcvr->msg.len + 1,
+ tok_label, tok_lastcnt, tok_serial);
+ }
+#endif
mp->data.dist_ext = dist_ext;
LINK_MESSAGE(rcvr, mp);
@@ -398,7 +450,11 @@ erts_queue_message(Process* receiver,
ErtsProcLocks *receiver_locks,
ErlHeapFragment* bp,
Eterm message,
- Eterm seq_trace_token)
+ Eterm seq_trace_token
+#ifdef USE_VM_PROBES
+ , Eterm dt_utag
+#endif
+)
{
ErlMessage* mp;
#ifdef ERTS_SMP
@@ -439,6 +495,9 @@ erts_queue_message(Process* receiver,
ERL_MESSAGE_TERM(mp) = message;
ERL_MESSAGE_TOKEN(mp) = seq_trace_token;
+#ifdef USE_VM_PROBES
+ ERL_MESSAGE_DT_UTAG(mp) = dt_utag;
+#endif
mp->next = NULL;
mp->data.heap_frag = bp;
@@ -462,12 +521,30 @@ erts_queue_message(Process* receiver,
LINK_MESSAGE(receiver, mp);
#endif
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(message_queued)) {
+ DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE);
+ Sint tok_label = 0;
+ Sint tok_lastcnt = 0;
+ Sint tok_serial = 0;
+
+ dtrace_proc_str(receiver, receiver_name);
+ if (seq_trace_token != NIL && is_tuple(seq_trace_token)) {
+ tok_label = signed_val(SEQ_TRACE_T_LABEL(seq_trace_token));
+ tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(seq_trace_token));
+ tok_serial = signed_val(SEQ_TRACE_T_SERIAL(seq_trace_token));
+ }
+ DTRACE6(message_queued,
+ receiver_name, size_object(message), receiver->msg.len,
+ tok_label, tok_lastcnt, tok_serial);
+ }
+#endif
notify_new_message(receiver);
if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE)) {
trace_receive(receiver, message);
}
-
+
#ifndef ERTS_SMP
ERTS_HOLE_CHECK(receiver);
#endif
@@ -497,6 +574,9 @@ erts_move_msg_mbuf_to_heap(Eterm** hpp, ErlOffHeap* off_heap, ErlMessage *msg)
Sint offs;
Uint sz;
ErlHeapFragment *bp;
+#ifdef USE_VM_PROBES
+ Eterm utag;
+#endif
#ifdef HARD_DEBUG
ProcBin *dbg_mso_start = off_heap->mso;
@@ -506,32 +586,56 @@ erts_move_msg_mbuf_to_heap(Eterm** hpp, ErlOffHeap* off_heap, ErlMessage *msg)
ErlHeapFragment *dbg_bp;
Uint *dbg_hp, *dbg_thp_start;
Uint dbg_term_sz, dbg_token_sz;
+#ifdef USE_VM_PROBES
+ Eterm dbg_utag;
+ Uint dbg_utag_sz;
+#endif
#endif
bp = msg->data.heap_frag;
term = ERL_MESSAGE_TERM(msg);
token = ERL_MESSAGE_TOKEN(msg);
+#ifdef USE_VM_PROBES
+ utag = ERL_MESSAGE_DT_UTAG(msg);
+#endif
if (!bp) {
+#ifdef USE_VM_PROBES
+ ASSERT(is_immed(term) && is_immed(token) && is_immed(utag));
+#else
ASSERT(is_immed(term) && is_immed(token));
+#endif
return;
}
#ifdef HARD_DEBUG
dbg_term_sz = size_object(term);
dbg_token_sz = size_object(token);
+ dbg_bp = new_message_buffer(dbg_term_sz + dbg_token_sz);
+#ifdef USE_VM_PROBES
+ dbg_utag_sz = size_object(utag);
+ dbg_bp = new_message_buffer(dbg_term_sz + dbg_token_sz + dbg_utag_sz );
+#endif
/*ASSERT(dbg_term_sz + dbg_token_sz == erts_msg_used_frag_sz(msg));
Copied size may be smaller due to removed SubBins's or garbage.
Copied size may be larger due to duplicated shared terms.
*/
- dbg_bp = new_message_buffer(dbg_term_sz + dbg_token_sz);
dbg_hp = dbg_bp->mem;
dbg_term = copy_struct(term, dbg_term_sz, &dbg_hp, &dbg_bp->off_heap);
dbg_token = copy_struct(token, dbg_token_sz, &dbg_hp, &dbg_bp->off_heap);
- dbg_thp_start = *hpp;
+#ifdef USE_VM_PROBES
+ dbg_utag = copy_struct(utag, dbg_utag_sz, &dbg_hp, &dbg_bp->off_heap);
+#endif
+ dbg_thp_start = *hpp;
#endif
if (bp->next != NULL) {
- move_multi_frags(hpp, off_heap, bp, msg->m, 2);
+ move_multi_frags(hpp, off_heap, bp, msg->m,
+#ifdef USE_VM_PROBES
+ 3
+#else
+ 2
+#endif
+ );
goto copy_done;
}
@@ -633,6 +737,16 @@ erts_move_msg_mbuf_to_heap(Eterm** hpp, ErlOffHeap* off_heap, ErlMessage *msg)
ASSERT(hp > ptr_val(ERL_MESSAGE_TERM(msg)));
#endif
}
+#ifdef USE_VM_PROBES
+ if (is_not_immed(utag)) {
+ ASSERT(in_heapfrag(ptr_val(utag), bp));
+ ERL_MESSAGE_DT_UTAG(msg) = offset_ptr(utag, offs);
+#ifdef HARD_DEBUG
+ ASSERT(dbg_thp_start <= ptr_val(ERL_MESSAGE_DT_UTAG(msg)));
+ ASSERT(hp > ptr_val(ERL_MESSAGE_DT_UTAG(msg)));
+#endif
+ }
+#endif
copy_done:
@@ -699,6 +813,9 @@ copy_done:
#ifdef HARD_DEBUG
ASSERT(eq(ERL_MESSAGE_TERM(msg), dbg_term));
ASSERT(eq(ERL_MESSAGE_TOKEN(msg), dbg_token));
+#ifdef USE_VM_PROBES
+ ASSERT(eq(ERL_MESSAGE_DT_UTAG(msg), dbg_utag));
+#endif
free_message_buffer(dbg_bp);
#endif
@@ -774,39 +891,101 @@ erts_send_message(Process* sender,
Uint msize;
ErlHeapFragment* bp = NULL;
Eterm token = NIL;
-
+#ifdef USE_VM_PROBES
+ DTRACE_CHARBUF(sender_name, 64);
+ DTRACE_CHARBUF(receiver_name, 64);
+ Sint tok_label = 0;
+ Sint tok_lastcnt = 0;
+ Sint tok_serial = 0;
+#endif
BM_STOP_TIMER(system);
BM_MESSAGE(message,sender,receiver);
BM_START_TIMER(send);
+ #ifdef USE_VM_PROBES
+ *sender_name = *receiver_name = '\0';
+ if (DTRACE_ENABLED(message_send)) {
+ erts_snprintf(sender_name, sizeof(sender_name), "%T", sender->id);
+ erts_snprintf(receiver_name, sizeof(receiver_name), "%T", receiver->id);
+ }
+#endif
if (SEQ_TRACE_TOKEN(sender) != NIL && !(flags & ERTS_SND_FLG_NO_SEQ_TRACE)) {
Eterm* hp;
+ Eterm stoken = SEQ_TRACE_TOKEN(sender);
+ Uint seq_trace_size = 0;
+#ifdef USE_VM_PROBES
+ Uint dt_utag_size = 0;
+ Eterm utag = NIL;
+#endif
- BM_SWAP_TIMER(send,size);
+ BM_SWAP_TIMER(send,size);
msize = size_object(message);
- BM_SWAP_TIMER(size,send);
+ BM_SWAP_TIMER(size,send);
+
+#ifdef USE_VM_PROBES
+ if (stoken != am_have_dt_utag) {
+#endif
+
+ seq_trace_update_send(sender);
+ seq_trace_output(stoken, message, SEQ_TRACE_SEND,
+ receiver->id, sender);
+ seq_trace_size = 6; /* TUPLE5 */
+#ifdef USE_VM_PROBES
+ }
+ if (DT_UTAG_FLAGS(sender) & DT_UTAG_SPREADING) {
+ dt_utag_size = size_object(DT_UTAG(sender));
+ } else if (stoken == am_have_dt_utag ) {
+ stoken = NIL;
+ }
+#endif
- seq_trace_update_send(sender);
- seq_trace_output(SEQ_TRACE_TOKEN(sender), message, SEQ_TRACE_SEND,
- receiver->id, sender);
- bp = new_message_buffer(msize + 6 /* TUPLE5 */);
+ bp = new_message_buffer(msize + seq_trace_size
+#ifdef USE_VM_PROBES
+ + dt_utag_size
+#endif
+ );
hp = bp->mem;
BM_SWAP_TIMER(send,copy);
- token = copy_struct(SEQ_TRACE_TOKEN(sender),
- 6 /* TUPLE5 */,
+ token = copy_struct(stoken,
+ seq_trace_size,
&hp,
&bp->off_heap);
message = copy_struct(message, msize, &hp, &bp->off_heap);
+#ifdef USE_VM_PROBES
+ if (DT_UTAG_FLAGS(sender) & DT_UTAG_SPREADING) {
+ utag = copy_struct(DT_UTAG(sender), dt_utag_size, &hp, &bp->off_heap);
+#ifdef DTRACE_TAG_HARDDEBUG
+ erts_fprintf(stderr,
+ "Dtrace -> (%T) Spreading tag (%T) with "
+ "message %T!\r\n",sender->id, utag, message);
+#endif
+ }
+#endif
BM_MESSAGE_COPIED(msize);
BM_SWAP_TIMER(copy,send);
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(message_send)) {
+ if (stoken != NIL && stoken != am_have_dt_utag) {
+ tok_label = signed_val(SEQ_TRACE_T_LABEL(stoken));
+ tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(stoken));
+ tok_serial = signed_val(SEQ_TRACE_T_SERIAL(stoken));
+ }
+ DTRACE6(message_send, sender_name, receiver_name,
+ msize, tok_label, tok_lastcnt, tok_serial);
+ }
+#endif
erts_queue_message(receiver,
receiver_locks,
bp,
message,
- token);
+ token
+#ifdef USE_VM_PROBES
+ , utag
+#endif
+ );
BM_SWAP_TIMER(send,system);
#ifdef HYBRID
} else {
@@ -835,8 +1014,13 @@ erts_send_message(Process* sender,
#endif
LAZY_COPY(sender,message);
BM_SWAP_TIMER(copy,send);
+ DTRACE6(message_send, sender_name, receiver_name,
+ size_object(message)msize, tok_label, tok_lastcnt, tok_serial);
ERL_MESSAGE_TERM(mp) = message;
ERL_MESSAGE_TOKEN(mp) = NIL;
+#ifdef USE_VM_PROBES
+ ERL_MESSAGE_DT_UTAG(mp) = NIL;
+#endif
mp->next = NULL;
LINK_MESSAGE(receiver, mp);
ACTIVATE(receiver);
@@ -874,9 +1058,14 @@ erts_send_message(Process* sender,
{
ErlMessage* mp = message_alloc();
+ DTRACE6(message_send, sender_name, receiver_name,
+ size_object(message), tok_label, tok_lastcnt, tok_serial);
mp->data.attached = NULL;
ERL_MESSAGE_TERM(mp) = message;
ERL_MESSAGE_TOKEN(mp) = NIL;
+#ifdef USE_VM_PROBES
+ ERL_MESSAGE_DT_UTAG(mp) = NIL;
+#endif
mp->next = NULL;
/*
* We move 'in queue' to 'private queue' and place
@@ -908,7 +1097,13 @@ erts_send_message(Process* sender,
message = copy_struct(message, msize, &hp, ohp);
BM_MESSAGE_COPIED(msz);
BM_SWAP_TIMER(copy,send);
- erts_queue_message(receiver, receiver_locks, bp, message, token);
+ DTRACE6(message_send, sender_name, receiver_name,
+ msize, tok_label, tok_lastcnt, tok_serial);
+ erts_queue_message(receiver, receiver_locks, bp, message, token
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
BM_SWAP_TIMER(send,system);
#else
ErlMessage* mp = message_alloc();
@@ -928,8 +1123,13 @@ erts_send_message(Process* sender,
message = copy_struct(message, msize, &hp, &receiver->off_heap);
BM_MESSAGE_COPIED(msize);
BM_SWAP_TIMER(copy,send);
+ DTRACE6(message_send, sender_name, receiver_name,
+ (uint32_t)msize, tok_label, tok_lastcnt, tok_serial);
ERL_MESSAGE_TERM(mp) = message;
ERL_MESSAGE_TOKEN(mp) = NIL;
+#ifdef USE_VM_PROBES
+ ERL_MESSAGE_DT_UTAG(mp) = NIL;
+#endif
mp->next = NULL;
mp->data.attached = NULL;
LINK_MESSAGE(receiver, mp);
@@ -968,7 +1168,11 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp,
Eterm temptoken;
ErlHeapFragment* bp = NULL;
- if (token != NIL) {
+ if (token != NIL
+#ifdef USE_VM_PROBES
+ && token != am_have_dt_utag
+#endif
+ ) {
ASSERT(is_tuple(token));
sz_reason = size_object(reason);
@@ -983,7 +1187,11 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp,
/* the trace token must in this case be updated by the caller */
seq_trace_output(token, save, SEQ_TRACE_SEND, to->id, NULL);
temptoken = copy_struct(token, sz_token, &hp, &bp->off_heap);
- erts_queue_message(to, to_locksp, bp, save, temptoken);
+ erts_queue_message(to, to_locksp, bp, save, temptoken
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
} else {
ErlOffHeap *ohp;
sz_reason = size_object(reason);
@@ -1000,7 +1208,11 @@ erts_deliver_exit_message(Eterm from, Process *to, ErtsProcLocks *to_locksp,
? from
: copy_struct(from, sz_from, &hp, ohp));
save = TUPLE3(hp, am_EXIT, from_copy, mess);
- erts_queue_message(to, to_locksp, bp, save, NIL);
+ erts_queue_message(to, to_locksp, bp, save, NIL
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
}
}
diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h
index 5aca0db6fe..7678c7c753 100644
--- a/erts/emulator/beam/erl_message.h
+++ b/erts/emulator/beam/erl_message.h
@@ -70,11 +70,18 @@ typedef struct erl_mesg {
ErlHeapFragment *heap_frag;
void *attached;
} data;
+#ifdef USE_VM_PROBES
+ Eterm m[3]; /* m[0] = message, m[1] = seq trace token, m[3] = dynamic trace user tag */
+#else
Eterm m[2]; /* m[0] = message, m[1] = seq trace token */
+#endif
} ErlMessage;
#define ERL_MESSAGE_TERM(mp) ((mp)->m[0])
#define ERL_MESSAGE_TOKEN(mp) ((mp)->m[1])
+#ifdef USE_VM_PROBES
+#define ERL_MESSAGE_DT_UTAG(mp) ((mp)->m[2])
+#endif
/* Size of default message buffer (erl_message.c) */
#define ERL_MESSAGE_BUF_SZ 500
@@ -221,7 +228,11 @@ ErlHeapFragment* erts_resize_message_buffer(ErlHeapFragment *, Uint,
Eterm *, Uint);
void free_message_buffer(ErlHeapFragment *);
void erts_queue_dist_message(Process*, ErtsProcLocks*, ErtsDistExternal *, Eterm);
-void erts_queue_message(Process*, ErtsProcLocks*, ErlHeapFragment*, Eterm, Eterm);
+void erts_queue_message(Process*, ErtsProcLocks*, ErlHeapFragment*, Eterm, Eterm
+#ifdef USE_VM_PROBES
+ , Eterm dt_utag
+#endif
+);
void erts_deliver_exit_message(Eterm, Process*, ErtsProcLocks *, Eterm, Eterm);
void erts_send_message(Process*, Process*, ErtsProcLocks*, Eterm, unsigned);
void erts_link_mbuf_to_proc(Process *proc, ErlHeapFragment *bp);
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index 58a09986d2..40f2fde578 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -66,6 +66,9 @@ static void add_readonly_check(ErlNifEnv*, unsigned char* ptr, unsigned sz);
static int is_offheap(const ErlOffHeap* off_heap);
#endif
+#ifdef USE_VM_PROBES
+void dtrace_nifenv_str(ErlNifEnv *, char *);
+#endif
#define MIN_HEAP_FRAG_SZ 200
static Eterm* alloc_heap_heavy(ErlNifEnv* env, unsigned need, Eterm* hp);
@@ -350,7 +353,11 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid,
if (flush_me) {
flush_env(env); /* Needed for ERTS_HOLE_CHECK */
}
- erts_queue_message(rp, &rp_locks, frags, msg, am_undefined);
+ erts_queue_message(rp, &rp_locks, frags, msg, am_undefined
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
if (rp_locks) {
ERTS_SMP_LC_ASSERT(rp_locks == (rp_had_locks | (ERTS_PROC_LOCK_MSGQ |
ERTS_PROC_LOCK_STATUS)));
@@ -1779,6 +1786,13 @@ void erl_nif_init()
resource_type_list.name = THE_NON_VALUE;
}
+#ifdef USE_VM_PROBES
+void dtrace_nifenv_str(ErlNifEnv *env, char *process_buf)
+{
+ dtrace_pid_str(env->proc->id, process_buf);
+}
+#endif
+
#ifdef READONLY_CHECK
/* Use checksums to assert that NIFs do not write into inspected binaries
*/
diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c
index 908ba755ed..1481f66b55 100644
--- a/erts/emulator/beam/erl_node_tables.c
+++ b/erts/emulator/beam/erl_node_tables.c
@@ -27,6 +27,7 @@
#include "big.h"
#include "error.h"
#include "erl_thr_progress.h"
+#include "dtrace-wrapper.h"
Hash erts_dist_table;
Hash erts_node_table;
@@ -42,6 +43,8 @@ Sint erts_no_of_not_connected_dist_entries;
DistEntry *erts_this_dist_entry;
ErlNode *erts_this_node;
+char erts_this_node_sysname_BUFFER[256],
+ *erts_this_node_sysname = "uninitialized yet";
static Uint node_entries;
static Uint dist_entries;
@@ -702,6 +705,9 @@ erts_set_this_node(Eterm sysname, Uint creation)
(void) hash_erase(&erts_node_table, (void *) erts_this_node);
erts_this_node->sysname = sysname;
erts_this_node->creation = creation;
+ erts_this_node_sysname = erts_this_node_sysname_BUFFER;
+ erts_snprintf(erts_this_node_sysname, sizeof(erts_this_node_sysname),
+ "%T", sysname);
(void) hash_put(&erts_node_table, (void *) erts_this_node);
erts_smp_rwmtx_rwunlock(&erts_dist_table_rwmtx);
@@ -789,6 +795,9 @@ void erts_init_node_tables(void)
erts_this_node->sysname = am_Noname;
erts_this_node->creation = 0;
erts_this_node->dist_entry = erts_this_dist_entry;
+ erts_this_node_sysname = erts_this_node_sysname_BUFFER;
+ erts_snprintf(erts_this_node_sysname, sizeof(erts_this_node_sysname),
+ "%T", erts_this_node->sysname);
(void) hash_put(&erts_node_table, (void *) erts_this_node);
diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h
index b0a63ae035..5cfd0ac641 100644
--- a/erts/emulator/beam/erl_node_tables.h
+++ b/erts/emulator/beam/erl_node_tables.h
@@ -169,6 +169,7 @@ extern Sint erts_no_of_not_connected_dist_entries;
extern DistEntry *erts_this_dist_entry;
extern ErlNode *erts_this_node;
+extern char *erts_this_node_sysname; /* must match erl_node_tables.c */
DistEntry *erts_channel_no_to_dist_entry(Uint);
DistEntry *erts_sysname_to_connected_dist_entry(Eterm);
diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c
index a2b08fcf56..a8cb4563d6 100644
--- a/erts/emulator/beam/erl_port_task.c
+++ b/erts/emulator/beam/erl_port_task.c
@@ -32,6 +32,7 @@
#include "global.h"
#include "erl_port_task.h"
#include "dist.h"
+#include "dtrace-wrapper.h"
#if defined(DEBUG) && 0
#define HARD_DEBUG
@@ -61,6 +62,20 @@ do { \
(P)->sched.next = NULL; \
} while (0)
+#ifdef USE_VM_PROBES
+#define DTRACE_DRIVER(PROBE_NAME, PP) \
+ if (DTRACE_ENABLED(driver_ready_input)) { \
+ DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE); \
+ DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); \
+ \
+ dtrace_pid_str(PP->connected, process_str); \
+ dtrace_port_str(PP, port_str); \
+ DTRACE3(PROBE_NAME, process_str, port_str, PP->name); \
+ }
+#else
+#define DTRACE_DRIVER(PROBE_NAME, PP) do {} while(0)
+#endif
+
erts_smp_atomic_t erts_port_task_outstanding_io_tasks;
struct ErtsPortTaskQueue_ {
@@ -823,12 +838,15 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
goto tasks_done;
case ERTS_PORT_TASK_TIMEOUT:
reds += ERTS_PORT_REDS_TIMEOUT;
- if (!(pp->status & ERTS_PORT_SFLGS_DEAD))
+ if (!(pp->status & ERTS_PORT_SFLGS_DEAD)) {
+ DTRACE_DRIVER(driver_timeout, pp);
(*pp->drv_ptr->timeout)((ErlDrvData) pp->drv_data);
+ }
break;
case ERTS_PORT_TASK_INPUT:
reds += ERTS_PORT_REDS_INPUT;
ASSERT((pp->status & ERTS_PORT_SFLGS_DEAD) == 0);
+ DTRACE_DRIVER(driver_ready_input, pp);
/* NOTE some windows drivers use ->ready_input for input and output */
(*pp->drv_ptr->ready_input)((ErlDrvData) pp->drv_data, ptp->event);
io_tasks_executed++;
@@ -836,12 +854,14 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp)
case ERTS_PORT_TASK_OUTPUT:
reds += ERTS_PORT_REDS_OUTPUT;
ASSERT((pp->status & ERTS_PORT_SFLGS_DEAD) == 0);
+ DTRACE_DRIVER(driver_ready_output, pp);
(*pp->drv_ptr->ready_output)((ErlDrvData) pp->drv_data, ptp->event);
io_tasks_executed++;
break;
case ERTS_PORT_TASK_EVENT:
reds += ERTS_PORT_REDS_EVENT;
ASSERT((pp->status & ERTS_PORT_SFLGS_DEAD) == 0);
+ DTRACE_DRIVER(driver_event, pp);
(*pp->drv_ptr->event)((ErlDrvData) pp->drv_data, ptp->event, ptp->event_data);
io_tasks_executed++;
break;
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index 45f6dc800c..95d408f79d 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -42,6 +42,7 @@
#include "erl_thr_progress.h"
#include "erl_thr_queue.h"
#include "erl_async.h"
+#include "dtrace-wrapper.h"
#define ERTS_RUNQ_CHECK_BALANCE_REDS_PER_SCHED (2000*CONTEXT_REDS)
#define ERTS_RUNQ_CALL_CHECK_BALANCE_REDS \
@@ -678,7 +679,11 @@ reply_sched_wall_time(void *vswtrp)
hpp = &hp;
}
- erts_queue_message(rp, &rp_locks, bp, msg, NIL);
+ erts_queue_message(rp, &rp_locks, bp, msg, NIL
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
if (swtrp->req_sched == esdp->no)
rp_locks &= ~ERTS_PROC_LOCK_MAIN;
@@ -6214,6 +6219,15 @@ Process *schedule(Process *p, int calls)
int actual_reds;
int reds;
+#ifdef USE_VM_PROBES
+ if (p != NULL && DTRACE_ENABLED(process_unscheduled)) {
+ DTRACE_CHARBUF(process_buf, DTRACE_TERM_BUF_SIZE);
+
+ dtrace_proc_str(p, process_buf);
+ DTRACE1(process_unscheduled, process_buf);
+ }
+#endif
+
if (ERTS_USE_MODIFIED_TIMING()) {
context_reds = ERTS_MODIFIED_TIMING_CONTEXT_REDS;
input_reductions = ERTS_MODIFIED_TIMING_INPUT_REDS;
@@ -7252,6 +7266,10 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->seq_trace_lastcnt = 0;
p->seq_trace_clock = 0;
SEQ_TRACE_TOKEN(p) = NIL;
+#ifdef USE_VM_PROBES
+ DT_UTAG(p) = NIL;
+ DT_UTAG_FLAGS(p) = 0;
+#endif
p->parent = parent->id == ERTS_INVALID_PID ? NIL : parent->id;
#ifdef HYBRID
@@ -7384,6 +7402,16 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
VERBOSE(DEBUG_PROCESSES, ("Created a new process: %T\n",p->id));
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(process_spawn)) {
+ DTRACE_CHARBUF(process_name, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(mfa, DTRACE_TERM_BUF_SIZE);
+
+ dtrace_fun_decode(p, mod, func, arity, process_name, mfa);
+ DTRACE2(process_spawn, process_name, mfa);
+ }
+#endif
+
error:
erts_smp_proc_unlock(parent, ERTS_PROC_LOCKS_ALL_MINOR);
@@ -7835,7 +7863,11 @@ static ERTS_INLINE void
send_exit_message(Process *to, ErtsProcLocks *to_locksp,
Eterm exit_term, Uint term_size, Eterm token)
{
- if (token == NIL) {
+ if (token == NIL
+#ifdef USE_VM_PROBES
+ || token == am_have_dt_utag
+#endif
+ ) {
Eterm* hp;
Eterm mess;
ErlHeapFragment* bp;
@@ -7843,7 +7875,11 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp,
hp = erts_alloc_message_heap(term_size, &bp, &ohp, to, to_locksp);
mess = copy_struct(exit_term, term_size, &hp, ohp);
- erts_queue_message(to, to_locksp, bp, mess, NIL);
+ erts_queue_message(to, to_locksp, bp, mess, NIL
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
} else {
ErlHeapFragment* bp;
Eterm* hp;
@@ -7859,7 +7895,11 @@ send_exit_message(Process *to, ErtsProcLocks *to_locksp,
/* the trace token must in this case be updated by the caller */
seq_trace_output(token, mess, SEQ_TRACE_SEND, to->id, NULL);
temp_token = copy_struct(token, sz_token, &hp, &bp->off_heap);
- erts_queue_message(to, to_locksp, bp, mess, temp_token);
+ erts_queue_message(to, to_locksp, bp, mess, temp_token
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
}
}
@@ -7952,9 +7992,26 @@ send_exit_signal(Process *c_p, /* current process if and only
ASSERT(reason != THE_NON_VALUE);
+#ifdef USE_VM_PROBES
+ if(DTRACE_ENABLED(process_exit_signal) && is_pid(from)) {
+ DTRACE_CHARBUF(sender_str, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(receiver_str, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(reason_buf, DTRACE_TERM_BUF_SIZE);
+
+ dtrace_pid_str(from, sender_str);
+ dtrace_proc_str(rp, receiver_str);
+ erts_snprintf(reason_buf, sizeof(reason_buf) - 1, "%T", reason);
+ DTRACE3(process_exit_signal, sender_str, receiver_str, reason_buf);
+ }
+#endif
+
if (ERTS_PROC_IS_TRAPPING_EXITS(rp)
&& (reason != am_kill || (flags & ERTS_XSIG_FLG_IGN_KILL))) {
- if (is_not_nil(token) && token_update)
+ if (is_not_nil(token)
+#ifdef USE_VM_PROBES
+ && token != am_have_dt_utag
+#endif
+ && token_update)
seq_trace_update_send(token_update);
if (is_value(exit_tuple))
send_exit_message(rp, rp_locks, exit_tuple, exit_tuple_sz, token);
@@ -8378,7 +8435,18 @@ erts_do_exit_process(Process* p, Eterm reason)
p->arity = 0; /* No live registers */
p->fvalue = reason;
-
+
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(process_exit)) {
+ DTRACE_CHARBUF(process_buf, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(reason_buf, DTRACE_TERM_BUF_SIZE);
+
+ dtrace_proc_str(p, process_buf);
+ erts_snprintf(reason_buf, DTRACE_TERM_BUF_SIZE - 1, "%T", reason);
+ DTRACE2(process_exit, process_buf, reason_buf);
+ }
+#endif
+
#ifdef ERTS_SMP
ERTS_SMP_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
/* By locking all locks (main lock is already locked) when going
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index d671638ce8..cff0783bc4 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -683,6 +683,10 @@ struct process {
Uint seq_trace_lastcnt;
Eterm seq_trace_token; /* Sequential trace token (tuple size 5 see below) */
+#ifdef USE_VM_PROBES
+ Eterm dt_utag; /* Place to store the dynamc trace user tag */
+ Uint dt_utag_flags; /* flag field for the dt_utag */
+#endif
BeamInstr initial[3]; /* Initial module(0), function(1), arity(2), often used instead
of pointer to funcinfo instruction, hence the BeamInstr datatype */
BeamInstr* current; /* Current Erlang function, part of the funcinfo:
@@ -998,6 +1002,14 @@ extern struct erts_system_profile_flags_t erts_system_profile_flags;
#define SEQ_TRACE_PRINT (1 << 2)
#define SEQ_TRACE_TIMESTAMP (1 << 3)
+#ifdef USE_VM_PROBES
+#define DT_UTAG_PERMANENT (1 << 0)
+#define DT_UTAG_SPREADING (1 << 1)
+#define DT_UTAG(P) ((P)->dt_utag)
+#define DT_UTAG_FLAGS(P) ((P)->dt_utag_flags)
+#endif
+
+
#ifdef ERTS_SMP
/* Status flags ... */
#define ERTS_PROC_SFLG_PENDADD2SCHEDQ (((Uint32) 1) << 0) /* Pending
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index b1d1e1d9b0..4261cd03be 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -125,8 +125,13 @@ do { \
enqueue_sys_msg_unlocked(SYS_MSG_TYPE_TRACE, (FPID), (TPID), (MSG), (BP)); \
} while(0)
#else
+#ifdef USE_VM_PROBES
#define ERTS_ENQ_TRACE_MSG(FPID, TPROC, MSG, BP) \
- erts_queue_message((TPROC), NULL, (BP), (MSG), NIL)
+ erts_queue_message((TPROC), NULL, (BP), (MSG), NIL, NIL)
+#else
+#define ERTS_ENQ_TRACE_MSG(FPID, TPROC, MSG, BP) \
+ erts_queue_message((TPROC), NULL, (BP), (MSG), NIL)
+#endif
#endif
/*
@@ -583,7 +588,11 @@ profile_send(Eterm from, Eterm message) {
hp = erts_alloc_message_heap(sz, &bp, &off_heap, profile_p, 0);
msg = copy_struct(message, sz, &hp, &bp->off_heap);
- erts_queue_message(profile_p, NULL, bp, msg, NIL);
+ erts_queue_message(profile_p, NULL, bp, msg, NIL
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
}
}
@@ -994,9 +1003,13 @@ seq_trace_update_send(Process *p)
{
Eterm seq_tracer = erts_get_system_seq_tracer();
ASSERT((is_tuple(SEQ_TRACE_TOKEN(p)) || is_nil(SEQ_TRACE_TOKEN(p))));
- if ( (p->id == seq_tracer) || (SEQ_TRACE_TOKEN(p) == NIL))
+ if ( (p->id == seq_tracer) || (SEQ_TRACE_TOKEN(p) == NIL)
+#ifdef USE_VM_PROBES
+ || (SEQ_TRACE_TOKEN(p) == am_have_dt_utag)
+#endif
+ ) {
return 0;
-
+ }
SEQ_TRACE_TOKEN_SENDER(p) = p->id; /* Internal pid */
SEQ_TRACE_TOKEN_SERIAL(p) =
make_small(++(p -> seq_trace_clock));
@@ -1178,7 +1191,11 @@ seq_trace_output_generic(Eterm token, Eterm msg, Uint type,
enqueue_sys_msg_unlocked(SYS_MSG_TYPE_SEQTRACE, NIL, NIL, mess, bp);
erts_smp_mtx_unlock(&smq_mtx);
#else
- erts_queue_message(tracer, NULL, bp, mess, NIL); /* trace_token must be NIL here */
+ erts_queue_message(tracer, NULL, bp, mess, NIL
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ ); /* trace_token must be NIL here */
#endif
}
}
@@ -2469,7 +2486,11 @@ monitor_long_gc(Process *p, Uint time) {
#ifdef ERTS_SMP
enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->id, NIL, msg, bp);
#else
- erts_queue_message(monitor_p, NULL, bp, msg, NIL);
+ erts_queue_message(monitor_p, NULL, bp, msg, NIL
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
#endif
}
@@ -2541,7 +2562,11 @@ monitor_large_heap(Process *p) {
#ifdef ERTS_SMP
enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->id, NIL, msg, bp);
#else
- erts_queue_message(monitor_p, NULL, bp, msg, NIL);
+ erts_queue_message(monitor_p, NULL, bp, msg, NIL
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
#endif
}
@@ -2571,7 +2596,11 @@ monitor_generic(Process *p, Eterm type, Eterm spec) {
#ifdef ERTS_SMP
enqueue_sys_msg(SYS_MSG_TYPE_SYSMON, p->id, NIL, msg, bp);
#else
- erts_queue_message(monitor_p, NULL, bp, msg, NIL);
+ erts_queue_message(monitor_p, NULL, bp, msg, NIL
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
#endif
}
@@ -3357,7 +3386,11 @@ sys_msg_dispatcher_func(void *unused)
}
else {
queue_proc_msg:
- erts_queue_message(proc,&proc_locks,smqp->bp,smqp->msg,NIL);
+ erts_queue_message(proc,&proc_locks,smqp->bp,smqp->msg,NIL
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
#ifdef DEBUG_PRINTOUTS
erts_fprintf(stderr, "delivered\n");
#endif
diff --git a/erts/emulator/beam/erlang_dtrace.d b/erts/emulator/beam/erlang_dtrace.d
new file mode 100644
index 0000000000..587e51cb67
--- /dev/null
+++ b/erts/emulator/beam/erlang_dtrace.d
@@ -0,0 +1,726 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Dustin Sallings, Michal Ptaszek, Scott Lystig Fritchie 2011.
+ * All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * A note on probe naming: if "__" appears in a provider probe
+ * definition, then two things happen during compilation:
+ *
+ * 1. The "__" will turn into a hypen, "-", for the probe name.
+ * 2. The "__" will turn into a single underscore, "_", for the
+ * macro names and function definitions that the compiler and
+ * C developers will see.
+ *
+ * We'll try to use the following naming convention. We're a bit
+ * limited because, as a USDT probe, we can only specify the 4th part
+ * of the probe name, e.g. erlang*:::mumble. The 2nd part of the
+ * probe name is always going to be "beam" or "beam.smp", and the 3rd
+ * part of the probe name will always be the name of the function
+ * that's calling the probe.
+ *
+ * So, all probes will be have names defined in this file using the
+ * convention category__name or category__sub_category__name. This
+ * will translate to probe names of category-name or
+ * category-sub_category-name.
+ *
+ * Each of "category", "sub_category", and "name" may have underscores
+ * but may not have hyphens.
+ */
+
+provider erlang {
+ /**
+ * Fired when a message is sent from one local process to another.
+ *
+ * NOTE: The 'size' parameter is in machine-dependent words and
+ * that the actual size of any binary terms in the message
+ * are not included.
+ *
+ * @param sender the PID (string form) of the sender
+ * @param receiver the PID (string form) of the receiver
+ * @param size the size of the message being delivered (words)
+ * @param token_label for the sender's sequential trace token
+ * @param token_previous count for the sender's sequential trace token
+ * @param token_current count for the sender's sequential trace token
+ */
+ probe message__send(char *sender, char *receiver, uint32_t size,
+ int token_label, int token_previous, int token_current);
+
+ /**
+ * Fired when a message is sent from a local process to a remote process.
+ *
+ * NOTE: The 'size' parameter is in machine-dependent words and
+ * that the actual size of any binary terms in the message
+ * are not included.
+ *
+ * @param sender the PID (string form) of the sender
+ * @param node_name the Erlang node name (string form) of the receiver
+ * @param receiver the PID/name (string form) of the receiver
+ * @param size the size of the message being delivered (words)
+ * @param token_label for the sender's sequential trace token
+ * @param token_previous count for the sender's sequential trace token
+ * @param token_current count for the sender's sequential trace token
+ */
+ probe message__send__remote(char *sender, char *node_name, char *receiver,
+ uint32_t size,
+ int token_label, int token_previous, int token_current);
+
+ /**
+ * Fired when a message is queued to a local process. This probe
+ * will not fire if the sender's pid == receiver's pid.
+ *
+ * NOTE: The 'size' parameter is in machine-dependent words and
+ * that the actual size of any binary terms in the message
+ * are not included.
+ *
+ * NOTE: In cases of messages in external format (i.e. from another
+ * Erlang node), we probably don't know the message size
+ * without performing substantial extra computation. To
+ * avoid the extra CPU overhead, the message size may be
+ * reported as -1, which can appear to a D script as 4294967295.
+ *
+ * @param receiver the PID (string form) of the receiver
+ * @param size the size of the message being delivered (words)
+ * @param queue_len length of the queue of the receiving process
+ * @param token_label for the sender's sequential trace token
+ * @param token_previous count for the sender's sequential trace token
+ * @param token_current count for the sender's sequential trace token
+ */
+ probe message__queued(char *receiver, uint32_t size, uint32_t queue_len,
+ int token_label, int token_previous, int token_current);
+
+ /**
+ * Fired when a message is 'receive'd by a local process and removed
+ * from its mailbox.
+ *
+ * NOTE: The 'size' parameter is in machine-dependent words and
+ * that the actual size of any binary terms in the message
+ * are not included.
+ *
+ * NOTE: In cases of messages in external format (i.e. from another
+ * Erlang node), we probably don't know the message size
+ * without performing substantial extra computation. To
+ * avoid the extra CPU overhead, the message size may be
+ * reported as -1, which can appear to a D script as 4294967295.
+ *
+ * @param receiver the PID (string form) of the receiver
+ * @param size the size of the message being delivered (words)
+ * @param queue_len length of the queue of the receiving process
+ * @param token_label for the sender's sequential trace token
+ * @param token_previous count for the sender's sequential trace token
+ * @param token_current count for the sender's sequential trace token
+ */
+ probe message__receive(char *receiver, uint32_t size, uint32_t queue_len,
+ int token_label, int token_previous, int token_current);
+
+ /**
+ * Fired when an Eterm structure is being copied.
+ *
+ * NOTE: Due to the placement of this probe, the process ID of
+ * owner of the Eterm is not available.
+ *
+ * @param size the size of the structure
+ */
+ probe copy__struct(uint32_t size);
+
+ /**
+ * Fired when an Eterm is being copied onto a process.
+ *
+ * @param proc the PID (string form) of the recipient process
+ * @param size the size of the structure
+ */
+ probe copy__object(char *proc, uint32_t size);
+
+ /* PID, Module, Function, Arity */
+
+ /**
+ * Fired whenever a user function is being called locally.
+ *
+ * @param p the PID (string form) of the process
+ * @param mfa the m:f/a of the function
+ * @param depth the stack depth
+ */
+ probe local__function__entry(char *p, char *mfa, int depth);
+
+ /**
+ * Fired whenever a user function is called externally
+ * (through an export entry).
+ *
+ * @param p the PID (string form) of the process
+ * @param mfa the m:f/a of the function
+ * @param depth the stack depth
+ */
+ probe global__function__entry(char *p, char *mfa, int depth);
+
+ /**
+ * Fired whenever a user function returns.
+ *
+ * @param p the PID (string form) of the process
+ * @param mfa the m:f/a of the function
+ * @param depth the stack depth
+ */
+ probe function__return(char *p, char *mfa, int depth);
+
+ /**
+ * Fired whenever a Built In Function is called.
+ *
+ * @param p the PID (string form) of the process
+ * @param mfa the m:f/a of the function
+ */
+ probe bif__entry(char *p, char *mfa);
+
+ /**
+ * Fired whenever a Built In Function returns.
+ *
+ * @param p the PID (string form) of the process
+ * @param mfa the m:f/a of the function
+ */
+ probe bif__return(char *p, char *mfa);
+
+ /**
+ * Fired whenever a Native Function is called.
+ *
+ * @param p the PID (string form) of the process
+ * @param mfa the m:f/a of the function
+ */
+ probe nif__entry(char *p, char *mfa);
+
+ /**
+ * Fired whenever a Native Function returns.
+ *
+ * @param p the PID (string form) of the process
+ * @param mfa the m:f/a of the function
+ */
+ probe nif__return(char *p, char *mfa);
+
+ /**
+ * Fired when a major GC is starting.
+ *
+ * @param p the PID (string form) of the exiting process
+ * @param need the number of words needed on the heap
+ */
+ probe gc_major__start(char *p, int need);
+
+ /**
+ * Fired when a minor GC is starting.
+ *
+ * @param p the PID (string form) of the exiting process
+ * @param need the number of words needed on the heap
+ */
+ probe gc_minor__start(char *p, int need);
+
+ /**
+ * Fired when a major GC is starting.
+ *
+ * @param p the PID (string form) of the exiting process
+ * @param reclaimed the amount of space reclaimed
+ */
+ probe gc_major__end(char *p, int reclaimed);
+
+ /**
+ * Fired when a minor GC is starting.
+ *
+ * @param p the PID (string form) of the exiting process
+ * @param reclaimed the amount of space reclaimed
+ */
+ probe gc_minor__end(char *p, int reclaimed);
+
+ /**
+ * Fired when a process is spawned.
+ *
+ * @param p the PID (string form) of the new process.
+ * @param mfa the m:f/a of the function
+ */
+ probe process__spawn(char *p, char *mfa);
+
+ /**
+ * Fired when a process is exiting.
+ *
+ * @param p the PID (string form) of the exiting process
+ * @param reason the reason for the exit (may be truncated)
+ */
+ probe process__exit(char *p, char *reason);
+
+ /**
+ * Fired when exit signal is delivered to a local process.
+ *
+ * @param sender the PID (string form) of the exiting process
+ * @param receiver the PID (string form) of the process receiving EXIT signal
+ * @param reason the reason for the exit (may be truncated)
+ */
+ probe process__exit_signal(char *sender, char *receiver, char *reason);
+
+ /**
+ * Fired when exit signal is delivered to a remote process.
+ *
+ * @param sender the PID (string form) of the exiting process
+ * @param node_name the Erlang node name (string form) of the receiver
+ * @param receiver the PID (string form) of the process receiving EXIT signal
+ * @param reason the reason for the exit (may be truncated)
+ * @param token_label for the sender's sequential trace token
+ * @param token_previous count for the sender's sequential trace token
+ * @param token_current count for the sender's sequential trace token
+ */
+ probe process__exit_signal__remote(char *sender, char *node_name,
+ char *receiver, char *reason,
+ int token_label, int token_previous, int token_current);
+
+ /**
+ * Fired when a process is scheduled.
+ *
+ * @param p the PID (string form) of the newly scheduled process
+ * @param mfa the m:f/a of the function it should run next
+ */
+ probe process__scheduled(char *p, char *mfa);
+
+ /**
+ * Fired when a process is unscheduled.
+ *
+ * @param p the PID (string form) of the process that has been
+ * unscheduled.
+ */
+ probe process__unscheduled(char *p);
+
+ /**
+ * Fired when a process goes into hibernation.
+ *
+ * @param p the PID (string form) of the process entering hibernation
+ * @param mfa the m:f/a of the location to resume
+ */
+ probe process__hibernate(char *p, char *mfa);
+
+ /**
+ * Fired when a process is unblocked after a port has been unblocked.
+ *
+ * @param p the PID (string form) of the process that has been
+ * unscheduled.
+ * @param port the port that is no longer busy (i.e., is now unblocked)
+ */
+ probe process__port_unblocked(char *p, char *port);
+
+ /**
+ * Fired when process' heap is growing.
+ *
+ * @param p the PID (string form)
+ * @param old_size the size of the old heap
+ * @param new_size the size of the new heap
+ */
+ probe process__heap_grow(char *p, int old_size, int new_size);
+
+ /**
+ * Fired when process' heap is shrinking.
+ *
+ * @param p the PID (string form)
+ * @param old_size the size of the old heap
+ * @param new_size the size of the new heap
+ */
+ probe process__heap_shrink(char *p, int old_size, int new_size);
+
+ /* network distribution */
+
+ /**
+ * Fired when network distribution event monitor events are triggered.
+ *
+ * @param node the name of the reporting node
+ * @param what the type of event, e.g., nodeup, nodedown
+ * @param monitored_node the name of the monitored node
+ * @param type the type of node, e.g., visible, hidden
+ * @param reason the reason term, e.g., normal, connection_closed, term()
+ */
+ probe dist__monitor(char *node, char *what, char *monitored_node,
+ char *type, char *reason);
+
+ /**
+ * Fired when network distribution port is busy (i.e. blocked),
+ * usually due to the remote node not consuming distribution
+ * data quickly enough.
+ *
+ * @param node the name of the reporting node
+ * @param port the port ID of the busy port
+ * @param remote_node the name of the remote node.
+ * @param pid the PID (string form) of the local process that has
+ * become unschedulable until the port becomes unblocked.
+ */
+ probe dist__port_busy(char *node, char *port, char *remote_node,
+ char *pid);
+
+ /**
+ * Fired when network distribution's driver's "output" callback is called
+ *
+ * @param node the name of the reporting node
+ * @param port the port ID of the busy port
+ * @param remote_node the name of the remote node.
+ * @param bytes the number of bytes written
+ */
+ probe dist__output(char *node, char *port, char *remote_node, int bytes);
+
+ /**
+ * Fired when network distribution's driver's "outputv" callback is called
+ *
+ * @param node the name of the reporting node
+ * @param port the port ID of the busy port
+ * @param remote_node the name of the remote node.
+ * @param bytes the number of bytes written
+ */
+ probe dist__outputv(char *node, char *port, char *remote_node, int bytes);
+
+ /**
+ * Fired when network distribution port is no longer busy (i.e. blocked).
+ *
+ * NOTE: This probe may fire multiple times after the same single
+ * dist-port_busy probe firing.
+ *
+ * @param node the name of the reporting node
+ * @param port the port ID of the busy port
+ * @param remote_node the name of the remote node.
+ */
+ probe dist__port_not_busy(char *node, char *port, char *remote_node);
+
+ /* ports */
+
+ /**
+ * Fired when new port is opened.
+ *
+ * @param process the PID (string form)
+ * @param port_name the string used when the port was opened
+ * @param port the Port (string form) of the new port
+ */
+ probe port__open(char *process, char *port_name, char *port);
+
+ /**
+ * Fired when port_command is issued.
+ *
+ * @param process the PID (string form)
+ * @param port the Port (string form)
+ * @param port_name the string used when the port was opened
+ * @param command_type type of the issued command, one of: "close", "command" or "connect"
+ */
+ probe port__command(char *process, char *port, char *port_name, char *command_type);
+
+ /**
+ * Fired when port_control is issued.
+ *
+ * @param process the PID (string form)
+ * @param port the Port (string form)
+ * @param port_name the string used when the port was opened
+ * @param command_no command number that has been issued to the port
+ */
+ probe port__control(char *process, char *port, char *port_name, int command_no);
+
+ /**
+ * Fired when port is closed via port_close/1 (reason = 'normal')
+ * or is sent an exit signal.
+ *
+ * @param process the PID (string form)
+ * @param port the Port (string form)
+ * @param port_name the string used when the port was opened
+ * @param reason Erlang term representing the exit signal, e.g. 'normal'
+ */
+ probe port__exit(char *process, char *port, char *port_name,
+ char *new_process);
+
+ /**
+ * Fired when port_connect is issued.
+ *
+ * @param process the PID (string form) of the current port owner
+ * @param port the Port (string form)
+ * @param port_name the string used when the port was opened
+ * @param new_process the PID (string form) of the new port owner
+ */
+ probe port__connect(char *process, char *port, char *port_name,
+ char *new_process);
+
+ /**
+ * Fired when a port is busy (i.e. blocked)
+ *
+ * @param port the port ID of the busy port
+ */
+ probe port__busy(char *port);
+
+ /**
+ * Fired when a port is no longer busy (i.e. no longer blocked)
+ *
+ * @param port the port ID of the not busy port
+ */
+ probe port__not_busy(char *port);
+
+ /* drivers */
+
+ /**
+ * Fired when drivers's "init" callback is called.
+ *
+ * @param name the name of the driver
+ * @param major the major version number
+ * @param minor the minor version number
+ * @param flags the flags argument
+ */
+ probe driver__init(char *name, int major, int minor, int flags);
+
+ /**
+ * Fired when drivers's "start" callback is called.
+ *
+ * @param process the PID (string form) of the calling process
+ * @param name the name of the driver
+ * @param port the Port (string form) of the driver's port
+ */
+ probe driver__start(char *process, char *name, char *port);
+
+ /**
+ * Fired when drivers's "stop" callback is called.
+ *
+ * @param process the PID (string form) of the calling process
+ * @param name the name of the driver
+ * @param port the Port (string form) of the driver's port
+ */
+ probe driver__stop(char *process, char *name, char *port);
+
+ /**
+ * Fired when drivers's "finish" callback is called.
+ *
+ * @param name the name of the driver
+ */
+ probe driver__finish(char *name);
+
+ /**
+ * Fired when drivers's "flush" callback is called.
+ *
+ * @param process the PID (string form)
+ * @param port the Port (string form)
+ * @param port_name the string used when the port was opened
+ */
+ probe driver__flush(char *process, char *port, char *port_name);
+
+ /**
+ * Fired when driver's "output" callback is called
+ *
+ * @param process the PID (string form)
+ * @param port the Port (string form)
+ * @param port_name the string used when the port was opened
+ * @param bytes the number of bytes written
+ */
+ probe driver__output(char *node, char *port, char *port_name, int bytes);
+
+ /**
+ * Fired when driver's "outputv" callback is called
+ *
+ * @param process the PID (string form)
+ * @param port the Port (string form)
+ * @param port_name the string used when the port was opened
+ * @param bytes the number of bytes written
+ */
+ probe driver__outputv(char *node, char *port, char *port_name, int bytes);
+
+ /**
+ * Fired when driver's "control" callback is called
+ *
+ * @param process the PID (string form)
+ * @param port the Port (string form)
+ * @param port_name the string used when the port was opened
+ * @param command the command #
+ * @param bytes the number of bytes written
+ */
+ probe driver__control(char *node, char *port, char *port_name,
+ int command, int bytes);
+
+ /**
+ * Fired when driver's "call" callback is called
+ *
+ * @param process the PID (string form)
+ * @param port the Port (string form)
+ * @param port_name the string used when the port was opened
+ * @param command the command #
+ * @param bytes the number of bytes written
+ */
+ probe driver__call(char *node, char *port, char *port_name,
+ int command, int bytes);
+
+ /**
+ * Fired when driver's "event" callback is called
+ *
+ * @param process the PID (string form)
+ * @param port the Port (string form)
+ * @param port_name the string used when the port was opened
+ */
+ probe driver__event(char *node, char *port, char *port_name);
+
+ /**
+ * Fired when driver's "ready_input" callback is called
+ *
+ * @param process the PID (string form)
+ * @param port the Port (string form)
+ * @param port_name the string used when the port was opened
+ */
+ probe driver__ready_input(char *node, char *port, char *port_name);
+
+ /**
+ * Fired when driver's "read_output" callback is called
+ *
+ * @param process the PID (string form)
+ * @param port the Port (string form)
+ * @param port_name the string used when the port was opened
+ */
+ probe driver__ready_output(char *node, char *port, char *port_name);
+
+ /**
+ * Fired when driver's "timeout" callback is called
+ *
+ * @param process the PID (string form)
+ * @param port the Port (string form)
+ * @param port_name the string used when the port was opened
+ */
+ probe driver__timeout(char *node, char *port, char *port_name);
+
+ /**
+ * Fired when drivers's "ready_async" callback is called.
+ *
+ * @param process the PID (string form)
+ * @param port the Port (string form)
+ * @param port_name the string used when the port was opened
+ */
+ probe driver__ready_async(char *process, char *port, char *port_name);
+
+ /**
+ * Fired when driver's "process_exit" callback is called
+ *
+ * @param process the PID (string form)
+ * @param port the Port (string form)
+ * @param port_name the string used when the port was opened
+ */
+ probe driver__process_exit(char *node, char *port, char *port_name);
+
+ /**
+ * Fired when driver's "stop_select" callback is called
+ *
+ * @param name the name of the driver
+ */
+ probe driver__stop_select(char *name);
+
+
+ /* Async driver pool */
+
+ /**
+ * Show the post-add length of the async driver thread pool member's queue.
+ *
+ * NOTE: The port name is not available: additional lock(s) must
+ * be acquired in order to get the port name safely in an SMP
+ * environment. The same is true for the aio__pool_get probe.
+ *
+ * @param port the Port (string form)
+ * @param new queue length
+ */
+ probe aio_pool__add(char *, int);
+
+ /**
+ * Show the post-get length of the async driver thread pool member's queue.
+ *
+ * @param port the Port (string form)
+ * @param new queue length
+ */
+ probe aio_pool__get(char *, int);
+
+ /* Probes for efile_drv.c */
+
+ /**
+ * Entry into the efile_drv.c file I/O driver
+ *
+ * For a list of command numbers used by this driver, see the section
+ * "Guide to probe arguments" in ../../../README.md. That section
+ * also contains explanation of the various integer and string
+ * arguments that may be present when any particular probe fires.
+ *
+ * NOTE: Not all Linux platforms (using SystemTap) can support
+ * arguments beyond arg9.
+ *
+ *
+ * TODO: Adding the port string, args[10], is a pain. Making that
+ * port string available to all the other efile_drv.c probes
+ * will be more pain. Is the pain worth it? If yes, then
+ * add them everywhere else and grit our teeth. If no, then
+ * rip it out.
+ *
+ * @param thread-id number of the scheduler Pthread arg0
+ * @param tag number: {thread-id, tag} uniquely names a driver operation
+ * @param user-tag string arg2
+ * @param command number arg3
+ * @param string argument 1 arg4
+ * @param string argument 2 arg5
+ * @param integer argument 1 arg6
+ * @param integer argument 2 arg7
+ * @param integer argument 3 arg8
+ * @param integer argument 4 arg9
+ * @param port the port ID of the busy port args[10]
+ */
+ probe efile_drv__entry(int, int, char *, int, char *, char *,
+ int64_t, int64_t, int64_t, int64_t, char *);
+
+ /**
+ * Entry into the driver's internal work function. Computation here
+ * is performed by a async worker pool Pthread.
+ *
+ * @param thread-id number
+ * @param tag number
+ * @param command number
+ */
+ probe efile_drv__int_entry(int, int, int);
+
+ /**
+ * Return from the driver's internal work function.
+ *
+ * @param thread-id number
+ * @param tag number
+ * @param command number
+ */
+ probe efile_drv__int_return(int, int, int);
+
+ /**
+ * Return from the efile_drv.c file I/O driver
+ *
+ * @param thread-id number arg0
+ * @param tag number arg1
+ * @param user-tag string arg2
+ * @param command number arg3
+ * @param Success? 1 is success, 0 is failure arg4
+ * @param If failure, the errno of the error. arg5
+ */
+ probe efile_drv__return(int, int, char *, int, int, int);
+
+/*
+ * NOTE:
+ * For formatting int64_t arguments within a D script, see:
+ *
+ * http://mail.opensolaris.org/pipermail/dtrace-discuss/2006-November/002830.html
+ * Summary:
+ * "1) you don't need the 'l' printf() modifiers with DTrace ever"
+ */
+
+/*
+ * NOTE: For file_drv_return + SMP + R14B03 (and perhaps other
+ * releases), the sched-thread-id will be the same as the
+ * work-thread-id: erl_async.c's async_main() function
+ * will call the asynchronous invoke function and then
+ * immediately call the drivers ready_async function while
+ * inside the same I/O worker pool thread.
+ * For R14B03's source, see erl_async.c lines 302-317.
+ */
+};
+
+#pragma D attributes Evolving/Evolving/Common provider erlang provider
+#pragma D attributes Private/Private/Common provider erlang module
+#pragma D attributes Private/Private/Common provider erlang function
+#pragma D attributes Evolving/Evolving/Common provider erlang name
+#pragma D attributes Evolving/Evolving/Common provider erlang args
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 97d7f0e904..b000e2c5d4 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -1974,4 +1974,46 @@ erts_alloc_message_heap(Uint size,
# define UseTmpHeapNoproc(Size) /* Nothing */
# define UnUseTmpHeapNoproc(Size) /* Nothing */
#endif /* HEAP_ON_C_STACK */
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+#include "dtrace-wrapper.h"
+
+ERTS_GLB_INLINE void
+dtrace_pid_str(Eterm pid, char *process_buf)
+{
+ erts_snprintf(process_buf, DTRACE_TERM_BUF_SIZE, "<%lu.%lu.%lu>",
+ pid_channel_no(pid),
+ pid_number(pid),
+ pid_serial(pid));
+}
+
+ERTS_GLB_INLINE void
+dtrace_proc_str(Process *process, char *process_buf)
+{
+ dtrace_pid_str(process->id, process_buf);
+}
+
+ERTS_GLB_INLINE void
+dtrace_port_str(Port *port, char *port_buf)
+{
+ erts_snprintf(port_buf, DTRACE_TERM_BUF_SIZE, "#Port<%lu.%lu>",
+ port_channel_no(port->id),
+ port_number(port->id));
+}
+
+ERTS_GLB_INLINE void
+dtrace_fun_decode(Process *process,
+ Eterm module, Eterm function, int arity,
+ char *process_buf, char *mfa_buf)
+{
+ if (process_buf) {
+ dtrace_proc_str(process, process_buf);
+ }
+
+ erts_snprintf(mfa_buf, DTRACE_TERM_BUF_SIZE, "%T:%T/%d",
+ module, function, arity);
+}
+#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
#endif /* !__GLOBAL_H__ */
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index fb1514a147..8a2a43bebd 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -43,6 +43,7 @@
#include "erl_version.h"
#include "error.h"
#include "erl_async.h"
+#include "dtrace-wrapper.h"
extern ErlDrvEntry fd_driver_entry;
extern ErlDrvEntry vanilla_driver_entry;
@@ -180,6 +181,20 @@ typedef struct line_buf_context {
#define LINEBUF_INITIAL 100
+#ifdef USE_VM_PROBES
+#define DTRACE_FORMAT_COMMON_PID_AND_PORT(PID, PORT) \
+ DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE); \
+ DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); \
+ \
+ dtrace_pid_str((PID), process_str); \
+ dtrace_port_str((PORT), port_str);
+#define DTRACE_FORMAT_COMMON_PROC_AND_PORT(PID, PORT) \
+ DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE); \
+ DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); \
+ \
+ dtrace_proc_str((PID), process_str); \
+ dtrace_port_str((PORT), port_str);
+#endif
/* The 'number' field in a port now has two parts: the lowest bits
contain the index in the port table, and the higher bits are a counter
@@ -639,6 +654,12 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */
trace_sched_ports_where(port, am_in, am_start);
}
port->caller = pid;
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(driver_start)) {
+ DTRACE_FORMAT_COMMON_PID_AND_PORT(pid, port)
+ DTRACE3(driver_start, process_str, driver->name, port_str);
+ }
+#endif
fpe_was_unmasked = erts_block_fpe();
drv_data = (*driver->start)((ErlDrvPort)(port_ix),
name, opts);
@@ -1170,6 +1191,12 @@ int erts_write_to_port(Eterm caller_id, Port *p, Eterm list)
ev.size = size; /* total size */
ev.iov = ivp;
ev.binv = bvp;
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(driver_outputv)) {
+ DTRACE_FORMAT_COMMON_PID_AND_PORT(caller_id, p)
+ DTRACE4(driver_outputv, process_str, port_str, p->name, size);
+ }
+#endif
fpe_was_unmasked = erts_block_fpe();
(*drv->outputv)((ErlDrvData)p->drv_data, &ev);
erts_unblock_fpe(fpe_was_unmasked);
@@ -1189,8 +1216,21 @@ int erts_write_to_port(Eterm caller_id, Port *p, Eterm list)
buf = erts_alloc(ERTS_ALC_T_TMP, size+1);
r = io_list_to_buf(list, buf, size);
+#ifdef USE_VM_PROBES
+ if(DTRACE_ENABLED(port_command)) {
+ DTRACE_FORMAT_COMMON_PID_AND_PORT(caller_id, p)
+ DTRACE4(port_command, process_str, port_str, p->name, "command");
+ }
+#endif
+
if (r >= 0) {
size -= r;
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(driver_output)) {
+ DTRACE_FORMAT_COMMON_PID_AND_PORT(caller_id, p)
+ DTRACE4(driver_output, process_str, port_str, p->name, size);
+ }
+#endif
fpe_was_unmasked = erts_block_fpe();
(*drv->output)((ErlDrvData)p->drv_data, buf, size);
erts_unblock_fpe(fpe_was_unmasked);
@@ -1214,6 +1254,12 @@ int erts_write_to_port(Eterm caller_id, Port *p, Eterm list)
*/
buf = erts_alloc(ERTS_ALC_T_TMP, size+1);
r = io_list_to_buf(list, buf, size);
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(driver_output)) {
+ DTRACE_FORMAT_COMMON_PID_AND_PORT(caller_id, p)
+ DTRACE4(driver_output, process_str, port_str, p->name, size);
+ }
+#endif
fpe_was_unmasked = erts_block_fpe();
(*drv->output)((ErlDrvData)p->drv_data, buf, size);
erts_unblock_fpe(fpe_was_unmasked);
@@ -1529,7 +1575,11 @@ deliver_result(Eterm sender, Eterm pid, Eterm res)
hp = erts_alloc_message_heap(sz_res + 3, &bp, &ohp, rp, &rp_locks);
res = copy_struct(res, sz_res, &hp, ohp);
tuple = TUPLE2(hp, sender, res);
- erts_queue_message(rp, &rp_locks, bp, tuple, NIL);
+ erts_queue_message(rp, &rp_locks, bp, tuple, NIL
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
erts_smp_proc_unlock(rp, rp_locks);
erts_smp_proc_dec_refc(rp);
}
@@ -1618,7 +1668,11 @@ static void deliver_read_message(Port* prt, Eterm to,
tuple = TUPLE2(hp, prt->id, tuple);
hp += 3;
- erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined);
+ erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
erts_smp_proc_unlock(rp, rp_locks);
erts_smp_proc_dec_refc(rp);
}
@@ -1771,7 +1825,11 @@ deliver_vec_message(Port* prt, /* Port */
tuple = TUPLE2(hp, prt->id, tuple);
hp += 3;
- erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined);
+ erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
erts_smp_proc_unlock(rp, rp_locks);
erts_smp_proc_dec_refc(rp);
}
@@ -1810,6 +1868,12 @@ static void flush_port(Port *p)
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p));
if (p->drv_ptr->flush != NULL) {
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(driver_flush)) {
+ DTRACE_FORMAT_COMMON_PID_AND_PORT(p->connected, p)
+ DTRACE3(driver_flush, process_str, port_str, p->name);
+ }
+#endif
if (IS_TRACED_FL(p, F_TRACE_SCHED_PORTS)) {
trace_sched_ports_where(p, am_in, am_flush);
}
@@ -1865,6 +1929,12 @@ terminate_port(Port *prt)
drv = prt->drv_ptr;
if ((drv != NULL) && (drv->stop != NULL)) {
int fpe_was_unmasked = erts_block_fpe();
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(driver_stop)) {
+ DTRACE_FORMAT_COMMON_PID_AND_PORT(prt->connected, prt)
+ DTRACE3(driver_stop, process_str, drv->name, port_str);
+ }
+#endif
(*drv->stop)((ErlDrvData)prt->drv_data);
erts_unblock_fpe(fpe_was_unmasked);
#ifdef ERTS_SMP
@@ -2026,6 +2096,19 @@ erts_do_exit_port(Port *p, Eterm from, Eterm reason)
rreason = (reason == am_kill) ? am_killed : reason;
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(port_exit)) {
+ DTRACE_CHARBUF(from_str, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE);
+ DTRACE_CHARBUF(rreason_str, 64);
+
+ erts_snprintf(from_str, sizeof(from_str), "%T", from);
+ dtrace_port_str(p, port_str);
+ erts_snprintf(rreason_str, sizeof(rreason_str), "%T", rreason);
+ DTRACE4(port_exit, from_str, port_str, p->name, rreason_str);
+ }
+#endif
+
if ((p->status & (ERTS_PORT_SFLGS_DEAD
| ERTS_PORT_SFLG_EXITING
| ERTS_PORT_SFLG_IMMORTAL))
@@ -2126,6 +2209,13 @@ void erts_port_command(Process *proc,
if (tp[2] == am_close) {
erts_port_status_bor_set(port, ERTS_PORT_SFLG_SEND_CLOSED);
erts_do_exit_port(port, pid, am_normal);
+
+#ifdef USE_VM_PROBES
+ if(DTRACE_ENABLED(port_command)) {
+ DTRACE_FORMAT_COMMON_PROC_AND_PORT(proc, port)
+ DTRACE4(port_command, process_str, port_str, port->name, "close");
+ }
+#endif
goto done;
} else if (is_tuple_arity(tp[2], 2)) {
tp = tuple_val(tp[2]);
@@ -2133,6 +2223,12 @@ void erts_port_command(Process *proc,
if (erts_write_to_port(caller_id, port, tp[2]) == 0)
goto done;
} else if ((tp[1] == am_connect) && is_internal_pid(tp[2])) {
+#ifdef USE_VM_PROBES
+ if(DTRACE_ENABLED(port_command)) {
+ DTRACE_FORMAT_COMMON_PROC_AND_PORT(proc, port)
+ DTRACE4(port_command, process_str, port_str, port->name, "connect");
+ }
+#endif
port->connected = tp[2];
deliver_result(port->id, pid, am_connected);
goto done;
@@ -2235,6 +2331,15 @@ erts_port_control(Process* p, Port* prt, Uint command, Eterm iolist)
erts_smp_proc_unlock(p, ERTS_PROC_LOCK_MAIN);
ERTS_SMP_CHK_NO_PROC_LOCKS;
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(port_control) || DTRACE_ENABLED(driver_control)) {
+ DTRACE_FORMAT_COMMON_PROC_AND_PORT(p, prt);
+ DTRACE4(port_control, process_str, port_str, prt->name, command);
+ DTRACE5(driver_control, process_str, port_str, prt->name,
+ command, to_len);
+ }
+#endif
+
/*
* Call the port's control routine.
*/
@@ -2375,6 +2480,10 @@ print_port_info(int to, void *arg, int i)
void
set_busy_port(ErlDrvPort port_num, int on)
{
+#ifdef USE_VM_PROBES
+ DTRACE_CHARBUF(port_str, 16);
+#endif
+
ERTS_SMP_CHK_NO_PROC_LOCKS;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(&erts_port[port_num]));
@@ -2382,12 +2491,26 @@ set_busy_port(ErlDrvPort port_num, int on)
if (on) {
erts_port_status_bor_set(&erts_port[port_num],
ERTS_PORT_SFLG_PORT_BUSY);
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(port_busy)) {
+ erts_snprintf(port_str, sizeof(port_str),
+ "%T", erts_port[port_num].id);
+ DTRACE1(port_busy, port_str);
+ }
+#endif
} else {
ErtsProcList* plp = erts_port[port_num].suspended;
erts_port_status_band_set(&erts_port[port_num],
~ERTS_PORT_SFLG_PORT_BUSY);
erts_port[port_num].suspended = NULL;
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(port_not_busy)) {
+ erts_snprintf(port_str, sizeof(port_str),
+ "%T", erts_port[port_num].id);
+ DTRACE1(port_not_busy, port_str);
+ }
+#endif
if (erts_port[port_num].dist_entry) {
/*
* Processes suspended on distribution ports are
@@ -2405,6 +2528,28 @@ set_busy_port(ErlDrvPort port_num, int on)
*/
if (plp) {
+#ifdef USE_VM_PROBES
+ /*
+ * Hrm, for blocked dist ports, plp always seems to be NULL.
+ * That's not so fun.
+ * Well, another way to get the same info is using a D
+ * script to correlate an earlier process-port_blocked+pid
+ * event with a later process-scheduled event. That's
+ * subject to the multi-CPU races with how events are
+ * handled, but hey, that way works most of the time.
+ */
+ if (DTRACE_ENABLED(process_port_unblocked)) {
+ DTRACE_CHARBUF(pid_str, 16);
+ ErtsProcList* plp2 = plp;
+
+ erts_snprintf(port_str, sizeof(port_str),
+ "%T", erts_port[port_num]);
+ while (plp2 != NULL) {
+ erts_snprintf(pid_str, sizeof(pid_str), "%T", plp2->pid);
+ DTRACE2(process_port_unblocked, pid_str, port_str);
+ }
+ }
+#endif
/* First proc should be resumed last */
if (plp->next) {
erts_resume_processes(plp->next);
@@ -2451,6 +2596,14 @@ void erts_raw_port_command(Port* p, byte* buf, Uint len)
p->drv_ptr->name ? p->drv_ptr->name : "unknown");
p->caller = NIL;
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(driver_output)) {
+ DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE);
+
+ dtrace_port_str(p, port_str);
+ DTRACE4(driver_output, "-raw-", port_str, p->name, len);
+ }
+#endif
fpe_was_unmasked = erts_block_fpe();
(*p->drv_ptr->output)((ErlDrvData)p->drv_data, (char*) buf, (int) len);
erts_unblock_fpe(fpe_was_unmasked);
@@ -2466,6 +2619,12 @@ int async_ready(Port *p, void* data)
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p));
ASSERT(!(p->status & ERTS_PORT_SFLGS_DEAD));
if (p->drv_ptr->ready_async != NULL) {
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(driver_ready_async)) {
+ DTRACE_FORMAT_COMMON_PID_AND_PORT(p->connected, p)
+ DTRACE3(driver_ready_async, process_str, port_str, p->name);
+ }
+#endif
(*p->drv_ptr->ready_async)((ErlDrvData)p->drv_data, data);
need_free = 0;
#ifdef ERTS_SMP
@@ -2660,7 +2819,11 @@ void driver_report_exit(int ix, int status)
hp += 3;
tuple = TUPLE2(hp, prt->id, tuple);
- erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined);
+ erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
erts_smp_proc_unlock(rp, rp_locks);
erts_smp_proc_dec_refc(rp);
@@ -3210,7 +3373,11 @@ driver_deliver_term(ErlDrvPort port,
HRelease(rp, hp_end, hp);
}
/* send message */
- erts_queue_message(rp, &rp_locks, bp, mess, am_undefined);
+ erts_queue_message(rp, &rp_locks, bp, mess, am_undefined
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
}
else {
if (b2t.ix > b2t.used)
@@ -4441,6 +4608,12 @@ void erts_fire_port_monitor(Port *prt, Eterm ref)
ASSERT(callback != NULL);
ref_to_driver_monitor(ref,&drv_monitor);
DRV_MONITOR_UNLOCK_PDL(prt);
+#ifdef USE_VM_PROBES
+ if (DTRACE_ENABLED(driver_process_exit)) {
+ DTRACE_FORMAT_COMMON_PID_AND_PORT(prt->connected, prt)
+ DTRACE3(driver_process_exit, process_str, port_str, prt->name);
+ }
+#endif
fpe_was_unmasked = erts_block_fpe();
(*callback)((ErlDrvData) (prt->drv_data), &drv_monitor);
erts_unblock_fpe(fpe_was_unmasked);
@@ -4884,6 +5057,8 @@ init_driver(erts_driver_t *drv, ErlDrvEntry *de, DE_Handle *handle)
else {
int res;
int fpe_was_unmasked = erts_block_fpe();
+ DTRACE4(driver_init, drv->name, drv->version.major, drv->version.minor,
+ drv->flags);
res = (*de->init)();
erts_unblock_fpe(fpe_was_unmasked);
return res;
diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab
index fc53a88a3a..b2fc571032 100644
--- a/erts/emulator/beam/ops.tab
+++ b/erts/emulator/beam/ops.tab
@@ -883,6 +883,95 @@ call_ext_last u==3 u$func:erlang:hibernate/3 D => i_hibernate
call_ext_only u==3 u$func:erlang:hibernate/3 => i_hibernate
#
+# If VM probes are not enabled, we want to short-circult calls to
+# the dt tag BIFs to make them as cheap as possible.
+#
+
+%unless USE_VM_PROBES
+
+call_ext Arity u$func:erlang:dt_get_tag/0 => \
+ move a=am_undefined r
+call_ext_last Arity u$func:erlang:dt_get_tag/0 D => \
+ move a=am_undefined r | deallocate D | return
+call_ext_only Arity u$func:erlang:dt_get_tag/0 => \
+ move a=am_undefined r | return
+
+move Any r | call_ext Arity u$func:erlang:dt_put_tag/1 => \
+ move a=am_undefined r
+move Any r | call_ext_last Arity u$func:erlang:dt_put_tag/1 D => \
+ move a=am_undefined r | deallocate D | return
+move Any r | call_ext_only Arity u$func:erlang:dt_put_tag/1 => \
+ move a=am_undefined r | return
+call_ext Arity u$func:erlang:dt_put_tag/1 => \
+ move a=am_undefined r
+call_ext_last Arity u$func:erlang:dt_put_tag/1 D => \
+ move a=am_undefined r | deallocate D | return
+call_ext_only Arity u$func:erlang:dt_put_tag/1 => \
+ move a=am_undefined r | return
+
+call_ext Arity u$func:erlang:dt_get_tag_data/0 => \
+ move a=am_undefined r
+call_ext_last Arity u$func:erlang:dt_get_tag_data/0 D => \
+ move a=am_undefined r | deallocate D | return
+call_ext_only Arity u$func:erlang:dt_get_tag_data/0 => \
+ move a=am_undefined r | return
+
+move Any r | call_ext Arity u$func:erlang:dt_spread_tag/1 => \
+ move a=am_true r
+move Any r | call_ext_last Arity u$func:erlang:dt_spread_tag/1 D => \
+ move a=am_true r | deallocate D | return
+move Any r | call_ext_only Arity u$func:erlang:dt_spread_tag/1 => \
+ move a=am_true r | return
+call_ext Arity u$func:erlang:dt_spread_tag/1 => \
+ move a=am_true r
+call_ext_last Arity u$func:erlang:dt_spread_tag/1 D => \
+ move a=am_true r | deallocate D | return
+call_ext_only Arity u$func:erlang:dt_spread_tag/1 => \
+ move a=am_true r | return
+
+move Any r | call_ext Arity u$func:erlang:dt_restore_tag/1 => \
+ move a=am_true r
+move Any r | call_ext_last Arity u$func:erlang:dt_restore_tag/1 D => \
+ move a=am_true r | deallocate D | return
+move Any r | call_ext_only Arity u$func:erlang:dt_restore_tag/1 => \
+ move a=am_true r | return
+call_ext Arity u$func:erlang:dt_restore_tag/1 => \
+ move a=am_true r
+call_ext_last Arity u$func:erlang:dt_restore_tag/1 D => \
+ move a=am_true r | deallocate D | return
+call_ext_only Arity u$func:erlang:dt_restore_tag/1 => \
+ move a=am_true r | return
+
+move Any r | call_ext Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \
+ move Any r
+move Any r | call_ext_last Arity u$func:erlang:dt_prepend_vm_tag_data/1 D => \
+ move Any r | deallocate D | return
+move Any r | call_ext_only Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \
+ move Any r | return
+call_ext Arity u$func:erlang:dt_prepend_vm_tag_data/1 =>
+call_ext_last Arity u$func:erlang:dt_prepend_vm_tag_data/1 D => \
+ deallocate D | return
+call_ext_only Arity u$func:erlang:dt_prepend_vm_tag_data/1 => \
+ return
+
+move Any r | call_ext Arity u$func:erlang:dt_append_vm_tag_data/1 => \
+ move Any r
+move Any r | call_ext_last Arity u$func:erlang:dt_append_vm_tag_data/1 D => \
+ move Any r | deallocate D | return
+move Any r | call_ext_only Arity u$func:erlang:dt_append_vm_tag_data/1 => \
+ move Any r | return
+call_ext Arity u$func:erlang:dt_append_vm_tag_data/1 =>
+call_ext_last Arity u$func:erlang:dt_append_vm_tag_data/1 D => \
+ deallocate D | return
+call_ext_only Arity u$func:erlang:dt_append_vm_tag_data/1 => \
+ return
+
+# Can happen after one of the transformations above.
+move Discarded r | move Something r => move Something r
+
+%endif
+
+#
# The general case for BIFs that have no special instructions.
# A BIF used in the tail must be followed by a return instruction.
#
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 49b6618f73..5ab51fab50 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -1697,7 +1697,11 @@ static int do_send_to_logger(Eterm tag, Eterm gleader, char *buf, int len)
erts_queue_error_logger_message(from, tuple3, bp);
}
#else
- erts_queue_message(p, NULL /* only used for smp build */, bp, tuple3, NIL);
+ erts_queue_message(p, NULL /* only used for smp build */, bp, tuple3, NIL
+#ifdef USE_VM_PROBES
+ , NIL
+#endif
+ );
#endif
return 0;
}