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:
parent
60317fa81d
commit
12f2f7371f
6 changed files with 154 additions and 0 deletions
18
ChangeLog
18
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
|
||||
|
|
|
@ -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
105
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);
|
||||
|
|
4
iseq.h
4
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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue