diff options
-rw-r--r-- | README.dtrace.md | 422 | ||||
-rw-r--r-- | configure.in | 4 | ||||
-rw-r--r-- | erts/configure.in | 76 | ||||
-rw-r--r-- | erts/emulator/Makefile.in | 25 | ||||
-rw-r--r-- | erts/emulator/beam/dtrace-wrapper.h | 118 | ||||
-rw-r--r-- | erts/emulator/beam/erl_driver.h | 2 | ||||
-rw-r--r-- | erts/emulator/beam/erl_node_tables.h | 1 | ||||
-rw-r--r-- | erts/emulator/beam/global.h | 7 | ||||
-rw-r--r-- | lib/Makefile | 2 | ||||
-rw-r--r-- | lib/dtrace/Makefile | 36 | ||||
-rw-r--r-- | lib/dtrace/c_src/Makefile | 4 | ||||
-rw-r--r-- | lib/dtrace/c_src/Makefile.in | 152 | ||||
-rw-r--r-- | lib/dtrace/c_src/dtrace.c | 174 | ||||
-rw-r--r-- | lib/dtrace/c_src/dtrace_user.d | 53 | ||||
-rw-r--r-- | lib/dtrace/ebin/.placeholder | 0 | ||||
-rw-r--r-- | lib/dtrace/src/Makefile | 102 | ||||
-rw-r--r-- | lib/dtrace/src/dtrace.app.src | 27 | ||||
-rw-r--r-- | lib/dtrace/src/dtrace.appup.src | 19 | ||||
-rw-r--r-- | lib/dtrace/src/dtrace.erl | 216 | ||||
-rw-r--r-- | lib/dtrace/vsn.mk | 1 |
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 |