ruby-changes:49310
From: ko1 <ko1@a...>
Date: Sat, 23 Dec 2017 23:47:10 +0900 (JST)
Subject: [ruby-changes:49310] ko1:r61427 (trunk): RubyVM::InstructionSequence#trace_points.
ko1 2017-12-23 23:46:59 +0900 (Sat, 23 Dec 2017) New Revision: 61427 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=61427 Log: 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. Modified files: trunk/NEWS trunk/compile.c trunk/iseq.c trunk/test/ruby/test_iseq.rb Index: iseq.c =================================================================== --- iseq.c (revision 61426) +++ iseq.c (revision 61427) @@ -1874,6 +1874,43 @@ iseqw_each_child(VALUE self) https://github.com/ruby/ruby/blob/trunk/iseq.c#L1874 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. * @@ -2679,6 +2716,7 @@ Init_ISeq(void) https://github.com/ruby/ruby/blob/trunk/iseq.c#L2716 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, "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); #if 0 /* TBD */ Index: compile.c =================================================================== --- compile.c (revision 61426) +++ compile.c (revision 61427) @@ -763,7 +763,7 @@ rb_vm_insn_addr2insn(const void *addr) / https://github.com/ruby/ruby/blob/trunk/compile.c#L763 VALUE * 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); original_code = ISEQ_ORIGINAL_ISEQ_ALLOC(iseq, iseq->body->iseq_size); Index: NEWS =================================================================== --- NEWS (revision 61426) +++ NEWS (revision 61427) @@ -168,6 +168,7 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L168 * New method: * RubyVM::InstructionSequence#each_child + * RubyVM::InstructionSequence#trace_points * String Index: test/ruby/test_iseq.rb =================================================================== --- test/ruby/test_iseq.rb (revision 61426) +++ test/ruby/test_iseq.rb (revision 61427) @@ -281,27 +281,31 @@ class TestISeq < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_iseq.rb#L281 end end - def test_each_child - iseq = ISeq.compile <<-EOS - class C - def foo - begin - rescue - p :rescue - ensure - p :ensure - end - end - def bar - 1.times{ - 2.times{ - } - } - end - end - class D < C - end + def sample_iseq + ISeq.compile <<-EOS.gsub(/^.*?: /, "") + 1: class C + 2: def foo + 3: begin + 4: rescue + 5: p :rescue + 6: ensure + 7: p :ensure + 8: end + 9: end + 10: def bar + 11: 1.times{ + 12: 2.times{ + 13: } + 14: } + 15: end + 16: end + 17: class D < C + 18: end EOS + end + + def test_each_child + iseq = sample_iseq collect_iseq = lambda{|iseq| iseqs = [] @@ -321,4 +325,36 @@ class TestISeq < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_iseq.rb#L325 assert_equal expected, collect_iseq.call(iseq) 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 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/