From afb02bbe92e55f877d50ed8705c95a41d541458d Mon Sep 17 00:00:00 2001 From: tenderlove Date: Sat, 1 Dec 2012 02:13:06 +0000 Subject: * variable.c (rb_class_path_no_cache): add a function to get the class path without caching the computed path. Some classes are frozen, and will raise an exception without this. * probes.d (cmethod-entry, cmethod-return): separate cmethods from regular methods to match set trace func. * probes_helper.h: refactor macros. Fix probes to avoid calling #inspect when profiling. * insns.def: update for use with new macros. * vm_eval.c: ditto * vm_insnhelper.c: ditto * test/dtrace/test_singleton_function.rb: fix test for new output. * test/dtrace/test_cmethod.rb: test the cmethod probes. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38099 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 22 +++++++++++ insns.def | 44 +++++++++------------ probes.d | 3 ++ probes_helper.h | 71 ++++++++++++++++++++++------------ test/dtrace/test_cmethod.rb | 49 +++++++++++++++++++++++ test/dtrace/test_singleton_function.rb | 4 +- variable.c | 34 +++++++++++++--- vm_eval.c | 10 ++--- vm_insnhelper.c | 10 ++--- 9 files changed, 178 insertions(+), 69 deletions(-) create mode 100644 test/dtrace/test_cmethod.rb diff --git a/ChangeLog b/ChangeLog index 252d6d4a7d..2c9d962809 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,25 @@ +Sat Dec 1 11:09:12 2012 Aaron Patterson + + * variable.c (rb_class_path_no_cache): add a function to get the class + path without caching the computed path. Some classes are frozen, and + will raise an exception without this. + + * probes.d (cmethod-entry, cmethod-return): separate cmethods from + regular methods to match set trace func. + + * probes_helper.h: refactor macros. Fix probes to avoid calling + #inspect when profiling. + + * insns.def: update for use with new macros. + + * vm_eval.c: ditto + + * vm_insnhelper.c: ditto + + * test/dtrace/test_singleton_function.rb: fix test for new output. + + * test/dtrace/test_cmethod.rb: test the cmethod probes. + Sat Dec 1 09:44:16 2012 Eric Hodel * test/rdoc/test_rdoc_options.rb: Windows drive letters are diff --git a/insns.def b/insns.def index e9716f4d0f..c4918961ce 100644 --- a/insns.def +++ b/insns.def @@ -843,32 +843,24 @@ trace { rb_event_flag_t flag = (rb_event_flag_t)nf; - if (RUBY_DTRACE_METHOD_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_METHOD_ENTRY( - RSTRING_PTR(rb_inspect(klass)), - rb_id2name(called_id), - rb_sourcefile(), - rb_sourceline()); - } - } - if (RUBY_DTRACE_METHOD_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_METHOD_RETURN( - RSTRING_PTR(rb_inspect(klass)), - rb_id2name(called_id), - rb_sourcefile(), - rb_sourceline()); + if (RUBY_DTRACE_METHOD_ENTRY_ENABLED() || + RUBY_DTRACE_METHOD_RETURN_ENABLED() || + RUBY_DTRACE_CMETHOD_ENTRY_ENABLED() || + RUBY_DTRACE_CMETHOD_RETURN_ENABLED()) { + + switch(flag) { + case RUBY_EVENT_CALL: + RUBY_DTRACE_METHOD_ENTRY_HOOK(th, 0, 0); + break; + case RUBY_EVENT_C_CALL: + RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, 0, 0); + break; + case RUBY_EVENT_RETURN: + RUBY_DTRACE_METHOD_RETURN_HOOK(th, 0, 0); + break; + case RUBY_EVENT_C_RETURN: + RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, 0, 0); + break; } } diff --git a/probes.d b/probes.d index 1c5e03f756..591b2f444f 100644 --- a/probes.d +++ b/probes.d @@ -4,6 +4,9 @@ provider ruby { probe method__entry(const char *, const char *, const char *, int); probe method__return(const char *, const char *, const char *, int); + probe cmethod__entry(const char *, const char *, const char *, int); + probe cmethod__return(const char *, const char *, const char *, int); + probe require__entry(const char *, const char *, int); probe require__return(const char *); diff --git a/probes_helper.h b/probes_helper.h index 7f4beb944f..13bcbdbbda 100644 --- a/probes_helper.h +++ b/probes_helper.h @@ -4,23 +4,11 @@ #include "ruby/ruby.h" #include "probes.h" -#define RUBY_DTRACE_METHOD_ENTRY_HOOK(klass, id) \ - if (RUBY_DTRACE_METHOD_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_METHOD_ENTRY( \ - classname, \ - methodname, \ - filename, \ - rb_sourceline()); \ - } \ - } \ +VALUE rb_class_path_no_cache(VALUE _klass); -#define RUBY_DTRACE_METHOD_RETURN_HOOK(th, klass, id) \ - if (RUBY_DTRACE_METHOD_RETURN_ENABLED()) { \ - VALUE _klass = (klass); \ +#define RUBY_DTRACE_HOOK(name, th, klazz, id) \ + if (RUBY_DTRACE_##name##_ENABLED()) { \ + VALUE _klass = (klazz); \ VALUE _id = (id); \ const char * classname; \ const char * methodname; \ @@ -28,16 +16,49 @@ if (!_klass) { \ rb_thread_method_id_and_class((th), &_id, &_klass); \ } \ - classname = rb_class2name(_klass); \ - methodname = rb_id2name(_id); \ - filename = rb_sourcefile(); \ - if (classname && methodname && filename) { \ - RUBY_DTRACE_METHOD_RETURN( \ - classname, \ - methodname, \ - filename, \ - rb_sourceline()); \ + if (_klass) { \ + if (RB_TYPE_P(_klass, T_ICLASS)) { \ + _klass = RBASIC(_klass)->klass; \ + } \ + else if (FL_TEST(_klass, FL_SINGLETON)) { \ + _klass = rb_iv_get(_klass, "__attached__"); \ + } \ + switch(TYPE(_klass)) { \ + case T_CLASS: \ + case T_ICLASS: \ + case T_MODULE: \ + { \ + VALUE _name = rb_class_path_no_cache(_klass); \ + if (!NIL_P(_name)) { \ + classname = StringValuePtr(_name); \ + } else { \ + classname = ""; \ + } \ + methodname = rb_id2name(_id); \ + filename = rb_sourcefile(); \ + if (classname && methodname && filename) { \ + RUBY_DTRACE_##name( \ + classname, \ + methodname, \ + filename, \ + rb_sourceline()); \ + } \ + break; \ + } \ + } \ } \ } \ +#define RUBY_DTRACE_METHOD_ENTRY_HOOK(th, klass, id) \ + RUBY_DTRACE_HOOK(METHOD_ENTRY, th, klass, id) + +#define RUBY_DTRACE_METHOD_RETURN_HOOK(th, klass, id) \ + RUBY_DTRACE_HOOK(METHOD_RETURN, th, klass, id) + +#define RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, klass, id) \ + RUBY_DTRACE_HOOK(CMETHOD_ENTRY, th, klass, id) + +#define RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, klass, id) \ + RUBY_DTRACE_HOOK(CMETHOD_RETURN, th, klass, id) + #endif /* RUBY_PROBES_HELPER_H */ diff --git a/test/dtrace/test_cmethod.rb b/test/dtrace/test_cmethod.rb new file mode 100644 index 0000000000..138ec55ab3 --- /dev/null +++ b/test/dtrace/test_cmethod.rb @@ -0,0 +1,49 @@ +require 'dtrace/helper' + +module DTrace + class TestCMethod < TestCase + def test_entry + probe = <<-eoprobe +ruby$target:::cmethod-entry +{ + 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[1] == 'times' + } + + assert_equal 1, foo_calls.length + } + end + + def test_exit + probe = <<-eoprobe +ruby$target:::cmethod-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[1] == 'times' + } + + assert_equal 1, foo_calls.length + } + end + + def ruby_program + <<-eoruby + class Foo + def self.foo; end + end + 10.times { Foo.foo } + eoruby + end + end +end if defined?(DTrace::TestCase) + diff --git a/test/dtrace/test_singleton_function.rb b/test/dtrace/test_singleton_function.rb index 06f070913b..4b5d3d7f18 100644 --- a/test/dtrace/test_singleton_function.rb +++ b/test/dtrace/test_singleton_function.rb @@ -13,7 +13,7 @@ ruby$target:::method-entry trap_probe(probe, ruby_program) { |d_file, rb_file, probes| foo_calls = probes.map { |line| line.split }.find_all { |row| - row.first == '#' && row[1] == 'foo' + row.first == 'Foo' && row[1] == 'foo' } assert_equal 10, foo_calls.length @@ -33,7 +33,7 @@ ruby$target:::method-return trap_probe(probe, ruby_program) { |d_file, rb_file, probes| foo_calls = probes.map { |line| line.split }.find_all { |row| - row.first == '#' && row[1] == 'foo' + row.first == 'Foo' && row[1] == 'foo' } assert_equal 10, foo_calls.length diff --git a/variable.c b/variable.c index 601efb955d..ebe532f28a 100644 --- a/variable.c +++ b/variable.c @@ -211,8 +211,10 @@ rb_mod_name(VALUE mod) return path; } +typedef VALUE (*path_cache_func)(VALUE obj, ID id, VALUE val); + static VALUE -rb_tmp_class_path(VALUE klass, int *permanent) +rb_tmp_class_path(VALUE klass, int *permanent, path_cache_func cache_path) { VALUE path = classname(klass, permanent); st_data_t n = (st_data_t)path; @@ -233,12 +235,17 @@ rb_tmp_class_path(VALUE klass, int *permanent) s = "Module"; } else { - s = rb_class2name(RBASIC(klass)->klass); + int perm; + VALUE path; + + path = rb_tmp_class_path(RBASIC(klass)->klass, &perm, cache_path); + s = RSTRING_PTR(path); } } path = rb_sprintf("#<%s:%p>", s, (void*)klass); OBJ_FREEZE(path); - rb_ivar_set(klass, tmp_classpath, path); + + cache_path(klass, tmp_classpath, path); *permanent = 0; return path; @@ -249,7 +256,22 @@ VALUE rb_class_path(VALUE klass) { int permanent; - VALUE path = rb_tmp_class_path(klass, &permanent); + VALUE path = rb_tmp_class_path(klass, &permanent, rb_ivar_set); + if (!NIL_P(path)) path = rb_str_dup(path); + return path; +} + +static VALUE +null_cache(VALUE obj, ID id, VALUE val) +{ + return Qnil; +} + +VALUE +rb_class_path_no_cache(VALUE klass) +{ + int permanent; + VALUE path = rb_tmp_class_path(klass, &permanent, null_cache); if (!NIL_P(path)) path = rb_str_dup(path); return path; } @@ -265,7 +287,7 @@ rb_set_class_path_string(VALUE klass, VALUE under, VALUE name) } else { int permanent; - str = rb_str_dup(rb_tmp_class_path(under, &permanent)); + str = rb_str_dup(rb_tmp_class_path(under, &permanent, rb_ivar_set)); rb_str_cat2(str, "::"); rb_str_append(str, name); OBJ_FREEZE(str); @@ -288,7 +310,7 @@ rb_set_class_path(VALUE klass, VALUE under, const char *name) } else { int permanent; - str = rb_str_dup(rb_tmp_class_path(under, &permanent)); + str = rb_str_dup(rb_tmp_class_path(under, &permanent, rb_ivar_set)); rb_str_cat2(str, "::"); rb_str_cat2(str, name); if (!permanent) { diff --git a/vm_eval.c b/vm_eval.c index 53c2918d34..f0b0c500e2 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -55,7 +55,7 @@ vm_call0_cfunc(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv) { VALUE val; - RUBY_DTRACE_METHOD_ENTRY_HOOK(ci->defined_class, ci->mid); + RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, ci->defined_class, ci->mid); EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, ci->recv, ci->mid, ci->defined_class, Qnil); { rb_control_frame_t *reg_cfp = th->cfp; @@ -85,7 +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, val); - RUBY_DTRACE_METHOD_RETURN_HOOK(th, ci->defined_class, ci->mid); + RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, ci->defined_class, ci->mid); return val; } @@ -103,7 +103,7 @@ vm_call0_cfunc_with_frame(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv ID mid = ci->mid; rb_block_t *blockptr = ci->blockptr; - RUBY_DTRACE_METHOD_ENTRY_HOOK(defined_class, mid); + RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, defined_class, mid); EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, mid, defined_class, Qnil); { rb_control_frame_t *reg_cfp = th->cfp; @@ -123,7 +123,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, recv, mid, defined_class, val); - RUBY_DTRACE_METHOD_RETURN_HOOK(th, defined_class, mid); + RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, defined_class, mid); return val; } @@ -1015,7 +1015,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, Qnil); - RUBY_DTRACE_METHOD_RETURN_HOOK(th, me->klass, me->called_id); + RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, me->klass, me->called_id); } th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp); diff --git a/vm_insnhelper.c b/vm_insnhelper.c index c40f66bbe9..75da83e525 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -1449,7 +1449,7 @@ vm_call_cfunc_with_frame(rb_thread_t *th, rb_control_frame_t *reg_cfp, rb_call_i rb_block_t *blockptr = ci->blockptr; int argc = ci->argc; - RUBY_DTRACE_METHOD_ENTRY_HOOK(me->klass, me->called_id); + RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, me->klass, me->called_id); EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, me->called_id, me->klass, Qundef); vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, recv, defined_class, @@ -1468,7 +1468,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, val); - RUBY_DTRACE_METHOD_RETURN_HOOK(th, me->klass, me->called_id); + RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, me->klass, me->called_id); return val; } @@ -1516,7 +1516,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_METHOD_ENTRY_HOOK(me->klass, me->called_id); + RUBY_DTRACE_CMETHOD_ENTRY_HOOK(th, me->klass, me->called_id); EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, me->called_id, me->klass, Qnil); if (!(ci->me->flag & NOEX_PROTECTED) && @@ -1526,7 +1526,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, val); - RUBY_DTRACE_METHOD_RETURN_HOOK(th, me->klass, me->called_id); + RUBY_DTRACE_CMETHOD_RETURN_HOOK(th, me->klass, me->called_id); return val; } @@ -1575,7 +1575,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_METHOD_ENTRY_HOOK(ci->me->klass, ci->me->called_id); + RUBY_DTRACE_METHOD_ENTRY_HOOK(th, ci->me->klass, ci->me->called_id); EXEC_EVENT_HOOK(th, RUBY_EVENT_CALL, ci->recv, ci->me->called_id, ci->me->klass, Qnil); /* control block frame */ -- cgit v1.2.3