зеркало из https://github.com/github/ruby.git
* 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:
Родитель
499ca89e24
Коммит
18e01f6381
21
ChangeLog
21
ChangeLog
|
@ -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")
|
|
@ -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
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)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче