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>
|
Sat Dec 1 01:49:52 2012 NAKAMURA Usaku <usa@ruby-lang.org>
|
||||||
|
|
||||||
* test/rubygems/test_gem_dependency_installler.rb: gems are of course
|
* 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_B_RETURN 0x0200
|
||||||
#define RUBY_EVENT_THREAD_BEGIN 0x0400
|
#define RUBY_EVENT_THREAD_BEGIN 0x0400
|
||||||
#define RUBY_EVENT_THREAD_END 0x0800
|
#define RUBY_EVENT_THREAD_END 0x0800
|
||||||
|
#define RUBY_EVENT_SPECIFIED_LINE 0x8000
|
||||||
#define RUBY_EVENT_TRACEPOINT_ALL 0xFFFF
|
#define RUBY_EVENT_TRACEPOINT_ALL 0xFFFF
|
||||||
|
|
||||||
typedef unsigned int rb_event_flag_t;
|
typedef unsigned int rb_event_flag_t;
|
||||||
|
|
105
iseq.c
105
iseq.c
|
@ -1889,6 +1889,107 @@ rb_iseq_build_for_ruby2cext(
|
||||||
return iseqval;
|
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
|
* 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, "to_a", iseq_to_a, 0);
|
||||||
rb_define_method(rb_cISeq, "eval", iseq_eval, 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 */
|
#if 0 /* TBD */
|
||||||
rb_define_method(rb_cISeq, "marshal_dump", iseq_marshal_dump, 0);
|
rb_define_method(rb_cISeq, "marshal_dump", iseq_marshal_dump, 0);
|
||||||
rb_define_method(rb_cISeq, "marshal_load", iseq_marshal_load, 1);
|
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);
|
struct st_table *ruby_insn_make_insn_table(void);
|
||||||
unsigned int rb_iseq_line_no(const rb_iseq_t *iseq, size_t pos);
|
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 */
|
/* proc.c */
|
||||||
rb_iseq_t *rb_method_get_iseq(VALUE body);
|
rb_iseq_t *rb_method_get_iseq(VALUE body);
|
||||||
rb_iseq_t *rb_proc_get_iseq(VALUE proc, int *is_proc);
|
rb_iseq_t *rb_proc_get_iseq(VALUE proc, int *is_proc);
|
||||||
|
|
|
@ -25,4 +25,28 @@ class TestISeq < Test::Unit::TestCase
|
||||||
ensure
|
ensure
|
||||||
Encoding.default_internal = enc
|
Encoding.default_internal = enc
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -525,6 +525,7 @@ get_event_id(rb_event_flag_t event)
|
||||||
C(b_return, B_RETURN);
|
C(b_return, B_RETURN);
|
||||||
C(thread_begin, THREAD_BEGIN);
|
C(thread_begin, THREAD_BEGIN);
|
||||||
C(thread_end, THREAD_END);
|
C(thread_end, THREAD_END);
|
||||||
|
C(specified_line, SPECIFIED_LINE);
|
||||||
#undef C
|
#undef C
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -632,6 +633,7 @@ symbol2event_flag(VALUE v)
|
||||||
C(b_return, B_RETURN);
|
C(b_return, B_RETURN);
|
||||||
C(thread_begin, THREAD_BEGIN);
|
C(thread_begin, THREAD_BEGIN);
|
||||||
C(thread_end, THREAD_END);
|
C(thread_end, THREAD_END);
|
||||||
|
C(specified_line, SPECIFIED_LINE);
|
||||||
#undef C
|
#undef C
|
||||||
rb_raise(rb_eArgError, "unknown event: %s", rb_id2name(SYM2ID(sym)));
|
rb_raise(rb_eArgError, "unknown event: %s", rb_id2name(SYM2ID(sym)));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue