[PATCH] FW: Ruby 1.8.2 DTrace Support
From:
Paul Duncan <pabs@...>
Date:
2005-08-18 19:36:36 UTC
List:
ruby-core #5557
Hi everyone, Richard Lowe (richlowe@richlowe.net) wrote this patch to add a DTrace provider into Ruby. He's not subscribed to ruby-core, so he asked me to forward the patch and his description on to the list. The patch is attached, and his description is below. I don't know anything about DTrace or the patch, so please forward questions or comments directly to him (discussion can stay here, he can read the archives like anyone else :D). Hopefully this is useful to some people. He showed me the output and it looked pretty interesting. ----- Forwarded message from Richard Lowe <richlowe@richlowe.net> ----- Date: Thu, 18 Aug 2005 10:59:33 -0400 User-Agent: Mozilla/5.0 (X11; U; SunOS i86pc; en-US; rv:1.7) Gecko/20050704 From: Richard Lowe <richlowe@richlowe.net> To: pabs@pablotron.org Subject: DTrace support. [I'm not subscribed, so please CC me on replies] I spent the last couple of days hacking a DTrace (http://www.sun.com/bigadmin/content/dtrace/) provider into ruby. What I have right now is basically functioning, but I don't have the understanding of the ruby internals necessary to improve upon it further or maintain it. I don't want to leave it to rot on my disk, so if anybody wants to use it, clean it up, maintain it, integrate it, or whatever, the patch (against ruby 1.8.2) is attached. Solaris patch(1) fails to apply it because of the file creation, GNU patch applies it just fine (removing the 'dtrace.d' hunk from the diff, and creating that file by hand should let Solaris patch(1) apply it). I hope it's useful to somebody :) I've tested it under Solaris Express on x86, 64bit builds of ruby will need the the -32 argument to dtrace in the Makefile replaced with -64. (I couldn't find a sensible way to autoconfize that). It defines two probes, ruby$pid:::function-entry (called before a method call is entered), and ruby$pid:::function-return (called after a method call returns). (where $pid is the process ID of a running ruby) The arguments to each are the classname, methodname, source file name, and line number. ----- End forwarded message ----- -- Paul Duncan <pabs@pablotron.org> OpenPGP Key ID: 0x82C29562 http://www.pablotron.org/ http://www.paulduncan.org/
Attachments (2)
ruby-1.8.2-dtrace.diff
(13.9 KB, text/x-diff)
Index: Makefile.in
===================================================================
--- Makefile.in (.../clean) (revision 23)
+++ Makefile.in (.../diff-tree) (revision 23)
@@ -103,6 +103,8 @@
version.@OBJEXT@ \
$(MISSING)
+DTRACE_OBJS = @DTRACE_OBJS@
+
MANTYPE = @MANTYPE@
SCRIPT_ARGS = --dest-dir="$(DESTDIR)" \
@@ -124,13 +126,13 @@
# We must `rm' the library each time this rule is invoked because "updating" a
# MAB library on Apple/NeXT (see --enable-fat-binary in configure) is not
# supported.
-$(LIBRUBY_A): $(OBJS) $(DMYEXT)
+$(LIBRUBY_A): $(OBJS) $(DMYEXT) $(DTRACE_OBJS)
@rm -f $@
- @AR@ rcu $@ $(OBJS) $(DMYEXT)
+ @AR@ rcu $@ $(DTRACE_OBJS) $(OBJS) $(DMYEXT)
@-@RANLIB@ $@ 2> /dev/null || true
-$(LIBRUBY_SO): $(OBJS) $(DLDOBJS) miniruby$(EXEEXT) $(PREP)
- $(LDSHARED) $(DLDFLAGS) $(OBJS) $(DLDOBJS) $(SOLIBS) -o $@
+$(LIBRUBY_SO): $(OBJS) $(DLDOBJS) miniruby$(EXEEXT) $(PREP) $(DTRACE_OBJS)
+ $(LDSHARED) $(DLDFLAGS) $(DTRACE_OBJS) $(OBJS) $(DLDOBJS) $(SOLIBS) -o $@
@-$(MINIRUBY) -e 'ARGV.each{|link| File.delete link if File.exist? link; \
File.symlink "$(LIBRUBY_SO)", link}' \
$(LIBRUBY_ALIASES) || true
@@ -378,3 +380,6 @@
missing.h env.h node.h st.h util.h
version.@OBJEXT@: version.c ruby.h config.h defines.h intern.h missing.h \
version.h
+
+dtrace.@OBJEXT@: dtrace.d $(OBJS) $(MAINOBJ)
+ /usr/sbin/dtrace -G -32 -o $@ -s dtrace.d $(OBJS) $(MAINOBJ)
Index: configure.in
===================================================================
--- configure.in (.../clean) (revision 23)
+++ configure.in (.../diff-tree) (revision 23)
@@ -383,6 +383,12 @@
sys/mkdev.h sys/utime.h netinet/in_systm.h float.h ieeefp.h pthread.h \
ucontext.h intrinsics.h)
+
+AC_CHECK_HEADER(sys/sdt.h)
+if test "$ac_cv_header_sys_sdt_h" == "yes"; then
+ AC_DEFINE(HAVE_SDT_H)
+fi
+
dnl Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_UID_T
AC_TYPE_SIZE_T
@@ -415,6 +421,18 @@
AC_DEFINE(USE_SETREUID)
AC_DEFINE(USE_SETREGID)
fi
+
+AC_ARG_ENABLE(dtrace,
+ [ --enable-dtrace Enable DTrace support.],
+ [enable_dtrace=$enableval])
+if test "$enable_dtrace" == "yes" -a "$ac_cv_header_sys_sdt_h" == "yes"; then
+ AC_DEFINE(ENABLE_DTRACE)
+ DTRACE_OBJS="dtrace.o"
+else
+ DTRACE_OBJS=""
+fi
+AC_SUBST(DTRACE_OBJS)
+
AC_STRUCT_TIMEZONE
AC_CACHE_CHECK(for struct tm.tm_gmtoff, rb_cv_member_struct_tm_tm_gmtoff,
[AC_TRY_COMPILE([#include <time.h>],
Index: configure
===================================================================
--- configure (.../clean) (revision 23)
+++ configure (.../diff-tree) (revision 23)
@@ -308,7 +308,7 @@
# include <unistd.h>
#endif"
-ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS MAJOR MINOR TEENY build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os CC ac_ct_CC CFLAGS LDFLAGS CPPFLAGS EXEEXT OBJEXT CPP EGREP GNU_LD CPPOUTFILE OUTFLAG YACC RANLIB ac_ct_RANLIB AR ac_ct_AR NM ac_ct_NM WINDRES ac_ct_WINDRES DLLWRAP ac_ct_DLLWRAP LN_S SET_MAKE LIBOBJS ALLOCA DLDFLAGS ARCH_FLAG STATIC CCDLFLAGS LDSHARED DLEXT DLEXT2 LIBEXT LINK_SO LIBPATHFLAG RPATHFLAG TRY_LINK STRIP EXTSTATIC setup MINIRUBY PREP ARCHFILE RDOCTARGET XCFLAGS XLDFLAGS LIBRUBY_LDSHARED LIBRUBY_DLDFLAGS RUBY_INSTALL_NAME rubyw_install_name RUBYW_INSTALL_NAME RUBY_SO_NAME LIBRUBY_A LIBRUBY_SO LIBRUBY_ALIASES LIBRUBY LIBRUBYARG LIBRUBYARG_STATIC LIBRUBYARG_SHARED SOLIBS DLDLIBS ENABLE_SHARED MAINLIBS COMMON_LIBS COMMON_MACROS COMMON_HEADERS EXPORT_PREFIX MAKEFILES arch sitearch sitedir configure_args NROFF MANTYPE LTLIBOBJS'
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS MAJOR MINOR TEENY build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os CC ac_ct_CC CFLAGS LDFLAGS CPPFLAGS EXEEXT OBJEXT CPP EGREP GNU_LD CPPOUTFILE OUTFLAG YACC RANLIB ac_ct_RANLIB AR ac_ct_AR NM ac_ct_NM WINDRES ac_ct_WINDRES DLLWRAP ac_ct_DLLWRAP LN_S SET_MAKE LIBOBJS ALLOCA DTRACE_OBJS DLDFLAGS ARCH_FLAG STATIC CCDLFLAGS LDSHARED DLEXT DLEXT2 LIBEXT LINK_SO LIBPATHFLAG RPATHFLAG TRY_LINK STRIP EXTSTATIC setup MINIRUBY PREP ARCHFILE RDOCTARGET XCFLAGS XLDFLAGS LIBRUBY_LDSHARED LIBRUBY_DLDFLAGS RUBY_INSTALL_NAME rubyw_install_name RUBYW_INSTALL_NAME RUBY_SO_NAME LIBRUBY_A LIBRUBY_SO LIBRUBY_ALIASES LIBRUBY LIBRUBYARG LIBRUBYARG_STATIC LIBRUBYARG_SHARED SOLIBS DLDLIBS ENABLE_SHARED MAINLIBS COMMON_LIBS COMMON_MACROS COMMON_HEADERS EXPORT_PREFIX MAKEFILES arch sitearch sitedir configure_args NROFF MANTYPE LTLIBOBJS'
ac_subst_files=''
# Initialize some variables set by options.
@@ -863,6 +863,7 @@
--disable-largefile omit support for large files
--enable-pthread use pthread library.
--enable-setreuid use setreuid()/setregid() according to need even if obsolete.
+ --enable-dtrace Enable DTrace support.
--disable-rpath embed run path into extension libraries.
--enable-shared build a shared library for Ruby.
--enable-install-doc build and install rdoc indexes during install
@@ -10146,6 +10147,152 @@
done
+
+if test "${ac_cv_header_sys_sdt_h+set}" = set; then
+ echo "$as_me:$LINENO: checking for sys/sdt.h" >&5
+echo $ECHO_N "checking for sys/sdt.h... $ECHO_C" >&6
+if test "${ac_cv_header_sys_sdt_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_sys_sdt_h" >&5
+echo "${ECHO_T}$ac_cv_header_sys_sdt_h" >&6
+else
+ # Is the header compilable?
+echo "$as_me:$LINENO: checking sys/sdt.h usability" >&5
+echo $ECHO_N "checking sys/sdt.h usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+$ac_includes_default
+#include <sys/sdt.h>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+ (eval $ac_compile) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } &&
+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; } &&
+ { ac_try='test -s conftest.$ac_objext'
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }; }; then
+ ac_header_compiler=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking sys/sdt.h presence" >&5
+echo $ECHO_N "checking sys/sdt.h presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+#include <sys/sdt.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+ (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } >/dev/null; then
+ if test -s conftest.err; then
+ ac_cpp_err=$ac_c_preproc_warn_flag
+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+ else
+ ac_cpp_err=
+ fi
+else
+ ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+ ac_header_preproc=yes
+else
+ echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+ yes:no: )
+ { echo "$as_me:$LINENO: WARNING: sys/sdt.h: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: sys/sdt.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/sdt.h: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: sys/sdt.h: proceeding with the compiler's result" >&2;}
+ ac_header_preproc=yes
+ ;;
+ no:yes:* )
+ { echo "$as_me:$LINENO: WARNING: sys/sdt.h: present but cannot be compiled" >&5
+echo "$as_me: WARNING: sys/sdt.h: present but cannot be compiled" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/sdt.h: check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: sys/sdt.h: check for missing prerequisite headers?" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/sdt.h: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: sys/sdt.h: see the Autoconf documentation" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/sdt.h: section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: sys/sdt.h: section \"Present But Cannot Be Compiled\"" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/sdt.h: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: sys/sdt.h: proceeding with the preprocessor's result" >&2;}
+ { echo "$as_me:$LINENO: WARNING: sys/sdt.h: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: sys/sdt.h: in the future, the compiler will take precedence" >&2;}
+ (
+ cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists. ##
+## ------------------------------------------ ##
+_ASBOX
+ ) |
+ sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+echo "$as_me:$LINENO: checking for sys/sdt.h" >&5
+echo $ECHO_N "checking for sys/sdt.h... $ECHO_C" >&6
+if test "${ac_cv_header_sys_sdt_h+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ ac_cv_header_sys_sdt_h=$ac_header_preproc
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_sys_sdt_h" >&5
+echo "${ECHO_T}$ac_cv_header_sys_sdt_h" >&6
+
+fi
+
+
+if test "$ac_cv_header_sys_sdt_h" == "yes"; then
+ cat >>confdefs.h <<\_ACEOF
+#define HAVE_SDT_H 1
+_ACEOF
+
+fi
+
echo "$as_me:$LINENO: checking for uid_t in sys/types.h" >&5
echo $ECHO_N "checking for uid_t in sys/types.h... $ECHO_C" >&6
if test "${ac_cv_type_uid_t+set}" = set; then
@@ -11773,6 +11920,23 @@
_ACEOF
fi
+
+# Check whether --enable-dtrace or --disable-dtrace was given.
+if test "${enable_dtrace+set}" = set; then
+ enableval="$enable_dtrace"
+ enable_dtrace=$enableval
+fi;
+if test "$enable_dtrace" == "yes" -a "$ac_cv_header_sys_sdt_h" == "yes"; then
+ cat >>confdefs.h <<\_ACEOF
+#define ENABLE_DTRACE 1
+_ACEOF
+
+ DTRACE_OBJS="dtrace.o"
+else
+ DTRACE_OBJS=""
+fi
+
+
echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5
echo $ECHO_N "checking whether struct tm is in sys/time.h or time.h... $ECHO_C" >&6
if test "${ac_cv_struct_tm+set}" = set; then
@@ -15810,6 +15974,7 @@
s,@SET_MAKE@,$SET_MAKE,;t t
s,@LIBOBJS@,$LIBOBJS,;t t
s,@ALLOCA@,$ALLOCA,;t t
+s,@DTRACE_OBJS@,$DTRACE_OBJS,;t t
s,@DLDFLAGS@,$DLDFLAGS,;t t
s,@ARCH_FLAG@,$ARCH_FLAG,;t t
s,@STATIC@,$STATIC,;t t
Index: eval.c
===================================================================
--- eval.c (.../clean) (revision 23)
+++ eval.c (.../diff-tree) (revision 23)
@@ -136,6 +136,10 @@
#include <sys/stat.h>
+#ifdef ENABLE_DTRACE
+#include <sys/sdt.h>
+#endif /* ENABLE_DTRACE */
+
VALUE rb_cProc;
static VALUE rb_cBinding;
static VALUE proc_invoke _((VALUE,VALUE,VALUE,VALUE));
@@ -1011,7 +1015,9 @@
#define YIELD_FUNC_AVALUE 1
#define YIELD_FUNC_SVALUE 2
-static VALUE rb_call _((VALUE,VALUE,ID,int,const VALUE*,int));
+/* DTrace cannot instrument a static function */
+VALUE rb_call _((VALUE,VALUE,ID,int,const VALUE*,int));
+
static VALUE module_setup _((VALUE,NODE*));
static VALUE massign _((VALUE,NODE*,VALUE,int));
@@ -5689,7 +5695,8 @@
return result;
}
-static VALUE
+/* DTrace cannot instrument a static function */
+VALUE
rb_call(klass, recv, mid, argc, argv, scope)
VALUE klass, recv;
ID mid;
@@ -5701,11 +5708,25 @@
int noex;
ID id = mid;
struct cache_entry *ent;
+ VALUE callret;
+ char *classname;
+ char *methodname;
if (!klass) {
rb_raise(rb_eNotImpError, "method `%s' called on terminated object (0x%lx)",
rb_id2name(mid), recv);
}
+
+ classname = rb_class2name(klass);
+ methodname = rb_id2name(mid);
+
+#ifdef ENABLE_DTRACE
+ /* Don't pass bad pointers into the probe routine */
+ if (classname && methodname && ruby_sourcefile)
+ DTRACE_PROBE4(ruby, function__entry, classname, methodname,
+ ruby_sourcefile, ruby_sourceline);
+#endif /* ENABLE_DTRACE */
+
/* is it in the method cache? */
ent = cache + EXPR1(klass, mid);
if (ent->mid == mid && ent->klass == klass) {
@@ -5740,7 +5761,17 @@
}
}
- return rb_call0(klass, recv, mid, id, argc, argv, body, noex & NOEX_NOSUPER);
+
+ callret = rb_call0(klass, recv, mid, id, argc, argv, body, noex & NOEX_NOSUPER);
+
+#ifdef ENABLE_DTRACE
+ /* Don't pass bad pointers into the probe routine */
+ if (classname && methodname && ruby_sourcefile)
+ DTRACE_PROBE4(ruby, function__return, classname, methodname,
+ ruby_sourcefile, ruby_sourceline);
+#endif /* ENABLE_DTRACE */
+
+ return (callret);
}
VALUE
signature.asc
(189 Bytes, application/pgp-signature)