aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.dtrace.md422
-rw-r--r--configure.in4
-rw-r--r--erts/configure.in76
-rw-r--r--erts/emulator/Makefile.in25
-rw-r--r--erts/emulator/beam/dtrace-wrapper.h118
-rw-r--r--erts/emulator/beam/erl_driver.h2
-rw-r--r--erts/emulator/beam/erl_node_tables.h1
-rw-r--r--erts/emulator/beam/global.h7
-rw-r--r--lib/Makefile2
-rw-r--r--lib/dtrace/Makefile36
-rw-r--r--lib/dtrace/c_src/Makefile4
-rw-r--r--lib/dtrace/c_src/Makefile.in152
-rw-r--r--lib/dtrace/c_src/dtrace.c174
-rw-r--r--lib/dtrace/c_src/dtrace_user.d53
-rw-r--r--lib/dtrace/ebin/.placeholder0
-rw-r--r--lib/dtrace/src/Makefile102
-rw-r--r--lib/dtrace/src/dtrace.app.src27
-rw-r--r--lib/dtrace/src/dtrace.appup.src19
-rw-r--r--lib/dtrace/src/dtrace.erl216
-rw-r--r--lib/dtrace/vsn.mk1
20 files changed, 1438 insertions, 3 deletions
diff --git a/README.dtrace.md b/README.dtrace.md
new file mode 100644
index 0000000000..71023dfe69
--- /dev/null
+++ b/README.dtrace.md
@@ -0,0 +1,422 @@
+DTrace and Erlang/OTP
+=====================
+
+History
+-------
+
+The first implementation of DTrace probes for the Erlang virtual
+machine was presented at the [2008 Erlang User Conference] [4]. That
+work, based on the Erlang/OTP R12 release, was discontinued due to
+what appears to be miscommunication with the original developers.
+
+Several users have created Erlang port drivers, linked-in drivers, or
+NIFs that allow Erlang code to try to activate a probe,
+e.g. `foo_module:dtrace_probe("message goes here!")`.
+
+Goals
+-----
+
+1. Annotate as much of the Erlang VM as is practical.
+ * The initial goal is to trace file I/O operations.
+2. Support all platforms that implement DTrace: OS X, Solaris,
+ and (I hope) FreeBSD and NetBSD.
+3. To the extent that it's practical, support SystemTap on Linux
+ via DTrace provider compatibility.
+4. Allow Erlang code to supply annotations.
+
+Supported platforms
+-------------------
+
+The autoconf procedure is supported, I believe, for OS X/Snow Leopard
+and OpenSolaris/64-bit. Just add the `--enable-dtrace` option your
+command to run the `configure` script.
+
+The code has been only very lightly tested on OS X. It ought to
+compile on a Solaris 10 or OpenSolaris machine, but no promises yet.
+
+The autoconf stuff is ugly right now. It could use some cleaning up.
+For example:
+
+* After editing the `erlang_dtrace.d` file, you need to re-run the
+* top-level "configure" script in order to update `erlang_dtrace.h`.
+* `make clean` will remove `erlang_dtrace.h`. A build will fail
+ unless the top-level "configure" script is re-run to re-create that
+ file.
+* The `erlang_dtrace.h` file's location should probably be moved to an
+ OTP platform-specific build dir, for example,
+ `path/to/somewhere/i386-apple-darwin10.8.0`
+* There are probably some other build by-products that are also being
+ put into the "wrong" directory, for example, `erlang_dtrace.o` for
+ Solaris platforms.
+
+Contributions
+-------------
+
+Code contributions are welcome! This is a side project for me (SLF),
+so things would go faster if other people are willing to pitch in.
+Please use the GitHub pull request mechanism or send me an email
+message.
+
+To build from scratch, use this recipe. If you're an experienced Git
+user and wish to add my repository as a remote repository, be my
+guest. Just resume the recipe at command #4.
+
+ % git clone git://github.com/slfritchie/otp.git
+ % cd otp
+ % git checkout -b dtrace-experiment origin/dtrace-experiment
+ % env ERL_TOP=`pwd` ./otp_build autoconf
+ % env ERL_TOP=`pwd` ./configure --enable-dtrace + whatever args you need
+ % env ERL_TOP=`pwd` make
+
+Then `make install` and then start an Erlang shell via
+`/path/to/installed/bin/erl +A 8`. The Erlang shell's banner should
+include `[dtrace]`.
+
+Try using this (ugly) DTrace command to watch file I/O probes in use
+(tested on OS X only, sorry):
+
+ dtrace -Z -n 'erlang*:::efile_drv-entry {printf("%d %d %s | %d | %s %s , %d %d %d", arg0, arg1, arg2 == NULL ? "" : copyinstr(arg2), arg3, arg4 == NULL ? "" : copyinstr(arg4), arg5 == NULL ? "" : copyinstr(arg5), arg6, arg7, arg8)} erlang*:::efile_drv-int* {printf("%d %d %d | %d", arg0, arg1, arg2, arg3);} erlang*:::efile_drv-return {printf("%d %d %s | %d | %d %d %d", arg0, arg1, arg2 == NULL ? "" : copyinstr(arg2), arg3, arg4, arg5, arg6 ) ; }'
+
+Implementation summary
+----------------------
+
+So far, most effort has been focused on the `efile_drv.erl` code,
+which implements most file I/O on behalf of the Erlang virtual
+machine. This driver also presents a big challenge: its use of an I/O
+worker pool (enabled by using the `erl +A 8` flag, for example) makes
+it much more difficult to trace I/O activity because each of the
+following may be executed in a different Pthread:
+
+* I/O initiation (Erlang code)
+* I/O proxy process handling, e.g. read/write when file is not opened
+ in `raw` mode, operations executed by the code & file server processes.
+ (Erlang code)
+* `efile_drv` command setup (C code)
+* `efile_drv` command execution (C code)
+* `efile_drv` status return (C code)
+
+**TODO: keep this description up-to-date.**
+
+Example output from `lib/dtrace/examples/efile_drv.d` while executing
+`file:rename("old-name", "new-name")`:
+
+ efile_drv enter tag={3,84} user tag some-user-tag | RENAME (12) | args: old-name new-name , 0 0 (port #Port<0.59>)
+ async I/O worker tag={3,83} | RENAME (12) | efile_drv-int_entry
+ async I/O worker tag={3,83} | RENAME (12) | efile_drv-int_return
+ efile_drv return tag={3,83} user tag | RENAME (12) | errno 2
+
+... where the following key can help decipher the output:
+
+* `{3,83}` is the Erlang scheduler thread number (3) and operation
+ counter number (83) assigned to this I/O operation. Together,
+ these two numbers form a unique ID for the I/O operation.
+* `12` is the command number for the rename operation. See the
+ definition for `FILE_RENAME` in the source code file `efile_drv.c`
+ or the `BEGIN` section of the D script `lib/dtrace/examples/efile_drv.d`.
+* `old-name` and `new-name` are the two string arguments for the
+ source and destination of the `rename(2)` system call.
+ The two integer arguments are unused; the simple formatting code
+ prints the arguments anyway, 0 and 0.
+* The worker pool code was called on behalf of Erlang port `#Port<0.59>`.
+* The system call failed with a POSIX errno value of 2: `ENOENT`,
+ because the path `old-name` does not exist.
+* The `efile_drv-int_entry` and `efile_drv_int_return` probes are
+ provided in case the user is
+ interested in measuring only the latency of code executed by
+ `efile_drv` asynchronous functions by I/O worker pool threads
+ and the OS system call that they encapsulate.
+
+So, where does the `some-user-tag` string come from?
+
+At the moment, the user tag comes from code like the following:
+
+ put(dtrace_utag, "some-user-tag"),
+ file:rename("old-name", "new-name").
+
+This method of tagging I/O at the Erlang level is subject to change.
+
+Example DTrace probe specification
+----------------------------------
+
+ /**
+ * 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.
+ *
+ * @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.
+ *
+ * @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);
+
+ /* ... */
+
+ /* 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.
+ *
+ * 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);
+
+Guide to efile_drv.c probe arguments
+------------------------------------
+
+ /* Driver op code: used by efile_drv-entry arg3 */
+ /* used by efile_drv-int_entry arg3 */
+ /* used by efile_drv-int_return arg3 */
+ /* used by efile_drv-return arg3 */
+
+ #define FILE_OPEN 1 (probe arg3)
+ probe arg6 = C driver dt_i1 = flags;
+ probe arg4 = C driver dt_s1 = path;
+
+ #define FILE_READ 2 (probe arg3)
+ probe arg6 = C driver dt_i1 = fd;
+ probe arg7 = C driver dt_i2 = flags;
+ probe arg8 = C driver dt_i3 = size;
+
+ #define FILE_LSEEK 3 (probe arg3)
+ probe arg6 = C driver dt_i1 = fd;
+ probe arg7 = C driver dt_i2 = offset;
+ probe arg8 = C driver dt_i3 = origin;
+
+ #define FILE_WRITE 4 (probe arg3)
+ probe arg6 = C driver dt_i1 = fd;
+ probe arg7 = C driver dt_i2 = flags;
+ probe arg8 = C driver dt_i3 = size;
+
+ #define FILE_FSTAT 5 (probe arg3)
+ probe arg6 = C driver dt_i1 = fd;
+
+ #define FILE_PWD 6 (probe arg3)
+ none
+
+ #define FILE_READDIR 7 (probe arg3)
+ probe arg4 = C driver dt_s1 = path;
+
+ #define FILE_CHDIR 8 (probe arg3)
+ probe arg4 = C driver dt_s1 = path;
+
+ #define FILE_FSYNC 9 (probe arg3)
+ probe arg6 = C driver dt_i1 = fd;
+
+ #define FILE_MKDIR 10 (probe arg3)
+ probe arg4 = C driver dt_s1 = path;
+
+ #define FILE_DELETE 11 (probe arg3)
+ probe arg4 = C driver dt_s1 = path;
+
+ #define FILE_RENAME 12 (probe arg3)
+ probe arg4 = C driver dt_s1 = old_name;
+ probe arg5 = C driver dt_s2 = new_name;
+
+ #define FILE_RMDIR 13 (probe arg3)
+ probe arg4 = C driver dt_s1 = path;
+
+ #define FILE_TRUNCATE 14 (probe arg3)
+ probe arg6 = C driver dt_i1 = fd;
+ probe arg7 = C driver dt_i2 = flags;
+
+ #define FILE_READ_FILE 15 (probe arg3)
+ probe arg4 = C driver dt_s1 = path;
+
+ #define FILE_WRITE_INFO 16 (probe arg3)
+ probe arg6 = C driver dt_i1 = mode;
+ probe arg7 = C driver dt_i2 = uid;
+ probe arg8 = C driver dt_i3 = gid;
+
+ #define FILE_LSTAT 19 (probe arg3)
+ probe arg4 = C driver dt_s1 = path;
+
+ #define FILE_READLINK 20 (probe arg3)
+ probe arg4 = C driver dt_s1 = path;
+
+ #define FILE_LINK 21 (probe arg3)
+ probe arg4 = C driver dt_s1 = existing_path;
+ probe arg5 = C driver dt_s2 = new_path;
+
+ #define FILE_SYMLINK 22 (probe arg3)
+ probe arg4 = C driver dt_s1 = existing_path;
+ probe arg5 = C driver dt_s2 = new_path;
+
+ #define FILE_CLOSE 23 (probe arg3)
+ probe arg6 = C driver dt_i1 = fd;
+ probe arg7 = C driver dt_i2 = flags;
+
+ #define FILE_PWRITEV 24 (probe arg3)
+ probe arg6 = C driver dt_i1 = fd;
+ probe arg7 = C driver dt_i2 = flags;
+ probe arg8 = C driver dt_i3 = size;
+
+ #define FILE_PREADV 25 (probe arg3)
+ probe arg6 = C driver dt_i1 = fd;
+ probe arg7 = C driver dt_i2 = flags;
+ probe arg8 = C driver dt_i3 = size;
+
+ #define FILE_SETOPT 26 (probe arg3)
+ probe arg6 = C driver dt_i1 = opt_name;
+ probe arg7 = C driver dt_i2 = opt_specific_value;
+
+ #define FILE_IPREAD 27 (probe arg3)
+ probe arg6 = C driver dt_i1 = fd;
+ probe arg7 = C driver dt_i2 = flags;
+ probe arg8 = C driver dt_i3 = offsets[0];
+ probe arg9 = C driver dt_i4 = size;
+
+ #define FILE_ALTNAME 28 (probe arg3)
+ probe arg4 = C driver dt_s1 = path;
+
+ #define FILE_READ_LINE 29 (probe arg3)
+ probe arg6 = C driver dt_i1 = fd;
+ probe arg7 = C driver dt_i2 = flags;
+ probe arg8 = C driver dt_i3 = read_offset;
+ probe arg9 = C driver dt_i4 = read_ahead;
+
+ #define FILE_FDATASYNC 30 (probe arg3)
+ probe arg6 = C driver dt_i1 = fd;
+
+ #define FILE_FADVISE 31 (probe arg3)
+ probe arg6 = C driver dt_i1 = fd;
+ probe arg7 = C driver dt_i2 = offset;
+ probe arg8 = C driver dt_i3 = length;
+ probe arg9 = C driver dt_i4 = advise_type;
+
+ [1]: http://www.erlang.org/euc/08/
diff --git a/configure.in b/configure.in
index 2c3ee1cb57..0d54dcd3a3 100644
--- a/configure.in
+++ b/configure.in
@@ -225,6 +225,10 @@ AC_ARG_ENABLE(native-libs,
AS_HELP_STRING([--enable-native-libs],
[compile Erlang libraries to native code]))
+AC_ARG_ENABLE(dtrace,
+AS_HELP_STRING([--enable-dtrace],
+ [Enable DTrace probes]))
+
AC_ARG_WITH(javac,
AS_HELP_STRING([--with-javac=JAVAC], [specify Java compiler to use])
AS_HELP_STRING([--with-javac], [use a Java compiler if found (default)])
diff --git a/erts/configure.in b/erts/configure.in
index b801994e14..88c73a7371 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -3546,6 +3546,81 @@ dnl
LM_FIND_EMU_CC
dnl
+dnl DTrace
+dnl
+
+AC_MSG_CHECKING(if --enable-dtrace option specified)
+AC_ARG_ENABLE(dtrace,
+ [AC_HELP_STRING([--enable-dtrace],
+ [Configure with dtrace static probes])],
+ [enable_dtrace="$enable_dtrace"]) dnl, [enable_dtrace="no"])
+
+if test "$enable_dtrace" = "yes"; then
+ AC_CHECK_TOOL(DTRACE, dtrace, none)
+ test "$DTRACE" = "none" && AC_MSG_ERROR([No dtrace utility found.])
+else
+ AC_MSG_RESULT([not specified])
+fi
+
+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" = "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
+ AC_DEFINE(HAVE_DTRACE, 1, [Define to enable DTrace probes (or SystemTap probes on Linux systems)])
+ 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.
@@ -4388,6 +4463,7 @@ dnl
../lib/os_mon/c_src/$host/Makefile:../lib/os_mon/c_src/Makefile.in
dnl ../lib/ssl/c_src/$host/Makefile:../lib/ssl/c_src/Makefile.in
../lib/crypto/c_src/$host/Makefile:../lib/crypto/c_src/Makefile.in
+ ../lib/dtrace/c_src/$host/Makefile:../lib/dtrace/c_src/Makefile.in
../lib/orber/c_src/$host/Makefile:../lib/orber/c_src/Makefile.in
../lib/runtime_tools/c_src/$host/Makefile:../lib/runtime_tools/c_src/Makefile.in
../lib/tools/c_src/$host/Makefile:../lib/tools/c_src/Makefile.in
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index 279844adb2..3fb0964810 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -23,6 +23,8 @@ 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@
LIBS = @LIBS@
Z_LIB=@Z_LIB@
NO_INLINE_FUNCTIONS=false
@@ -483,6 +485,10 @@ GENERATE += $(HIPE_ASM) \
endif
endif
+ifdef DTRACE_ENABLED
+GENERATE += $(TARGET)/erlang_dtrace.h
+endif
+
ifdef HIPE_ENABLED
OPCODE_TABLES += hipe/hipe_ops.tab
endif
@@ -590,6 +596,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 +644,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 +843,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/dtrace-wrapper.h b/erts/emulator/beam/dtrace-wrapper.h
new file mode 100644
index 0000000000..f93871bd25
--- /dev/null
+++ b/erts/emulator/beam/dtrace-wrapper.h
@@ -0,0 +1,118 @@
+/*
+ * %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
+
+#ifndef DTRACE_DRIVER_SKIP_FUNC_DECLARATIONS
+inline void dtrace_proc_str(Process *process, char *process_buf);
+inline void dtrace_pid_str(Eterm pid, char *process_buf);
+inline void dtrace_port_str(Port *port, char *port_buf);
+inline void dtrace_fun_decode(Process *process,
+ Eterm module, Eterm function, int arity,
+ char *process_buf, char *mfa_buf);
+#endif
+
+/*
+ * 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
+
+#ifdef HAVE_DTRACE
+
+#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 /* HAVE_DTRACE */
+
+/* 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 /* HAVE_DTRACE */
+
+#endif /* __DTRACE_WRAPPER_H */
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_node_tables.h b/erts/emulator/beam/erl_node_tables.h
index b0a63ae035..8abb748a78 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[256]; /* 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/global.h b/erts/emulator/beam/global.h
index 97d7f0e904..6f6263d160 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -1072,6 +1072,13 @@ void process_main(void);
Eterm build_stacktrace(Process* c_p, Eterm exc);
Eterm expand_error_value(Process* c_p, Uint freason, Eterm Value);
void erts_save_stacktrace(Process* p, struct StackTrace* s, int depth);
+ERTS_INLINE void dtrace_proc_str(Process *process, char *process_buf);
+ERTS_INLINE void dtrace_pid_str(Eterm pid, char *process_buf);
+ERTS_INLINE void dtrace_port_str(Port *port, char *port_buf);
+ERTS_INLINE void dtrace_drvport_str(ErlDrvPort port, char *port_buf);
+ERTS_INLINE void dtrace_fun_decode(Process *process,
+ Eterm module, Eterm function, int arity,
+ char *process_buf, char *mfa_buf);
/* erl_init.c */
diff --git a/lib/Makefile b/lib/Makefile
index aa4e074830..96ce2d1315 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -31,7 +31,7 @@ ifdef BUILD_ALL
cosTransactions cosEvent cosTime cosNotification \
cosProperty cosFileTransfer cosEventDomain et megaco webtool \
xmerl edoc eunit ssh inviso typer erl_docgen \
- percept dialyzer hipe
+ percept dialyzer dtrace hipe
EXTRA_FILE := $(wildcard EXTRA-APPLICATIONS)
EXTRA_APPLICATIONS := $(if $(EXTRA_FILE),$(shell cat $(EXTRA_FILE)))
endif
diff --git a/lib/dtrace/Makefile b/lib/dtrace/Makefile
new file mode 100644
index 0000000000..29d463aab1
--- /dev/null
+++ b/lib/dtrace/Makefile
@@ -0,0 +1,36 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-2009. 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%
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+#
+# Macros
+#
+
+SUB_DIRECTORIES = src c_src
+
+include vsn.mk
+VSN = $(DTRACE_VSN)
+
+SPECIAL_TARGETS =
+
+#
+# Default Subdir Targets
+#
+include $(ERL_TOP)/make/otp_subdir.mk
diff --git a/lib/dtrace/c_src/Makefile b/lib/dtrace/c_src/Makefile
new file mode 100644
index 0000000000..f3320bb766
--- /dev/null
+++ b/lib/dtrace/c_src/Makefile
@@ -0,0 +1,4 @@
+#
+# Invoke with GNU make or clearmake -C gnu.
+#
+include $(ERL_TOP)/make/run_make.mk
diff --git a/lib/dtrace/c_src/Makefile.in b/lib/dtrace/c_src/Makefile.in
new file mode 100644
index 0000000000..ed13684a95
--- /dev/null
+++ b/lib/dtrace/c_src/Makefile.in
@@ -0,0 +1,152 @@
+#
+# %CopyrightBegin%
+#
+# Copyright 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%
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+include $(ERL_TOP)/make/$(TARGET)/otp_ded.mk
+
+# ----------------------------------------------------
+# Items from top-level configure
+# ----------------------------------------------------
+DTRACE_ENABLED=@DTRACE_ENABLED@
+DTRACE_ENABLED_2STEP=@DTRACE_ENABLED_2STEP@
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(DTRACE_VSN)
+
+# ----------------------------------------------------
+# The following variables differ between systems.
+# Set by configure.
+# ----------------------------------------------------
+CC = $(DED_CC)
+LD = $(DED_LD)
+SHELL = /bin/sh
+LIBS = $(DED_LIBS)
+LDFLAGS += $(DED_LDFLAGS)
+CFLAGS = $(DED_CFLAGS)
+
+DTRACE_LIBNAME = dtrace
+
+
+INCLUDES = $(DED_INCLUDES)
+
+ifeq ($(TYPE),debug)
+TYPEMARKER = .debug
+TYPE_FLAGS = $(subst -O3,,$(subst -O2,,$(CFLAGS))) -DDEBUG
+else
+ifeq ($(TYPE),valgrind)
+TYPEMARKER = .valgrind
+TYPE_FLAGS = $(subst -O3,,$(subst -O2,,$(CFLAGS))) -DVALGRIND
+else
+TYPEMARKER =
+TYPE_FLAGS = $(CFLAGS)
+endif
+endif
+
+ALL_CFLAGS = $(TYPE_FLAGS) $(INCLUDES) -I$(OBJDIR) \
+ -I$(ERL_TOP)/erts/emulator/$(TARGET)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/dtrace-$(VSN)
+
+# ----------------------------------------------------
+# Misc Macros
+# ----------------------------------------------------
+OBJS = $(OBJDIR)/dtrace$(TYPEMARKER).o
+## NIF_MAKEFILE = $(PRIVDIR)/Makefile
+
+# Higher-level makefiles says that we can only compile on UNIX flavors
+NIF_LIB = $(LIBDIR)/dtrace$(TYPEMARKER).so
+
+ifeq ($(HOST_OS),)
+HOST_OS := $(shell $(ERL_TOP)/erts/autoconf/config.guess)
+endif
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt valgrind: $(OBJDIR) $(LIBDIR) $(NIF_LIB)
+
+ifdef DTRACE_ENABLED
+DTRACE_USER_HEADER=$(OBJDIR)/dtrace_user.h
+$(OBJDIR)/dtrace_user.h: ./dtrace_user.d
+ dtrace -h -C $(INCLUDES) \
+ -s ./dtrace_user.d \
+ -o ./dtrace_user.tmp
+ sed -e '/^#define[ ]*ERLANG_[A-Z0-9_]*(.*)/y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/' ./dtrace_user.tmp > $@
+ rm ./dtrace_user.tmp
+else
+DTRACE_USER_HEADER=
+endif
+
+ifdef DTRACE_ENABLED_2STEP
+OBJS += $(OBJDIR)/dtrace_user.o
+$(OBJDIR)/dtrace_user.o: $(OBJS) $(OBJDIR)/dtrace_user.h
+ touch $(OBJDIR)/erlang_dtrace.c
+ $(CC) $(CFLAGS) -c -o $@ $(OBJDIR)/erlang_dtrace.c
+ # The object file created above is immediately clobbered below.
+ # But creating it above avoids chicken-and-egg problem with OBJS
+ dtrace -G -C \
+ -s ./dtrace_user.d \
+ -o $@ $(OBJS)
+endif
+
+$(OBJDIR):
+ -@mkdir -p $(OBJDIR)
+
+$(LIBDIR):
+ -@mkdir -p $(LIBDIR)
+
+$(OBJDIR)/%$(TYPEMARKER).o: %.c $(DTRACE_USER_HEADER)
+ $(INSTALL_DIR) $(OBJDIR)
+ $(CC) -c -o $@ $(ALL_CFLAGS) $<
+
+$(LIBDIR)/dtrace$(TYPEMARKER).so: $(OBJS)
+ $(INSTALL_DIR) $(LIBDIR)
+ $(LD) $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+clean:
+ rm -f $(LIBDIR)/dtrace.so
+ rm -f $(LIBDIR)/dtrace.debug.so
+ rm -f $(LIBDIR)/dtrace.valgrind.so
+ rm -f $(OBJDIR)/dtrace.o
+ rm -f $(OBJDIR)/dtrace.debug.o
+ rm -f $(OBJDIR)/dtrace.valgrind.o
+ rm -f core *~
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/priv/obj
+ $(INSTALL_DIR) $(RELSYSDIR)/priv/lib
+ # $(INSTALL_DATA) $(NIF_MAKEFILE) $(RELSYSDIR)/priv/obj
+ $(INSTALL_PROGRAM) $(OBJS) $(RELSYSDIR)/priv/obj
+ $(INSTALL_PROGRAM) $(NIF_LIB) $(RELSYSDIR)/priv/lib
+
+release_docs_spec:
diff --git a/lib/dtrace/c_src/dtrace.c b/lib/dtrace/c_src/dtrace.c
new file mode 100644
index 0000000000..c9d25ece9c
--- /dev/null
+++ b/lib/dtrace/c_src/dtrace.c
@@ -0,0 +1,174 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright 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%
+ */
+
+/*
+ * Purpose: Dynamically loadable NIF library for DTrace
+ */
+
+
+#include "erl_nif.h"
+#include "config.h"
+#include "sys.h"
+#define DTRACE_DRIVER_SKIP_FUNC_DECLARATIONS
+#include "dtrace-wrapper.h"
+#ifdef HAVE_DTRACE
+#include "dtrace_user.h"
+#endif
+
+void dtrace_nifenv_str(ErlNifEnv *env, char *process_buf);
+void get_string_maybe(ErlNifEnv *env, const ERL_NIF_TERM term, char **ptr, char *buf, int bufsiz);
+
+#ifdef VALGRIND
+ # include <valgrind/memcheck.h>
+#endif
+
+#ifdef __GNUC__
+ # define INLINE __inline__
+#else
+ # define INLINE
+#endif
+
+#define MESSAGE_BUFSIZ 1024
+
+/* NIF interface declarations */
+static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info);
+
+/* The NIFs: */
+static ERL_NIF_TERM available(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM user_trace_s1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM user_trace_i4s4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+
+static ErlNifFunc nif_funcs[] = {
+ {"available", 0, available},
+ {"user_trace_s1", 1, user_trace_s1},
+ {"user_trace_i4s4", 9, user_trace_i4s4}
+};
+
+ERL_NIF_INIT(dtrace, nif_funcs, load, NULL, NULL, NULL)
+
+static ERL_NIF_TERM atom_true;
+static ERL_NIF_TERM atom_false;
+static ERL_NIF_TERM atom_error;
+static ERL_NIF_TERM atom_not_available;
+static ERL_NIF_TERM atom_badarg;
+static ERL_NIF_TERM atom_ok;
+
+static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
+{
+ atom_true = enif_make_atom(env,"true");
+ atom_false = enif_make_atom(env,"false");
+ atom_error = enif_make_atom(env,"error");
+ atom_not_available = enif_make_atom(env,"not_available");
+ atom_badarg = enif_make_atom(env,"badarg");
+ atom_ok = enif_make_atom(env,"ok");
+
+ return 0;
+}
+
+static ERL_NIF_TERM available(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+#ifdef HAVE_DTRACE
+ return atom_true;
+#else
+ return atom_false;
+#endif
+}
+
+static ERL_NIF_TERM user_trace_s1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+#ifdef HAVE_DTRACE
+ ErlNifBinary message_bin;
+ DTRACE_CHARBUF(messagebuf, MESSAGE_BUFSIZ + 1);
+
+ if (DTRACE_ENABLED(user_trace_s1)) {
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &message_bin) ||
+ message_bin.size > MESSAGE_BUFSIZ) {
+ return atom_badarg;
+ }
+ memcpy(messagebuf, (char *) message_bin.data, message_bin.size);
+ messagebuf[message_bin.size] = '\0';
+ DTRACE1(user_trace_s1, messagebuf);
+ return atom_true;
+ } else {
+ return atom_false;
+ }
+#else
+ return atom_error;
+#endif
+}
+
+void
+get_string_maybe(ErlNifEnv *env,
+ const ERL_NIF_TERM term, char **ptr, char *buf, int bufsiz)
+{
+ ErlNifBinary str_bin;
+
+ if (!enif_inspect_iolist_as_binary(env, term, &str_bin) ||
+ str_bin.size > bufsiz) {
+ *ptr = NULL;
+ } else {
+ memcpy(buf, (char *) str_bin.data, str_bin.size);
+ buf[str_bin.size] = '\0';
+ *ptr = buf;
+ }
+}
+
+static ERL_NIF_TERM user_trace_i4s4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+#ifdef HAVE_DTRACE
+ DTRACE_CHARBUF(procbuf, 32 + 1);
+ DTRACE_CHARBUF(user_tagbuf, MESSAGE_BUFSIZ + 1);
+ char *utbuf = NULL;
+ ErlNifSInt64 i1, i2, i3, i4;
+ DTRACE_CHARBUF(messagebuf1, MESSAGE_BUFSIZ + 1);
+ DTRACE_CHARBUF(messagebuf2, MESSAGE_BUFSIZ + 1);
+ DTRACE_CHARBUF(messagebuf3, MESSAGE_BUFSIZ + 1);
+ DTRACE_CHARBUF(messagebuf4, MESSAGE_BUFSIZ + 1);
+ char *mbuf1 = NULL, *mbuf2 = NULL, *mbuf3 = NULL, *mbuf4 = NULL;
+
+ if (DTRACE_ENABLED(user_trace_i4s4)) {
+ dtrace_nifenv_str(env, procbuf);
+ get_string_maybe(env, argv[0], &utbuf,
+ user_tagbuf, sizeof(user_tagbuf)-1);
+ if (! enif_get_int64(env, argv[1], &i1))
+ i1 = 0;
+ if (! enif_get_int64(env, argv[2], &i2))
+ i2 = 0;
+ if (! enif_get_int64(env, argv[3], &i3))
+ i3 = 0;
+ if (! enif_get_int64(env, argv[4], &i4))
+ i4 = 0;
+ get_string_maybe(env, argv[5], &mbuf1,
+ messagebuf1, sizeof(messagebuf1)-1);
+ get_string_maybe(env, argv[6], &mbuf2,
+ messagebuf2, sizeof(messagebuf2)-1);
+ get_string_maybe(env, argv[7], &mbuf3,
+ messagebuf3, sizeof(messagebuf3)-1);
+ get_string_maybe(env, argv[8], &mbuf4,
+ messagebuf4, sizeof(messagebuf4)-1);
+ DTRACE10(user_trace_i4s4, procbuf, utbuf,
+ i1, i2, i3, i4, mbuf1, mbuf2, mbuf3, mbuf4);
+ return atom_true;
+ } else {
+ return atom_false;
+ }
+#else
+ return atom_error;
+#endif
+}
diff --git a/lib/dtrace/c_src/dtrace_user.d b/lib/dtrace/c_src/dtrace_user.d
new file mode 100644
index 0000000000..45d3ef3b66
--- /dev/null
+++ b/lib/dtrace/c_src/dtrace_user.d
@@ -0,0 +1,53 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright 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%
+ */
+
+provider erlang {
+ /**
+ * Send a single string to a probe.
+ *
+ * @param NUL-terminated string
+ */
+ probe user_trace__s1(char* message);
+
+ /**
+ * Multi-purpose probe: up to 4 NUL-terminated strings and 4
+ * 64-bit integer arguments.
+ *
+ * @param proc, the PID (string form) of the sending process
+ * @param user_tag, the user tag of the sender
+ * @param i1, integer
+ * @param i2, integer
+ * @param i3, integer
+ * @param i4, integer
+ * @param s1, string/iolist. D's arg6 is NULL if not given by Erlang
+ * @param s2, string/iolist. D's arg7 is NULL if not given by Erlang
+ * @param s3, string/iolist. D's arg8 is NULL if not given by Erlang
+ * @param s4, string/iolist. D's arg9 is NULL if not given by Erlang
+ */
+ probe user_trace__i4s4(char *proc, char *user_tag,
+ int i1, int i2, int i3, int i4,
+ char *s1, char *s2, char *s3, char *s4);
+};
+
+#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/lib/dtrace/ebin/.placeholder b/lib/dtrace/ebin/.placeholder
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dtrace/ebin/.placeholder
diff --git a/lib/dtrace/src/Makefile b/lib/dtrace/src/Makefile
new file mode 100644
index 0000000000..d613402a63
--- /dev/null
+++ b/lib/dtrace/src/Makefile
@@ -0,0 +1,102 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-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%
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(DTRACE_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/dtrace-$(VSN)
+
+# ----------------------------------------------------
+# Common Macros
+# ----------------------------------------------------
+
+MODULES= \
+ dtrace
+
+HRL_FILES= \
+
+INTERNAL_HRL_FILES= \
+
+ERL_FILES= $(MODULES:%=%.erl)
+EXAMPLE_FILES= \
+ ../examples/*
+
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET)
+
+EXECUTABLES= \
+
+APP_FILE= dtrace.app
+
+APP_SRC= $(APP_FILE).src
+APP_TARGET= $(EBIN)/$(APP_FILE)
+
+APPUP_FILE= dtrace.appup
+
+APPUP_SRC= $(APPUP_FILE).src
+APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ERL_COMPILE_FLAGS += \
+ -I../include \
+ -I ../../et/include \
+ -I ../../../libraries/et/include
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt: $(TARGET_FILES)
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f errs core *~
+
+$(APP_TARGET): $(APP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src
+ # $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/examples
+ $(INSTALL_DATA) $(EXAMPLE_FILES) $(RELSYSDIR)/examples
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
+
+release_docs_spec:
diff --git a/lib/dtrace/src/dtrace.app.src b/lib/dtrace/src/dtrace.app.src
new file mode 100644
index 0000000000..764e863559
--- /dev/null
+++ b/lib/dtrace/src/dtrace.app.src
@@ -0,0 +1,27 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2002-2009. 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%
+%%
+{application, dtrace,
+ [{description, "DTRACE version 1"},
+ {vsn, "%VSN%"},
+ {modules, [
+ dtrace
+ ]},
+ {registered, []},
+ {applications, [kernel, stdlib]},
+ {env, []}]}.
diff --git a/lib/dtrace/src/dtrace.appup.src b/lib/dtrace/src/dtrace.appup.src
new file mode 100644
index 0000000000..f730a2f8df
--- /dev/null
+++ b/lib/dtrace/src/dtrace.appup.src
@@ -0,0 +1,19 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2002-2009. 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%
+%%
+{"%VSN%",[],[]}.
diff --git a/lib/dtrace/src/dtrace.erl b/lib/dtrace/src/dtrace.erl
new file mode 100644
index 0000000000..45addafc53
--- /dev/null
+++ b/lib/dtrace/src/dtrace.erl
@@ -0,0 +1,216 @@
+-module(dtrace).
+
+%%% @doc The DTrace interface module
+%%%
+%%% This DTrace interface module, with the corresponding NIFs, should
+%%% work on any operating system platform where user-space DTrace
+%%% probes are supported.
+%%%
+%%% Use the `dtrace:init()' function to load the NIF shared library and
+%%% to initialize library's private state.
+%%%
+%%% It is recommended that you use the `dtrace:p()' function to add
+%%% DTrace probes to your Erlang code. This function can accept up to
+%%% four integer arguments and four string arguments; the integer
+%%% argument(s) must come before any string argument. For example:
+%%% ```
+%%% 1> put(dtrace_utag, "GGOOOAAALL!!!!!").
+%%% undefined
+%%% 2> dtrace:init().
+%%% ok
+%%%
+%%% % % % Enable the DTrace probe using the 'dtrace' command.
+%%%
+%%% 3> dtrace:p(7, 8, 9, "one", "four").
+%%% true
+%%% '''
+%%%
+%%% Output from the example D script `user-probe.d' looks like:
+%%% ```
+%%% <0.34.0> GGOOOAAALL!!!!! 7 8 9 0 'one' 'four' '' ''
+%%% '''
+%%%
+%%% If the expected type of variable is not present, e.g. integer when
+%%% integer() is expected, or an I/O list when iolist() is expected,
+%%% then the driver will ignore the user's input and use a default
+%%% value of 0 or NULL, respectively.
+
+-export([init/0, available/0,
+ user_trace_s1/1, % TODO: unify with pid & tag args like user_trace_i4s4
+ p/0, p/1, p/2, p/3, p/4, p/5, p/6, p/7, p/8]).
+-export([scaff/0]). % Development only
+-export([user_trace_i4s4/9]). % Know what you're doing!
+
+-type probe_arg() :: integer() | iolist().
+-type int_p_arg() :: integer() | iolist() | undef.
+%% The *_maybe() types use atom() instead of a stricter 'undef'
+%% because user_trace_i4s4/9 is exposed to the outside world, and
+%% because the driver will allow any atom to be used as a "not
+%% present" indication, we'll allow any atom in the types.
+-type integer_maybe() :: integer() | atom().
+-type iolist_maybe() :: iolist() | atom().
+
+-spec init() -> ok | {error, {term(), term()}}.
+
+init() ->
+ PrivDir = code:priv_dir(dtrace),
+ Lib = filename:join([PrivDir, "lib", "dtrace"]),
+ erlang:load_nif(Lib, 0).
+
+%%%
+%%% NIF placeholders
+%%%
+
+-spec available() -> true | false.
+
+available() ->
+ erlang:nif_error(nif_not_loaded).
+
+-spec user_trace_s1(iolist()) -> true | false | error | badarg.
+
+user_trace_s1(Message) ->
+ erlang:nif_error(nif_not_loaded).
+
+-spec user_trace_i4s4(iolist(),
+ integer_maybe(), integer_maybe(),
+ integer_maybe(), integer_maybe(),
+ iolist_maybe(), iolist_maybe(),
+ iolist_maybe(), iolist_maybe()) ->
+ true | false | error | badarg.
+
+user_trace_i4s4(_, _, _, _, _, _, _, _, _) ->
+ erlang:nif_error(nif_not_loaded).
+
+%%%
+%%% Erlang support functions
+%%%
+
+-spec p() -> true | false | error | badarg.
+
+p() ->
+ user_trace_int(undef, undef, undef, undef, undef, undef, undef, undef).
+
+-spec p(probe_arg()) -> true | false | error | badarg.
+
+p(I1) when is_integer(I1) ->
+ user_trace_int(I1, undef, undef, undef, undef, undef, undef, undef);
+p(S1) ->
+ user_trace_int(undef, undef, undef, undef, S1, undef, undef, undef).
+
+-spec p(probe_arg(), probe_arg()) -> true | false | error | badarg.
+
+p(I1, I2) when is_integer(I1), is_integer(I2) ->
+ user_trace_int(I1, I2, undef, undef, undef, undef, undef, undef);
+p(I1, S1) when is_integer(I1) ->
+ user_trace_int(I1, undef, undef, undef, S1, undef, undef, undef);
+p(S1, S2) ->
+ user_trace_int(undef, undef, undef, undef, S1, S2, undef, undef).
+
+-spec p(probe_arg(), probe_arg(), probe_arg()) -> true | false | error | badarg.
+
+p(I1, I2, I3) when is_integer(I1), is_integer(I2), is_integer(I3) ->
+ user_trace_int(I1, I2, I3, undef, undef, undef, undef, undef);
+p(I1, I2, S1) when is_integer(I1), is_integer(I2) ->
+ user_trace_int(I1, I2, undef, undef, S1, undef, undef, undef);
+p(I1, S1, S2) when is_integer(I1) ->
+ user_trace_int(I1, undef, undef, undef, S1, S2, undef, undef);
+p(S1, S2, S3) ->
+ user_trace_int(undef, undef, undef, undef, S1, S2, S3, undef).
+
+-spec p(probe_arg(), probe_arg(), probe_arg(), probe_arg()) ->
+ true | false | error | badarg.
+
+p(I1, I2, I3, I4) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) ->
+ user_trace_int(I1, I2, I3, I4, undef, undef, undef, undef);
+p(I1, I2, I3, S1) when is_integer(I1), is_integer(I2), is_integer(I3) ->
+ user_trace_int(I1, I2, I3, undef, S1, undef, undef, undef);
+p(I1, I2, S1, S2) when is_integer(I1), is_integer(I2) ->
+ user_trace_int(I1, I2, undef, undef, S1, S2, undef, undef);
+p(I1, S1, S2, S3) when is_integer(I1) ->
+ user_trace_int(I1, undef, undef, undef, S1, S2, S3, undef);
+p(S1, S2, S3, S4) ->
+ user_trace_int(undef, undef, undef, undef, S1, S2, S3, S4).
+
+-spec p(probe_arg(), probe_arg(), probe_arg(), probe_arg(),
+ probe_arg()) ->
+ true | false | error | badarg.
+
+p(I1, I2, I3, I4, S1) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) ->
+ user_trace_int(I1, I2, I3, I4, S1, undef, undef, undef);
+p(I1, I2, I3, S1, S2) when is_integer(I1), is_integer(I2), is_integer(I3) ->
+ user_trace_int(I1, I2, I3, undef, S1, S2, undef, undef);
+p(I1, I2, S1, S2, S3) when is_integer(I1), is_integer(I2) ->
+ user_trace_int(I1, I2, undef, undef, S1, S2, S3, undef);
+p(I1, S1, S2, S3, S4) when is_integer(I1) ->
+ user_trace_int(I1, undef, undef, undef, S1, S2, S3, S4).
+
+-spec p(probe_arg(), probe_arg(), probe_arg(), probe_arg(),
+ probe_arg(), probe_arg()) ->
+ true | false | error | badarg.
+
+p(I1, I2, I3, I4, S1, S2) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) ->
+ user_trace_int(I1, I2, I3, I4, S1, S2, undef, undef);
+p(I1, I2, I3, S1, S2, S3) when is_integer(I1), is_integer(I2), is_integer(I3) ->
+ user_trace_int(I1, I2, I3, undef, S1, S2, S3, undef);
+p(I1, I2, S1, S2, S3, S4) when is_integer(I1), is_integer(I2) ->
+ user_trace_int(I1, I2, undef, undef, S1, S2, S3, S4).
+
+-spec p(probe_arg(), probe_arg(), probe_arg(), probe_arg(),
+ probe_arg(), probe_arg(), probe_arg()) ->
+ true | false | error | badarg.
+
+p(I1, I2, I3, I4, S1, S2, S3) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) ->
+ user_trace_int(I1, I2, I3, I4, S1, S2, S3, undef);
+p(I1, I2, I3, S1, S2, S3, S4) when is_integer(I1), is_integer(I2), is_integer(I3) ->
+ user_trace_int(I1, I2, I3, undef, S1, S2, S3, S4).
+
+-spec p(probe_arg(), probe_arg(), probe_arg(), probe_arg(),
+ probe_arg(), probe_arg(), probe_arg(), probe_arg()) ->
+ true | false | error | badarg.
+
+p(I1, I2, I3, I4, S1, S2, S3, S4) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) ->
+ user_trace_int(I1, I2, I3, I4, S1, S2, S3, S4).
+
+-spec user_trace_int(int_p_arg(), int_p_arg(), int_p_arg(), int_p_arg(),
+ int_p_arg(), int_p_arg(), int_p_arg(), int_p_arg()) ->
+ true | false | error | badarg.
+
+user_trace_int(I1, I2, I3, I4, S1, S2, S3, S4) ->
+ UTag = prim_file:get_dtrace_utag(),
+ user_trace_i4s4(UTag, I1, I2, I3, I4, S1, S2, S3, S4).
+
+%% Scaffolding to write tedious code: quick brute force and not 100% correct.
+
+scaff_int_args(N) ->
+ L = lists:sublist(["I1", "I2", "I3", "I4"], N),
+ [string:join(L, ", ")].
+
+scaff_int_guards(N) ->
+ L = lists:sublist(["is_integer(I1)", "is_integer(I2)", "is_integer(I3)",
+ "is_integer(I4)"], N),
+ lists:flatten(string:join(L, ", ")).
+
+scaff_char_args(N) ->
+ L = lists:sublist(["S1", "S2", "S3", "S4"], N),
+ [string:join(L, ", ")].
+
+scaff_fill(N) ->
+ [string:join(lists:duplicate(N, "undef"), ", ")].
+
+scaff() ->
+ L = [begin
+ IntArgs = scaff_int_args(N_int),
+ IntGuards = scaff_int_guards(N_int),
+ IntFill = scaff_fill(4 - N_int),
+ CharArgs = scaff_char_args(N_char),
+ CharFill = scaff_fill(4 - N_char),
+ InArgs = string:join(IntArgs ++ CharArgs, ", "),
+ OutArgs = string:join(IntArgs ++ IntFill ++ CharArgs ++ CharFill,
+ ", "),
+ {N_int + N_char,
+ lists:flatten([io_lib:format("p(~s) when ~s ->\n",
+ [InArgs, IntGuards]),
+ io_lib:format(" user_trace_int(~s);\n", [OutArgs])
+ ])}
+ end || N_int <- [0,1,2,3,4], N_char <- [0,1,2,3,4]],
+ [io:format("%%~p\n~s", [N, Str]) || {N, Str} <- lists:sort(L)].
diff --git a/lib/dtrace/vsn.mk b/lib/dtrace/vsn.mk
new file mode 100644
index 0000000000..73bf983c00
--- /dev/null
+++ b/lib/dtrace/vsn.mk
@@ -0,0 +1 @@
+DTRACE_VSN = 0.8