Project

General

Profile

Bug #15313 ยป 0001-Add-a-tail-call-predicate-for-TracePoint.patch

alanwu (Alan Wu), 11/22/2018 05:45 PM

View differences:

include/ruby/debug.h
VALUE rb_tracearg_return_value(rb_trace_arg_t *trace_arg);
VALUE rb_tracearg_raised_exception(rb_trace_arg_t *trace_arg);
VALUE rb_tracearg_object(rb_trace_arg_t *trace_arg);
VALUE rb_tracearg_is_tailcall(rb_trace_arg_t *trace_arg);
/*
* Postponed Job API
spec/ruby/core/tracepoint/tailcall_spec.rb
require_relative '../../spec_helper'
describe 'TracePoint#tailcall?' do
it 'returns true when a tailcall happens' do
iseq = RubyVM::InstructionSequence.compile(<<-RUBY, "#{__FILE__}:#{__LINE__}", tailcall_optimization: true)
Module.new do
def self.leaf(foo, bar)
foo * bar
end
def self.middle
leaf(2, 3)
end
def self.tco
leaf(1, 9)
middle
end
end
RUBY
mod = iseq.eval
events = []
TracePoint.new(:call) { |tp| events << [tp.method_id, tp.tailcall?] }.enable do
mod.tco
mod.leaf(2, 3)
end
events.should == [
[:tco, false],
[:leaf, false],
[:middle, true],
[:leaf, true],
[:leaf, false]
]
end
end
test/ruby/test_settracefunc.rb
begin
eval <<-EOF.gsub(/^.*?: /, ""), nil, 'xyzzy'
1: trace = TracePoint.trace(*trace_events){|tp| next if !target_thread?
2: events << [tp.event, tp.lineno, tp.path, _defined_class.(tp), tp.method_id, tp.self, tp.binding.eval("_local_var"), _get_data.(tp)] if tp.path == 'xyzzy'
2: events << [tp.event, tp.lineno, tp.path, _defined_class.(tp), tp.method_id, tp.self, tp.binding.eval("_local_var"), _get_data.(tp), tp.tailcall?] if tp.path == 'xyzzy'
3: }
4: 1.times{|;_local_var| _local_var = :inner
5: tap{}
......
answer_events = [
#
[:c_return, 1, "xyzzy", TracePoint, :trace, TracePoint, :outer, trace],
[:line, 4, 'xyzzy', self.class, method, self, :outer, :nothing],
[:c_call, 4, 'xyzzy', Integer, :times, 1, :outer, :nothing],
[:line, 4, 'xyzzy', self.class, method, self, nil, :nothing],
[:line, 5, 'xyzzy', self.class, method, self, :inner, :nothing],
[:c_call, 5, 'xyzzy', Kernel, :tap, self, :inner, :nothing],
[:c_return, 5, "xyzzy", Kernel, :tap, self, :inner, self],
[:c_return, 4, "xyzzy", Integer, :times, 1, :outer, 1],
[:line, 7, 'xyzzy', self.class, method, self, :outer, :nothing],
[:c_call, 7, "xyzzy", Class, :inherited, Object, :outer, :nothing],
[:c_return, 7, "xyzzy", Class, :inherited, Object, :outer, nil],
[:class, 7, "xyzzy", nil, nil, xyzzy.class, nil, :nothing],
[:line, 8, "xyzzy", nil, nil, xyzzy.class, nil, :nothing],
[:line, 9, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
[:c_call, 9, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, :nothing],
[:c_return, 9, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, nil],
[:line, 13, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
[:c_call, 13, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, :nothing],
[:c_return,13, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, nil],
[:end, 17, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
[:line, 18, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
[:c_call, 18, "xyzzy", Class, :new, xyzzy.class, :outer, :nothing],
[:c_call, 18, "xyzzy", BasicObject, :initialize, xyzzy, :outer, :nothing],
[:c_return,18, "xyzzy", BasicObject, :initialize, xyzzy, :outer, nil],
[:c_return,18, "xyzzy", Class, :new, xyzzy.class, :outer, xyzzy],
[:line, 19, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
[:call, 9, "xyzzy", xyzzy.class, :foo, xyzzy, nil, :nothing],
[:line, 10, "xyzzy", xyzzy.class, :foo, xyzzy, nil, :nothing],
[:line, 11, "xyzzy", xyzzy.class, :foo, xyzzy, :XYZZY_foo, :nothing],
[:call, 13, "xyzzy", xyzzy.class, :bar, xyzzy, nil, :nothing],
[:line, 14, "xyzzy", xyzzy.class, :bar, xyzzy, nil, :nothing],
[:line, 15, "xyzzy", xyzzy.class, :bar, xyzzy, :XYZZY_bar, :nothing],
[:c_call, 15, "xyzzy", Kernel, :tap, xyzzy, :XYZZY_bar, :nothing],
[:c_return,15, "xyzzy", Kernel, :tap, xyzzy, :XYZZY_bar, xyzzy],
[:return, 16, "xyzzy", xyzzy.class, :bar, xyzzy, :XYZZY_bar, xyzzy],
[:return, 12, "xyzzy", xyzzy.class, :foo, xyzzy, :XYZZY_foo, xyzzy],
[:line, 20, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
[:c_call, 20, "xyzzy", Kernel, :raise, self, :outer, :nothing],
[:c_call, 20, "xyzzy", Exception, :exception, RuntimeError, :outer, :nothing],
[:c_call, 20, "xyzzy", Exception, :initialize, raised_exc, :outer, :nothing],
[:c_return,20, "xyzzy", Exception, :initialize, raised_exc, :outer, raised_exc],
[:c_return,20, "xyzzy", Exception, :exception, RuntimeError, :outer, raised_exc],
[:c_return,20, "xyzzy", Kernel, :raise, self, :outer, nil],
[:c_call, 20, "xyzzy", Exception, :backtrace, raised_exc, :outer, :nothing],
[:c_return,20, "xyzzy", Exception, :backtrace, raised_exc, :outer, nil],
[:raise, 20, "xyzzy", TestSetTraceFunc, :trace_by_tracepoint, self, :outer, raised_exc],
[:c_call, 20, "xyzzy", Module, :===, RuntimeError,:outer, :nothing],
[:c_return,20, "xyzzy", Module, :===, RuntimeError,:outer, true],
[:line, 21, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
[:c_call, 21, "xyzzy", TracePoint, :disable, trace, :outer, :nothing],
[:c_return, 1, "xyzzy", TracePoint, :trace, TracePoint, :outer, trace, false],
[:line, 4, 'xyzzy', self.class, method, self, :outer, :nothing, false],
[:c_call, 4, 'xyzzy', Integer, :times, 1, :outer, :nothing, false],
[:line, 4, 'xyzzy', self.class, method, self, nil, :nothing, false],
[:line, 5, 'xyzzy', self.class, method, self, :inner, :nothing, false],
[:c_call, 5, 'xyzzy', Kernel, :tap, self, :inner, :nothing, false],
[:c_return, 5, "xyzzy", Kernel, :tap, self, :inner, self, false],
[:c_return, 4, "xyzzy", Integer, :times, 1, :outer, 1, false],
[:line, 7, 'xyzzy', self.class, method, self, :outer, :nothing, false],
[:c_call, 7, "xyzzy", Class, :inherited, Object, :outer, :nothing, false],
[:c_return, 7, "xyzzy", Class, :inherited, Object, :outer, nil, false],
[:class, 7, "xyzzy", nil, nil, xyzzy.class, nil, :nothing, false],
[:line, 8, "xyzzy", nil, nil, xyzzy.class, nil, :nothing, false],
[:line, 9, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing, false],
[:c_call, 9, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, :nothing, false],
[:c_return, 9, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, nil, false],
[:line, 13, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing, false],
[:c_call, 13, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, :nothing, false],
[:c_return,13, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, nil, false],
[:end, 17, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing, false],
[:line, 18, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing, false],
[:c_call, 18, "xyzzy", Class, :new, xyzzy.class, :outer, :nothing, false],
[:c_call, 18, "xyzzy", BasicObject, :initialize, xyzzy, :outer, :nothing, false],
[:c_return,18, "xyzzy", BasicObject, :initialize, xyzzy, :outer, nil, false],
[:c_return,18, "xyzzy", Class, :new, xyzzy.class, :outer, xyzzy, false],
[:line, 19, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing, false],
[:call, 9, "xyzzy", xyzzy.class, :foo, xyzzy, nil, :nothing, false],
[:line, 10, "xyzzy", xyzzy.class, :foo, xyzzy, nil, :nothing, false],
[:line, 11, "xyzzy", xyzzy.class, :foo, xyzzy, :XYZZY_foo, :nothing, false],
[:call, 13, "xyzzy", xyzzy.class, :bar, xyzzy, nil, :nothing, false],
[:line, 14, "xyzzy", xyzzy.class, :bar, xyzzy, nil, :nothing, false],
[:line, 15, "xyzzy", xyzzy.class, :bar, xyzzy, :XYZZY_bar, :nothing, false],
[:c_call, 15, "xyzzy", Kernel, :tap, xyzzy, :XYZZY_bar, :nothing, false],
[:c_return,15, "xyzzy", Kernel, :tap, xyzzy, :XYZZY_bar, xyzzy, false],
[:return, 16, "xyzzy", xyzzy.class, :bar, xyzzy, :XYZZY_bar, xyzzy, false],
[:return, 12, "xyzzy", xyzzy.class, :foo, xyzzy, :XYZZY_foo, xyzzy, false],
[:line, 20, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing, false],
[:c_call, 20, "xyzzy", Kernel, :raise, self, :outer, :nothing, false],
[:c_call, 20, "xyzzy", Exception, :exception, RuntimeError, :outer, :nothing, false],
[:c_call, 20, "xyzzy", Exception, :initialize, raised_exc, :outer, :nothing, false],
[:c_return,20, "xyzzy", Exception, :initialize, raised_exc, :outer, raised_exc, false],
[:c_return,20, "xyzzy", Exception, :exception, RuntimeError, :outer, raised_exc, false],
[:c_return,20, "xyzzy", Kernel, :raise, self, :outer, nil, false],
[:c_call, 20, "xyzzy", Exception, :backtrace, raised_exc, :outer, :nothing, false],
[:c_return,20, "xyzzy", Exception, :backtrace, raised_exc, :outer, nil, false],
[:raise, 20, "xyzzy", TestSetTraceFunc, :trace_by_tracepoint, self, :outer, raised_exc, false],
[:c_call, 20, "xyzzy", Module, :===, RuntimeError,:outer, :nothing, false],
[:c_return,20, "xyzzy", Module, :===, RuntimeError,:outer, true, false],
[:line, 21, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing, false],
[:c_call, 21, "xyzzy", TracePoint, :disable, trace, :outer, :nothing, false],
]
return events, answer_events
vm.c
iseq->body->stack_max);
RUBY_DTRACE_METHOD_ENTRY_HOOK(ec, me->owner, me->def->original_id);
EXEC_EVENT_HOOK(ec, RUBY_EVENT_CALL, self, me->def->original_id, me->called_id, me->owner, Qnil);
EXEC_EVENT_HOOK(ec, RUBY_EVENT_CALL, self, me->def->original_id, me->called_id, me->owner, Qfalse);
VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH);
ret = vm_exec(ec, TRUE);
EXEC_EVENT_HOOK(ec, RUBY_EVENT_RETURN, self, me->def->original_id, me->called_id, me->owner, ret);
vm_core.h
enum {
/* Frame/Environment flag bits:
* MMMM MMMM MMMM MMMM ____ __FF FFFF EEEX (LSB)
* MMMM MMMM MMMM MMMM ____ _FFF FFFF EEEX (LSB)
*
* X : tag for GC marking (It seems as Fixnum)
* EEE : 3 bits Env flags
* FF..: 6 bits Frame flags
* FF..: 7 bits Frame flags
* MM..: 15 bits frame magic (to check frame corruption)
*/
......
VM_FRAME_FLAG_CFRAME = 0x0080,
VM_FRAME_FLAG_LAMBDA = 0x0100,
VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM = 0x0200,
VM_FRAME_FLAG_TAILCALLED = 0x0400,
/* env flag */
VM_ENV_FLAG_LOCAL = 0x0002,
vm_insnhelper.c
*sp++ = src_argv[i];
}
vm_push_frame(ec, iseq, VM_FRAME_MAGIC_METHOD | VM_ENV_FLAG_LOCAL | finish_flag,
vm_push_frame(ec, iseq, VM_FRAME_MAGIC_METHOD | VM_FRAME_FLAG_TAILCALLED | VM_ENV_FLAG_LOCAL | finish_flag,
calling->recv, calling->block_handler, (VALUE)me,
iseq->body->iseq_encoded + opt_pc, sp,
iseq->body->local_table_size - iseq->body->param.size,
......
VM_ASSERT(vm_event_flags & events);
/* increment PC because source line is calculated with PC-1 */
if ((event = (events & (RUBY_EVENT_CLASS | RUBY_EVENT_CALL | RUBY_EVENT_B_CALL))) != 0) {
VM_ASSERT(event == RUBY_EVENT_CLASS ||
event == RUBY_EVENT_CALL ||
event == RUBY_EVENT_B_CALL);
if ((event = (events & (RUBY_EVENT_CLASS | RUBY_EVENT_B_CALL))) != 0) {
VM_ASSERT(event == RUBY_EVENT_CLASS || event == RUBY_EVENT_B_CALL);
reg_cfp->pc++;
vm_dtrace(event, ec);
EXEC_EVENT_HOOK(ec, event, GET_SELF(), 0, 0, 0, Qundef);
reg_cfp->pc--;
}
if (events & RUBY_EVENT_CALL) {
VALUE is_tailcall = (VM_ENV_FLAGS(reg_cfp->ep, VM_FRAME_FLAG_TAILCALLED)) ? Qtrue : Qfalse;
reg_cfp->pc++;
vm_dtrace(RUBY_EVENT_CALL, ec);
EXEC_EVENT_HOOK(ec, RUBY_EVENT_CALL, GET_SELF(), 0, 0, 0, is_tailcall);
reg_cfp->pc--;
}
if (events & RUBY_EVENT_LINE) {
reg_cfp->pc++;
vm_dtrace(RUBY_EVENT_LINE, ec);
vm_trace.c
return trace_arg->data;
}
VALUE
rb_tracearg_is_tailcall(rb_trace_arg_t *trace_arg)
{
if (trace_arg->event == RUBY_EVENT_CALL) {
return (trace_arg->data == Qtrue) ? Qtrue : Qfalse;
}
return Qfalse;
}
/*
* Type of event
*
......
return rb_tracearg_raised_exception(get_trace_arg());
}
/*
* Whether the interpreter performed a tail call. Can only
* be true for +:call+ events.
*/
static VALUE
tracepoint_attr_is_tailcall(VALUE tpval)
{
return rb_tracearg_is_tailcall(get_trace_arg());
}
static void
tp_call_trace(VALUE tpval, rb_trace_arg_t *trace_arg)
{
......
rb_define_method(rb_cTracePoint, "self", tracepoint_attr_self, 0);
rb_define_method(rb_cTracePoint, "return_value", tracepoint_attr_return_value, 0);
rb_define_method(rb_cTracePoint, "raised_exception", tracepoint_attr_raised_exception, 0);
rb_define_method(rb_cTracePoint, "tailcall?", tracepoint_attr_is_tailcall, 0);
rb_define_singleton_method(rb_cTracePoint, "stat", tracepoint_stat_s, 0);
}
    (1-1/1)