[EXPERIMENTAL: NEED DISCUSS]

* vm_trace.c: add events
* :thread_begin - hook at thread beggining.
* :thead_end - hook at thread ending.
* :b_call - hook at block enter.
* :b_return - hook at block leave.
  This change slow down block invocation.
  Please try and give us feedback until 2.0 code freeze.
* include/ruby/ruby.h: ditto.
* compile.c (rb_iseq_compile_node): ditto.
* insns.def: ditto.
* thread.c: ditto.
* vm.c: ditto.
* include/ruby/debug.h: add a comment.
* test/ruby/test_settracefunc.rb: add a tests.



git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38007 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
ko1 2012-11-29 22:28:16 +00:00
parent 2dc5e62545
commit 4db8340398
9 changed files with 136 additions and 29 deletions

View File

@ -1,3 +1,28 @@
Fri Nov 30 07:21:33 2012 Koichi Sasada <ko1@atdot.net>
[EXPERIMENTAL: NEED DISCUSS]
* vm_trace.c: add events
* :thread_begin - hook at thread beggining.
* :thead_end - hook at thread ending.
* :b_call - hook at block enter.
* :b_return - hook at block leave.
This change slow down block invocation.
Please try and give us feedback until 2.0 code freeze.
* include/ruby/ruby.h: ditto.
* compile.c (rb_iseq_compile_node): ditto.
* insns.def: ditto.
* thread.c: ditto.
* vm.c: ditto.
* include/ruby/debug.h: add a comment.
* test/ruby/test_settracefunc.rb: add a tests.
Fri Nov 30 06:56:30 2012 Ryan Davis <ryand-ruby@zenspider.com> Fri Nov 30 06:56:30 2012 Ryan Davis <ryand-ruby@zenspider.com>
* test/minitest/*: Imported minitest 4.3.2 (r8027) * test/minitest/*: Imported minitest 4.3.2 (r8027)

View File

@ -475,31 +475,36 @@ rb_iseq_compile_node(VALUE self, NODE *node)
iseq_set_arguments(iseq, ret, node->nd_args); iseq_set_arguments(iseq, ret, node->nd_args);
switch (iseq->type) { switch (iseq->type) {
case ISEQ_TYPE_BLOCK: { case ISEQ_TYPE_BLOCK:
LABEL *start = iseq->compile_data->start_label = NEW_LABEL(0); {
LABEL *end = iseq->compile_data->end_label = NEW_LABEL(0); LABEL *start = iseq->compile_data->start_label = NEW_LABEL(0);
LABEL *end = iseq->compile_data->end_label = NEW_LABEL(0);
ADD_LABEL(ret, start); ADD_LABEL(ret, start);
COMPILE(ret, "block body", node->nd_body); ADD_TRACE(ret, FIX2INT(iseq->location.first_lineno), RUBY_EVENT_B_CALL);
ADD_LABEL(ret, end); COMPILE(ret, "block body", node->nd_body);
ADD_TRACE(ret, nd_line(node), RUBY_EVENT_B_RETURN);
ADD_LABEL(ret, end);
/* wide range catch handler must put at last */ /* wide range catch handler must put at last */
ADD_CATCH_ENTRY(CATCH_TYPE_REDO, start, end, 0, start); ADD_CATCH_ENTRY(CATCH_TYPE_REDO, start, end, 0, start);
ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, start, end, 0, end); ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, start, end, 0, end);
break; break;
} }
case ISEQ_TYPE_CLASS: { case ISEQ_TYPE_CLASS:
ADD_TRACE(ret, FIX2INT(iseq->location.first_lineno), RUBY_EVENT_CLASS); {
COMPILE(ret, "scoped node", node->nd_body); ADD_TRACE(ret, FIX2INT(iseq->location.first_lineno), RUBY_EVENT_CLASS);
ADD_TRACE(ret, nd_line(node), RUBY_EVENT_END); COMPILE(ret, "scoped node", node->nd_body);
break; ADD_TRACE(ret, nd_line(node), RUBY_EVENT_END);
} break;
case ISEQ_TYPE_METHOD: { }
ADD_TRACE(ret, FIX2INT(iseq->location.first_lineno), RUBY_EVENT_CALL); case ISEQ_TYPE_METHOD:
COMPILE(ret, "scoped node", node->nd_body); {
ADD_TRACE(ret, nd_line(node), RUBY_EVENT_RETURN); ADD_TRACE(ret, FIX2INT(iseq->location.first_lineno), RUBY_EVENT_CALL);
break; COMPILE(ret, "scoped node", node->nd_body);
} ADD_TRACE(ret, nd_line(node), RUBY_EVENT_RETURN);
break;
}
default: { default: {
COMPILE(ret, "scoped node", node->nd_body); COMPILE(ret, "scoped node", node->nd_body);
break; break;

View File

@ -38,8 +38,10 @@ VALUE rb_debug_inspector_backtrace_locations(const rb_debug_inspector_t *dc);
/* Old style set_trace_func APIs */ /* Old style set_trace_func APIs */
/* duplicated def of include/ruby/ruby.h */
void rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data); void rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data);
int rb_remove_event_hook(rb_event_hook_func_t func); int rb_remove_event_hook(rb_event_hook_func_t func);
int rb_remove_event_hook_with_data(rb_event_hook_func_t func, VALUE data); int rb_remove_event_hook_with_data(rb_event_hook_func_t func, VALUE data);
void rb_thread_add_event_hook(VALUE thval, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data); void rb_thread_add_event_hook(VALUE thval, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data);
int rb_thread_remove_event_hook(VALUE thval, rb_event_hook_func_t func); int rb_thread_remove_event_hook(VALUE thval, rb_event_hook_func_t func);

View File

@ -1574,6 +1574,13 @@ int ruby_native_thread_p(void);
#define RUBY_EVENT_SWITCH 0x20000 #define RUBY_EVENT_SWITCH 0x20000
#define RUBY_EVENT_COVERAGE 0x40000 #define RUBY_EVENT_COVERAGE 0x40000
/* for TracePoint extended events */
#define RUBY_EVENT_B_CALL 0x0100
#define RUBY_EVENT_B_RETURN 0x0200
#define RUBY_EVENT_THREAD_BEGIN 0x0400
#define RUBY_EVENT_THREAD_END 0x0800
#define RUBY_EVENT_TRACEPOINT_ALL 0xFFFF
typedef unsigned int rb_event_flag_t; typedef unsigned int rb_event_flag_t;
typedef void (*rb_event_hook_func_t)(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass); typedef void (*rb_event_hook_func_t)(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass);

View File

@ -871,8 +871,9 @@ trace
rb_sourceline()); rb_sourceline());
} }
} }
EXEC_EVENT_HOOK(th, flag, GET_SELF(), 0, 0 /* id and klass are resolved at callee */, EXEC_EVENT_HOOK(th, flag, GET_SELF(), 0, 0 /* id and klass are resolved at callee */,
flag & RUBY_EVENT_RETURN ? TOPN(0) : Qundef); (flag & RUBY_EVENT_RETURN | RUBY_EVENT_B_RETURN) ? TOPN(0) : Qundef);
} }
/**********************************************************/ /**********************************************************/

View File

@ -532,9 +532,14 @@ class TestSetTraceFunc < Test::Unit::TestCase
end end
def test_tracepoint def test_tracepoint
events1, answer_events = *trace_by_tracepoint() events1, answer_events = *trace_by_tracepoint(:line, :class, :end, :call, :return, :c_call, :c_return, :raise)
mesg = events1.map{|e| mesg = events1.map{|e|
if false
p [:event, e[0]]
p [:line_file, e[1], e[2]]
p [:id, e[4]]
end
"#{e[0]} - #{e[2]}:#{e[1]} id: #{e[4]}" "#{e[0]} - #{e[2]}:#{e[1]} id: #{e[4]}"
}.join("\n") }.join("\n")
answer_events.zip(events1){|answer, event| answer_events.zip(events1){|answer, event|
@ -682,4 +687,54 @@ class TestSetTraceFunc < Test::Unit::TestCase
end end
} }
end end
def method_for_test_tracepoint_block
yield
end
def test_tracepoint_block
events = []
TracePoint.new(:call, :return, :c_call, :b_call, :c_return, :b_return){|tp|
events << [
tp.event, tp.method_id, tp.defined_class, tp.self.class,
/return/ =~ tp.event ? tp.return_value : nil
]
}.enable{
1.times{
3
}
method_for_test_tracepoint_block{
4
}
}
# pp events
expected_events =
[[:b_call, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
[:c_call, :times, Integer, Fixnum, nil],
[:b_call, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
[:b_return, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 3],
[:c_return, :times, Integer, Fixnum, 1],
[:call, :method_for_test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
[:b_call, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, nil],
[:b_return, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 4],
[:return, :method_for_test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 4],
[:b_return, :test_tracepoint_block, TestSetTraceFunc, TestSetTraceFunc, 4]
].zip(events){|expected, actual|
assert_equal(expected, actual)
}
end
def test_tracepoint_thread
events = []
created_thread = nil
TracePoint.new(:thread_begin, :thread_end){|tp|
events << [Thread.current, tp.event, tp.self]
}.enable{
created_thread = Thread.new{}
created_thread.join
}
assert_equal([created_thread, :thread_begin, self], events[0])
assert_equal([created_thread, :thread_end, self], events[1])
assert_equal(2, events.size)
end
end end

View File

@ -478,8 +478,9 @@ thread_start_func_2(rb_thread_t *th, VALUE *stack_start, VALUE *register_stack_s
th->errinfo = Qnil; th->errinfo = Qnil;
th->root_lep = rb_vm_ep_local_ep(proc->block.ep); th->root_lep = rb_vm_ep_local_ep(proc->block.ep);
th->root_svar = Qnil; th->root_svar = Qnil;
th->value = rb_vm_invoke_proc(th, proc, EXEC_EVENT_HOOK(th, RUBY_EVENT_THREAD_BEGIN, proc->block.self, 0, 0, th->self);
(int)RARRAY_LEN(args), RARRAY_PTR(args), 0); th->value = rb_vm_invoke_proc(th, proc, (int)RARRAY_LEN(args), RARRAY_PTR(args), 0);
EXEC_EVENT_HOOK(th, RUBY_EVENT_THREAD_END, proc->block.self, 0, 0, th->self);
} }
else { else {
th->value = (*th->first_func)((void *)args); th->value = (*th->first_func)((void *)args);

3
vm.c
View File

@ -1353,6 +1353,9 @@ vm_exec(rb_thread_t *th)
RUBY_DTRACE_METHOD_RETURN_HOOK(th, 0, 0) RUBY_DTRACE_METHOD_RETURN_HOOK(th, 0, 0)
EXEC_EVENT_HOOK(th, RUBY_EVENT_RETURN, th->cfp->self, 0, 0, Qnil); EXEC_EVENT_HOOK(th, RUBY_EVENT_RETURN, th->cfp->self, 0, 0, Qnil);
break; break;
case VM_FRAME_MAGIC_BLOCK:
EXEC_EVENT_HOOK(th, RUBY_EVENT_B_RETURN, th->cfp->self, 0, 0, Qnil);
break;
case VM_FRAME_MAGIC_CLASS: case VM_FRAME_MAGIC_CLASS:
EXEC_EVENT_HOOK(th, RUBY_EVENT_END, th->cfp->self, 0, 0, Qnil); EXEC_EVENT_HOOK(th, RUBY_EVENT_END, th->cfp->self, 0, 0, Qnil);
break; break;

View File

@ -507,6 +507,10 @@ get_event_id(rb_event_flag_t event)
C(c_call, C_CALL); C(c_call, C_CALL);
C(c_return, C_RETURN); C(c_return, C_RETURN);
C(raise, RAISE); C(raise, RAISE);
C(b_call, B_CALL);
C(b_return, B_RETURN);
C(thread_begin, THREAD_BEGIN);
C(thread_end, THREAD_END);
#undef C #undef C
default: default:
return 0; return 0;
@ -610,6 +614,10 @@ symbol2event_flag(VALUE v)
C(c_call, C_CALL); C(c_call, C_CALL);
C(c_return, C_RETURN); C(c_return, C_RETURN);
C(raise, RAISE); C(raise, RAISE);
C(b_call, B_CALL);
C(b_return, B_RETURN);
C(thread_begin, THREAD_BEGIN);
C(thread_end, THREAD_END);
#undef C #undef C
rb_raise(rb_eArgError, "unknown event: %s", rb_id2name(SYM2ID(sym))); rb_raise(rb_eArgError, "unknown event: %s", rb_id2name(SYM2ID(sym)));
} }
@ -736,7 +744,7 @@ rb_tracearg_self(rb_trace_arg_t *trace_arg)
VALUE VALUE
rb_tracearg_return_value(rb_trace_arg_t *trace_arg) rb_tracearg_return_value(rb_trace_arg_t *trace_arg)
{ {
if (trace_arg->event & (RUBY_EVENT_RETURN | RUBY_EVENT_C_RETURN)) { if (trace_arg->event & (RUBY_EVENT_RETURN | RUBY_EVENT_C_RETURN | RUBY_EVENT_B_RETURN)) {
/* ok */ /* ok */
} }
else { else {
@ -975,7 +983,7 @@ tracepoint_new_s(int argc, VALUE *argv, VALUE self)
} }
} }
else { else {
events = RUBY_EVENT_ALL; events = RUBY_EVENT_TRACEPOINT_ALL;
} }
if (!rb_block_given_p()) { if (!rb_block_given_p()) {