1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

[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
This commit is contained in:
ko1 2012-11-30 17:00:30 +00:00
parent 60317fa81d
commit 12f2f7371f
6 changed files with 154 additions and 0 deletions

View file

@ -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

View file

@ -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;

105
iseq.c
View file

@ -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);

4
iseq.h
View file

@ -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);

View file

@ -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

View file

@ -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)));
}