aboutsummaryrefslogtreecommitdiffstats
path: root/erts
diff options
context:
space:
mode:
Diffstat (limited to 'erts')
-rw-r--r--erts/configure.in123
-rw-r--r--erts/doc/src/erlang.xml23
-rw-r--r--erts/emulator/Makefile.in31
-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
-rw-r--r--erts/emulator/drivers/common/efile_drv.c815
-rw-r--r--erts/emulator/sys/common/erl_check_io.c14
-rwxr-xr-xerts/emulator/utils/beam_makeops54
-rw-r--r--erts/lib_src/common/erl_printf.c4
-rw-r--r--erts/preloaded/ebin/prim_file.beambin39536 -> 40608 bytes
-rw-r--r--erts/preloaded/src/prim_file.erl58
41 files changed, 3478 insertions, 198 deletions
diff --git a/erts/configure.in b/erts/configure.in
index b801994e14..cb1b00b8b1 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -276,6 +276,61 @@ else
[Define to enable hrvtime() on Linux systems with perfctr extension])
fi
+
+AC_ARG_WITH(dynamic-trace,
+AS_HELP_STRING([--with-dynamic-trace={dtrace|systemtap}],
+ [specify use of dynamic trace framework, dtrace or systemtap])
+AS_HELP_STRING([--without-dynamic-trace],
+ [don't enable any dynamic tracing (default)]))
+
+if test X"$with_dynamic_trace" = X""; then
+ with_dynamic_trace=no
+fi
+
+case "$with_dynamic_trace" in
+ no) DYNAMIC_TRACE_FRAMEWORK=;;
+ dtrace)
+ AC_DEFINE(USE_DTRACE,[1],
+ [Define if you want to use dtrace for dynamic tracing])
+ DYNAMIC_TRACE_FRAMEWORK=dtrace;;
+ systemtap)
+ AC_DEFINE(USE_SYSTEMTAP,[1],
+ [Define if you want to use systemtap for dynamic tracing])
+ DYNAMIC_TRACE_FRAMEWORK=systemtap;;
+ *)
+ AC_MSG_ERROR(Unknown dynamic tracing framework specified with --with-dynamic-trace!);;
+esac
+
+if test X"$DYNAMIC_TRACE_FRAMEWORK" != X""; then
+ AC_DEFINE(USE_DYNAMIC_TRACE,[1],
+ [Define if you want to use dynamic tracing])
+fi
+
+AC_ARG_ENABLE(vm-probes,
+AS_HELP_STRING([--enable-vm-probes],
+ [add dynamic trace probes to the Beam VM (only possible if --with-dynamic-trace is enabled, and then default)]),
+ [ case "$enableval" in
+ no) use_vm_probes=no ;;
+ *)
+ if test X"$DYNAMIC_TRACE_FRAMEWORK" != X""; then
+ use_vm_probes=yes ;
+ else
+ AC_MSG_ERROR(Can not enable VM probes without any dynamic tracing framework!);
+ fi;;
+ esac ], if test X"$DYNAMIC_TRACE_FRAMEWORK" != X""; then
+ use_vm_probes=yes ;
+ else
+ use_vm_probes=no
+ fi)
+
+AC_SUBST(USE_VM_PROBES)
+if test X"$use_vm_probes" = X"yes"; then
+ USE_VM_PROBES=yes
+ AC_DEFINE(USE_VM_PROBES,[1],
+ [Define to enable VM dynamic trace probes])
+fi
+
+
AC_ARG_ENABLE(clock-gettime,
AS_HELP_STRING([--enable-clock-gettime],
[use clock-gettime for time correction]),
@@ -3546,6 +3601,74 @@ dnl
LM_FIND_EMU_CC
dnl
+dnl DTrace
+dnl
+case $DYNAMIC_TRACE_FRAMEWORK in
+ dtrace|systemtap)
+ AC_CHECK_TOOL(DTRACE, dtrace, none)
+ test "$DTRACE" = "none" && AC_MSG_ERROR([No dtrace utility found.]);
+ enable_dtrace_test=yes;;
+ *) enable_dtrace_test=no;;
+esac
+
+AC_SUBST(DTRACE)
+
+AC_SUBST(DTRACE_CPP)
+AC_SUBST(DTRACE_ENABLED)
+AC_SUBST(DTRACE_ENABLED_2STEP)
+DTRACE_CPP=-C
+DTRACE_ENABLED=
+DTRACE_ENABLED_2STEP=
+DTRACE_2STEP_TEST=./dtrace-test.o
+DTRACE_BITS_FLAG=
+case $OPSYS in
+ freebsd)
+ if test "$BITS64" = "yes" ; then
+ DTRACE_BITS_FLAG=-64
+ else
+ DTRACE_BITS_FLAG=-32
+ fi
+ ;;
+ *)
+ : # Nothing to do
+ ;;
+esac
+if test "$enable_dtrace_test" = "yes" ; then
+ if test "$DTRACE" = "dtrace" ; then
+ AC_CHECK_HEADERS(sys/sdt.h)
+ # The OS X version of dtrace prints a spurious line here.
+ if ! dtrace -h $DTRACE_CPP -Iemulator/beam -o ./foo-dtrace.h -s emulator/beam/erlang_dtrace.d; then
+ AC_MSG_ERROR([Could not precompile erlang_dtrace.d: dtrace -h failed])
+ fi
+ rm -f foo-dtrace.h
+
+ $RM -f $DTRACE_2STEP_TEST
+ if dtrace -G $DTRACE_CPP $DTRACE_BITS_FLAG -Iemulator/beam -o $DTRACE_2STEP_TEST -s emulator/beam/erlang_dtrace.d 2> /dev/null && \
+ test -f $DTRACE_2STEP_TEST ; then
+ rm $DTRACE_2STEP_TEST
+ DTRACE_ENABLED_2STEP=yes
+ AC_MSG_NOTICE([dtrace precompilation for 2-stage DTrace successful])
+ else
+ AC_MSG_NOTICE([dtrace precompilation for 1-stage DTrace successful])
+ fi
+ DTRACE_ENABLED=yes
+ case $OPSYS in
+ linux)
+ : # No extra libs to add to LIBS
+ ;;
+ freebsd)
+ LIBS="$LIBS -lelf"
+ ;;
+ *)
+ LIBS="$LIBS -ldtrace"
+ ;;
+ esac
+ else
+ AC_MSG_ERROR([Dtrace preprocessing test failed.])
+ fi
+fi
+
+dnl
dnl SSL, SSH and CRYPTO need the OpenSSL libraries
dnl
dnl Check flags --with-ssl, --without-ssl --with-ssl=PATH.
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 0776599fae..0963904b83 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -5744,6 +5744,29 @@ ok
used by the runtime system. It will be on the form
<seealso marker="erts:erl_driver#version_management">"&lt;major ver&gt;.&lt;minor ver&gt;"</seealso>.</p>
</item>
+ <tag><c>dynamic_trace</c></tag>
+ <item>
+ <p>Returns an atom describing the dynamic trace framework
+ compiled into the virtual machine. It can currently be either
+ <c>dtrace</c>, <c>systemtap</c> or <c>none</c>. For a
+ commercial or standard build, this is always <c>none</c>,
+ the other return values indicate a custom configuration
+ (e.g. <c>./configure --with-dynamic-trace=dtrace</c>). See
+ the <seealso marker="runtime_tools:dyntrace">dyntrace
+ </seealso> manual page and the
+ <c>README.dtrace</c>/<c>README.systemtap</c> files in the
+ Erlang source code top directory for more information
+ about dynamic tracing.</p>
+ </item>
+ <tag><c>dynamic_trace_probes</c></tag>
+ <item>
+ <p>Returns a <c>boolean()</c> indicating if dynamic trace probes
+ (either dtrace or systemtap) are built into the
+ emulator. This can only be <c>true</c> if the virtual
+ machine was built for dynamic tracing
+ (i.e. <c>system_info(dynamic_trace)</c> returns
+ <c>dtrace</c> or <c>systemtap</c>).</p>
+ </item>
<tag><c>elib_malloc</c></tag>
<item>
<p>This option will be removed in a future release.
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index 279844adb2..2efbe2d57e 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -23,6 +23,9 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
ENABLE_ALLOC_TYPE_VARS = @ENABLE_ALLOC_TYPE_VARS@
HIPE_ENABLED=@HIPE_ENABLED@
+DTRACE_ENABLED=@DTRACE_ENABLED@
+DTRACE_ENABLED_2STEP=@DTRACE_ENABLED_2STEP@
+USE_VM_PROBES=@USE_VM_PROBES@
LIBS = @LIBS@
Z_LIB=@Z_LIB@
NO_INLINE_FUNCTIONS=false
@@ -483,6 +486,14 @@ GENERATE += $(HIPE_ASM) \
endif
endif
+ifdef DTRACE_ENABLED
+# global.h causes problems by including dtrace-wrapper.h which includes
+# the autogenerated erlang_dtrace.h ... so make erlang_dtrace.h very early.
+generate: $(TARGET)/erlang_dtrace.h $(GENERATE)
+else
+generate: $(GENERATE)
+endif
+
ifdef HIPE_ENABLED
OPCODE_TABLES += hipe/hipe_ops.tab
endif
@@ -498,6 +509,7 @@ $(TTF_DIR)/OPCODES-GENERATED: $(OPCODE_TABLES) utils/beam_makeops
LANG=C $(PERL) utils/beam_makeops \
-wordsize @EXTERNAL_WORD_SIZE@ \
-outdir $(TTF_DIR) \
+ -DUSE_VM_PROBES=$(if $(USE_VM_PROBES),1,0) \
-emulator $(OPCODE_TABLES) && echo $? >$(TTF_DIR)/OPCODES-GENERATED
GENERATE += $(TTF_DIR)/OPCODES-GENERATED
@@ -590,6 +602,11 @@ $(TTF_DIR)/GENERATED: $(GENERATE)
echo $? >$(TTF_DIR)/GENERATED
endif
+$(TARGET)/erlang_dtrace.h: beam/erlang_dtrace.d
+ dtrace -h -C -Ibeam -s $< -o ./erlang_dtrace.tmp
+ sed -e '/^#define[ ]*ERLANG_[A-Z0-9_]*(.*)/y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/' ./erlang_dtrace.tmp > $@
+ rm ./erlang_dtrace.tmp
+
# ----------------------------------------------------------------------
# Pattern rules
#
@@ -633,7 +650,6 @@ $(OBJDIR)/beam_emu.o: beam/beam_emu.c
$(EMU_CC) $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@
endif
-
$(OBJDIR)/%.o: beam/%.c
$(CC) $(subst -O2, $(GEN_OPT_FLGS), $(CFLAGS)) $(INCLUDES) -c $< -o $@
@@ -833,7 +849,18 @@ endif
BASE_OBJS = $(RUN_OBJS) $(EMU_OBJS) $(OS_OBJS) $(EXTRA_BASE_OBJS)
-OBJS = $(BASE_OBJS) $(DRV_OBJS)
+before_DTrace_OBJS = $(BASE_OBJS) $(DRV_OBJS)
+
+DTRACE_OBJS =
+ifdef DTRACE_ENABLED_2STEP
+DTRACE_OBJS = $(OBJDIR)/erlang_dtrace.o
+$(OBJDIR)/erlang_dtrace.o: $(before_DTrace_OBJS) $(TARGET)/erlang_dtrace.h
+ dtrace -G -C -Ibeam \
+ -s beam/erlang_dtrace.d \
+ -o $@ $(before_DTrace_OBJS)
+endif
+
+OBJS = $(before_DTrace_OBJS) $(DTRACE_OBJS)
$(INIT_OBJS): $(TTF_DIR)/GENERATED
$(OBJS): $(TTF_DIR)/GENERATED
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;
}
diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c
index a251b064da..b33a8d210b 100644
--- a/erts/emulator/drivers/common/efile_drv.c
+++ b/erts/emulator/drivers/common/efile_drv.c
@@ -100,7 +100,7 @@
#endif
#include <stdlib.h>
-// Need (NON)BLOCKING macros for sendfile
+/* Need (NON)BLOCKING macros for sendfile */
#ifndef WANT_NONBLOCKING
#define WANT_NONBLOCKING
#endif
@@ -112,6 +112,7 @@
#include "erl_threads.h"
#include "zlib.h"
#include "gzio.h"
+#include "dtrace-wrapper.h"
#include <ctype.h>
#include <sys/types.h>
@@ -119,6 +120,39 @@ void erl_exit(int n, char *fmt, ...);
static ErlDrvSysInfo sys_info;
+/* For explanation of this var, see comment for same var in erl_async.c */
+static unsigned gcc_optimizer_hack = 0;
+
+#ifdef USE_VM_PROBES
+
+#define DTRACE_EFILE_BUFSIZ 128
+
+#define DTRACE_INVOKE_SETUP(op) \
+ do { DTRACE3(efile_drv_int_entry, d->sched_i1, d->sched_i2, op); } while (0)
+#define DTRACE_INVOKE_SETUP_BY_NAME(op) \
+ struct t_data *d = (struct t_data *) data ; \
+ DTRACE_INVOKE_SETUP(op)
+#define DTRACE_INVOKE_RETURN(op) \
+ do { DTRACE3(efile_drv_int_return, d->sched_i1, d->sched_i2, \
+ op); } while (0) ; gcc_optimizer_hack++ ;
+
+/* Assign human-friendlier id numbers to scheduler & I/O worker threads */
+int dt_driver_idnum = 0;
+int dt_driver_io_worker_base = 5000;
+erts_mtx_t dt_driver_mutex;
+pthread_key_t dt_driver_key;
+
+typedef struct {
+ int thread_num;
+ Uint64 tag;
+} dt_private;
+
+dt_private *get_dt_private(int);
+#else /* USE_VM_PROBES */
+#define DTRACE_INVOKE_SETUP(op) do {} while (0)
+#define DTRACE_INVOKE_SETUP_BY_NAME(op) do {} while (0)
+#define DTRACE_INVOKE_RETURN(op) do {} while (0)
+#endif /* USE_VM_PROBES */
/* #define TRACE 1 */
#ifdef TRACE
@@ -174,6 +208,9 @@ static ErlDrvSysInfo sys_info;
#ifdef FILENAMES_16BIT
+#ifdef USE_VM_PROBES
+#error 16bit characters in filenames and dtrace in combination is not supported.
+#endif
# define FILENAME_BYTELEN(Str) filename_len_16bit(Str)
# define FILENAME_COPY(To,From) filename_cpy_16bit((To),(From))
# define FILENAME_CHARSIZE 2
@@ -289,6 +326,10 @@ typedef struct {
ErlDrvPDL q_mtx; /* Mutex for the driver queue, known by the emulator. Also used for
mutual exclusion when accessing field(s) below. */
size_t write_buffered;
+#ifdef USE_VM_PROBES
+ int idnum; /* Unique ID # for this driver thread/desc */
+ char port_str[DTRACE_TERM_BUF_SIZE];
+#endif
} file_descriptor;
@@ -386,6 +427,11 @@ struct t_data
void (*free)(void *);
int again;
int reply;
+#ifdef USE_VM_PROBES
+ int sched_i1;
+ Uint64 sched_i2;
+ char sched_utag[DTRACE_EFILE_BUFSIZ+1];
+#endif
int result_ok;
Efile_error errInfo;
int flags;
@@ -458,8 +504,6 @@ struct t_data
char b[1];
};
-
-
#define EF_ALLOC(S) driver_alloc((S))
#define EF_REALLOC(P, S) driver_realloc((P), (S))
#define EF_SAFE_ALLOC(S) ef_safe_alloc((S))
@@ -488,7 +532,7 @@ static void *ef_safe_realloc(void *op, Uint s)
* ErlIOVec manipulation functions.
*/
-/* char EV_CHAR(ErlIOVec *ev, int p, int q) */
+/* char EV_CHAR_P(ErlIOVec *ev, int p, int q) */
#define EV_CHAR_P(ev, p, q) \
(((char *)(ev)->iov[(q)].iov_base) + (p))
@@ -683,6 +727,11 @@ file_init(void)
: 0);
driver_system_info(&sys_info, sizeof(ErlDrvSysInfo));
+#ifdef USE_VM_PROBES
+ erts_mtx_init(&dt_driver_mutex, "efile_drv dtrace mutex");
+ pthread_key_create(&dt_driver_key, NULL);
+#endif /* USE_VM_PROBES */
+
return 0;
}
@@ -722,6 +771,10 @@ file_start(ErlDrvPort port, char* command)
desc->write_error = 0;
MUTEX_INIT(desc->q_mtx, port); /* Refc is one, referenced by emulator now */
desc->write_buffered = 0;
+#ifdef USE_VM_PROBES
+ dtrace_drvport_str(port, desc->port_str);
+ get_dt_private(0); /* throw away return value */
+#endif /* USE_VM_PROBES */
return (ErlDrvData) desc;
}
@@ -741,8 +794,10 @@ static void do_close(int flags, SWord fd) {
static void invoke_close(void *data)
{
struct t_data *d = (struct t_data *) data;
+ DTRACE_INVOKE_SETUP(FILE_CLOSE);
d->again = 0;
do_close(d->flags, d->fd);
+ DTRACE_INVOKE_RETURN(FILE_CLOSE);
}
/*********************************************************************
@@ -972,49 +1027,63 @@ static void invoke_name(void *data, int (*f)(Efile_error *, char *))
static void invoke_mkdir(void *data)
{
+ DTRACE_INVOKE_SETUP_BY_NAME(FILE_MKDIR);
invoke_name(data, efile_mkdir);
+ DTRACE_INVOKE_RETURN(FILE_MKDIR);
}
static void invoke_rmdir(void *data)
{
+ DTRACE_INVOKE_SETUP_BY_NAME(FILE_RMDIR);
invoke_name(data, efile_rmdir);
+ DTRACE_INVOKE_RETURN(FILE_RMDIR);
}
static void invoke_delete_file(void *data)
{
+ DTRACE_INVOKE_SETUP_BY_NAME(FILE_DELETE);
invoke_name(data, efile_delete_file);
+ DTRACE_INVOKE_RETURN(FILE_DELETE);
}
static void invoke_chdir(void *data)
{
+ DTRACE_INVOKE_SETUP_BY_NAME(FILE_CHDIR);
invoke_name(data, efile_chdir);
+ DTRACE_INVOKE_RETURN(FILE_CHDIR);
}
static void invoke_fdatasync(void *data)
{
struct t_data *d = (struct t_data *) data;
int fd = (int) d->fd;
+ DTRACE_INVOKE_SETUP(FILE_FDATASYNC);
d->again = 0;
d->result_ok = efile_fdatasync(&d->errInfo, fd);
+ DTRACE_INVOKE_RETURN(FILE_FDATASYNC);
}
static void invoke_fsync(void *data)
{
struct t_data *d = (struct t_data *) data;
int fd = (int) d->fd;
+ DTRACE_INVOKE_SETUP(FILE_FSYNC);
d->again = 0;
d->result_ok = efile_fsync(&d->errInfo, fd);
+ DTRACE_INVOKE_RETURN(FILE_FSYNC);
}
static void invoke_truncate(void *data)
{
struct t_data *d = (struct t_data *) data;
int fd = (int) d->fd;
+ DTRACE_INVOKE_SETUP(FILE_TRUNCATE);
d->again = 0;
d->result_ok = efile_truncate_file(&d->errInfo, &fd, d->flags);
+ DTRACE_INVOKE_RETURN(FILE_TRUNCATE);
}
static void invoke_read(void *data)
@@ -1022,6 +1091,7 @@ static void invoke_read(void *data)
struct t_data *d = (struct t_data *) data;
int status, segment;
size_t size, read_size;
+ DTRACE_INVOKE_SETUP(FILE_READ);
segment = d->again && d->c.read.bin_size >= 2*FILE_SEGMENT_READ;
if (segment) {
@@ -1056,6 +1126,7 @@ static void invoke_read(void *data)
} else {
d->again = 0;
}
+ DTRACE_INVOKE_RETURN(FILE_READ);
}
static void free_read(void *data)
@@ -1072,6 +1143,7 @@ static void invoke_read_line(void *data)
int status;
size_t read_size;
int local_loop = (d->again == 0);
+ DTRACE_INVOKE_SETUP(FILE_READ_LINE);
do {
size_t size = (d->c.read_line.binp)->orig_size -
@@ -1163,6 +1235,7 @@ static void invoke_read_line(void *data)
break;
}
} while (local_loop);
+ DTRACE_INVOKE_RETURN(FILE_READ_LINE);
}
static void free_read_line(void *data)
@@ -1178,6 +1251,7 @@ static void invoke_read_file(void *data)
struct t_data *d = (struct t_data *) data;
size_t read_size;
int chop;
+ DTRACE_INVOKE_SETUP(FILE_READ_FILE);
if (! d->c.read_file.binp) { /* First invocation only */
int fd;
@@ -1214,12 +1288,14 @@ static void invoke_read_file(void *data)
&read_size);
if (d->result_ok) {
d->c.read_file.offset += read_size;
- if (chop) return; /* again */
+ if (chop) goto chop_done; /* again */
}
close:
efile_closefile((int) d->fd);
done:
d->again = 0;
+ chop_done:
+ DTRACE_INVOKE_RETURN(FILE_READ_FILE);
}
static void free_read_file(void *data)
@@ -1239,6 +1315,7 @@ static void invoke_preadv(void *data)
ErlIOVec *ev = &c->eiov;
size_t bytes_read_so_far = 0;
unsigned char *p = (unsigned char *)ev->iov[0].iov_base + 4+4+8*c->cnt;
+ DTRACE_INVOKE_SETUP(FILE_PREADV);
while (c->cnt < c->n) {
size_t read_size = ev->iov[1 + c->cnt].iov_len - c->size;
@@ -1260,7 +1337,7 @@ static void invoke_preadv(void *data)
bytes_read_so_far += bytes_read;
if (chop && bytes_read == read_size) {
c->size += bytes_read;
- return;
+ goto done;
}
ASSERT(bytes_read <= read_size);
ev->iov[1 + c->cnt].iov_len = bytes_read + c->size;
@@ -1271,7 +1348,7 @@ static void invoke_preadv(void *data)
if (d->again
&& bytes_read_so_far >= FILE_SEGMENT_READ
&& c->cnt < c->n) {
- return;
+ goto done;
}
} else {
/* In case of a read error, ev->size will not be correct,
@@ -1282,6 +1359,8 @@ static void invoke_preadv(void *data)
}
}
d->again = 0;
+ done:
+ DTRACE_INVOKE_RETURN(FILE_PREADV);
}
static void free_preadv(void *data) {
@@ -1303,6 +1382,7 @@ static void invoke_ipread(void *data)
size_t bytes_read = 0;
char buf[2*sizeof(Uint32)];
Uint32 offset, size;
+ DTRACE_INVOKE_SETUP(FILE_IPREAD);
/* Read indirection header */
if (! efile_pread(&d->errInfo, (int) d->fd, c->offsets[0],
@@ -1341,14 +1421,17 @@ static void invoke_ipread(void *data)
/* Read data block */
d->invoke = invoke_preadv;
invoke_preadv(data);
+ DTRACE_INVOKE_RETURN(FILE_IPREAD);
return;
error:
d->result_ok = 0;
d->again = 0;
+ DTRACE_INVOKE_RETURN(FILE_IPREAD);
return;
done:
d->result_ok = !0;
d->again = 0;
+ DTRACE_INVOKE_RETURN(FILE_IPREAD);
}
/* invoke_writev and invoke_pwritev are the only thread functions that
@@ -1371,6 +1454,7 @@ static void invoke_writev(void *data) {
size_t size;
size_t p;
int segment;
+ DTRACE_INVOKE_SETUP(FILE_WRITE);
segment = d->again && d->c.writev.size >= 2*FILE_SEGMENT_WRITE;
if (segment) {
@@ -1444,6 +1528,7 @@ static void invoke_writev(void *data) {
TRACE_F(("w%lu", (unsigned long)size));
}
+ DTRACE_INVOKE_RETURN(FILE_WRITE);
}
static void free_writev(void *data) {
@@ -1457,34 +1542,40 @@ static void free_writev(void *data) {
static void invoke_pwd(void *data)
{
struct t_data *d = (struct t_data *) data;
+ DTRACE_INVOKE_SETUP(FILE_PWD);
d->again = 0;
d->result_ok = efile_getdcwd(&d->errInfo,d->drive, d->b+1,
RESBUFSIZE-1);
+ DTRACE_INVOKE_RETURN(FILE_PWD);
}
static void invoke_readlink(void *data)
{
struct t_data *d = (struct t_data *) data;
char resbuf[RESBUFSIZE]; /* Result buffer. */
+ DTRACE_INVOKE_SETUP(FILE_READLINK);
d->again = 0;
d->result_ok = efile_readlink(&d->errInfo, d->b, resbuf+1,
RESBUFSIZE-1);
if (d->result_ok != 0)
FILENAME_COPY((char *) d->b + 1, resbuf+1);
+ DTRACE_INVOKE_RETURN(FILE_READLINK);
}
static void invoke_altname(void *data)
{
struct t_data *d = (struct t_data *) data;
char resbuf[RESBUFSIZE]; /* Result buffer. */
+ DTRACE_INVOKE_SETUP(FILE_ALTNAME);
d->again = 0;
d->result_ok = efile_altname(&d->errInfo, d->b, resbuf+1,
RESBUFSIZE-1);
if (d->result_ok != 0)
FILENAME_COPY((char *) d->b + 1, resbuf+1);
+ DTRACE_INVOKE_RETURN(FILE_ALTNAME);
}
static void invoke_pwritev(void *data) {
@@ -1497,6 +1588,7 @@ static void invoke_pwritev(void *data) {
size_t p;
int segment;
size_t size, write_size;
+ DTRACE_INVOKE_SETUP(FILE_PWRITEV);
segment = d->again && c->size >= 2*FILE_SEGMENT_WRITE;
if (segment) {
@@ -1576,6 +1668,7 @@ static void invoke_pwritev(void *data) {
}
done:
EF_FREE(iov); /* Free our copy of the vector, nothing to restore */
+ DTRACE_INVOKE_RETURN(FILE_PWRITEV);
}
static void free_pwritev(void *data) {
@@ -1591,9 +1684,14 @@ static void invoke_flstat(void *data)
{
struct t_data *d = (struct t_data *) data;
+ DTRACE3(efile_drv_int_entry, d->sched_i1, d->sched_i2,
+ d->command == FILE_LSTAT ? FILE_LSTAT : FILE_FSTAT);
d->again = 0;
d->result_ok = efile_fileinfo(&d->errInfo, &d->info,
d->b, d->command == FILE_LSTAT);
+ DTRACE3(efile_drv_int_entry, d->sched_i1, d->sched_i2,
+ d->command == FILE_LSTAT ? FILE_LSTAT : FILE_FSTAT);
+ gcc_optimizer_hack++;
}
static void invoke_link(void *data)
@@ -1601,10 +1699,12 @@ static void invoke_link(void *data)
struct t_data *d = (struct t_data *) data;
char *name = d->b;
char *new_name;
+ DTRACE_INVOKE_SETUP(FILE_LINK);
d->again = 0;
new_name = name+FILENAME_BYTELEN(name)+FILENAME_CHARSIZE;
d->result_ok = efile_link(&d->errInfo, name, new_name);
+ DTRACE_INVOKE_RETURN(FILE_LINK);
}
static void invoke_symlink(void *data)
@@ -1612,10 +1712,12 @@ static void invoke_symlink(void *data)
struct t_data *d = (struct t_data *) data;
char *name = d->b;
char *new_name;
+ DTRACE_INVOKE_SETUP(FILE_SYMLINK);
d->again = 0;
new_name = name+FILENAME_BYTELEN(name)+FILENAME_CHARSIZE;
d->result_ok = efile_symlink(&d->errInfo, name, new_name);
+ DTRACE_INVOKE_RETURN(FILE_SYMLINK);
}
static void invoke_rename(void *data)
@@ -1623,24 +1725,29 @@ static void invoke_rename(void *data)
struct t_data *d = (struct t_data *) data;
char *name = d->b;
char *new_name;
+ DTRACE_INVOKE_SETUP(FILE_RENAME);
d->again = 0;
new_name = name+FILENAME_BYTELEN(name)+FILENAME_CHARSIZE;
d->result_ok = efile_rename(&d->errInfo, name, new_name);
+ DTRACE_INVOKE_RETURN(FILE_RENAME);
}
static void invoke_write_info(void *data)
{
struct t_data *d = (struct t_data *) data;
+ DTRACE_INVOKE_SETUP(FILE_WRITE_INFO);
d->again = 0;
d->result_ok = efile_write_info(&d->errInfo, &d->info, d->b);
+ DTRACE_INVOKE_RETURN(FILE_WRITE_INFO);
}
static void invoke_lseek(void *data)
{
struct t_data *d = (struct t_data *) data;
int status;
+ DTRACE_INVOKE_SETUP(FILE_LSEEK);
d->again = 0;
if (d->flags & EFILE_COMPRESSED) {
@@ -1665,6 +1772,7 @@ static void invoke_lseek(void *data)
&d->c.lseek.location);
}
d->result_ok = status;
+ DTRACE_INVOKE_RETURN(FILE_LSEEK);
}
static void invoke_readdir(void *data)
@@ -1675,6 +1783,7 @@ static void invoke_readdir(void *data)
size_t n = 0, total = 0;
struct t_readdir_buf *b = NULL;
int res = 0;
+ DTRACE_INVOKE_SETUP(FILE_READDIR);
d->again = 0;
d->errInfo.posix_errno = 0;
@@ -1710,13 +1819,14 @@ static void invoke_readdir(void *data)
} while(res);
d->result_ok = (d->errInfo.posix_errno == 0);
+ DTRACE_INVOKE_RETURN(FILE_READDIR);
}
static void invoke_open(void *data)
{
struct t_data *d = (struct t_data *) data;
-
int status = 1; /* Status of open call. */
+ DTRACE_INVOKE_SETUP(FILE_OPEN);
d->again = 0;
if ((d->flags & EFILE_COMPRESSED) == 0) {
@@ -1749,6 +1859,7 @@ static void invoke_open(void *data)
}
d->result_ok = status;
+ DTRACE_INVOKE_RETURN(FILE_OPEN);
}
static void invoke_fadvise(void *data)
@@ -1758,9 +1869,11 @@ static void invoke_fadvise(void *data)
off_t offset = (off_t) d->c.fadvise.offset;
off_t length = (off_t) d->c.fadvise.length;
int advise = (int) d->c.fadvise.advise;
+ DTRACE_INVOKE_SETUP(FILE_FADVISE);
d->again = 0;
d->result_ok = efile_fadvise(&d->errInfo, fd, offset, length, advise);
+ DTRACE_INVOKE_RETURN(FILE_FADVISE);
}
#ifdef HAVE_SENDFILE
@@ -1841,6 +1954,7 @@ static void free_readdir(void *data)
{
struct t_data *d = (struct t_data *) data;
struct t_readdir_buf *b1 = d->c.read_dir.first_buf;
+
while (b1) {
struct t_readdir_buf *b2 = b1;
b1 = b1->next;
@@ -1909,12 +2023,16 @@ static void cq_execute(file_descriptor *desc) {
DRIVER_ASYNC(d->level, desc, d->invoke, void_ptr=d, d->free);
}
-static int async_write(file_descriptor *desc, int *errp,
- int reply, Uint32 reply_size) {
+static struct t_data *async_write(file_descriptor *desc, int *errp,
+ int reply, Uint32 reply_size
+#ifdef USE_VM_PROBES
+ ,Sint64 *dt_i1, Sint64 *dt_i2, Sint64 *dt_i3
+#endif
+) {
struct t_data *d;
if (! (d = EF_ALLOC(sizeof(struct t_data) - 1))) {
if (errp) *errp = ENOMEM;
- return -1;
+ return NULL;
}
TRACE_F(("w%lu", (unsigned long)desc->write_buffered));
d->command = FILE_WRITE;
@@ -1923,6 +2041,13 @@ static int async_write(file_descriptor *desc, int *errp,
d->c.writev.port = desc->port;
d->c.writev.q_mtx = desc->q_mtx;
d->c.writev.size = desc->write_buffered;
+#ifdef USE_VM_PROBES
+ if (dt_i1 != NULL) {
+ *dt_i1 = d->fd;
+ *dt_i2 = d->flags;
+ *dt_i3 = d->c.writev.size;
+ }
+#endif
d->reply = reply;
d->c.writev.free_size = 0;
d->c.writev.reply_size = reply_size;
@@ -1931,18 +2056,49 @@ static int async_write(file_descriptor *desc, int *errp,
d->level = 1;
cq_enq(desc, d);
desc->write_buffered = 0;
- return 0;
+ return d;
}
-static int flush_write(file_descriptor *desc, int *errp) {
- int result;
+static int flush_write(file_descriptor *desc, int *errp
+#ifdef USE_VM_PROBES
+ , dt_private *dt_priv, char *dt_utag
+#endif
+) {
+ int result = 0;
+#ifdef USE_VM_PROBES
+ Sint64 dt_i1 = 0, dt_i2 = 0, dt_i3 = 0;
+#endif
+ struct t_data *d = NULL;
+
MUTEX_LOCK(desc->q_mtx);
if (desc->write_buffered > 0) {
- result = async_write(desc, errp, 0, 0);
- } else {
- result = 0;
+ if ((d = async_write(desc, errp, 0, 0
+#ifdef USE_VM_PROBES
+ ,&dt_i1, &dt_i2, &dt_i3
+#endif
+ )) == NULL) {
+ result = -1;
+ }
}
MUTEX_UNLOCK(desc->q_mtx);
+#ifdef USE_VM_PROBES
+ if (d != NULL) {
+ d->sched_i1 = dt_priv->thread_num;
+ d->sched_i2 = dt_priv->tag;
+ d->sched_utag[0] = '\0';
+ if (dt_utag != NULL) {
+ if (dt_utag[0] == '\0') {
+ dt_utag = NULL;
+ } else {
+ strncpy(d->sched_utag, dt_utag, sizeof(d->sched_utag) - 1);
+ d->sched_utag[sizeof(d->sched_utag) - 1] = '\0';
+ }
+ }
+ DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag++,
+ dt_utag, FILE_WRITE,
+ NULL, NULL, dt_i1, dt_i2, dt_i3, 0, desc->port_str);
+ }
+#endif /* USE_VM_PROBES */
return result;
}
@@ -1955,9 +2111,17 @@ static int check_write_error(file_descriptor *desc, int *errp) {
return 0;
}
-static int flush_write_check_error(file_descriptor *desc, int *errp) {
+static int flush_write_check_error(file_descriptor *desc, int *errp
+#ifdef USE_VM_PROBES
+ , dt_private *dt_priv, char *dt_utag
+#endif
+ ) {
int r;
- if ( (r = flush_write(desc, errp)) != 0) {
+ if ( (r = flush_write(desc, errp
+#ifdef USE_VM_PROBES
+ , dt_priv, dt_utag
+#endif
+ )) != 0) {
check_write_error(desc, NULL);
return r;
} else {
@@ -1965,12 +2129,16 @@ static int flush_write_check_error(file_descriptor *desc, int *errp) {
}
}
-static int async_lseek(file_descriptor *desc, int *errp, int reply,
- Sint64 offset, int origin) {
+static struct t_data *async_lseek(file_descriptor *desc, int *errp, int reply,
+ Sint64 offset, int origin
+#ifdef USE_VM_PROBES
+ , Sint64 *dt_i1, Sint64 *dt_i2, Sint64 *dt_i3
+#endif
+ ) {
struct t_data *d;
if (! (d = EF_ALLOC(sizeof(struct t_data)))) {
*errp = ENOMEM;
- return -1;
+ return NULL;
}
d->flags = desc->flags;
d->fd = desc->fd;
@@ -1978,11 +2146,18 @@ static int async_lseek(file_descriptor *desc, int *errp, int reply,
d->reply = reply;
d->c.lseek.offset = offset;
d->c.lseek.origin = origin;
+#ifdef USE_VM_PROBES
+ if (dt_i1 != NULL) {
+ *dt_i1 = d->fd;
+ *dt_i2 = d->c.lseek.offset;
+ *dt_i3 = d->c.lseek.origin;
+ }
+#endif
d->invoke = invoke_lseek;
d->free = free_data;
d->level = 1;
cq_enq(desc, d);
- return 0;
+ return d;
}
static void flush_read(file_descriptor *desc) {
@@ -1994,18 +2169,45 @@ static void flush_read(file_descriptor *desc) {
}
}
-static int lseek_flush_read(file_descriptor *desc, int *errp) {
+static int lseek_flush_read(file_descriptor *desc, int *errp
+#ifdef USE_VM_PROBES
+ ,dt_private *dt_priv, char *dt_utag
+#endif
+ ) {
int r = 0;
size_t read_size = desc->read_size;
+#ifdef USE_VM_PROBES
+ Sint64 dt_i1 = 0, dt_i2 = 0, dt_i3 = 0;
+#endif
+ struct t_data *d;
+
+ flush_read(desc);
if (read_size != 0) {
- flush_read(desc);
- if ((r = async_lseek(desc, errp, 0,
- -((ssize_t)read_size), EFILE_SEEK_CUR))
- < 0) {
- return r;
- }
- } else {
- flush_read(desc);
+ if ((d = async_lseek(desc, errp, 0,
+ -((ssize_t)read_size), EFILE_SEEK_CUR
+#ifdef USE_VM_PROBES
+ , &dt_i1, &dt_i2, &dt_i3
+#endif
+ )) == NULL) {
+ r = -1;
+ } else {
+#ifdef USE_VM_PROBES
+ d->sched_i1 = dt_priv->thread_num;
+ d->sched_i2 = dt_priv->tag;
+ d->sched_utag[0] = '\0';
+ if (dt_utag != NULL) {
+ if (dt_utag[0] == '\0') {
+ dt_utag = NULL;
+ } else {
+ strncpy(d->sched_utag, dt_utag, sizeof(d->sched_utag) - 1);
+ d->sched_utag[sizeof(d->sched_utag) - 1] = '\0';
+ }
+ }
+ DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag++,
+ dt_utag, FILE_LSEEK,
+ NULL, NULL, dt_i1, dt_i2, dt_i3, 0, desc->port_str);
+#endif /* USE_VM_PROBES */
+ }
}
return r;
}
@@ -2022,11 +2224,23 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data)
struct t_data *d = (struct t_data *) data;
char header[5]; /* result code + count */
char resbuf[RESBUFSIZE]; /* Result buffer. */
-
+#ifdef USE_VM_PROBES
+ int sched_i1 = d->sched_i1, sched_i2 = d->sched_i2, command = d->command,
+ result_ok = d->result_ok,
+ posix_errno = d->result_ok ? 0 : d->errInfo.posix_errno;
+ DTRACE_CHARBUF(sched_utag, DTRACE_EFILE_BUFSIZ+1);
+
+ sched_utag[0] = '\0';
+ if (DTRACE_ENABLED(efile_drv_return)) {
+ strncpy(sched_utag, d->sched_utag, DTRACE_EFILE_BUFSIZ);
+ sched_utag[DTRACE_EFILE_BUFSIZ] = '\0';
+ }
+#endif /* USE_VM_PROBES */
TRACE_C('r');
if (try_again(desc, d)) {
+ /* DTRACE TODO: what kind of probe makes sense here? */
return;
}
@@ -2224,6 +2438,9 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data)
if (d->reply) {
TRACE_C('K');
reply_ok(desc);
+#ifdef USE_VM_PROBES
+ result_ok = 1;
+#endif
}
free_data(data);
break;
@@ -2279,6 +2496,8 @@ file_async_ready(ErlDrvData e, ErlDrvThreadData data)
default:
abort();
}
+ DTRACE6(efile_drv_return, sched_i1, sched_i2, sched_utag,
+ command, result_ok, posix_errno);
if (desc->write_buffered != 0 && desc->timer_state == timer_idle) {
desc->timer_state = timer_write;
driver_set_timer(desc->port, desc->write_delay);
@@ -2301,7 +2520,15 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
char* name; /* Points to the filename in buf. */
int command;
struct t_data *d = NULL;
-
+#ifdef USE_VM_PROBES
+ char *dt_utag = NULL;
+ char *dt_s1 = NULL, *dt_s2 = NULL;
+ Sint64 dt_i1 = 0;
+ Sint64 dt_i2 = 0;
+ Sint64 dt_i3 = 0;
+ Sint64 dt_i4 = 0;
+ dt_private *dt_priv = get_dt_private(0);
+#endif /* USE_VM_PROBES */
TRACE_C('o');
@@ -2316,6 +2543,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE);
FILENAME_COPY(d->b, name);
+#ifdef USE_VM_PROBES
+ dt_s1 = d->b;
+ dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE;
+#endif
d->command = command;
d->invoke = invoke_mkdir;
d->free = free_data;
@@ -2327,6 +2558,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE);
FILENAME_COPY(d->b, name);
+#ifdef USE_VM_PROBES
+ dt_s1 = d->b;
+ dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE;
+#endif
d->command = command;
d->invoke = invoke_rmdir;
d->free = free_data;
@@ -2338,6 +2573,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE);
FILENAME_COPY(d->b, name);
+#ifdef USE_VM_PROBES
+ dt_s1 = d->b;
+ dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE;
+#endif
d->command = command;
d->invoke = invoke_delete_file;
d->free = free_data;
@@ -2355,6 +2594,11 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
FILENAME_COPY(d->b, name);
FILENAME_COPY(d->b + namelen, new_name);
+#ifdef USE_VM_PROBES
+ dt_s1 = d->b;
+ dt_s2 = d->b + namelen;
+ dt_utag = buf + namelen + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE;
+#endif
d->flags = desc->flags;
d->fd = fd;
d->command = command;
@@ -2368,6 +2612,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE);
FILENAME_COPY(d->b, name);
+#ifdef USE_VM_PROBES
+ dt_s1 = d->b;
+ dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE;
+#endif
d->command = command;
d->invoke = invoke_chdir;
d->free = free_data;
@@ -2379,6 +2627,9 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + RESBUFSIZE + 1);
d->drive = *(uchar*)buf;
+#ifdef USE_VM_PROBES
+ dt_utag = buf + 1;
+#endif
d->command = command;
d->invoke = invoke_pwd;
d->free = free_data;
@@ -2394,6 +2645,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
FILENAME_CHARSIZE);
FILENAME_COPY(d->b, name);
+#ifdef USE_VM_PROBES
+ dt_s1 = d->b;
+ dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE;
+#endif
d->dir_handle = NULL;
d->command = command;
d->invoke = invoke_readdir;
@@ -2418,6 +2673,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
dir_handle = NULL;
resbuf[0] = FILE_RESP_LFNAME;
+#ifdef USE_VM_PROBES
+ dt_s1 = name;
+ dt_utag = name + FILENAME_BYTELEN(name) + FILENAME_CHARSIZE;
+#endif
/* Fill the buffer with multiple directory listings before sending it to the
* receiving process. READDIR_CHUNKS is minimum number of files sent to the
* receiver.
@@ -2451,6 +2710,17 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
reply_error(desc, &errInfo);
return;
}
+#ifdef USE_VM_PROBES
+ if (dt_utag != NULL && dt_utag[0] == '\0') {
+ dt_utag = NULL;
+ }
+
+ DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag,
+ dt_utag, command, name, dt_s2,
+ dt_i1, dt_i2, dt_i3, dt_i4, desc->port_str);
+ DTRACE6(efile_drv_return, dt_priv->thread_num, dt_priv->tag++,
+ dt_utag, command, 1, 0);
+#endif
TRACE_C('R');
driver_output2(desc->port, resbuf, 1, NULL, 0);
return;
@@ -2463,6 +2733,11 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
d->flags = get_int32((uchar*)buf);
name = buf+4;
FILENAME_COPY(d->b, name);
+#ifdef USE_VM_PROBES
+ dt_i1 = d->flags;
+ dt_s1 = d->b;
+ dt_utag = name + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE;
+#endif
d->command = command;
d->invoke = invoke_open;
d->free = free_data;
@@ -2475,6 +2750,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
d = EF_SAFE_ALLOC(sizeof(struct t_data));
d->fd = fd;
+#ifdef USE_VM_PROBES
+ dt_utag = name;
+ dt_i1 = fd;
+#endif
d->command = command;
d->invoke = invoke_fdatasync;
d->free = free_data;
@@ -2487,6 +2766,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
d = EF_SAFE_ALLOC(sizeof(struct t_data));
d->fd = fd;
+#ifdef USE_VM_PROBES
+ dt_utag = name;
+ dt_i1 = fd;
+#endif
d->command = command;
d->invoke = invoke_fsync;
d->free = free_data;
@@ -2503,6 +2786,14 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
FILENAME_COPY(d->b, name);
d->fd = fd;
+#ifdef USE_VM_PROBES
+ dt_utag = name + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE;
+ if (command == FILE_LSTAT) {
+ dt_s1 = d->b;
+ } else {
+ dt_i1 = fd;
+ }
+#endif
d->command = command;
d->invoke = invoke_flstat;
d->free = free_data;
@@ -2516,6 +2807,11 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
d->flags = desc->flags;
d->fd = fd;
+#ifdef USE_VM_PROBES
+ dt_utag = name;
+ dt_i1 = fd;
+ dt_i2 = d->flags;
+#endif
d->command = command;
d->invoke = invoke_truncate;
d->free = free_data;
@@ -2536,6 +2832,13 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
d->info.cTime = (time_t)((Sint64)get_int64(buf + 7 * 4));
FILENAME_COPY(d->b, buf + 9*4);
+#ifdef USE_VM_PROBES
+ dt_i1 = d->info.mode;
+ dt_i2 = d->info.uid;
+ dt_i3 = d->info.gid;
+ dt_s1 = d->b;
+ dt_utag = buf + 9 * 4 + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE;
+#endif
d->command = command;
d->invoke = invoke_write_info;
d->free = free_data;
@@ -2548,6 +2851,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + RESBUFSIZE + 1);
FILENAME_COPY(d->b, name);
+#ifdef USE_VM_PROBES
+ dt_s1 = d->b;
+ dt_utag = name + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE;
+#endif
d->command = command;
d->invoke = invoke_readlink;
d->free = free_data;
@@ -2559,6 +2866,10 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
{
d = EF_SAFE_ALLOC(sizeof(struct t_data) - 1 + RESBUFSIZE + 1);
FILENAME_COPY(d->b, name);
+#ifdef USE_VM_PROBES
+ dt_s1 = d->b;
+ dt_utag = name + FILENAME_BYTELEN(d->b) + FILENAME_CHARSIZE;
+#endif
d->command = command;
d->invoke = invoke_altname;
d->free = free_data;
@@ -2579,6 +2890,11 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
FILENAME_COPY(d->b, name);
FILENAME_COPY(d->b + namelen, new_name);
+#ifdef USE_VM_PROBES
+ dt_s1 = d->b;
+ dt_s2 = d->b + namelen;
+ dt_utag = buf + namelen + FILENAME_BYTELEN(dt_s2) + FILENAME_CHARSIZE;
+#endif
d->flags = desc->flags;
d->fd = fd;
d->command = command;
@@ -2600,6 +2916,11 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
FILENAME_COPY(d->b, name);
FILENAME_COPY(d->b + namelen, new_name);
+#ifdef USE_VM_PROBES
+ dt_s1 = d->b;
+ dt_s2 = d->b + namelen;
+ dt_utag = buf + namelen + FILENAME_BYTELEN(dt_s2) + FILENAME_CHARSIZE;
+#endif
d->flags = desc->flags;
d->fd = fd;
d->command = command;
@@ -2621,6 +2942,13 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
d->c.fadvise.offset = get_int64((uchar*) buf);
d->c.fadvise.length = get_int64(((uchar*) buf) + sizeof(Sint64));
d->c.fadvise.advise = get_int32(((uchar*) buf) + 2 * sizeof(Sint64));
+#ifdef USE_VM_PROBES
+ dt_i1 = d->fd;
+ dt_i2 = d->c.fadvise.offset;
+ dt_i3 = d->c.fadvise.length;
+ dt_i4 = d->c.fadvise.advise;
+ dt_utag = buf + 3 * sizeof(Sint64);
+#endif
goto done;
}
@@ -2634,6 +2962,22 @@ file_output(ErlDrvData e, char* buf, ErlDrvSizeT count)
done:
if (d) {
+#ifdef USE_VM_PROBES
+ d->sched_i1 = dt_priv->thread_num;
+ d->sched_i2 = dt_priv->tag;
+ d->sched_utag[0] = '\0';
+ if (dt_utag != NULL) {
+ if (dt_utag[0] == '\0') {
+ dt_utag = NULL;
+ } else {
+ strncpy(d->sched_utag, dt_utag, sizeof(d->sched_utag) - 1);
+ d->sched_utag[sizeof(d->sched_utag) - 1] = '\0';
+ }
+ }
+ DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag++,
+ dt_utag, command, dt_s1, dt_s2,
+ dt_i1, dt_i2, dt_i3, dt_i4, desc->port_str);
+#endif
cq_enq(desc, d);
}
}
@@ -2647,6 +2991,9 @@ file_flush(ErlDrvData e) {
#ifdef DEBUG
int r;
#endif
+#ifdef USE_VM_PROBES
+ dt_private *dt_priv = get_dt_private(dt_driver_io_worker_base);
+#endif
TRACE_C('f');
@@ -2657,7 +3004,11 @@ file_flush(ErlDrvData e) {
#ifdef DEBUG
r =
#endif
- flush_write(desc, NULL);
+ flush_write(desc, NULL
+#ifdef USE_VM_PROBES
+ , dt_priv, (desc->d == NULL) ? NULL : desc->d->sched_utag
+#endif
+ );
/* Only possible reason for bad return value is ENOMEM, and
* there is nobody to tell...
*/
@@ -2699,6 +3050,9 @@ static void
file_timeout(ErlDrvData e) {
file_descriptor *desc = (file_descriptor *)e;
enum e_timer timer_state = desc->timer_state;
+#ifdef USE_VM_PROBES
+ dt_private *dt_priv = get_dt_private(dt_driver_io_worker_base);
+#endif
TRACE_C('t');
@@ -2716,7 +3070,11 @@ file_timeout(ErlDrvData e) {
#ifdef DEBUG
int r =
#endif
- flush_write(desc, NULL);
+ flush_write(desc, NULL
+#ifdef USE_VM_PROBES
+ , dt_priv, (desc->d == NULL) ? NULL : desc->d->sched_utag
+#endif
+ );
/* Only possible reason for bad return value is ENOMEM, and
* there is nobody to tell...
*/
@@ -2737,6 +3095,14 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
char command;
int p, q;
int err;
+ struct t_data *d = NULL;
+#ifdef USE_VM_PROBES
+ Sint64 dt_i1 = 0, dt_i2 = 0, dt_i3 = 0;
+ Sint64 dt_i4 = 0;
+ char *dt_utag = NULL;
+ char *dt_s1 = NULL;
+ dt_private *dt_priv = get_dt_private(dt_driver_io_worker_base);
+#endif
TRACE_C('v');
@@ -2756,18 +3122,19 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
switch (command) {
case FILE_CLOSE: {
+#ifdef USE_VM_PROBES
+ dt_utag = EV_CHAR_P(ev, p, q);
+#endif
flush_read(desc);
- if (flush_write_check_error(desc, &err) < 0) {
+ if (flush_write_check_error(desc, &err
+#ifdef USE_VM_PROBES
+ , dt_priv, dt_utag
+#endif
+ ) < 0) {
reply_posix_error(desc, err);
goto done;
}
- if (ev->size != 1) {
- /* Wrong command length */
- reply_posix_error(desc, EINVAL);
- goto done;
- }
if (desc->fd != FILE_FD_INVALID) {
- struct t_data *d;
if (! (d = EF_ALLOC(sizeof(struct t_data)))) {
reply_posix_error(desc, ENOMEM);
} else {
@@ -2775,6 +3142,10 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
d->reply = !0;
d->fd = desc->fd;
d->flags = desc->flags;
+#ifdef USE_VM_PROBES
+ dt_i1 = d->fd;
+ dt_i2 = d->flags;
+#endif
d->invoke = invoke_close;
d->free = free_data;
d->level = 2;
@@ -2790,8 +3161,21 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
case FILE_READ: {
Uint32 sizeH, sizeL;
size_t size, alloc_size;
- struct t_data *d;
- if (flush_write_check_error(desc, &err) < 0) {
+
+ if (!EV_GET_UINT32(ev, &sizeH, &p, &q)
+ || !EV_GET_UINT32(ev, &sizeL, &p, &q)) {
+ /* Wrong buffer length to contain the read count */
+ reply_posix_error(desc, EINVAL);
+ goto done;
+ }
+#ifdef USE_VM_PROBES
+ dt_utag = EV_CHAR_P(ev, p, q);
+#endif
+ if (flush_write_check_error(desc, &err
+#ifdef USE_VM_PROBES
+ , dt_priv, dt_utag
+#endif
+ ) < 0) {
reply_posix_error(desc, err);
goto done;
}
@@ -2799,19 +3183,16 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
if (desc->read_bufsize == 0 && desc->read_binp != NULL && desc->read_size > 0) {
/* We have allocated a buffer for line mode but should not really have a
read-ahead buffer... */
- if (lseek_flush_read(desc, &err) < 0) {
+ if (lseek_flush_read(desc, &err
+#ifdef USE_VM_PROBES
+ , dt_priv, dt_utag
+#endif
+ ) < 0) {
reply_posix_error(desc, err);
goto done;
}
}
#endif
- if (ev->size != 1+8
- || !EV_GET_UINT32(ev, &sizeH, &p, &q)
- || !EV_GET_UINT32(ev, &sizeL, &p, &q)) {
- /* Wrong buffer length to contain the read count */
- reply_posix_error(desc, EINVAL);
- goto done;
- }
#if SIZEOF_SIZE_T == 4
if (sizeH != 0) {
reply_posix_error(desc, EINVAL);
@@ -2887,6 +3268,11 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
d->c.read.bin_offset = desc->read_offset + desc->read_size;
d->c.read.bin_size = desc->read_binp->orig_size - d->c.read.bin_offset;
d->c.read.size = size;
+#ifdef USE_VM_PROBES
+ dt_i1 = d->fd;
+ dt_i2 = d->flags;
+ dt_i3 = d->c.read.size;
+#endif
driver_binary_inc_refc(d->c.read.binp);
d->invoke = invoke_read;
d->free = free_read;
@@ -2904,12 +3290,22 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
* allocated binary + dealing with offsets and lengts are done in file_async ready
* for this OP.
*/
- struct t_data *d;
- if (flush_write_check_error(desc, &err) < 0) {
+#ifdef USE_VM_PROBES
+ dt_utag = EV_CHAR_P(ev, p, q);
+#endif
+ if (flush_write_check_error(desc, &err
+#ifdef USE_VM_PROBES
+ , dt_priv, dt_utag
+#endif
+ ) < 0) {
reply_posix_error(desc, err);
goto done;
}
- if (ev->size != 1) {
+ if (ev->size != 1
+#ifdef USE_VM_PROBES
+ + FILENAME_BYTELEN(dt_utag) + FILENAME_CHARSIZE
+#endif
+ ) {
/* Wrong command length */
reply_posix_error(desc, EINVAL);
goto done;
@@ -2965,8 +3361,16 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
d->c.read_line.binp = desc->read_binp;
d->c.read_line.read_offset = desc->read_offset;
d->c.read_line.read_size = desc->read_size;
+#ifdef USE_VM_PROBES
+ dt_i1 = d->fd;
+ dt_i2 = d->flags;
+ dt_i3 = d->c.read_line.read_offset;
+#endif
#if !ALWAYS_READ_LINE_AHEAD
d->c.read_line.read_ahead = (desc->read_bufsize > 0);
+#ifdef USE_VM_PROBES
+ dt_i4 = d->c.read_line.read_ahead;
+#endif
#endif
driver_binary_inc_refc(d->c.read.binp);
d->invoke = invoke_read_line;
@@ -2974,10 +3378,22 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
d->level = 1;
cq_enq(desc, d);
} goto done;
- case FILE_WRITE: {
+ case FILE_WRITE: { /* Dtrace: The dtrace user tag is not last in message,
+ but follows the message tag directly.
+ This is handled specially in prim_file.erl */
ErlDrvSizeT skip = 1;
ErlDrvSizeT size = ev->size - skip;
- if (lseek_flush_read(desc, &err) < 0) {
+
+#ifdef USE_VM_PROBES
+ dt_utag = EV_CHAR_P(ev, p, q);
+ skip += FILENAME_BYTELEN(dt_utag) + FILENAME_CHARSIZE;
+ size = ev->size - skip;
+#endif
+ if (lseek_flush_read(desc, &err
+#ifdef USE_VM_PROBES
+ , dt_priv, dt_utag
+#endif
+ ) < 0) {
reply_posix_error(desc, err);
goto done;
}
@@ -3004,7 +3420,11 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
driver_set_timer(desc->port, desc->write_delay);
}
} else {
- if (async_write(desc, &err, !0, size) != 0) {
+ if ((d = async_write(desc, &err, !0, size
+#ifdef USE_VM_PROBES
+ , &dt_i1, &dt_i2, &dt_i3
+#endif
+ )) == NULL) {
MUTEX_UNLOCK(desc->q_mtx);
reply_posix_error(desc, err);
goto done;
@@ -3014,24 +3434,49 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
}
} goto done; /* case FILE_WRITE */
- case FILE_PWRITEV: {
+ case FILE_PWRITEV: { /* Dtrace: The dtrace user tag is not last in message,
+ but follows the message tag directly.
+ This is handled specially in prim_file.erl */
Uint32 i, j, n;
size_t total;
- struct t_data *d;
- if (lseek_flush_read(desc, &err) < 0) {
- reply_Uint_posix_error(desc, 0, err);
- goto done;
- }
- if (flush_write_check_error(desc, &err) < 0) {
- reply_Uint_posix_error(desc, 0, err);
- goto done;
+#ifdef USE_VM_PROBES
+ char dt_tmp;
+ int dt_utag_bytes = 1;
+
+ dt_utag = EV_CHAR_P(ev, p, q);
+ /* This will work for UTF-8, but not for UTF-16 - extra reminder here */
+#ifdef FILENAMES_16BIT
+#error 16bit characters in filenames and dtrace in combination is not supported.
+#endif
+ while (EV_GET_CHAR(ev, &dt_tmp, &p, &q) && dt_tmp != '\0') {
+ dt_utag_bytes++;
}
+#endif
if (ev->size < 1+4
+#ifdef USE_VM_PROBES
+ + dt_utag_bytes
+#endif
|| !EV_GET_UINT32(ev, &n, &p, &q)) {
/* Buffer too short to contain even the number of pos/size specs */
reply_Uint_posix_error(desc, 0, EINVAL);
goto done;
}
+ if (lseek_flush_read(desc, &err
+#ifdef USE_VM_PROBES
+ , dt_priv, dt_utag
+#endif
+ ) < 0) {
+ reply_Uint_posix_error(desc, 0, err);
+ goto done;
+ }
+ if (flush_write_check_error(desc, &err
+#ifdef USE_VM_PROBES
+ , dt_priv, dt_utag
+#endif
+ ) < 0) {
+ reply_Uint_posix_error(desc, 0, err);
+ goto done;
+ }
if (n == 0) {
/* Trivial case - nothing to write */
if (ev->size != 1+4) {
@@ -3041,7 +3486,11 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
}
goto done;
}
- if (ev->size < 1+4+8*(2*n)) {
+ if (ev->size < 1+4+8*(2*n)
+#ifdef USE_VM_PROBES
+ + dt_utag_bytes
+#endif
+ ) {
/* Buffer too short to contain even the pos/size specs */
reply_Uint_posix_error(desc, 0, EINVAL);
goto done;
@@ -3056,6 +3505,10 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
d->reply = !0;
d->fd = desc->fd;
d->flags = desc->flags;
+#ifdef USE_VM_PROBES
+ dt_i1 = d->fd;
+ dt_i2 = d->flags;
+#endif
d->c.pwritev.port = desc->port;
d->c.pwritev.q_mtx = desc->q_mtx;
d->c.pwritev.n = n;
@@ -3093,13 +3546,20 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
}
}
d->c.pwritev.size = total;
+#ifdef USE_VM_PROBES
+ dt_i3 = d->c.pwritev.size;
+#endif
d->c.pwritev.free_size = 0;
if (j == 0) {
/* Trivial case - nothing to write */
EF_FREE(d);
reply_Uint(desc, 0);
} else {
- ErlDrvSizeT skip = 1 + 4 + 8*(2*n);
+ ErlDrvSizeT skip = 1 + 4 + 8 * (2*n)
+#ifdef USE_VM_PROBES
+ + dt_utag_bytes
+#endif
+ ;
if (skip + total != ev->size) {
/* Actual amount of data does not match
* total of all pos/size specs
@@ -3120,27 +3580,55 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
}
} goto done; /* case FILE_PWRITEV: */
- case FILE_PREADV: {
+ case FILE_PREADV: { /* Dtrace: The dtrace user tag is not last in message,
+ but follows the message tag directly.
+ This is handled specially in prim_file.erl */
register void * void_ptr;
Uint32 i, n;
- struct t_data *d;
ErlIOVec *res_ev;
- if (lseek_flush_read(desc, &err) < 0) {
+#ifdef USE_VM_PROBES
+ char dt_tmp;
+ int dt_utag_bytes = 1;
+ /* This will work for UTF-8, but not for UTF-16 - extra reminder here */
+#ifdef FILENAMES_16BIT
+#error 16bit characters in filenames and dtrace in combination is not supported.
+#endif
+ dt_utag = EV_CHAR_P(ev, p, q);
+ while (EV_GET_CHAR(ev, &dt_tmp, &p, &q) && dt_tmp != '\0') {
+ dt_utag_bytes++;
+ }
+#endif
+ if (lseek_flush_read(desc, &err
+#ifdef USE_VM_PROBES
+ , dt_priv, dt_utag
+#endif
+ ) < 0) {
reply_posix_error(desc, err);
goto done;
}
- if (flush_write_check_error(desc, &err) < 0) {
+ if (flush_write_check_error(desc, &err
+#ifdef USE_VM_PROBES
+ , dt_priv, dt_utag
+#endif
+ ) < 0) {
reply_posix_error(desc, err);
goto done;
}
if (ev->size < 1+8
+#ifdef USE_VM_PROBES
+ + dt_utag_bytes
+#endif
|| !EV_GET_UINT32(ev, &n, &p, &q)
|| !EV_GET_UINT32(ev, &n, &p, &q)) {
/* Buffer too short to contain even the number of pos/size specs */
reply_posix_error(desc, EINVAL);
goto done;
}
- if (ev->size != 1+8+8*(2*n)) {
+ if (ev->size < 1+8+8*(2*n)
+#ifdef USE_VM_PROBES
+ + dt_utag_bytes
+#endif
+ ) {
/* Buffer wrong length to contain the pos/size specs */
reply_posix_error(desc, EINVAL);
goto done;
@@ -3160,6 +3648,10 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
d->reply = !0;
d->fd = desc->fd;
d->flags = desc->flags;
+#ifdef USE_VM_PROBES
+ dt_i1 = d->fd;
+ dt_i2 = d->flags;
+#endif
d->c.preadv.n = n;
d->c.preadv.cnt = 0;
d->c.preadv.size = 0;
@@ -3187,6 +3679,9 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
#else
size = ((size_t)sizeH<<32) | sizeL;
#endif
+#ifdef USE_VM_PROBES
+ dt_i3 += size;
+#endif
if (! (res_ev->binv[i] = driver_alloc_binary(size))) {
reply_posix_error(desc, ENOMEM);
break;
@@ -3233,42 +3728,68 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
} goto done; /* case FILE_PREADV: */
case FILE_LSEEK: {
- Sint64 offset; /* Offset for seek */
+ Sint64 offset; /* Offset for seek */
Uint32 origin; /* Origin of seek. */
- if (lseek_flush_read(desc, &err) < 0) {
- reply_posix_error(desc, err);
+
+ if (ev->size < 1+8+4
+ || !EV_GET_UINT64(ev, &offset, &p, &q)
+ || !EV_GET_UINT32(ev, &origin, &p, &q)) {
+ /* Wrong length of buffer to contain offset and origin */
+ reply_posix_error(desc, EINVAL);
goto done;
}
- if (flush_write_check_error(desc, &err) < 0) {
+#ifdef USE_VM_PROBES
+ dt_utag = EV_CHAR_P(ev, p, q);
+#endif
+ if (lseek_flush_read(desc, &err
+#ifdef USE_VM_PROBES
+ , dt_priv, dt_utag
+#endif
+ ) < 0) {
reply_posix_error(desc, err);
goto done;
}
- if (ev->size != 1+8+4
- || !EV_GET_UINT64(ev, &offset, &p, &q)
- || !EV_GET_UINT32(ev, &origin, &p, &q)) {
- /* Wrong length of buffer to contain offset and origin */
- reply_posix_error(desc, EINVAL);
+ if (flush_write_check_error(desc, &err
+#ifdef USE_VM_PROBES
+ , dt_priv, dt_utag
+#endif
+ ) < 0) {
+ reply_posix_error(desc, err);
goto done;
}
- if (async_lseek(desc, &err, !0, offset, origin) < 0) {
+ if ((d = async_lseek(desc, &err, !0, offset, origin
+#ifdef USE_VM_PROBES
+ , &dt_i1, &dt_i2, &dt_i3
+#endif
+ )) == NULL) {
reply_posix_error(desc, err);
goto done;
}
} goto done;
case FILE_READ_FILE: {
- struct t_data *d;
char *filename;
if (ev->size < 1+1) {
/* Buffer contains empty name */
reply_posix_error(desc, ENOENT);
goto done;
}
+#ifndef USE_VM_PROBES
+ /* In the dtrace case, the iov has an extra element, the dtrace utag - we will need
+ another test to see that
+ the filename is in a single buffer: */
if (ev->size-1 != ev->iov[q].iov_len-p) {
/* Name not in one single buffer */
reply_posix_error(desc, EINVAL);
goto done;
}
+#else
+ if (((byte *)ev->iov[q].iov_base)[ev->iov[q].iov_len-1] != '\0') {
+ /* Name not in one single buffer */
+ reply_posix_error(desc, EINVAL);
+ goto done;
+ }
+#endif
filename = EV_CHAR_P(ev, p, q);
d = EF_ALLOC(sizeof(struct t_data) -1 + FILENAME_BYTELEN(filename) + FILENAME_CHARSIZE);
if (! d) {
@@ -3279,6 +3800,20 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
d->reply = !0;
/* Copy name */
FILENAME_COPY(d->b, filename);
+#ifdef USE_VM_PROBES
+ {
+ char dt_tmp;
+
+ /* This will work for UTF-8, but not for UTF-16 - extra reminder here */
+#ifdef FILENAMES_16BIT
+#error 16bit characters in filenames and dtrace in combination is not supported.
+#endif
+ while (EV_GET_CHAR(ev, &dt_tmp, &p, &q) && dt_tmp != '\0')
+ ;
+ dt_s1 = d->b;
+ dt_utag = EV_CHAR_P(ev, p, q);
+ }
+#endif
d->c.read_file.binp = NULL;
d->invoke = invoke_read_file;
d->free = free_read_file;
@@ -3298,7 +3833,6 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
char mode;
Sint64 hdr_offset;
Uint32 max_size;
- struct t_data *d;
ErlIOVec *res_ev;
int vsize;
if (! EV_GET_CHAR(ev, &mode, &p, &q)) {
@@ -3310,14 +3844,6 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
reply_posix_error(desc, EINVAL);
goto done;
}
- if (lseek_flush_read(desc, &err) < 0) {
- reply_posix_error(desc, err);
- goto done;
- }
- if (flush_write_check_error(desc, &err) < 0) {
- reply_posix_error(desc, err);
- goto done;
- }
if (ev->size < 1+1+8+4
|| !EV_GET_UINT64(ev, &hdr_offset, &p, &q)
|| !EV_GET_UINT32(ev, &max_size, &p, &q)) {
@@ -3326,6 +3852,25 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
reply_posix_error(desc, EINVAL);
goto done;
}
+#ifdef USE_VM_PROBES
+ dt_utag = EV_CHAR_P(ev, p, q);
+#endif
+ if (lseek_flush_read(desc, &err
+#ifdef USE_VM_PROBES
+ , dt_priv, dt_utag
+#endif
+ ) < 0) {
+ reply_posix_error(desc, err);
+ goto done;
+ }
+ if (flush_write_check_error(desc, &err
+#ifdef USE_VM_PROBES
+ , dt_priv, dt_utag
+#endif
+ ) < 0) {
+ reply_posix_error(desc, err);
+ goto done;
+ }
/* Create the thread data structure with the contained ErlIOVec
* and corresponding binaries for the response
*/
@@ -3342,6 +3887,12 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
d->flags = desc->flags;
d->c.preadv.offsets[0] = hdr_offset;
d->c.preadv.size = max_size;
+#ifdef USE_VM_PROBES
+ dt_i1 = d->fd;
+ dt_i2 = d->flags;
+ dt_i3 = d->c.preadv.offsets[0];
+ dt_i4 = d->c.preadv.size;
+#endif
res_ev = &d->c.preadv.eiov;
/* XXX possible alignment problems here for weird machines */
res_ev->iov = void_ptr = d + 1;
@@ -3356,16 +3907,24 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
case FILE_SETOPT: {
char opt;
+
if (ev->size < 1+1
|| !EV_GET_CHAR(ev, &opt, &p, &q)) {
/* Buffer too short to contain even the option type */
reply_posix_error(desc, EINVAL);
goto done;
}
+#ifdef USE_VM_PROBES
+ dt_i1 = opt;
+ dt_utag = EV_CHAR_P(ev, p, q);
+#endif
switch (opt) {
case FILE_OPT_DELAYED_WRITE: {
Uint32 sizeH, sizeL, delayH, delayL;
if (ev->size != 1+1+4*sizeof(Uint32)
+#ifdef USE_VM_PROBES
+ + FILENAME_BYTELEN(dt_utag) + FILENAME_CHARSIZE
+#endif
|| !EV_GET_UINT32(ev, &sizeH, &p, &q)
|| !EV_GET_UINT32(ev, &sizeL, &p, &q)
|| !EV_GET_UINT32(ev, &delayH, &p, &q)
@@ -3392,12 +3951,18 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
#else
desc->write_delay = ((unsigned long)delayH << 32) | delayL;
#endif
+#ifdef USE_VM_PROBES
+ dt_i2 = desc->write_delay;
+#endif
TRACE_C('K');
reply_ok(desc);
} goto done;
case FILE_OPT_READ_AHEAD: {
Uint32 sizeH, sizeL;
if (ev->size != 1+1+2*sizeof(Uint32)
+#ifdef USE_VM_PROBES
+ + FILENAME_BYTELEN(dt_utag)+FILENAME_CHARSIZE
+#endif
|| !EV_GET_UINT32(ev, &sizeH, &p, &q)
|| !EV_GET_UINT32(ev, &sizeL, &p, &q)) {
/* Buffer has wrong length to contain the option values */
@@ -3413,6 +3978,9 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
#else
desc->read_bufsize = ((size_t)sizeH << 32) | sizeL;
#endif
+#ifdef USE_VM_PROBES
+ dt_i2 = desc->read_bufsize;
+#endif
TRACE_C('K');
reply_ok(desc);
} goto done;
@@ -3499,11 +4067,19 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
} /* switch(command) */
- if (lseek_flush_read(desc, &err) < 0) {
+ if (lseek_flush_read(desc, &err
+#ifdef USE_VM_PROBES
+ , dt_priv, dt_utag
+#endif
+ ) < 0) {
reply_posix_error(desc, err);
goto done;
}
- if (flush_write_check_error(desc, &err) < 0) {
+ if (flush_write_check_error(desc, &err
+#ifdef USE_VM_PROBES
+ , dt_priv, dt_utag
+#endif
+ ) < 0) {
reply_posix_error(desc, err);
goto done;
} else {
@@ -3521,5 +4097,50 @@ file_outputv(ErlDrvData e, ErlIOVec *ev) {
}
done:
+ if (d != NULL) {
+#ifdef USE_VM_PROBES
+ /*
+ * If d == NULL, then either:
+ * 1). There was an error of some sort, or
+ * 2). The command given to us is actually implemented
+ * by file_output() instead.
+ *
+ * Case #1 is probably a TODO item, perhaps?
+ * Case #2 we definitely don't want to activate a probe.
+ */
+ d->sched_i1 = dt_priv->thread_num;
+ d->sched_i2 = dt_priv->tag;
+ d->sched_utag[0] = '\0';
+ if (dt_utag != NULL) {
+ if (dt_utag[0] == '\0') {
+ dt_utag = NULL;
+ } else {
+ strncpy(d->sched_utag, dt_utag, sizeof(d->sched_utag) - 1);
+ d->sched_utag[sizeof(d->sched_utag) - 1] = '\0';
+ }
+ }
+ DTRACE11(efile_drv_entry, dt_priv->thread_num, dt_priv->tag++,
+ dt_utag, command, dt_s1, NULL, dt_i1, dt_i2, dt_i3, dt_i4,
+ desc->port_str);
+#endif
+ }
cq_execute(desc);
}
+
+#ifdef USE_VM_PROBES
+dt_private *
+get_dt_private(int base)
+{
+ dt_private *dt_priv = (dt_private *) pthread_getspecific(dt_driver_key);
+
+ if (dt_priv == NULL) {
+ dt_priv = EF_SAFE_ALLOC(sizeof(dt_private));
+ erts_mtx_lock(&dt_driver_mutex);
+ dt_priv->thread_num = (base + dt_driver_idnum++);
+ erts_mtx_unlock(&dt_driver_mutex);
+ dt_priv->tag = 0;
+ pthread_setspecific(dt_driver_key, dt_priv);
+ }
+ return dt_priv;
+}
+#endif /* USE_VM_PROBES */
diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c
index 23a4bf1b04..1d173a758a 100644
--- a/erts/emulator/sys/common/erl_check_io.c
+++ b/erts/emulator/sys/common/erl_check_io.c
@@ -36,6 +36,7 @@
#include "global.h"
#include "erl_check_io.h"
#include "erl_thr_progress.h"
+#include "dtrace-wrapper.h"
#ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS
# define ERTS_DRV_EV_STATE_EXTRA_SIZE 128
@@ -314,6 +315,7 @@ forget_removed(struct pollset_info* psi)
erts_smp_mtx_unlock(mtx);
if (drv_ptr) {
int was_unmasked = erts_block_fpe();
+ DTRACE1(driver_stop_select, drv_ptr->name);
(*drv_ptr->stop_select) ((ErlDrvEvent) fd, NULL);
erts_unblock_fpe(was_unmasked);
if (drv_ptr->handle) {
@@ -496,6 +498,9 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix,
ErtsDrvEventState *state;
int wake_poller;
int ret;
+#ifdef USE_VM_PROBES
+ DTRACE_CHARBUF(name, 64);
+#endif
ERTS_SMP_LC_ASSERT(erts_drvport2port(ix)
&& erts_lc_is_port_locked(erts_drvport2port(ix)));
@@ -525,6 +530,10 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix,
if (IS_FD_UNKNOWN(state)) {
/* fast track to stop_select callback */
stop_select_fn = erts_drvport2port(ix)->drv_ptr->stop_select;
+#ifdef USE_VM_PROBES
+ strncpy(name, erts_drvport2port(ix)->drv_ptr->name, sizeof(name)-1);
+ name[sizeof(name)-1] = '\0';
+#endif
ret = 0;
goto done_unknown;
}
@@ -661,6 +670,10 @@ ERTS_CIO_EXPORT(driver_select)(ErlDrvPort ix,
/* Safe to close fd now as it is not in pollset
or there was no need to eject fd (kernel poll) */
stop_select_fn = drv_ptr->stop_select;
+#ifdef USE_VM_PROBES
+ strncpy(name, erts_drvport2port(ix)->drv_ptr->name, sizeof(name)-1);
+ name[sizeof(name)-1] = '\0';
+#endif
}
else {
/* Not safe to close fd, postpone stop_select callback. */
@@ -686,6 +699,7 @@ done_unknown:
erts_smp_mtx_unlock(fd_mtx(fd));
if (stop_select_fn) {
int was_unmasked = erts_block_fpe();
+ DTRACE1(driver_stop_select, name);
(*stop_select_fn)(e, NULL);
erts_unblock_fpe(was_unmasked);
}
diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops
index 58c36c3bdc..ea57000c82 100755
--- a/erts/emulator/utils/beam_makeops
+++ b/erts/emulator/utils/beam_makeops
@@ -28,6 +28,7 @@ my $verbose = 0;
my $hot = 1;
my $num_file_opcodes = 0;
my $wordsize = 32;
+my %defs; # Defines (from command line).
# This is shift counts and mask for the packer.
my $WHOLE_WORD = '';
@@ -96,6 +97,12 @@ my %unnumbered;
my %is_transformed;
#
+# Pre-processor.
+#
+my @if_val;
+my @if_line;
+
+#
# Code transformations.
#
my $te_max_vars = 0; # Max number of variables ever needed.
@@ -223,6 +230,7 @@ while (@ARGV && $ARGV[0] =~ /^-(.*)/) {
($outdir = shift), next if /^outdir/;
($wordsize = shift), next if /^wordsize/;
($verbose = 1), next if /^v/;
+ ($defs{$1} = $2), next if /^D(\w+)=(\w+)/;
die "$0: Bad option: -$_\n";
}
@@ -239,7 +247,43 @@ while (<>) {
}
next if /^\s*$/;
next if /^\#/;
-
+
+ #
+ # Handle %if.
+ #
+ if (/^\%if (\w+)/) {
+ my $name = $1;
+ my $val = $defs{$name};
+ defined $val or error("'$name' is undefined");
+ push @if_val, $val;
+ push @if_line, $.;
+ next;
+ } elsif (/^\%unless (\w+)/) {
+ my $name = $1;
+ my $val = $defs{$name};
+ defined $val or error("'$name' is undefined");
+ push @if_val, !$val;
+ push @if_line, $.;
+ next;
+ } elsif (/^\%else$/) {
+ unless (@if_line) {
+ error("%else without a preceding %if/%unless");
+ }
+ $if_line[$#if_line] = $.;
+ $if_val[$#if_val] = !$if_val[$#if_val];
+ next;
+ } elsif (/^\%endif$/) {
+ unless (@if_line) {
+ error("%endif without a preceding %if/%unless/%else");
+ }
+ pop @if_val;
+ pop @if_line;
+ next;
+ }
+ if (@if_val and not $if_val[$#if_val]) {
+ next;
+ }
+
#
# Handle assignments.
#
@@ -349,7 +393,13 @@ while (<>) {
$unnumbered{$name,$arity} = 1;
}
} continue {
- close(ARGV) if eof(ARGV);
+ if (eof(ARGV)) {
+ close(ARGV);
+ if (@if_line) {
+ error("Unterminated %if/%unless/%else at " .
+ "line $if_line[$#if_line]\n");
+ }
+ }
}
$num_file_opcodes = @gen_opname;
diff --git a/erts/lib_src/common/erl_printf.c b/erts/lib_src/common/erl_printf.c
index 108a8bb531..399c83384e 100644
--- a/erts/lib_src/common/erl_printf.c
+++ b/erts/lib_src/common/erl_printf.c
@@ -173,6 +173,7 @@ typedef struct {
static int
write_sn(void *vwsnap, char* buf, size_t len)
{
+ int rv = 0;
write_sn_arg_t *wsnap = (write_sn_arg_t *) vwsnap;
ASSERT(wsnap);
ASSERT(len > 0);
@@ -180,12 +181,13 @@ write_sn(void *vwsnap, char* buf, size_t len)
size_t sz = len;
if (sz >= wsnap->len)
sz = wsnap->len;
+ rv = (int)sz;
memcpy((void *) wsnap->buf, (void *) buf, sz);
wsnap->buf += sz;
wsnap->len -= sz;
return sz;
}
- return 0;
+ return rv;
}
static int
diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam
index 87e80aae9b..6400cda2b5 100644
--- a/erts/preloaded/ebin/prim_file.beam
+++ b/erts/preloaded/ebin/prim_file.beam
Binary files differ
diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl
index 36cbe329e8..0ecb720726 100644
--- a/erts/preloaded/src/prim_file.erl
+++ b/erts/preloaded/src/prim_file.erl
@@ -268,7 +268,7 @@ advise(#file_descriptor{module = ?MODULE, data = {Port, _}},
%% Returns {error, Reason} | ok.
write(#file_descriptor{module = ?MODULE, data = {Port, _}}, Bytes) ->
- case drv_command(Port, [?FILE_WRITE,Bytes]) of
+ case drv_command_nt(Port, [?FILE_WRITE,erlang:dt_prepend_vm_tag_data(Bytes)],undefined) of
{ok, _Size} ->
ok;
Error ->
@@ -283,8 +283,8 @@ pwrite(#file_descriptor{module = ?MODULE, data = {Port, _}}, L)
pwrite_int(_, [], 0, [], []) ->
ok;
pwrite_int(Port, [], N, Spec, Data) ->
- Header = list_to_binary([<<?FILE_PWRITEV, N:32>> | reverse(Spec)]),
- case drv_command_raw(Port, [Header | reverse(Data)]) of
+ Header = list_to_binary([?FILE_PWRITEV, erlang:dt_prepend_vm_tag_data(<<N:32>>) | reverse(Spec)]),
+ case drv_command_nt(Port, [Header | reverse(Data)], undefined) of
{ok, _Size} ->
ok;
Error ->
@@ -402,7 +402,7 @@ pread(#file_descriptor{module = ?MODULE, data = {Port, _}}, L)
pread_int(_, [], 0, []) ->
{ok, []};
pread_int(Port, [], N, Spec) ->
- drv_command(Port, [<<?FILE_PREADV, 0:32, N:32>> | reverse(Spec)]);
+ drv_command_nt(Port, [?FILE_PREADV, erlang:dt_prepend_vm_tag_data(<<0:32, N:32>>) | reverse(Spec)],undefined);
pread_int(Port, [{Offs, Size} | T], N, Spec)
when is_integer(Offs), is_integer(Size), 0 =< Size ->
if
@@ -423,9 +423,9 @@ pread(#file_descriptor{module = ?MODULE, data = {Port, _}}, Offs, Size)
if
-(?LARGEFILESIZE) =< Offs, Offs < ?LARGEFILESIZE,
Size < ?LARGEFILESIZE ->
- case drv_command(Port,
- <<?FILE_PREADV, 0:32, 1:32,
- Offs:64/signed, Size:64>>) of
+ case drv_command_nt(Port,
+ [?FILE_PREADV, erlang:dt_prepend_vm_tag_data(<<0:32, 1:32,
+ Offs:64/signed, Size:64>>)], undefined) of
{ok, [eof]} ->
eof;
{ok, [Data]} ->
@@ -923,12 +923,17 @@ drv_open(Driver, Portopts) ->
%% Closes a port in a safe way. Returns ok.
drv_close(Port) ->
- try erlang:port_close(Port) catch error:_ -> ok end,
- receive %% Ugly workaround in case the caller==owner traps exits
- {'EXIT', Port, _Reason} ->
- ok
- after 0 ->
- ok
+ Save = erlang:dt_spread_tag(false),
+ try
+ try erlang:port_close(Port) catch error:_ -> ok end,
+ receive %% Ugly workaround in case the caller==owner traps exits
+ {'EXIT', Port, _Reason} ->
+ ok
+ after 0 ->
+ ok
+ end
+ after
+ erlang:dt_restore_tag(Save)
end.
@@ -938,9 +943,6 @@ drv_close(Port) ->
%% then closed after the result has been received.
%% Returns {ok, Result} or {error, Reason}.
-drv_command_raw(Port, Command) ->
- drv_command(Port, Command, false, undefined).
-
drv_command(Port, Command) ->
drv_command(Port, Command, undefined).
@@ -956,7 +958,8 @@ drv_command(Port, Command, R) ->
end.
drv_command(Port, Command, Validated, R) when is_port(Port) ->
- try erlang:port_command(Port, Command) of
+ Save = erlang:dt_spread_tag(false),
+ try erlang:port_command(Port, erlang:dt_append_vm_tag_data(Command)) of
true ->
drv_get_response(Port, R)
catch
@@ -975,6 +978,8 @@ drv_command(Port, Command, Validated, R) when is_port(Port) ->
end;
error:Reason ->
{error, Reason}
+ after
+ erlang:dt_restore_tag(Save)
end;
drv_command({Driver, Portopts}, Command, Validated, R) ->
case drv_open(Driver, Portopts) of
@@ -985,6 +990,25 @@ drv_command({Driver, Portopts}, Command, Validated, R) ->
Error ->
Error
end.
+drv_command_nt(Port, Command, R) when is_port(Port) ->
+ Save = erlang:dt_spread_tag(false),
+ try erlang:port_command(Port, Command) of
+ true ->
+ drv_get_response(Port, R)
+ catch
+ error:badarg ->
+ try erlang:iolist_size(Command) of
+ _ -> % Valid
+ {error, einval}
+ catch
+ error:_ ->
+ {error, badarg}
+ end;
+ error:Reason ->
+ {error, Reason}
+ after
+ erlang:dt_restore_tag(Save)
+ end.