Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

* probes.d: add DTrace probe declarations. [ruby-core:27448]

* array.c (empty_ary_alloc, ary_new): added array create DTrace probe.

* compile.c (rb_insns_name): allowing DTrace probes to access
  instruction sequence name.

* Makefile.in: translate probes.d file to appropriate header file.

* common.mk: declare dependencies on the DTrace header.

* configure.in: add a test for existence of DTrace.

* eval.c (setup_exception): add a probe for when an exception is
  raised.

* gc.c: Add DTrace probes for mark begin and end, and sweep begin and
  end.

* hash.c (empty_hash_alloc): Add a probe for hash allocation.

* insns.def: Add probes for function entry and return.

* internal.h: function declaration for compile.c change.

* load.c (rb_f_load): add probes for `load` entry and exit, require
  entry and exit, and wrapping search_required for load path search.

* object.c (rb_obj_alloc): added a probe for general object creation.

* parse.y (yycompile0): added a probe around parse and compile phase.

* string.c (empty_str_alloc, str_new): DTrace probes for string
  allocation.

* test/dtrace/*: tests for DTrace probes.

* vm.c (vm_invoke_proc): add probes for function return on exception
  raise, hash create, and instruction sequence execution.

* vm_core.h: add probe declarations for function entry and exit.

* vm_dump.c: add probes header file.

* vm_eval.c (vm_call0_cfunc, vm_call0_cfunc_with_frame): add probe on
  function entry and return.

* vm_exec.c: expose instruction number to instruction name function.

* vm_insnshelper.c: add function entry and exit probes for cfunc
  methods.

* vm_insnhelper.h: vm usage information is always collected, so
  uncomment the functions.

12 19:14:50 2012  Akinori MUSHA  <knu@iDaemons.org>

* configure.in (isinf, isnan): isinf() and isnan() are macros on
  DragonFly which cannot be found by AC_REPLACE_FUNCS().  This
  workaround enforces the fact that they exist on DragonFly.

12 15:59:38 2012  Shugo Maeda  <shugo@ruby-lang.org>

* vm_core.h (rb_call_info_t::refinements), compile.c (new_callinfo),
  vm_insnhelper.c (vm_search_method): revert r37616 because it's too
  slow.  [ruby-dev:46477]

* test/ruby/test_refinement.rb (test_inline_method_cache): skip
  the test until the bug is fixed efficiently.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37631 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
  • Loading branch information...
commit 4c740bae97f31e16cc6d15753868b6bd49e330d0 1 parent 25f64fc
@tenderlove tenderlove authored
Showing with 850 additions and 25 deletions.
  1. +1 −0  .gitignore
  2. +57 −0 ChangeLog
  3. +13 −0 Makefile.in
  4. +17 −1 array.c
  5. +12 −10 common.mk
  6. +6 −0 compile.c
  7. +3 −0  configure.in
  8. +6 −0 eval.c
  9. +29 −1 gc.c
  10. +12 −1 hash.c
  11. +33 −0 insns.def
  12. +1 −0  internal.h
  13. +40 −0 load.c
  14. +9 −0 object.c
  15. +13 −0 parse.y
  16. +37 −0 probes.d
  17. +19 −1 string.c
  18. +1 −0  test/dtrace/dummy.rb
  19. +33 −0 test/dtrace/helper.rb
  20. +35 −0 test/dtrace/test_array_create.rb
  21. +58 −0 test/dtrace/test_function_entry.rb
  22. +26 −0 test/dtrace/test_gc.rb
  23. +52 −0 test/dtrace/test_hash_create.rb
  24. +52 −0 test/dtrace/test_load.rb
  25. +35 −0 test/dtrace/test_object_create_start.rb
  26. +29 −0 test/dtrace/test_raise.rb
  27. +34 −0 test/dtrace/test_require.rb
  28. +55 −0 test/dtrace/test_singleton_function.rb
  29. +27 −0 test/dtrace/test_string.rb
  30. +24 −0 tool/gen_dummy_probes.sed
  31. +34 −8 vm.c
  32. +30 −0 vm_core.h
  33. +1 −0  vm_dump.c
  34. +5 −0 vm_eval.c
  35. +2 −0  vm_exec.c
  36. +7 −0 vm_insnhelper.c
  37. +2 −3 vm_insnhelper.h
View
1  .gitignore
@@ -55,6 +55,7 @@ y.tab.c
/exts.mk
/goruby
/id.h
+/probes.h
/largefile.h
/lex.c
/libruby*.*
View
57 ChangeLog
@@ -1,3 +1,60 @@
+Tue Nov 13 06:50:02 2012 Aaron Patterson <aaron@tenderlovemaking.com>
+
+ * probes.d: add DTrace probe declarations. [ruby-core:27448]
+
+ * array.c (empty_ary_alloc, ary_new): added array create DTrace probe.
+
+ * compile.c (rb_insns_name): allowing DTrace probes to access
+ instruction sequence name.
+
+ * Makefile.in: translate probes.d file to appropriate header file.
+
+ * common.mk: declare dependencies on the DTrace header.
+
+ * configure.in: add a test for existence of DTrace.
+
+ * eval.c (setup_exception): add a probe for when an exception is
+ raised.
+
+ * gc.c: Add DTrace probes for mark begin and end, and sweep begin and
+ end.
+
+ * hash.c (empty_hash_alloc): Add a probe for hash allocation.
+
+ * insns.def: Add probes for function entry and return.
+
+ * internal.h: function declaration for compile.c change.
+
+ * load.c (rb_f_load): add probes for `load` entry and exit, require
+ entry and exit, and wrapping search_required for load path search.
+
+ * object.c (rb_obj_alloc): added a probe for general object creation.
+
+ * parse.y (yycompile0): added a probe around parse and compile phase.
+
+ * string.c (empty_str_alloc, str_new): DTrace probes for string
+ allocation.
+
+ * test/dtrace/*: tests for DTrace probes.
+
+ * vm.c (vm_invoke_proc): add probes for function return on exception
+ raise, hash create, and instruction sequence execution.
+
+ * vm_core.h: add probe declarations for function entry and exit.
+
+ * vm_dump.c: add probes header file.
+
+ * vm_eval.c (vm_call0_cfunc, vm_call0_cfunc_with_frame): add probe on
+ function entry and return.
+
+ * vm_exec.c: expose instruction number to instruction name function.
+
+ * vm_insnshelper.c: add function entry and exit probes for cfunc
+ methods.
+
+ * vm_insnhelper.h: vm usage information is always collected, so
+ uncomment the functions.
+
Mon Nov 12 19:14:50 2012 Akinori MUSHA <knu@iDaemons.org>
* configure.in (isinf, isnan): isinf() and isnan() are macros on
View
13 Makefile.in
@@ -147,6 +147,7 @@ OBJDUMP = @OBJDUMP@
OBJCOPY = @OBJCOPY@
VCS = @VCS@
VCSUP = @VCSUP@
+DTRACE = @DTRACE@
OBJEXT = @OBJEXT@
ASMEXT = S
@@ -263,6 +264,8 @@ reconfig config.status: $(srcdir)/configure $(srcdir)/enc/Makefile.in \
$(srcdir)/configure: $(srcdir)/configure.in
$(CHDIR) $(srcdir) && exec $(AUTOCONF)
+incs: id.h probes.h
+
# Things which should be considered:
# * with gperf v.s. without gperf
# * committers may have various versions of gperf
@@ -318,6 +321,16 @@ enc/unicode/name2ctype.h: enc/unicode/name2ctype.kwd
@$(ECHO) preprocessing $<
$(Q) $(CPP) $(warnflags) $(XCFLAGS) $(CPPFLAGS) $(COUTFLAG)$@ -E $< > $@
+.d.h:
+ @$(ECHO) translating probes $<
+ $(Q)if test -n '$(DTRACE)'; then\
+ $(DTRACE) -o $@.tmp -h -s $<; \
+ sed -e 's/RUBY_/RUBY_DTRACE_/g' $@.tmp | sed -e 's/PROBES_H_TMP/PROBES_H/g' >$@; \
+ rm $@.tmp; \
+ else \
+ sed -f $(srcdir)/tool/gen_dummy_probes.sed $< > $@; \
+ fi
+
clean-local::
$(Q)$(RM) ext/extinit.c ext/extinit.$(OBJEXT) ext/ripper/y.output \
enc/encinit.c enc/encinit.$(OBJEXT)
View
18 array.c
@@ -16,6 +16,7 @@
#include "ruby/st.h"
#include "ruby/encoding.h"
#include "internal.h"
+#include "probes.h"
#ifndef ARRAY_DEBUG
# define NDEBUG
@@ -373,6 +374,16 @@ ary_alloc(VALUE klass)
}
static VALUE
+empty_ary_alloc(VALUE klass)
+{
+ if(RUBY_DTRACE_ARRAY_CREATE_ENABLED()) {
+ RUBY_DTRACE_ARRAY_CREATE(0, rb_sourcefile(), rb_sourceline());
+ }
+
+ return ary_alloc(klass);
+}
+
+static VALUE
ary_new(VALUE klass, long capa)
{
VALUE ary;
@@ -383,6 +394,11 @@ ary_new(VALUE klass, long capa)
if (capa > ARY_MAX_SIZE) {
rb_raise(rb_eArgError, "array size too big");
}
+
+ if(RUBY_DTRACE_ARRAY_CREATE_ENABLED()) {
+ RUBY_DTRACE_ARRAY_CREATE(capa, rb_sourcefile(), rb_sourceline());
+ }
+
ary = ary_alloc(klass);
if (capa > RARRAY_EMBED_LEN_MAX) {
FL_UNSET_EMBED(ary);
@@ -5282,7 +5298,7 @@ Init_Array(void)
rb_cArray = rb_define_class("Array", rb_cObject);
rb_include_module(rb_cArray, rb_mEnumerable);
- rb_define_alloc_func(rb_cArray, ary_alloc);
+ rb_define_alloc_func(rb_cArray, empty_ary_alloc);
rb_define_singleton_method(rb_cArray, "[]", rb_ary_s_create, -1);
rb_define_singleton_method(rb_cArray, "try_convert", rb_ary_s_try_convert, 1);
rb_define_method(rb_cArray, "initialize", rb_ary_initialize, -1);
View
22 common.mk
@@ -2,7 +2,7 @@ bin: $(PROGRAM) $(WPROGRAM)
lib: $(LIBRUBY)
dll: $(LIBRUBY_SO)
-.SUFFIXES: .inc .h .c .y .i
+.SUFFIXES: .inc .h .c .y .i .d
# V=0 quiet, V=1 verbose. other values don't work.
V = 0
@@ -609,13 +609,14 @@ RUBY_H_INCLUDES = {$(VPATH)}ruby.h {$(VPATH)}config.h {$(VPATH)}defines.h \
{$(VPATH)}subst.h
ENCODING_H_INCLUDES= {$(VPATH)}encoding.h {$(VPATH)}oniguruma.h
ID_H_INCLUDES = {$(VPATH)}id.h
+PROBES_H_INCLUDES = {$(VPATH)}probes.h
VM_CORE_H_INCLUDES = {$(VPATH)}vm_core.h {$(VPATH)}thread_$(THREAD_MODEL).h \
{$(VPATH)}node.h {$(VPATH)}method.h {$(VPATH)}ruby_atomic.h \
- $(ID_H_INCLUDES)
+ $(ID_H_INCLUDES) $(PROBES_H_INCLUDES)
addr2line.$(OBJEXT): {$(VPATH)}addr2line.c {$(VPATH)}addr2line.h {$(VPATH)}config.h
array.$(OBJEXT): {$(VPATH)}array.c $(RUBY_H_INCLUDES) {$(VPATH)}util.h \
- $(ENCODING_H_INCLUDES) {$(VPATH)}internal.h
+ $(ENCODING_H_INCLUDES) {$(VPATH)}internal.h $(PROBES_H_INCLUDES)
bignum.$(OBJEXT): {$(VPATH)}bignum.c $(RUBY_H_INCLUDES) {$(VPATH)}util.h \
{$(VPATH)}thread.h {$(VPATH)}internal.h
class.$(OBJEXT): {$(VPATH)}class.c $(RUBY_H_INCLUDES) \
@@ -648,11 +649,11 @@ error.$(OBJEXT): {$(VPATH)}error.c {$(VPATH)}known_errors.inc \
eval.$(OBJEXT): {$(VPATH)}eval.c {$(VPATH)}eval_intern.h {$(VPATH)}vm.h \
$(RUBY_H_INCLUDES) $(VM_CORE_H_INCLUDES) {$(VPATH)}eval_error.c \
{$(VPATH)}eval_jump.c {$(VPATH)}debug.h {$(VPATH)}gc.h {$(VPATH)}iseq.h \
- $(ENCODING_H_INCLUDES) {$(VPATH)}internal.h
+ $(ENCODING_H_INCLUDES) {$(VPATH)}internal.h $(PROBES_H_INCLUDES)
load.$(OBJEXT): {$(VPATH)}load.c {$(VPATH)}eval_intern.h \
{$(VPATH)}util.h $(RUBY_H_INCLUDES) $(VM_CORE_H_INCLUDES) \
{$(VPATH)}dln.h {$(VPATH)}debug.h \
- {$(VPATH)}internal.h
+ {$(VPATH)}internal.h $(PROBES_H_INCLUDES)
file.$(OBJEXT): {$(VPATH)}file.c $(RUBY_H_INCLUDES) {$(VPATH)}io.h \
$(ENCODING_H_INCLUDES) {$(VPATH)}util.h {$(VPATH)}dln.h \
{$(VPATH)}internal.h
@@ -660,9 +661,9 @@ gc.$(OBJEXT): {$(VPATH)}gc.c $(RUBY_H_INCLUDES) {$(VPATH)}re.h \
{$(VPATH)}regex.h $(ENCODING_H_INCLUDES) $(VM_CORE_H_INCLUDES) \
{$(VPATH)}gc.h {$(VPATH)}io.h {$(VPATH)}eval_intern.h {$(VPATH)}util.h \
{$(VPATH)}debug.h {$(VPATH)}internal.h {$(VPATH)}constant.h \
- {$(VPATH)}thread.h
+ {$(VPATH)}thread.h $(PROBES_H_INCLUDES)
hash.$(OBJEXT): {$(VPATH)}hash.c $(RUBY_H_INCLUDES) {$(VPATH)}util.h \
- $(ENCODING_H_INCLUDES) {$(VPATH)}internal.h
+ $(ENCODING_H_INCLUDES) {$(VPATH)}internal.h $(PROBES_H_INCLUDES)
inits.$(OBJEXT): {$(VPATH)}inits.c $(RUBY_H_INCLUDES) \
{$(VPATH)}internal.h
io.$(OBJEXT): {$(VPATH)}io.c $(RUBY_H_INCLUDES) {$(VPATH)}io.h \
@@ -679,7 +680,7 @@ node.$(OBJEXT): {$(VPATH)}node.c $(RUBY_H_INCLUDES) \
numeric.$(OBJEXT): {$(VPATH)}numeric.c $(RUBY_H_INCLUDES) \
{$(VPATH)}util.h $(ENCODING_H_INCLUDES) {$(VPATH)}internal.h
object.$(OBJEXT): {$(VPATH)}object.c $(RUBY_H_INCLUDES) {$(VPATH)}util.h \
- {$(VPATH)}internal.h {$(VPATH)}constant.h $(ENCODING_H_INCLUDES)
+ {$(VPATH)}internal.h {$(VPATH)}constant.h $(ENCODING_H_INCLUDES) $(PROBES_H_INCLUDES)
pack.$(OBJEXT): {$(VPATH)}pack.c $(RUBY_H_INCLUDES) {$(VPATH)}encoding.h \
{$(VPATH)}oniguruma.h
parse.$(OBJEXT): {$(VPATH)}parse.c $(RUBY_H_INCLUDES) {$(VPATH)}node.h \
@@ -730,7 +731,7 @@ st.$(OBJEXT): {$(VPATH)}st.c $(RUBY_H_INCLUDES)
strftime.$(OBJEXT): {$(VPATH)}strftime.c $(RUBY_H_INCLUDES) \
{$(VPATH)}timev.h $(ENCODING_H_INCLUDES)
string.$(OBJEXT): {$(VPATH)}string.c $(RUBY_H_INCLUDES) {$(VPATH)}re.h \
- {$(VPATH)}regex.h $(ENCODING_H_INCLUDES) {$(VPATH)}internal.h
+ {$(VPATH)}regex.h $(ENCODING_H_INCLUDES) {$(VPATH)}internal.h $(PROBES_H_INCLUDES)
struct.$(OBJEXT): {$(VPATH)}struct.c $(RUBY_H_INCLUDES) {$(VPATH)}internal.h
thread.$(OBJEXT): {$(VPATH)}thread.c {$(VPATH)}eval_intern.h \
$(RUBY_H_INCLUDES) {$(VPATH)}gc.h $(VM_CORE_H_INCLUDES) \
@@ -761,13 +762,14 @@ compile.$(OBJEXT): {$(VPATH)}compile.c {$(VPATH)}iseq.h \
iseq.$(OBJEXT): {$(VPATH)}iseq.c {$(VPATH)}gc.h {$(VPATH)}iseq.h \
$(RUBY_H_INCLUDES) $(VM_CORE_H_INCLUDES) {$(VPATH)}insns.inc \
{$(VPATH)}insns_info.inc {$(VPATH)}node_name.inc {$(VPATH)}debug.h {$(VPATH)}internal.h
+{$(VPATH)}vm_insnhelper.c:$(PROBES_H_INCLUDES)
vm.$(OBJEXT): {$(VPATH)}vm.c {$(VPATH)}gc.h {$(VPATH)}iseq.h \
{$(VPATH)}eval_intern.h $(RUBY_H_INCLUDES) $(ENCODING_H_INCLUDES) \
$(VM_CORE_H_INCLUDES) {$(VPATH)}vm_method.c {$(VPATH)}vm_eval.c \
{$(VPATH)}vm_insnhelper.c {$(VPATH)}vm_insnhelper.h {$(VPATH)}vm_exec.c \
{$(VPATH)}vm_exec.h {$(VPATH)}insns.def {$(VPATH)}vmtc.inc \
{$(VPATH)}vm.inc {$(VPATH)}insns.inc {$(VPATH)}debug.h \
- {$(VPATH)}internal.h {$(VPATH)}vm.h {$(VPATH)}constant.h
+ {$(VPATH)}internal.h {$(VPATH)}vm.h {$(VPATH)}constant.h $(PROBES_H_INCLUDES)
vm_dump.$(OBJEXT): {$(VPATH)}vm_dump.c $(RUBY_H_INCLUDES) \
$(VM_CORE_H_INCLUDES) {$(VPATH)}debug.h {$(VPATH)}addr2line.h \
{$(VPATH)}internal.h
View
6 compile.c
@@ -5348,6 +5348,12 @@ dump_disasm_list(struct iseq_link_element *link)
printf("---------------------\n");
}
+const char *
+rb_insns_name(int i)
+{
+ return insn_name_info[i];
+}
+
VALUE
rb_insns_name_array(void)
{
View
3  configure.in
@@ -372,6 +372,9 @@ AS_CASE(["$host_os:$build_os"],
if test x"${build}" != x"${host}"; then
AC_CHECK_TOOL(CC, gcc)
fi
+
+AC_CHECK_TOOL(DTRACE, dtrace)
+
AC_PROG_CC
AC_PROG_CXX
RUBY_MINGW32
View
6 eval.c
@@ -18,6 +18,7 @@
#include "ruby/encoding.h"
#include "internal.h"
#include "vm_core.h"
+#include "probes.h"
#define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
@@ -499,6 +500,11 @@ setup_exception(rb_thread_t *th, int tag, volatile VALUE mesg)
}
if (tag != TAG_FATAL) {
+ if(RUBY_DTRACE_RAISE_ENABLED()) {
+ RUBY_DTRACE_RAISE(rb_obj_classname(th->errinfo),
+ rb_sourcefile(),
+ rb_sourceline());
+ }
EXEC_EVENT_HOOK(th, RUBY_EVENT_RAISE, th->cfp->self, 0, 0);
}
}
View
30 gc.c
@@ -23,6 +23,7 @@
#include "gc.h"
#include "constant.h"
#include "ruby_atomic.h"
+#include "probes.h"
#include <stdio.h>
#include <setjmp.h>
#include <sys/types.h>
@@ -120,7 +121,6 @@ typedef struct gc_profile_record {
#endif
} gc_profile_record;
-
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__CYGWIN__)
#pragma pack(push, 1) /* magic for reducing sizeof(RVALUE): 24 -> 20 */
#endif
@@ -1294,6 +1294,7 @@ define_final(int argc, VALUE *argv, VALUE os)
rb_raise(rb_eArgError, "wrong type argument %s (should be callable)",
rb_obj_classname(block));
}
+
return define_final0(obj, block);
}
@@ -3017,6 +3018,9 @@ garbage_collect(rb_objspace_t *objspace)
during_gc++;
gc_marks(objspace);
+ if (RUBY_DTRACE_GC_SWEEP_BEGIN_ENABLED()) {
+ RUBY_DTRACE_GC_SWEEP_BEGIN();
+ }
gc_prof_sweep_timer_start(objspace);
gc_sweep(objspace);
gc_prof_sweep_timer_stop(objspace);
@@ -3901,21 +3905,33 @@ gc_prof_timer_stop(rb_objspace_t *objspace, int marked)
static inline void
gc_prof_mark_timer_start(rb_objspace_t *objspace)
{
+ if (RUBY_DTRACE_GC_MARK_BEGIN_ENABLED()) {
+ RUBY_DTRACE_GC_MARK_BEGIN();
+ }
}
static inline void
gc_prof_mark_timer_stop(rb_objspace_t *objspace)
{
+ if (RUBY_DTRACE_GC_MARK_END_ENABLED()) {
+ RUBY_DTRACE_GC_MARK_END();
+ }
}
static inline void
gc_prof_sweep_timer_start(rb_objspace_t *objspace)
{
+ if (RUBY_DTRACE_GC_SWEEP_BEGIN_ENABLED()) {
+ RUBY_DTRACE_GC_SWEEP_BEGIN();
+ }
}
static inline void
gc_prof_sweep_timer_stop(rb_objspace_t *objspace)
{
+ if (RUBY_DTRACE_GC_SWEEP_END_ENABLED()) {
+ RUBY_DTRACE_GC_SWEEP_END();
+ }
}
static inline void
@@ -3949,6 +3965,9 @@ gc_prof_dec_live_num(rb_objspace_t *objspace)
static inline void
gc_prof_mark_timer_start(rb_objspace_t *objspace)
{
+ if (RUBY_DTRACE_GC_MARK_BEGIN_ENABLED()) {
+ RUBY_DTRACE_GC_MARK_BEGIN();
+ }
if (objspace->profile.run) {
size_t count = objspace->profile.count;
@@ -3959,6 +3978,9 @@ gc_prof_mark_timer_start(rb_objspace_t *objspace)
static inline void
gc_prof_mark_timer_stop(rb_objspace_t *objspace)
{
+ if (RUBY_DTRACE_GC_MARK_END_ENABLED()) {
+ RUBY_DTRACE_GC_MARK_END();
+ }
if (objspace->profile.run) {
double mark_time = 0;
size_t count = objspace->profile.count;
@@ -3973,6 +3995,9 @@ gc_prof_mark_timer_stop(rb_objspace_t *objspace)
static inline void
gc_prof_sweep_timer_start(rb_objspace_t *objspace)
{
+ if (RUBY_DTRACE_GC_SWEEP_BEGIN_ENABLED()) {
+ RUBY_DTRACE_GC_SWEEP_BEGIN();
+ }
if (objspace->profile.run) {
size_t count = objspace->profile.count;
@@ -3983,6 +4008,9 @@ gc_prof_sweep_timer_start(rb_objspace_t *objspace)
static inline void
gc_prof_sweep_timer_stop(rb_objspace_t *objspace)
{
+ if (RUBY_DTRACE_GC_SWEEP_END_ENABLED()) {
+ RUBY_DTRACE_GC_SWEEP_END();
+ }
if (objspace->profile.run) {
double sweep_time = 0;
size_t count = objspace->profile.count;
View
13 hash.c
@@ -17,6 +17,7 @@
#include "ruby/encoding.h"
#include "internal.h"
#include <errno.h>
+#include "probes.h"
#ifdef __APPLE__
#include <crt_externs.h>
@@ -215,6 +216,16 @@ hash_alloc(VALUE klass)
return (VALUE)hash;
}
+static VALUE
+empty_hash_alloc(VALUE klass)
+{
+ if(RUBY_DTRACE_HASH_CREATE_ENABLED()) {
+ RUBY_DTRACE_HASH_CREATE(0, rb_sourcefile(), rb_sourceline());
+ }
+
+ return hash_alloc(klass);
+}
+
VALUE
rb_hash_new(void)
{
@@ -3413,7 +3424,7 @@ Init_Hash(void)
rb_include_module(rb_cHash, rb_mEnumerable);
- rb_define_alloc_func(rb_cHash, hash_alloc);
+ rb_define_alloc_func(rb_cHash, empty_hash_alloc);
rb_define_singleton_method(rb_cHash, "[]", rb_hash_s_create, -1);
rb_define_singleton_method(rb_cHash, "try_convert", rb_hash_s_try_convert, 1);
rb_define_method(rb_cHash,"initialize", rb_hash_initialize, -1);
View
33 insns.def
@@ -524,6 +524,11 @@ newhash
(VALUE val) // inc += 1 - num;
{
rb_num_t i;
+
+ if(RUBY_DTRACE_HASH_CREATE_ENABLED()) {
+ RUBY_DTRACE_HASH_CREATE(num, rb_sourcefile(), rb_sourceline());
+ }
+
val = rb_hash_new();
for (i = num; i > 0; i -= 2) {
@@ -838,6 +843,34 @@ trace
{
rb_event_flag_t flag = (rb_event_flag_t)nf;
+ if (RUBY_DTRACE_FUNCTION_ENTRY_ENABLED()) {
+ if (flag == RUBY_EVENT_CALL || flag == RUBY_EVENT_C_CALL) {
+ VALUE klass;
+ ID called_id;
+
+ rb_thread_method_id_and_class(th, &called_id, &klass);
+
+ RUBY_DTRACE_FUNCTION_ENTRY(
+ RSTRING_PTR(rb_inspect(klass)),
+ rb_id2name(called_id),
+ rb_sourcefile(),
+ rb_sourceline());
+ }
+ }
+ if (RUBY_DTRACE_FUNCTION_RETURN_ENABLED()) {
+ if (flag == RUBY_EVENT_RETURN || flag == RUBY_EVENT_C_RETURN) {
+ VALUE klass;
+ ID called_id;
+
+ rb_thread_method_id_and_class(th, &called_id, &klass);
+
+ RUBY_DTRACE_FUNCTION_RETURN(
+ RSTRING_PTR(rb_inspect(klass)),
+ rb_id2name(called_id),
+ rb_sourcefile(),
+ rb_sourceline());
+ }
+ }
EXEC_EVENT_HOOK(th, flag, GET_SELF(), 0, 0 /* TODO: id, klass */);
}
View
1  internal.h
@@ -69,6 +69,7 @@ int rb_dvar_defined(ID);
int rb_local_defined(ID);
int rb_parse_in_eval(void);
int rb_parse_in_main(void);
+const char * rb_insns_name(int i);
VALUE rb_insns_name_array(void);
/* cont.c */
View
40 load.c
@@ -7,6 +7,7 @@
#include "internal.h"
#include "dln.h"
#include "eval_intern.h"
+#include "probes.h"
VALUE ruby_dln_librefs;
@@ -621,6 +622,14 @@ rb_f_load(int argc, VALUE *argv)
VALUE fname, wrap, path;
rb_scan_args(argc, argv, "11", &fname, &wrap);
+
+ if(RUBY_DTRACE_LOAD_ENTRY_ENABLED()) {
+ RUBY_DTRACE_LOAD_ENTRY(
+ StringValuePtr(fname),
+ rb_sourcefile(),
+ rb_sourceline());
+ }
+
path = rb_find_file(FilePathValue(fname));
if (!path) {
if (!rb_file_load_ok(RSTRING_PTR(fname)))
@@ -628,6 +637,11 @@ rb_f_load(int argc, VALUE *argv)
path = fname;
}
rb_load_internal(path, RTEST(wrap));
+
+ if(RUBY_DTRACE_LOAD_RETURN_ENABLED()) {
+ RUBY_DTRACE_LOAD_RETURN(StringValuePtr(fname));
+ }
+
return Qtrue;
}
@@ -861,6 +875,13 @@ rb_require_safe(VALUE fname, int safe)
} volatile saved;
char *volatile ftptr = 0;
+ if(RUBY_DTRACE_REQUIRE_ENTRY_ENABLED()) {
+ RUBY_DTRACE_REQUIRE_ENTRY(
+ StringValuePtr(fname),
+ rb_sourcefile(),
+ rb_sourceline());
+ }
+
PUSH_TAG();
saved.safe = rb_safe_level();
if ((state = EXEC_TAG()) == 0) {
@@ -871,7 +892,22 @@ rb_require_safe(VALUE fname, int safe)
rb_set_safe_level_force(safe);
FilePathValue(fname);
rb_set_safe_level_force(0);
+
+ if(RUBY_DTRACE_FIND_REQUIRE_ENTRY_ENABLED()) {
+ RUBY_DTRACE_FIND_REQUIRE_ENTRY(
+ StringValuePtr(fname),
+ rb_sourcefile(),
+ rb_sourceline());
+ }
+
found = search_required(fname, &path, safe);
+
+ if(RUBY_DTRACE_FIND_REQUIRE_RETURN_ENABLED()) {
+ RUBY_DTRACE_FIND_REQUIRE_RETURN(
+ StringValuePtr(fname),
+ rb_sourcefile(),
+ rb_sourceline());
+ }
if (found) {
if (!path || !(ftptr = load_lock(RSTRING_PTR(path)))) {
result = Qfalse;
@@ -907,6 +943,10 @@ rb_require_safe(VALUE fname, int safe)
th->errinfo = errinfo;
+ if(RUBY_DTRACE_REQUIRE_RETURN_ENABLED()) {
+ RUBY_DTRACE_REQUIRE_RETURN(StringValuePtr(fname));
+ }
+
return result;
}
View
9 object.c
@@ -22,6 +22,7 @@
#include <float.h>
#include "constant.h"
#include "internal.h"
+#include "probes.h"
VALUE rb_cBasicObject;
VALUE rb_mKernel;
@@ -1685,7 +1686,15 @@ rb_obj_alloc(VALUE klass)
klass);
}
+ if (RUBY_DTRACE_OBJECT_CREATE_ENABLED()) {
+ const char * file = rb_sourcefile();
+ RUBY_DTRACE_OBJECT_CREATE(rb_class2name(klass),
+ file ? file : "",
+ rb_sourceline());
+ }
+
obj = (*allocator)(klass);
+
if (rb_obj_class(obj) != rb_class_real(klass)) {
rb_raise(rb_eTypeError, "wrong instance allocation");
}
View
13 parse.y
@@ -29,6 +29,7 @@
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
+#include "probes.h"
#define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
@@ -5310,7 +5311,19 @@ yycompile0(VALUE arg)
#ifndef RIPPER
parser->parser_token_info_enabled = !compile_for_eval && RTEST(ruby_verbose);
#endif
+#ifndef RIPPER
+ if(RUBY_DTRACE_PARSE_BEGIN_ENABLED()) {
+ RUBY_DTRACE_PARSE_BEGIN(parser->parser_ruby_sourcefile,
+ parser->parser_ruby_sourceline);
+ }
+#endif
n = yyparse((void*)parser);
+#ifndef RIPPER
+ if(RUBY_DTRACE_PARSE_END_ENABLED()) {
+ RUBY_DTRACE_PARSE_END(parser->parser_ruby_sourcefile,
+ parser->parser_ruby_sourceline);
+ }
+#endif
ruby_debug_lines = 0;
ruby_coverage = 0;
compile_for_eval = 0;
View
37 probes.d
@@ -0,0 +1,37 @@
+provider ruby {
+ probe function__entry(const char *, const char *, const char *, int);
+ probe function__return(const char *, const char *, const char *, int);
+
+ probe require__entry(const char *, const char *, int);
+ probe require__return(const char *);
+
+ probe find__require__entry(const char *, const char *, int);
+ probe find__require__return(const char *, const char *, int);
+
+ probe load__entry(const char *, const char *, int);
+ probe load__return(const char *);
+
+ probe raise(const char *, const char *, int);
+
+ probe object__create(const char *, const char *, int);
+ probe array__create(long, const char *, int);
+ probe hash__create(long, const char *, int);
+ probe string__create(long, const char *, int);
+
+ probe parse__begin(const char *, int);
+ probe parse__end(const char *, int);
+
+ probe insn(const char *);
+ probe insn__operand(const char *, const char *);
+
+ probe gc__mark__begin();
+ probe gc__mark__end();
+ probe gc__sweep__begin();
+ probe gc__sweep__end();
+};
+
+#pragma D attributes Stable/Evolving/Common provider ruby provider
+#pragma D attributes Stable/Evolving/Common provider ruby module
+#pragma D attributes Stable/Evolving/Common provider ruby function
+#pragma D attributes Evolving/Evolving/Common provider ruby name
+#pragma D attributes Evolving/Evolving/Common provider ruby args
View
20 string.c
@@ -17,6 +17,7 @@
#include "node.h"
#include "eval_intern.h"
#include "internal.h"
+#include "probes.h"
#include <assert.h>
#define BEG(no) (regs->beg[(no)])
@@ -381,6 +382,15 @@ str_alloc(VALUE klass)
return (VALUE)str;
}
+static inline VALUE
+empty_str_alloc(VALUE klass)
+{
+ if(RUBY_DTRACE_STRING_CREATE_ENABLED()) {
+ RUBY_DTRACE_STRING_CREATE(0, rb_sourcefile(), rb_sourceline());
+ }
+ return str_alloc(klass);
+}
+
static VALUE
str_new(VALUE klass, const char *ptr, long len)
{
@@ -390,6 +400,10 @@ str_new(VALUE klass, const char *ptr, long len)
rb_raise(rb_eArgError, "negative string size (or size too big)");
}
+ if(RUBY_DTRACE_STRING_CREATE_ENABLED()) {
+ RUBY_DTRACE_STRING_CREATE(len, rb_sourcefile(), rb_sourceline());
+ }
+
str = str_alloc(klass);
if (len > RSTRING_EMBED_LEN_MAX) {
RSTRING(str)->as.heap.aux.capa = len;
@@ -918,6 +932,10 @@ rb_str_dup(VALUE str)
VALUE
rb_str_resurrect(VALUE str)
{
+ if(RUBY_DTRACE_STRING_CREATE_ENABLED()) {
+ RUBY_DTRACE_STRING_CREATE(
+ RSTRING_LEN(str), rb_sourcefile(), rb_sourceline());
+ }
return str_replace(str_alloc(rb_cString), str);
}
@@ -7920,7 +7938,7 @@ Init_String(void)
rb_cString = rb_define_class("String", rb_cObject);
rb_include_module(rb_cString, rb_mComparable);
- rb_define_alloc_func(rb_cString, str_alloc);
+ rb_define_alloc_func(rb_cString, empty_str_alloc);
rb_define_singleton_method(rb_cString, "try_convert", rb_str_s_try_convert, 1);
rb_define_method(rb_cString, "initialize", rb_str_init, -1);
rb_define_method(rb_cString, "initialize_copy", rb_str_replace, 1);
View
1  test/dtrace/dummy.rb
@@ -0,0 +1 @@
+# this is a dummy file used by test/dtrace/test_require.rb
View
33 test/dtrace/helper.rb
@@ -0,0 +1,33 @@
+require 'minitest/autorun'
+require 'tempfile'
+
+module DTrace
+ class TestCase < MiniTest::Unit::TestCase
+ INCLUDE = File.expand_path(File.join(File.dirname(__FILE__), '..'))
+
+ def setup
+ skip "must be setuid 0 to run dtrace tests" unless Process.euid == 0
+ end
+
+ def trap_probe d_program, ruby_program
+ d = Tempfile.new('probe.d')
+ d.write d_program
+ d.flush
+
+ rb = Tempfile.new('probed.rb')
+ rb.write ruby_program
+ rb.flush
+
+ d_path = d.path
+ rb_path = rb.path
+
+ cmd = "dtrace -q -s #{d_path} -c '#{Gem.ruby} -I#{INCLUDE} #{rb_path}'"
+ probes = IO.popen(cmd) do |io|
+ io.readlines
+ end
+ d.close(true)
+ rb.close(true)
+ yield(d_path, rb_path, probes)
+ end
+ end
+end
View
35 test/dtrace/test_array_create.rb
@@ -0,0 +1,35 @@
+require 'dtrace/helper'
+
+module DTrace
+ class TestArrayCreate < TestCase
+ def test_lit
+ trap_probe(probe, '[]') { |_,rbfile,saw|
+ saw = saw.map(&:split).find_all { |num, file, line|
+ file == rbfile && num == '0'
+ }
+ assert_equal([rbfile], saw.map { |line| line[1] })
+ assert_equal(['1'], saw.map { |line| line[2] })
+ }
+ end
+
+ def test_many_lit
+ trap_probe(probe, '[1,2,3,4]') { |_,rbfile,saw|
+ saw = saw.map(&:split).find_all { |num, file, line|
+ file == rbfile && num == '4' && line == '1'
+ }
+ assert_operator saw.length, :>, 0
+ }
+ end
+
+ private
+ def probe type = 'array'
+ <<-eoprobe
+ruby$target:::#{type}-create
+/arg1/
+{
+ printf("%d %s %d\\n", arg0, copyinstr(arg1), arg2);
+}
+ eoprobe
+ end
+ end
+end
View
58 test/dtrace/test_function_entry.rb
@@ -0,0 +1,58 @@
+require 'dtrace/helper'
+
+module DTrace
+ class TestFunctionEntry < TestCase
+ def test_function_entry
+ probe = <<-eoprobe
+ruby$target:::function-entry
+/arg0 && arg1 && arg2/
+{
+ printf("%s %s %s %d\\n", copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3);
+}
+ eoprobe
+
+ trap_probe(probe, ruby_program) { |d_file, rb_file, probes|
+ foo_calls = probes.map { |line| line.split }.find_all { |row|
+ row.first == 'Foo' && row[1] == 'foo'
+ }
+
+ assert_equal 10, foo_calls.length
+ line = '2'
+ foo_calls.each { |f| assert_equal line, f[3] }
+ foo_calls.each { |f| assert_equal rb_file, f[2] }
+ }
+ end
+
+ def test_function_return
+ probe = <<-eoprobe
+ruby$target:::function-return
+/arg0 && arg1 && arg2/
+{
+ printf("%s %s %s %d\\n", copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3);
+}
+ eoprobe
+
+ trap_probe(probe, ruby_program) { |d_file, rb_file, probes|
+ foo_calls = probes.map { |line| line.split }.find_all { |row|
+ row.first == 'Foo' && row[1] == 'foo'
+ }
+
+ assert_equal 10, foo_calls.length
+ line = '2'
+ foo_calls.each { |f| assert_equal line, f[3] }
+ foo_calls.each { |f| assert_equal rb_file, f[2] }
+ }
+ end
+
+ private
+ def ruby_program
+ <<-eoruby
+ class Foo
+ def foo; end
+ end
+ x = Foo.new
+ 10.times { x.foo }
+ eoruby
+ end
+ end
+end
View
26 test/dtrace/test_gc.rb
@@ -0,0 +1,26 @@
+require 'dtrace/helper'
+
+module DTrace
+ class TestGC < TestCase
+ %w{
+ gc-mark-begin
+ gc-mark-end
+ gc-sweep-begin
+ gc-sweep-end
+ }.each do |probe_name|
+ define_method(:"test_#{probe_name.gsub(/-/, '_')}") do
+ probe = "ruby$target:::#{probe_name} { printf(\"#{probe_name}\\n\"); }"
+
+ trap_probe(probe, ruby_program) { |_, _, saw|
+ assert_operator saw.length, :>, 0
+ }
+
+ end
+ end
+
+ private
+ def ruby_program
+ "100000.times { Object.new }"
+ end
+ end
+end
View
52 test/dtrace/test_hash_create.rb
@@ -0,0 +1,52 @@
+require 'dtrace/helper'
+
+module DTrace
+ class TestHashCreate < TestCase
+ def test_hash_new
+ trap_probe(probe, 'Hash.new') { |_,rbfile,saw|
+ saw = saw.map(&:split).find_all { |num, file, line|
+ file == rbfile && num == '0'
+ }
+ assert_operator saw.length, :>, 0
+ }
+ end
+
+ def test_hash_lit
+ trap_probe(probe, '{}') { |_,rbfile,saw|
+ saw = saw.map(&:split).find_all { |num, file, line|
+ file == rbfile && num == '0'
+ }
+ assert_operator saw.length, :>, 0
+ }
+ end
+
+ def test_hash_lit_elements
+ trap_probe(probe, '{ :foo => :bar }') { |_,rbfile,saw|
+ saw = saw.map(&:split).find_all { |num, file, line|
+ file == rbfile && num == '2'
+ }
+ assert_operator saw.length, :>, 0
+ }
+ end
+
+ def test_hash_lit_elements_string
+ trap_probe(probe, '{ :foo => :bar, :bar => "baz" }') { |_,rbfile,saw|
+ saw = saw.map(&:split).find_all { |num, file, line|
+ file == rbfile && num == '4'
+ }
+ assert_operator saw.length, :>, 0
+ }
+ end
+
+ private
+ def probe
+ <<-eoprobe
+ruby$target:::hash-create
+/arg1/
+{
+ printf("%d %s %d\\n", arg0, copyinstr(arg1), arg2);
+}
+ eoprobe
+ end
+ end
+end
View
52 test/dtrace/test_load.rb
@@ -0,0 +1,52 @@
+require 'dtrace/helper'
+require 'tempfile'
+
+module DTrace
+ class TestLoad < TestCase
+ def setup
+ super
+ @rbfile = Tempfile.new(['omg', 'rb'])
+ @rbfile.write 'x = 10'
+ end
+
+ def teardown
+ super
+ @rbfile.close(true) if @rbfile
+ end
+
+ def test_load_entry
+ probe = <<-eoprobe
+ruby$target:::load-entry
+{
+ printf("%s %s %d\\n", copyinstr(arg0), copyinstr(arg1), arg2);
+}
+ eoprobe
+ trap_probe(probe, program) { |dpath, rbpath, saw|
+ saw = saw.map(&:split).find_all { |loaded, _, _|
+ loaded == @rbfile.path
+ }
+ assert_equal 10, saw.length
+ }
+ end
+
+ def test_load_return
+ probe = <<-eoprobe
+ruby$target:::load-return
+{
+ printf("%s\\n", copyinstr(arg0));
+}
+ eoprobe
+ trap_probe(probe, program) { |dpath, rbpath, saw|
+ saw = saw.map(&:split).find_all { |loaded, _, _|
+ loaded == @rbfile.path
+ }
+ assert_equal 10, saw.length
+ }
+ end
+
+ private
+ def program
+ "10.times { load '#{@rbfile.path}' }"
+ end
+ end
+end
View
35 test/dtrace/test_object_create_start.rb
@@ -0,0 +1,35 @@
+require 'dtrace/helper'
+
+module DTrace
+ class TestObjectCreateStart < TestCase
+ def test_object_create_start
+ trap_probe(probe, '10.times { Object.new }') { |_,rbfile,saw|
+ saw = saw.map(&:split).find_all { |_, file, _|
+ file == rbfile
+ }
+ assert_equal 10, saw.length
+ }
+ end
+
+ def test_object_create_start_name
+ trap_probe(probe, 'Hash.new') { |_,rbfile,saw|
+ saw = saw.map(&:split).find_all { |klass, file, line|
+ file == rbfile
+ }
+ assert_equal(%w{ Hash }, saw.map(&:first))
+ assert_equal([rbfile], saw.map { |line| line[1] })
+ assert_equal(['1'], saw.map { |line| line[2] })
+ }
+ end
+
+ private
+ def probe
+ <<-eoprobe
+ruby$target:::object-create
+{
+ printf("%s %s %d\\n", copyinstr(arg0), copyinstr(arg1), arg2);
+}
+ eoprobe
+ end
+ end
+end
View
29 test/dtrace/test_raise.rb
@@ -0,0 +1,29 @@
+require 'dtrace/helper'
+
+module DTrace
+ class TestRaise < TestCase
+ def test_raise
+ probe = <<-eoprobe
+ruby$target:::raise
+{
+ printf("%s %s %d\\n", copyinstr(arg0), copyinstr(arg1), arg2);
+}
+ eoprobe
+ trap_probe(probe, program) { |dpath, rbpath, saw|
+ saw = saw.map(&:split).find_all { |_, source_file, _|
+ source_file == rbpath
+ }
+ assert_equal 10, saw.length
+ saw.each do |klass, _, source_line|
+ assert_equal 'RuntimeError', klass
+ assert_equal '1', source_line
+ end
+ }
+ end
+
+ private
+ def program
+ '10.times { raise rescue nil }'
+ end
+ end
+end
View
34 test/dtrace/test_require.rb
@@ -0,0 +1,34 @@
+require 'dtrace/helper'
+
+module DTrace
+ class TestRequire < TestCase
+ def test_require_entry
+ probe = <<-eoprobe
+ruby$target:::require-entry
+{
+ printf("%s %s %d\\n", copyinstr(arg0), copyinstr(arg1), arg2);
+}
+ eoprobe
+ trap_probe(probe, ruby_program) { |d_file, rb_file, saw|
+ required = saw.map { |s| s.split }.find_all do |(required, _)|
+ required == 'dtrace/dummy'
+ end
+ assert_equal 10, required.length
+ }
+ end
+
+ def test_require_return
+ probe = <<-eoprobe
+ruby$target:::require-return
+{
+ printf("%s\\n", copyinstr(arg0));
+}
+ eoprobe
+ end
+
+ private
+ def ruby_program
+ "10.times { require 'dtrace/dummy' }"
+ end
+ end
+end
View
55 test/dtrace/test_singleton_function.rb
@@ -0,0 +1,55 @@
+require 'dtrace/helper'
+
+module DTrace
+ class TestSingletonFunctionEntry < TestCase
+ def test_entry
+ probe = <<-eoprobe
+ruby$target:::function-entry
+/strstr(copyinstr(arg0), "Foo") != NULL/
+{
+ printf("%s %s %s %d\\n", copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3);
+}
+ eoprobe
+
+ trap_probe(probe, ruby_program) { |d_file, rb_file, probes|
+ foo_calls = probes.map { |line| line.split }.find_all { |row|
+ row.first == '#<Class:Foo>' && row[1] == 'foo'
+ }
+
+ assert_equal 10, foo_calls.length
+ line = '2'
+ foo_calls.each { |f| assert_equal line, f[3] }
+ foo_calls.each { |f| assert_equal rb_file, f[2] }
+ }
+ end
+
+ def test_exit
+ probe = <<-eoprobe
+ruby$target:::function-return
+{
+ printf("%s %s %s %d\\n", copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3);
+}
+ eoprobe
+
+ trap_probe(probe, ruby_program) { |d_file, rb_file, probes|
+ foo_calls = probes.map { |line| line.split }.find_all { |row|
+ row.first == '#<Class:Foo>' && row[1] == 'foo'
+ }
+
+ assert_equal 10, foo_calls.length
+ line = '2'
+ foo_calls.each { |f| assert_equal line, f[3] }
+ foo_calls.each { |f| assert_equal rb_file, f[2] }
+ }
+ end
+
+ def ruby_program
+ <<-eoruby
+ class Foo
+ def self.foo; end
+ end
+ 10.times { Foo.foo }
+ eoruby
+ end
+ end
+end
View
27 test/dtrace/test_string.rb
@@ -0,0 +1,27 @@
+require 'dtrace/helper'
+
+module DTrace
+ class TestStringProbes < TestCase
+ def test_object_create_start_string_lit
+ trap_probe(probe, '"omglolwutbbq"') { |_,rbfile,saw|
+ saw = saw.map(&:split).find_all { |klass, file, line, len|
+ file == rbfile && len == '12' && line == '1'
+ }
+ assert_equal(%w{ String }, saw.map(&:first))
+ assert_equal([rbfile], saw.map { |line| line[1] })
+ assert_equal(['1'], saw.map { |line| line[2] })
+ }
+ end
+
+ private
+ def probe
+ <<-eoprobe
+ruby$target:::string-create
+/arg1/
+{
+ printf("String %s %d %d\\n", copyinstr(arg1), arg2, arg0);
+}
+ eoprobe
+ end
+ end
+end
View
24 tool/gen_dummy_probes.sed
@@ -0,0 +1,24 @@
+
+# upper case everything
+y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
+
+# remove the pragma declarations
+s/^#PRAGMA.*$//
+
+# replace the provider section with the start of the header file
+s/PROVIDER RUBY {/#ifndef _PROBES_H\
+#define _PROBES_H/
+
+# finish up the #ifndef sandwich
+s/};/#endif \/* _PROBES_H *\//
+
+s/__/_/g
+
+s/([^,)]\{1,\})/(arg0)/
+s/([^,)]\{1,\},[^,)]\{1,\})/(arg0, arg1)/
+s/([^,)]\{1,\},[^,)]\{1,\},[^,)]\{1,\})/(arg0, arg1, arg2)/
+s/([^,)]\{1,\},[^,)]\{1,\},[^,)]\{1,\},[^,)]\{1,\})/(arg0, arg1, arg2, arg3)/
+s/([^,)]\{1,\},[^,)]\{1,\},[^,)]\{1,\},[^,)]\{1,\},[^,)]\{1,\})/(arg0, arg1, arg2, arg3, arg4)/
+
+s/[ ]*PROBE[ ]\([^\(]*\)\(([^\)]*)\);/#define RUBY_DTRACE_\1_ENABLED() 0\
+#define RUBY_DTRACE_\1\2\ do \{ \} while\(0\)/
View
42 vm.c
@@ -18,6 +18,7 @@
#include "vm_core.h"
#include "iseq.h"
#include "eval_intern.h"
+#include "probes.h"
static inline VALUE *
VM_EP_LEP(VALUE *ep)
@@ -65,10 +66,11 @@ rb_vm_control_frame_block_ptr(rb_control_frame_t *cfp)
#define VM_COLLECT_USAGE_DETAILS 0
#endif
-#if VM_COLLECT_USAGE_DETAILS
static void vm_collect_usage_operand(int insn, int n, VALUE op);
-static void vm_collect_usage_register(int reg, int isset);
static void vm_collect_usage_insn(int insn);
+
+#if VM_COLLECT_USAGE_DETAILS
+static void vm_collect_usage_register(int reg, int isset);
#endif
static VALUE
@@ -1179,6 +1181,7 @@ vm_exec(rb_thread_t *th)
if (UNLIKELY(VM_FRAME_TYPE(th->cfp) == VM_FRAME_MAGIC_CFUNC)) {
const rb_method_entry_t *me = th->cfp->me;
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, th->cfp->self, me->called_id, me->klass);
+ RUBY_DTRACE_FUNC_RETURN_HOOK(me->klass, me->called_id);
}
th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp);
}
@@ -1983,6 +1986,10 @@ m_core_hash_from_ary(VALUE self, VALUE ary)
VALUE hash = rb_hash_new();
int i;
+ if(RUBY_DTRACE_HASH_CREATE_ENABLED()) {
+ RUBY_DTRACE_HASH_CREATE(RARRAY_LEN(ary), rb_sourcefile(), rb_sourceline());
+ }
+
for (i=0; i<RARRAY_LEN(ary); i+=2) {
rb_hash_aset(hash, RARRAY_PTR(ary)[i], RARRAY_PTR(ary)[i+1]);
}
@@ -2308,6 +2315,11 @@ rb_ruby_debug_ptr(void)
return ruby_vm_debug_ptr(GET_VM());
}
+/* iseq.c */
+VALUE insn_operand_intern(rb_iseq_t *iseq,
+ VALUE insn, int op_no, VALUE op,
+ int len, size_t pos, VALUE *pnop, VALUE child);
+
#if VM_COLLECT_USAGE_DETAILS
#define HASH_ASET(h, k, v) st_insert(RHASH_TBL(h), (st_data_t)(k), (st_data_t)(v))
@@ -2365,11 +2377,6 @@ vm_analysis_insn(int insn)
prev_insn = insn;
}
-/* iseq.c */
-VALUE insn_operand_intern(rb_iseq_t *iseq,
- VALUE insn, int op_no, VALUE op,
- int len, size_t pos, VALUE *pnop, VALUE child);
-
static void
vm_analysis_operand(int insn, int n, VALUE op)
{
@@ -2476,10 +2483,21 @@ usage_analysis_register_stop(VALUE self)
return Qnil;
}
+#else
+
+void (*ruby_vm_collect_usage_func_insn)(int insn) = NULL;
+void (*ruby_vm_collect_usage_func_operand)(int insn, int n, VALUE op) = NULL;
+void (*ruby_vm_collect_usage_func_register)(int reg, int isset) = NULL;
+
+#endif
+
/* @param insn instruction number */
static void
vm_collect_usage_insn(int insn)
{
+ if (RUBY_DTRACE_INSN_ENABLED()) {
+ RUBY_DTRACE_INSN(rb_insns_name(insn));
+ }
if (ruby_vm_collect_usage_func_insn)
(*ruby_vm_collect_usage_func_insn)(insn);
}
@@ -2491,10 +2509,18 @@ vm_collect_usage_insn(int insn)
static void
vm_collect_usage_operand(int insn, int n, VALUE op)
{
+ if (RUBY_DTRACE_INSN_OPERAND_ENABLED()) {
+ VALUE valstr;
+
+ valstr = insn_operand_intern(GET_THREAD()->cfp->iseq, insn, n, op, 0, 0, 0, 0);
+
+ RUBY_DTRACE_INSN_OPERAND(RSTRING_PTR(valstr), rb_insns_name(insn));
+ }
if (ruby_vm_collect_usage_func_operand)
(*ruby_vm_collect_usage_func_operand)(insn, n, op);
}
+#if VM_COLLECT_USAGE_DETAILS
/* @param reg register id. see code of vm_analysis_register() */
/* @param iseset 0: read, 1: write */
static void
@@ -2503,5 +2529,5 @@ vm_collect_usage_register(int reg, int isset)
if (ruby_vm_collect_usage_func_register)
(*ruby_vm_collect_usage_func_register)(reg, isset);
}
-
#endif
+
View
30 vm_core.h
@@ -23,6 +23,7 @@
#include "id.h"
#include "method.h"
#include "ruby_atomic.h"
+#include "probes.h"
#if defined(_WIN32)
#include "thread_win32.h"
@@ -898,6 +899,35 @@ rb_threadptr_exec_event_hooks(rb_thread_t *th, rb_event_flag_t flag, VALUE self,
} \
} while (0)
+#define RUBY_DTRACE_FUNC_ENTRY_HOOK(klass, id) \
+ if (RUBY_DTRACE_FUNCTION_ENTRY_ENABLED()) { \
+ const char * classname = rb_class2name((klass)); \
+ const char * methodname = rb_id2name((id)); \
+ const char * filename = rb_sourcefile(); \
+ if (classname && methodname && filename) { \
+ RUBY_DTRACE_FUNCTION_ENTRY( \
+ classname, \
+ methodname, \
+ filename, \
+ rb_sourceline()); \
+ } \
+ } \
+
+#define RUBY_DTRACE_FUNC_RETURN_HOOK(klass, id) \
+ if (RUBY_DTRACE_FUNCTION_RETURN_ENABLED()) { \
+ const char * classname = rb_class2name((klass)); \
+ const char * methodname = rb_id2name((id)); \
+ const char * filename = rb_sourcefile(); \
+ if (classname && methodname && filename) { \
+ RUBY_DTRACE_FUNCTION_RETURN( \
+ classname, \
+ methodname, \
+ filename, \
+ rb_sourceline()); \
+ } \
+ } \
+
+
#if defined __GNUC__ && __GNUC__ >= 4
#pragma GCC visibility push(default)
#endif
View
1  vm_dump.c
@@ -13,6 +13,7 @@
#include "addr2line.h"
#include "vm_core.h"
#include "internal.h"
+#include "probes.h"
/* see vm_insnhelper.h for the values */
#ifndef VMDEBUG
View
5 vm_eval.c
@@ -55,6 +55,7 @@ vm_call0_cfunc(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv)
{
VALUE val;
+ RUBY_DTRACE_FUNC_ENTRY_HOOK(ci->defined_class, ci->mid);
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, ci->recv, ci->mid, ci->defined_class);
{
rb_control_frame_t *reg_cfp = th->cfp;
@@ -84,6 +85,7 @@ vm_call0_cfunc(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv)
}
}
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, ci->recv, ci->mid, ci->defined_class);
+ RUBY_DTRACE_FUNC_RETURN_HOOK(ci->defined_class, ci->mid);
return val;
}
@@ -93,6 +95,7 @@ vm_call0_cfunc_with_frame(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv
{
VALUE val;
+ RUBY_DTRACE_FUNC_ENTRY_HOOK(ci->defined_class, ci->mid);
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, ci->recv, ci->mid, ci->defined_class);
{
rb_control_frame_t *reg_cfp = th->cfp;
@@ -116,6 +119,7 @@ vm_call0_cfunc_with_frame(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv
vm_pop_frame(th);
}
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, ci->recv, ci->mid, ci->defined_class);
+ RUBY_DTRACE_FUNC_RETURN_HOOK(ci->defined_class, ci->mid);
return val;
}
@@ -1007,6 +1011,7 @@ rb_iterate(VALUE (* it_proc) (VALUE), VALUE data1,
if (UNLIKELY(VM_FRAME_TYPE(th->cfp) == VM_FRAME_MAGIC_CFUNC)) {
const rb_method_entry_t *me = th->cfp->me;
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, th->cfp->self, me->called_id, me->klass);
+ RUBY_DTRACE_FUNC_RETURN_HOOK(me->klass, me->called_id);
}
th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp);
View
2  vm_exec.c
@@ -11,6 +11,8 @@
#include <math.h>
+void vm_analysis_insn(int insn);
+
#if VMDEBUG > 0
#define DECL_SC_REG(type, r, reg) register type reg_##r
View
7 vm_insnhelper.c
@@ -13,6 +13,7 @@
#include <math.h>
#include "constant.h"
#include "internal.h"
+#include "probes.h"
/* control stack frame */
@@ -1431,6 +1432,7 @@ vm_call_cfunc_with_frame(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_i
int len = cfunc->argc;
VALUE recv = ci->recv;
+ RUBY_DTRACE_FUNC_ENTRY_HOOK(me->klass, me->called_id);
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, me->called_id, me->klass);
vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, recv, ci->defined_class,
@@ -1449,6 +1451,7 @@ vm_call_cfunc_with_frame(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_i
vm_pop_frame(th);
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, me->called_id, me->klass);
+ RUBY_DTRACE_FUNC_RETURN_HOOK(me->klass, me->called_id);
return val;
}
@@ -1496,6 +1499,7 @@ vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci)
if (len >= 0) rb_check_arity(ci->argc, len, len);
+ RUBY_DTRACE_FUNC_ENTRY_HOOK(me->klass, me->called_id);
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, me->called_id, me->klass);
if (!(ci->me->flag & NOEX_PROTECTED) &&
@@ -1505,6 +1509,7 @@ vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_info_t *ci)
val = vm_call_cfunc_latter(th, reg_cfp, ci);
EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, me->called_id, me->klass);
+ RUBY_DTRACE_FUNC_RETURN_HOOK(me->klass, me->called_id);
return val;
}
@@ -1553,6 +1558,7 @@ vm_call_bmethod_body(rb_thread_t *th, rb_call_info_t *ci, const VALUE *argv)
rb_proc_t *proc;
VALUE val;
+ RUBY_DTRACE_FUNC_ENTRY_HOOK(ci->me->klass, ci->me->called_id);
EXEC_EVENT_HOOK(th, RUBY_EVENT_CALL, ci->recv, ci->me->called_id, ci->me->klass);
/* control block frame */
@@ -1561,6 +1567,7 @@ vm_call_bmethod_body(rb_thread_t *th, rb_call_info_t *ci, const VALUE *argv)
val = vm_invoke_proc(th, proc, ci->recv, ci->defined_class, ci->argc, argv, ci->blockptr);
EXEC_EVENT_HOOK(th, RUBY_EVENT_RETURN, ci->recv, ci->me->called_id, ci->me->klass);
+ RUBY_DTRACE_FUNC_RETURN_HOOK(ci->me->klass, ci->me->called_id);
return val;
}
View
5 vm_insnhelper.h
@@ -62,13 +62,12 @@ enum {
extern char ruby_vm_redefined_flag[BOP_LAST_];
extern VALUE ruby_vm_const_missing_count;
-#if VM_COLLECT_USAGE_DETAILS
#define COLLECT_USAGE_INSN(insn) vm_collect_usage_insn(insn)
#define COLLECT_USAGE_OPERAND(insn, n, op) vm_collect_usage_operand((insn), (n), ((VALUE)(op)))
+
+#if VM_COLLECT_USAGE_DETAILS
#define COLLECT_USAGE_REGISTER(reg, s) vm_collect_usage_register((reg), (s))
#else
-#define COLLECT_USAGE_INSN(insn) /* none */
-#define COLLECT_USAGE_OPERAND(insn, n, op) /* none */
#define COLLECT_USAGE_REGISTER(reg, s) /* none */
#endif

8 comments on commit 4c740ba

@cdwijayarathna

Is this the commit which add Dtrace support features to Ruby 2.0? Can I use these features with Ruby installed to Windows?

@zzak
Collaborator

@cdwijayarathna Yes, this was implemented in Ruby 2.0. I'm not sure on Windows support. Try looking for "dtrace windows", and if you get stuck open a ticket on bugs.ruby-lang.org.

Thanks!

@cdwijayarathna

What are the other external libraries I have to install before I can use Dtrace feature in Ruby?

@cdwijayarathna

I'm working on a project to implement Ruby 2.0 features in Jruby. I'm very interested in Dtrace support since it seems very useful for programmers for debugging. As I found dtrace feature is invoked using 'dtrace' command, not by 'ruby' command. So why should we change internals in Ruby libraries? What are the new things we have to add to make ruby working with dtrace? If you have time please give me some useful advice.

@zzak
Collaborator

@cdwijayarathna Try asking on the ruby-core or ruby-talk mailing lists, you will have much better luck finding help there.

@cdwijayarathna

Okay, Thank you very much for helping.

@cdwijayarathna

I installed Ruby in Ubuntu. My current version is "ruby 2.0.0p0 (2013-02-24 revision 39474) [i686-linux]".
But when I tried to get probes provided by Ruby using "dtrace -l -P ruby", I get "failed to match ruby:::: No probe matches description".
My system still don't identify any probes in Ruby. How can I fix this?
Sorry for bothering you again and again.

@cdwijayarathna

Hello, does symbol-create available in ruby 2.0.0p247 ?

Please sign in to comment.
Something went wrong with that request. Please try again.