зеркало из https://github.com/github/ruby.git
* 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:
Родитель
d3c6187a4d
Коммит
afb02bbe92
22
ChangeLog
22
ChangeLog
|
@ -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
|
||||
|
|
42
insns.def
42
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
3
probes.d
3
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 *);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
34
variable.c
34
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) {
|
||||
|
|
10
vm_eval.c
10
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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
Загрузка…
Ссылка в новой задаче