summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2012-11-30 17:00:30 +0000
committerko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2012-11-30 17:00:30 +0000
commit12f2f7371f2dcf9c8da2a96fa251d1af2a7e977e (patch)
tree70a25ad7b5cf5a7404817c2c0d03a4d0060e8603
parent60317fa81df069a7a2a7bdcb0aaaaebc9b77bed9 (diff)
[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
-rw-r--r--ChangeLog18
-rw-r--r--include/ruby/ruby.h1
-rw-r--r--iseq.c105
-rw-r--r--iseq.h4
-rw-r--r--test/ruby/test_iseq.rb24
-rw-r--r--vm_trace.c2
6 files changed, 154 insertions, 0 deletions
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 <ko1@atdot.net>
+
+ [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 <usa@ruby-lang.org>
* 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)));
}