* vm_backtrace.c: fix issue of rb_debug_inspector_open().

The order of making binding should be stack (frame) top to bottom.
  [Bug #7635]
  And also fix issue of collecting klass. Collecting klass is same
  as TracePoint#defined_class.
  (previous version, it returns T_ICLASS (internal objects).
* test/-ext-/debug/test_debug.rb: add a test.
* ext/-test-/debug/extconf.rb, init.c, inspector.c: ditto.
* vm_backtrace.c: remove magic number and add enum CALLER_BINDING_*.
* vm_backtrace.c, include/ruby/debug.h: add new C api (experimental)
  rb_debug_inspector_frame_self_get().
* vm.c, vm_core.h, vm_trace.c: move decl. of
  rb_vm_control_frame_id_and_class() and constify first parameter.



git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38970 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
ko1 2013-01-29 08:25:32 +00:00
Родитель 499ca89e24
Коммит 18e01f6381
10 изменённых файлов: 204 добавлений и 17 удалений

Просмотреть файл

@ -1,3 +1,24 @@
Tue Jan 29 17:03:28 2013 Koichi Sasada <ko1@atdot.net>
* vm_backtrace.c: fix issue of rb_debug_inspector_open().
The order of making binding should be stack (frame) top to bottom.
[Bug #7635]
And also fix issue of collecting klass. Collecting klass is same
as TracePoint#defined_class.
(previous version, it returns T_ICLASS (internal objects).
* test/-ext-/debug/test_debug.rb: add a test.
* ext/-test-/debug/extconf.rb, init.c, inspector.c: ditto.
* vm_backtrace.c: remove magic number and add enum CALLER_BINDING_*.
* vm_backtrace.c, include/ruby/debug.h: add new C api (experimental)
rb_debug_inspector_frame_self_get().
* vm.c, vm_core.h, vm_trace.c: move decl. of
rb_vm_control_frame_id_and_class() and constify first parameter.
Tue Jan 29 16:50:58 2013 Nobuyoshi Nakada <nobu@ruby-lang.org>
* vm_trace.c (rb_tracepoint_enable, rb_tracepoint_disable): check safe

Просмотреть файл

@ -0,0 +1,6 @@
$srcs = Dir[File.join($srcdir, "*.{#{SRC_EXT.join(%q{,})}}")]
inits = $srcs.map {|s| File.basename(s, ".*")}
inits.delete("init")
inits.map! {|s|"X(#{s})"}
$defs << "-DTEST_INIT_FUNCS(X)=\"#{inits.join(' ')}\""
create_makefile("-test-/debug")

11
ext/-test-/debug/init.c Normal file
Просмотреть файл

@ -0,0 +1,11 @@
#include "ruby.h"
#define init(n) {void Init_##n(VALUE klass); Init_##n(klass);}
void
Init_debug(void)
{
VALUE mBug = rb_define_module("Bug");
VALUE klass = rb_define_class_under(mBug, "Debug", rb_cModule);
TEST_INIT_FUNCS(init);
}

Просмотреть файл

@ -0,0 +1,32 @@
#include "ruby/ruby.h"
#include "ruby/debug.h"
static VALUE
callback(const rb_debug_inspector_t *dbg_context, void *data)
{
VALUE locs = rb_debug_inspector_backtrace_locations(dbg_context);
int i, len = RARRAY_LENINT(locs);
VALUE binds = rb_ary_new();
for (i = 0; i < len; ++i) {
VALUE entry = rb_ary_new();
rb_ary_push(binds, entry);
rb_ary_push(entry, rb_debug_inspector_frame_self_get(dbg_context, i));
rb_ary_push(entry, rb_debug_inspector_frame_binding_get(dbg_context, i));
rb_ary_push(entry, rb_debug_inspector_frame_class_get(dbg_context, i));
rb_ary_push(entry, rb_debug_inspector_frame_iseq_get(dbg_context, i));
rb_ary_push(entry, rb_ary_entry(locs, i));
}
return binds;
}
static VALUE
debug_inspector(VALUE self)
{
return rb_debug_inspector_open(callback, NULL);
}
void
Init_inspector(VALUE klass)
{
rb_define_module_function(klass, "inspector", debug_inspector, 0);
}

Просмотреть файл

@ -31,8 +31,9 @@ typedef struct rb_debug_inspector_struct rb_debug_inspector_t;
typedef VALUE (*rb_debug_inspector_func_t)(const rb_debug_inspector_t *, void *);
VALUE rb_debug_inspector_open(rb_debug_inspector_func_t func, void *data);
VALUE rb_debug_inspector_frame_binding_get(const rb_debug_inspector_t *dc, long index);
VALUE rb_debug_inspector_frame_self_get(const rb_debug_inspector_t *dc, long index);
VALUE rb_debug_inspector_frame_class_get(const rb_debug_inspector_t *dc, long index);
VALUE rb_debug_inspector_frame_binding_get(const rb_debug_inspector_t *dc, long index);
VALUE rb_debug_inspector_frame_iseq_get(const rb_debug_inspector_t *dc, long index);
VALUE rb_debug_inspector_backtrace_locations(const rb_debug_inspector_t *dc);

Просмотреть файл

@ -0,0 +1,58 @@
require 'test/unit'
require '-test-/debug'
class TestDebug < Test::Unit::TestCase
def binds_check binds
count = Hash.new(0)
assert_instance_of(Array, binds)
binds.each{|(_self, bind, klass, iseq, loc)|
if _self == self
count[:self] += 1
end
if bind
assert_instance_of(Binding, bind)
count[:bind] += 1
end
if klass
assert(klass.instance_of?(Module) || klass.instance_of?(Class))
count[:class] += 1
end
if iseq
count[:iseq] += 1
assert_instance_of(RubyVM::InstructionSequence, iseq)
# check same location
assert_equal(loc.path, iseq.path)
assert_equal(loc.absolute_path, iseq.absolute_path)
assert_equal(loc.label, iseq.label)
assert_operator(loc.lineno, :>=, iseq.first_lineno)
end
assert_instance_of(Thread::Backtrace::Location, loc)
}
assert_operator(0, :<, count[:self])
assert_operator(0, :<, count[:bind])
assert_operator(0, :<, count[:iseq])
assert_operator(0, :<, count[:class])
end
def test_inspector_open
binds = Bug::Debug.inspector
binds_check binds
end
def inspector_in_eval
eval("Bug::Debug.inspector")
end
def test_inspector_open_in_eval
bug7635 = '[ruby-core:51640]'
binds = inspector_in_eval
binds_check binds
end
end

2
vm.c
Просмотреть файл

@ -1414,7 +1414,7 @@ rb_iseq_eval_main(VALUE iseqval)
}
int
rb_vm_control_frame_id_and_class(rb_control_frame_t *cfp, ID *idp, VALUE *klassp)
rb_vm_control_frame_id_and_class(const rb_control_frame_t *cfp, ID *idp, VALUE *klassp)
{
rb_iseq_t *iseq = cfp->iseq;
if (!iseq && cfp->me) {

Просмотреть файл

@ -1012,8 +1012,15 @@ struct rb_debug_inspector_struct {
long backtrace_size;
};
enum {
CALLER_BINDING_SELF,
CALLER_BINDING_CLASS,
CALLER_BINDING_BINDING,
CALLER_BINDING_ISEQ,
CALLER_BINDING_CFP
};
struct collect_caller_bindings_data {
rb_thread_t *th;
VALUE ary;
};
@ -1023,37 +1030,82 @@ collect_caller_bindings_init(void *arg, size_t size)
/* */
}
static VALUE
get_klass(const rb_control_frame_t *cfp)
{
VALUE klass;
if (rb_vm_control_frame_id_and_class(cfp, 0, &klass)) {
if (RB_TYPE_P(klass, T_ICLASS)) {
return RBASIC(klass)->klass;
}
else {
return klass;
}
}
else {
return Qnil;
}
}
static void
collect_caller_bindings_iseq(void *arg, const rb_control_frame_t *cfp)
{
struct collect_caller_bindings_data *data = (struct collect_caller_bindings_data *)arg;
rb_ary_push(data->ary,
rb_ary_new3(4,
cfp->klass,
rb_binding_new_with_cfp(data->th, cfp),
cfp->iseq ? cfp->iseq->self : Qnil,
GC_GUARDED_PTR(cfp)));
VALUE frame = rb_ary_new2(5);
rb_ary_store(frame, CALLER_BINDING_SELF, cfp->self);
rb_ary_store(frame, CALLER_BINDING_CLASS, get_klass(cfp));
rb_ary_store(frame, CALLER_BINDING_BINDING, GC_GUARDED_PTR(cfp)); /* create later */
rb_ary_store(frame, CALLER_BINDING_ISEQ, cfp->iseq ? cfp->iseq->self : Qnil);
rb_ary_store(frame, CALLER_BINDING_CFP, GC_GUARDED_PTR(cfp));
rb_ary_push(data->ary, frame);
}
static void
collect_caller_bindings_cfunc(void *arg, const rb_control_frame_t *cfp, ID mid)
{
struct collect_caller_bindings_data *data = (struct collect_caller_bindings_data *)arg;
rb_ary_push(data->ary, rb_ary_new3(2, cfp->klass, Qnil));
VALUE frame = rb_ary_new2(5);
rb_ary_store(frame, CALLER_BINDING_SELF, cfp->self);
rb_ary_store(frame, CALLER_BINDING_CLASS, get_klass(cfp));
rb_ary_store(frame, CALLER_BINDING_BINDING, Qnil); /* not available */
rb_ary_store(frame, CALLER_BINDING_ISEQ, Qnil); /* not available */
rb_ary_store(frame, CALLER_BINDING_CFP, GC_GUARDED_PTR(cfp));
rb_ary_push(data->ary, frame);
}
static VALUE
collect_caller_bindings(rb_thread_t *th)
{
struct collect_caller_bindings_data data;
VALUE result;
int i;
data.ary = rb_ary_new();
data.th = th;
backtrace_each(th,
collect_caller_bindings_init,
collect_caller_bindings_iseq,
collect_caller_bindings_cfunc,
&data);
return rb_ary_reverse(data.ary);
result = rb_ary_reverse(data.ary);
/* bindings should be created from top of frame */
for (i=0; i<RARRAY_LEN(result); i++) {
VALUE entry = rb_ary_entry(result, i);
VALUE cfp_val = rb_ary_entry(entry, CALLER_BINDING_BINDING);
if (!NIL_P(cfp_val)) {
rb_control_frame_t *cfp = GC_GUARDED_PTR_REF(cfp_val);
rb_ary_store(entry, CALLER_BINDING_BINDING, rb_binding_new_with_cfp(th, cfp));
}
}
return result;
}
/*
@ -1099,25 +1151,32 @@ frame_get(const rb_debug_inspector_t *dc, long index)
return rb_ary_entry(dc->contexts, index);
}
VALUE
rb_debug_inspector_frame_self_get(const rb_debug_inspector_t *dc, long index)
{
VALUE frame = frame_get(dc, index);
return rb_ary_entry(frame, CALLER_BINDING_SELF);
}
VALUE
rb_debug_inspector_frame_class_get(const rb_debug_inspector_t *dc, long index)
{
VALUE frame = frame_get(dc, index);
return rb_ary_entry(frame, 0);
return rb_ary_entry(frame, CALLER_BINDING_CLASS);
}
VALUE
rb_debug_inspector_frame_binding_get(const rb_debug_inspector_t *dc, long index)
{
VALUE frame = frame_get(dc, index);
return rb_ary_entry(frame, 1);
return rb_ary_entry(frame, CALLER_BINDING_BINDING);
}
VALUE
rb_debug_inspector_frame_iseq_get(const rb_debug_inspector_t *dc, long index)
{
VALUE frame = frame_get(dc, index);
return rb_ary_entry(frame, 2);
return rb_ary_entry(frame, CALLER_BINDING_ISEQ);
}
VALUE

Просмотреть файл

@ -847,6 +847,7 @@ int rb_vm_get_sourceline(const rb_control_frame_t *);
VALUE rb_name_err_mesg_new(VALUE obj, VALUE mesg, VALUE recv, VALUE method);
void rb_vm_stack_to_heap(rb_thread_t *th);
void ruby_thread_init_stack(rb_thread_t *th);
int rb_vm_control_frame_id_and_class(const rb_control_frame_t *cfp, ID *idp, VALUE *klassp);
void rb_gc_mark_machine_stack(rb_thread_t *th);

Просмотреть файл

@ -694,8 +694,6 @@ rb_tracearg_event(rb_trace_arg_t *trace_arg)
return ID2SYM(get_event_id(trace_arg->event));
}
int rb_vm_control_frame_id_and_class(rb_control_frame_t *cfp, ID *idp, VALUE *klassp);
static void
fill_path_and_lineno(rb_trace_arg_t *trace_arg)
{