* 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
This commit is contained in:
tenderlove 2012-12-01 02:13:06 +00:00
Родитель d3c6187a4d
Коммит afb02bbe92
9 изменённых файлов: 177 добавлений и 68 удалений

Просмотреть файл

@ -1,3 +1,25 @@
Sat Dec 1 11:09:12 2012 Aaron Patterson <aaron@tenderlovemaking.com>
* 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 <drbrain@segment7.net>
* test/rdoc/test_rdoc_options.rb: Windows drive letters are

Просмотреть файл

@ -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;
if (RUBY_DTRACE_METHOD_ENTRY_ENABLED() ||
RUBY_DTRACE_METHOD_RETURN_ENABLED() ||
RUBY_DTRACE_CMETHOD_ENTRY_ENABLED() ||
RUBY_DTRACE_CMETHOD_RETURN_ENABLED()) {
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());
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;
}
}

Просмотреть файл

@ -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 *);

Просмотреть файл

@ -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 = "<unknown>"; \
} \
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 */

Просмотреть файл

@ -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)

Просмотреть файл

@ -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 == '#<Class:Foo>' && 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 == '#<Class:Foo>' && row[1] == 'foo'
row.first == 'Foo' && row[1] == 'foo'
}
assert_equal 10, foo_calls.length

Просмотреть файл

@ -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) {

Просмотреть файл

@ -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);

Просмотреть файл

@ -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 */