mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* 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:
parent
499ca89e24
commit
18e01f6381
10 changed files with 204 additions and 17 deletions
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>
|
Tue Jan 29 16:50:58 2013 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||||
|
|
||||||
* vm_trace.c (rb_tracepoint_enable, rb_tracepoint_disable): check safe
|
* vm_trace.c (rb_tracepoint_enable, rb_tracepoint_disable): check safe
|
||||||
|
|
6
ext/-test-/debug/extconf.rb
Normal file
6
ext/-test-/debug/extconf.rb
Normal file
|
@ -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
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);
|
||||||
|
}
|
32
ext/-test-/debug/inspector.c
Normal file
32
ext/-test-/debug/inspector.c
Normal file
|
@ -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 *);
|
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_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_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_frame_iseq_get(const rb_debug_inspector_t *dc, long index);
|
||||||
VALUE rb_debug_inspector_backtrace_locations(const rb_debug_inspector_t *dc);
|
VALUE rb_debug_inspector_backtrace_locations(const rb_debug_inspector_t *dc);
|
||||||
|
|
||||||
|
|
58
test/-ext-/debug/test_debug.rb
Normal file
58
test/-ext-/debug/test_debug.rb
Normal file
|
@ -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
|
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;
|
rb_iseq_t *iseq = cfp->iseq;
|
||||||
if (!iseq && cfp->me) {
|
if (!iseq && cfp->me) {
|
||||||
|
|
|
@ -1012,8 +1012,15 @@ struct rb_debug_inspector_struct {
|
||||||
long backtrace_size;
|
long backtrace_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CALLER_BINDING_SELF,
|
||||||
|
CALLER_BINDING_CLASS,
|
||||||
|
CALLER_BINDING_BINDING,
|
||||||
|
CALLER_BINDING_ISEQ,
|
||||||
|
CALLER_BINDING_CFP
|
||||||
|
};
|
||||||
|
|
||||||
struct collect_caller_bindings_data {
|
struct collect_caller_bindings_data {
|
||||||
rb_thread_t *th;
|
|
||||||
VALUE ary;
|
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
|
static void
|
||||||
collect_caller_bindings_iseq(void *arg, const rb_control_frame_t *cfp)
|
collect_caller_bindings_iseq(void *arg, const rb_control_frame_t *cfp)
|
||||||
{
|
{
|
||||||
struct collect_caller_bindings_data *data = (struct collect_caller_bindings_data *)arg;
|
struct collect_caller_bindings_data *data = (struct collect_caller_bindings_data *)arg;
|
||||||
rb_ary_push(data->ary,
|
VALUE frame = rb_ary_new2(5);
|
||||||
rb_ary_new3(4,
|
|
||||||
cfp->klass,
|
rb_ary_store(frame, CALLER_BINDING_SELF, cfp->self);
|
||||||
rb_binding_new_with_cfp(data->th, cfp),
|
rb_ary_store(frame, CALLER_BINDING_CLASS, get_klass(cfp));
|
||||||
cfp->iseq ? cfp->iseq->self : Qnil,
|
rb_ary_store(frame, CALLER_BINDING_BINDING, GC_GUARDED_PTR(cfp)); /* create later */
|
||||||
GC_GUARDED_PTR(cfp)));
|
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
|
static void
|
||||||
collect_caller_bindings_cfunc(void *arg, const rb_control_frame_t *cfp, ID mid)
|
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;
|
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
|
static VALUE
|
||||||
collect_caller_bindings(rb_thread_t *th)
|
collect_caller_bindings(rb_thread_t *th)
|
||||||
{
|
{
|
||||||
struct collect_caller_bindings_data data;
|
struct collect_caller_bindings_data data;
|
||||||
|
VALUE result;
|
||||||
|
int i;
|
||||||
|
|
||||||
data.ary = rb_ary_new();
|
data.ary = rb_ary_new();
|
||||||
data.th = th;
|
|
||||||
backtrace_each(th,
|
backtrace_each(th,
|
||||||
collect_caller_bindings_init,
|
collect_caller_bindings_init,
|
||||||
collect_caller_bindings_iseq,
|
collect_caller_bindings_iseq,
|
||||||
collect_caller_bindings_cfunc,
|
collect_caller_bindings_cfunc,
|
||||||
&data);
|
&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);
|
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
|
VALUE
|
||||||
rb_debug_inspector_frame_class_get(const rb_debug_inspector_t *dc, long index)
|
rb_debug_inspector_frame_class_get(const rb_debug_inspector_t *dc, long index)
|
||||||
{
|
{
|
||||||
VALUE frame = frame_get(dc, index);
|
VALUE frame = frame_get(dc, index);
|
||||||
return rb_ary_entry(frame, 0);
|
return rb_ary_entry(frame, CALLER_BINDING_CLASS);
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUE
|
VALUE
|
||||||
rb_debug_inspector_frame_binding_get(const rb_debug_inspector_t *dc, long index)
|
rb_debug_inspector_frame_binding_get(const rb_debug_inspector_t *dc, long index)
|
||||||
{
|
{
|
||||||
VALUE frame = frame_get(dc, index);
|
VALUE frame = frame_get(dc, index);
|
||||||
return rb_ary_entry(frame, 1);
|
return rb_ary_entry(frame, CALLER_BINDING_BINDING);
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUE
|
VALUE
|
||||||
rb_debug_inspector_frame_iseq_get(const rb_debug_inspector_t *dc, long index)
|
rb_debug_inspector_frame_iseq_get(const rb_debug_inspector_t *dc, long index)
|
||||||
{
|
{
|
||||||
VALUE frame = frame_get(dc, index);
|
VALUE frame = frame_get(dc, index);
|
||||||
return rb_ary_entry(frame, 2);
|
return rb_ary_entry(frame, CALLER_BINDING_ISEQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
VALUE
|
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);
|
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 rb_vm_stack_to_heap(rb_thread_t *th);
|
||||||
void ruby_thread_init_stack(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);
|
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));
|
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
|
static void
|
||||||
fill_path_and_lineno(rb_trace_arg_t *trace_arg)
|
fill_path_and_lineno(rb_trace_arg_t *trace_arg)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue