RubyVM::InstructionSequence#trace_points.

* iseq.c (iseqw_trace_points): add `RubyVM::InstructionSequence#trace_points`
  method for tools which want to manipulate ISeq (and traces).

* test/ruby/test_iseq.rb: add a test for this method.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@61427 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
ko1 2017-12-23 14:46:59 +00:00
parent ac40fc997b
commit 3044138bd7
4 changed files with 96 additions and 21 deletions

1
NEWS
View File

@ -168,6 +168,7 @@ with all sufficient information, see the ChangeLog file or Redmine
* New method: * New method:
* RubyVM::InstructionSequence#each_child * RubyVM::InstructionSequence#each_child
* RubyVM::InstructionSequence#trace_points
* String * String

View File

@ -763,7 +763,7 @@ rb_vm_insn_addr2insn(const void *addr) /* cold path */
VALUE * VALUE *
rb_iseq_original_iseq(const rb_iseq_t *iseq) /* cold path */ rb_iseq_original_iseq(const rb_iseq_t *iseq) /* cold path */
{ {
VALUE *original_code; VALUE *original_code;3
if (ISEQ_ORIGINAL_ISEQ(iseq)) return ISEQ_ORIGINAL_ISEQ(iseq); if (ISEQ_ORIGINAL_ISEQ(iseq)) return ISEQ_ORIGINAL_ISEQ(iseq);
original_code = ISEQ_ORIGINAL_ISEQ_ALLOC(iseq, iseq->body->iseq_size); original_code = ISEQ_ORIGINAL_ISEQ_ALLOC(iseq, iseq->body->iseq_size);

38
iseq.c
View File

@ -1874,6 +1874,43 @@ iseqw_each_child(VALUE self)
return self; return self;
} }
static void
push_event_info(const rb_iseq_t *iseq, rb_event_flag_t events, int line, VALUE ary)
{
#define C(ev, cstr, l) if (events & ev) rb_ary_push(ary, rb_ary_new_from_args(2, l, ID2SYM(rb_intern(cstr))));
C(RUBY_EVENT_CLASS, "class", rb_iseq_first_lineno(iseq));
C(RUBY_EVENT_CALL, "call", rb_iseq_first_lineno(iseq));
C(RUBY_EVENT_B_CALL, "b_call", rb_iseq_first_lineno(iseq));
C(RUBY_EVENT_LINE, "line", INT2FIX(line));
C(RUBY_EVENT_END, "end", INT2FIX(line));
C(RUBY_EVENT_RETURN, "return", INT2FIX(line));
C(RUBY_EVENT_B_RETURN, "b_return", INT2FIX(line));
#undef C
}
/*
* call-seq:
* iseq.trace_points -> ary
*
* Return trace points in the instruction sequence.
* Return an array of [line, event_symbol] pair.
*/
static VALUE
iseqw_trace_points(VALUE self)
{
const rb_iseq_t *iseq = iseqw_check(self);
unsigned int i;
VALUE ary = rb_ary_new();
for (i=0; i<iseq->body->insns_info_size; i++) {
const struct iseq_insn_info_entry *entry = &iseq->body->insns_info[i];
if (entry->events) {
push_event_info(iseq, entry->events, entry->line_no, ary);
}
}
return ary;
}
/* /*
* Returns the instruction sequence containing the given proc or method. * Returns the instruction sequence containing the given proc or method.
* *
@ -2679,6 +2716,7 @@ Init_ISeq(void)
rb_define_method(rb_cISeq, "label", iseqw_label, 0); rb_define_method(rb_cISeq, "label", iseqw_label, 0);
rb_define_method(rb_cISeq, "base_label", iseqw_base_label, 0); rb_define_method(rb_cISeq, "base_label", iseqw_base_label, 0);
rb_define_method(rb_cISeq, "first_lineno", iseqw_first_lineno, 0); rb_define_method(rb_cISeq, "first_lineno", iseqw_first_lineno, 0);
rb_define_method(rb_cISeq, "trace_points", iseqw_trace_points, 0);
rb_define_method(rb_cISeq, "each_child", iseqw_each_child, 0); rb_define_method(rb_cISeq, "each_child", iseqw_each_child, 0);
#if 0 /* TBD */ #if 0 /* TBD */

View File

@ -281,27 +281,31 @@ class TestISeq < Test::Unit::TestCase
end end
end end
def test_each_child def sample_iseq
iseq = ISeq.compile <<-EOS ISeq.compile <<-EOS.gsub(/^.*?: /, "")
class C 1: class C
def foo 2: def foo
begin 3: begin
rescue 4: rescue
p :rescue 5: p :rescue
ensure 6: ensure
p :ensure 7: p :ensure
end 8: end
end 9: end
def bar 10: def bar
1.times{ 11: 1.times{
2.times{ 12: 2.times{
} 13: }
} 14: }
end 15: end
end 16: end
class D < C 17: class D < C
end 18: end
EOS EOS
end
def test_each_child
iseq = sample_iseq
collect_iseq = lambda{|iseq| collect_iseq = lambda{|iseq|
iseqs = [] iseqs = []
@ -321,4 +325,36 @@ class TestISeq < Test::Unit::TestCase
assert_equal expected, collect_iseq.call(iseq) assert_equal expected, collect_iseq.call(iseq)
end end
def test_trace_points
collect_iseq = lambda{|iseq|
iseqs = []
iseq.each_child{|child_iseq|
iseqs << collect_iseq.call(child_iseq)
}
[["#{iseq.label}@#{iseq.first_lineno}", iseq.trace_points], *iseqs.sort_by{|k, *| k}]
}
assert_equal [["<compiled>@1", [[1, :line],
[17, :line]]],
[["<class:C>@1", [[1, :class],
[2, :line],
[10, :line],
[16, :end]]],
[["bar@10", [[10, :call],
[11, :line],
[15, :return]]],
[["block in bar@11", [[11, :b_call],
[12, :line],
[14, :b_return]]],
[["block (2 levels) in bar@12", [[12, :b_call],
[13, :b_return]]]]]],
[["foo@2", [[2, :call],
[4, :line],
[7, :line],
[9, :return]]],
[["ensure in foo@2", [[7, :line]]]],
[["rescue in foo@4", [[5, :line]]]]]],
[["<class:D>@17", [[17, :class],
[18, :end]]]]], collect_iseq.call(sample_iseq)
end
end end