From 12f2f7371f2dcf9c8da2a96fa251d1af2a7e977e Mon Sep 17 00:00:00 2001 From: ko1 Date: Fri, 30 Nov 2012 17:00:30 +0000 Subject: [EXPERIMENTAL] * iseq.c: add following two methods. * ISeq#line_trace_all returns all line traces (line numbers) * ISeq#line_trace_specify(pos, set) set `pos'th line event to specified_line event (if set is true). These features are introduced for debuggers (mainly to make breakpoint). * iseq.h: add decl. of C APIs. * test/ruby/test_iseq.rb: add tests. * vm_trace.c: add `specified_line' event. * include/ruby/ruby.h: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38076 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 18 +++++++++ include/ruby/ruby.h | 1 + iseq.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++++ iseq.h | 4 ++ test/ruby/test_iseq.rb | 24 +++++++++++ vm_trace.c | 2 + 6 files changed, 154 insertions(+) diff --git a/ChangeLog b/ChangeLog index 989d559154..8bd0bfc6e1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +Sat Dec 1 01:51:06 2012 Koichi Sasada + + [EXPERIMENTAL] + * iseq.c: add following two methods. + * ISeq#line_trace_all returns all line traces (line numbers) + * ISeq#line_trace_specify(pos, set) set `pos'th line event to + specified_line event (if set is true). + These features are introduced for debuggers (mainly to make + breakpoint). + + * iseq.h: add decl. of C APIs. + + * test/ruby/test_iseq.rb: add tests. + + * vm_trace.c: add `specified_line' event. + + * include/ruby/ruby.h: ditto. + Sat Dec 1 01:49:52 2012 NAKAMURA Usaku * test/rubygems/test_gem_dependency_installler.rb: gems are of course diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h index 9d74d82625..d67ff6a8ea 100644 --- a/include/ruby/ruby.h +++ b/include/ruby/ruby.h @@ -1581,6 +1581,7 @@ int ruby_native_thread_p(void); #define RUBY_EVENT_B_RETURN 0x0200 #define RUBY_EVENT_THREAD_BEGIN 0x0400 #define RUBY_EVENT_THREAD_END 0x0800 +#define RUBY_EVENT_SPECIFIED_LINE 0x8000 #define RUBY_EVENT_TRACEPOINT_ALL 0xFFFF typedef unsigned int rb_event_flag_t; diff --git a/iseq.c b/iseq.c index 06cf4f122b..2e178d2b54 100644 --- a/iseq.c +++ b/iseq.c @@ -1889,6 +1889,107 @@ rb_iseq_build_for_ruby2cext( return iseqval; } +/* Experimental tracing support: trace(line) -> trace(specified_line) + * MRI Specific. + */ + +int +rb_iseq_line_trace_each(VALUE iseqval, int (*func)(int line, rb_event_flag_t *events_ptr, void *d), void *data) +{ + int trace_num = 0; + size_t pos, insn; + rb_iseq_t *iseq; + int cont = 1; + GetISeqPtr(iseqval, iseq); + + for (pos = 0; cont && pos < iseq->iseq_size; pos += insn_len(insn)) { + insn = iseq->iseq[pos]; + + if (insn == BIN(trace)) { + rb_event_flag_t current_events = (VALUE)iseq->iseq[pos+1]; + rb_event_flag_t events = current_events & RUBY_EVENT_SPECIFIED_LINE; + trace_num++; + + if (func) { + int line = find_line_no(iseq, pos); + /* printf("line: %d\n", line); */ + cont = (*func)(line, &events, data); + if (current_events != events) { + iseq->iseq[pos+1] = iseq->iseq_encoded[pos+1] = (VALUE)(current_events | (events & RUBY_EVENT_SPECIFIED_LINE)); + } + } + } + } + return trace_num; +} + +static int +collect_trace(int line, rb_event_flag_t *events_ptr, void *ptr) +{ + VALUE result = (VALUE)ptr; + rb_ary_push(result, INT2NUM(line)); + return 1; +} + +VALUE +rb_iseq_line_trace_all(VALUE iseqval) +{ + VALUE result = rb_ary_new(); + rb_iseq_line_trace_each(iseqval, collect_trace, (void *)result); + return result; +} + +struct set_specifc_data { + int pos; + int set; + int prev; /* 1: set, 2: unset, 0: not found */ +}; + +static int +line_trace_specify(int line, rb_event_flag_t *events_ptr, void *ptr) +{ + struct set_specifc_data *data = (struct set_specifc_data *)ptr; + + if (data->pos == 0) { + data->prev = *events_ptr & RUBY_EVENT_SPECIFIED_LINE ? 1 : 2; + if (data->set) { + *events_ptr = *events_ptr | RUBY_EVENT_SPECIFIED_LINE; + } + else { + *events_ptr = *events_ptr & ~RUBY_EVENT_SPECIFIED_LINE; + } + return 0; /* found */ + } + else { + data->pos--; + return 1; + } +} + +VALUE +rb_iseq_line_trace_specify(VALUE iseqval, VALUE pos, VALUE set) +{ + struct set_specifc_data data; + + data.prev = 0; + data.pos = NUM2INT(pos); + if (data.pos < 0) rb_raise(rb_eTypeError, "`pos' is negative"); + + switch (set) { + case Qtrue: data.set = 1; break; + case Qfalse: data.set = 0; break; + default: + rb_raise(rb_eTypeError, "`set' should be true/false"); + } + + rb_iseq_line_trace_each(iseqval, line_trace_specify, (void *)&data); + + if (data.prev == 0) { + rb_raise(rb_eTypeError, "`pos' is out of range."); + } + return data.prev == 1 ? Qtrue : Qfalse; +} + /* * Document-class: RubyVM::InstructionSequence * @@ -1918,6 +2019,10 @@ Init_ISeq(void) rb_define_method(rb_cISeq, "to_a", iseq_to_a, 0); rb_define_method(rb_cISeq, "eval", iseq_eval, 0); + /* experimental */ + rb_define_method(rb_cISeq, "line_trace_all", rb_iseq_line_trace_all, 0); + rb_define_method(rb_cISeq, "line_trace_specify", rb_iseq_line_trace_specify, 2); + #if 0 /* TBD */ rb_define_method(rb_cISeq, "marshal_dump", iseq_marshal_dump, 0); rb_define_method(rb_cISeq, "marshal_load", iseq_marshal_load, 1); diff --git a/iseq.h b/iseq.h index 17eebfff12..1b51d2a14d 100644 --- a/iseq.h +++ b/iseq.h @@ -28,6 +28,10 @@ VALUE rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc); struct st_table *ruby_insn_make_insn_table(void); unsigned int rb_iseq_line_no(const rb_iseq_t *iseq, size_t pos); +int rb_iseq_line_trace_each(VALUE iseqval, int (*func)(int line, rb_event_flag_t *events_ptr, void *d), void *data); +VALUE rb_iseq_line_trace_all(VALUE iseqval); +VALUE rb_iseq_line_trace_specify(VALUE iseqval, VALUE pos, VALUE set); + /* proc.c */ rb_iseq_t *rb_method_get_iseq(VALUE body); rb_iseq_t *rb_proc_get_iseq(VALUE proc, int *is_proc); diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb index ad5391383e..ac140aabde 100644 --- a/test/ruby/test_iseq.rb +++ b/test/ruby/test_iseq.rb @@ -25,4 +25,28 @@ class TestISeq < Test::Unit::TestCase ensure Encoding.default_internal = enc end + + def test_line_trace + iseq = ISeq.compile \ + %q{ a = 1 + b = 2 + c = 3 + # d = 4 + e = 5 + # f = 6 + g = 7 + + } + assert_equal([1, 2, 3, 5, 7], iseq.line_trace_all) + iseq.line_trace_specify(1, true) # line 2 + iseq.line_trace_specify(3, true) # line 5 + + result = [] + TracePoint.new(:specified_line){|tp| + result << tp.lineno + }.enable{ + iseq.eval + } + assert_equal([2, 5], result) + end end diff --git a/vm_trace.c b/vm_trace.c index e70d4aad04..e2919ff79e 100644 --- a/vm_trace.c +++ b/vm_trace.c @@ -525,6 +525,7 @@ get_event_id(rb_event_flag_t event) C(b_return, B_RETURN); C(thread_begin, THREAD_BEGIN); C(thread_end, THREAD_END); + C(specified_line, SPECIFIED_LINE); #undef C default: return 0; @@ -632,6 +633,7 @@ symbol2event_flag(VALUE v) C(b_return, B_RETURN); C(thread_begin, THREAD_BEGIN); C(thread_end, THREAD_END); + C(specified_line, SPECIFIED_LINE); #undef C rb_raise(rb_eArgError, "unknown event: %s", rb_id2name(SYM2ID(sym))); } -- cgit v1.2.3