From 585ac7f1a2a1e96460cc78e050d2700d2e2680e5 Mon Sep 17 00:00:00 2001 From: ko1 Date: Thu, 29 Nov 2012 07:05:27 +0000 Subject: * include/ruby/debug.h: add rb_debug_inspector_* APIs. * vm_backtrace.c: ditto. * common.mk: add dpendency from vm_backtrace.o to include/ruby/debug.h. * proc.c (rb_binding_new_with_cfp): constify. * vm.c (rb_vm_get_ruby_level_next_cfp): consitify. * vm_core.h, vm_trace.c: move decls. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37979 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 15 +++++ common.mk | 2 +- include/ruby/debug.h | 12 +++- proc.c | 2 +- vm.c | 4 +- vm_backtrace.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++--- vm_core.h | 3 +- vm_trace.c | 2 - 8 files changed, 180 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4e76e9fca6..dff7a703ef 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +Thu Nov 29 15:59:55 2012 Koichi Sasada + + * include/ruby/debug.h: add rb_debug_inspector_* APIs. + + * vm_backtrace.c: ditto. + + * common.mk: add dpendency from vm_backtrace.o to + include/ruby/debug.h. + + * proc.c (rb_binding_new_with_cfp): constify. + + * vm.c (rb_vm_get_ruby_level_next_cfp): consitify. + + * vm_core.h, vm_trace.c: move decls. + Thu Nov 29 15:56:14 2012 NARUSE, Yui * lib/rdoc/test_case.rb (RDoc::TestCase#verbose_capture_io): diff --git a/common.mk b/common.mk index 245df4264e..ed98a037ac 100644 --- a/common.mk +++ b/common.mk @@ -774,7 +774,7 @@ debug.$(OBJEXT): {$(VPATH)}debug.c $(RUBY_H_INCLUDES) \ id.$(OBJEXT): {$(VPATH)}id.c $(RUBY_H_INCLUDES) $(ID_H_INCLUDES) {$(VPATH)}vm_opts.h vm_backtrace.$(OBJEXT): {$(VPATH)}vm_backtrace.c \ $(VM_CORE_H_INCLUDES) $(RUBY_H_INCLUDES) $(ENCODING_H_INCLUDES) \ - {$(VPATH)}internal.h {$(VPATH)}iseq.h + {$(VPATH)}internal.h {$(VPATH)}iseq.h {$(VPATH)}debug.h vm_trace.$(OBJEXT): {$(VPATH)}vm_trace.c $(ENCODING_H_INCLUDES) \ $(VM_CORE_H_INCLUDES) $(RUBY_H_INCLUDES) {$(VPATH)}debug.h \ {$(VPATH)}internal.h diff --git a/include/ruby/debug.h b/include/ruby/debug.h index 8eec34501a..88a6ecfe6b 100644 --- a/include/ruby/debug.h +++ b/include/ruby/debug.h @@ -26,6 +26,16 @@ extern "C" { /* Note: This file contains experimental APIs. */ /* APIs can be replaced at Ruby 2.0.1 or later */ +/* debug inspector APIs */ +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, int index); +VALUE rb_debug_inspector_frame_class_get(const rb_debug_inspector_t *dc, int index); +VALUE rb_debug_inspector_frame_iseq_get(const rb_debug_inspector_t *dc, int index); +VALUE rb_debug_inspector_backtrace_locations(const rb_debug_inspector_t *dc); + /* Old style set_trace_func APIs */ void rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data); @@ -55,7 +65,7 @@ VALUE rb_tracearg_self(struct rb_trace_arg_struct *trace_arg); VALUE rb_tracearg_return_value(struct rb_trace_arg_struct *trace_arg); VALUE rb_tracearg_raised_exception(struct rb_trace_arg_struct *trace_arg); -/* undocumented advanced APIs */ +/* undocumented advanced tracing APIs */ typedef enum { RUBY_EVENT_HOOK_FLAG_SAFE = 0x01, diff --git a/proc.c b/proc.c index c15be180ab..0aa34abd12 100644 --- a/proc.c +++ b/proc.c @@ -310,7 +310,7 @@ binding_clone(VALUE self) } VALUE -rb_binding_new_with_cfp(rb_thread_t *th, rb_control_frame_t *src_cfp) +rb_binding_new_with_cfp(rb_thread_t *th, const rb_control_frame_t *src_cfp) { rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(th, src_cfp); VALUE bindval = binding_alloc(rb_cBinding); diff --git a/vm.c b/vm.c index 237b385946..ccf21d7a35 100644 --- a/vm.c +++ b/vm.c @@ -186,11 +186,11 @@ vm_set_main_stack(rb_thread_t *th, VALUE iseqval) } rb_control_frame_t * -rb_vm_get_ruby_level_next_cfp(rb_thread_t *th, rb_control_frame_t *cfp) +rb_vm_get_ruby_level_next_cfp(rb_thread_t *th, const rb_control_frame_t *cfp) { while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, cfp)) { if (RUBY_VM_NORMAL_ISEQ_P(cfp->iseq)) { - return cfp; + return (rb_control_frame_t *)cfp; } cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); } diff --git a/vm_backtrace.c b/vm_backtrace.c index a15c533bf8..a87cbc9fb2 100644 --- a/vm_backtrace.c +++ b/vm_backtrace.c @@ -11,9 +11,11 @@ #include "ruby/ruby.h" #include "ruby/encoding.h" +#include "ruby/debug.h" #include "internal.h" #include "vm_core.h" +#include "eval_intern.h" #include "iseq.h" static VALUE rb_cBacktrace; @@ -358,8 +360,8 @@ backtrace_alloc(VALUE klass) static void backtrace_each(rb_thread_t *th, void (*init)(void *arg, size_t size), - void (*iter_iseq)(void *arg, const rb_iseq_t *iseq, const VALUE *pc), - void (*iter_cfunc)(void *arg, ID mid), + void (*iter_iseq)(void *arg, const rb_control_frame_t *cfp), + void (*iter_cfunc)(void *arg, const rb_control_frame_t *cfp, ID mid), void *arg) { rb_control_frame_t *last_cfp = th->cfp; @@ -395,13 +397,13 @@ backtrace_each(rb_thread_t *th, /* fprintf(stderr, "cfp: %d\n", (rb_control_frame_t *)(th->stack + th->stack_size) - cfp); */ if (cfp->iseq) { if (cfp->pc) { - iter_iseq(arg, cfp->iseq, cfp->pc); + iter_iseq(arg, cfp); } } else if (RUBYVM_CFUNC_FRAME_P(cfp)) { ID mid = cfp->me->def ? cfp->me->def->original_id : cfp->me->called_id; - iter_cfunc(arg, mid); + iter_cfunc(arg, cfp, mid); } } } @@ -423,8 +425,10 @@ bt_init(void *ptr, size_t size) } static void -bt_iter_iseq(void *ptr, const rb_iseq_t *iseq, const VALUE *pc) +bt_iter_iseq(void *ptr, const rb_control_frame_t *cfp) { + const rb_iseq_t *iseq = cfp->iseq; + const VALUE *pc = cfp->pc; struct bt_iter_arg *arg = (struct bt_iter_arg *)ptr; rb_backtrace_location_t *loc = &arg->bt->backtrace[arg->bt->backtrace_size++]; loc->type = LOCATION_TYPE_ISEQ; @@ -434,7 +438,7 @@ bt_iter_iseq(void *ptr, const rb_iseq_t *iseq, const VALUE *pc) } static void -bt_iter_cfunc(void *ptr, ID mid) +bt_iter_cfunc(void *ptr, const rb_control_frame_t *cfp, ID mid) { struct bt_iter_arg *arg = (struct bt_iter_arg *)ptr; rb_backtrace_location_t *loc = &arg->bt->backtrace[arg->bt->backtrace_size++]; @@ -599,8 +603,10 @@ oldbt_init(void *ptr, size_t dmy) } static void -oldbt_iter_iseq(void *ptr, const rb_iseq_t *iseq, const VALUE *pc) +oldbt_iter_iseq(void *ptr, const rb_control_frame_t *cfp) { + const rb_iseq_t *iseq = cfp->iseq; + const VALUE *pc = cfp->pc; struct oldbt_arg *arg = (struct oldbt_arg *)ptr; VALUE file = arg->filename = iseq->location.path; VALUE name = iseq->location.label; @@ -610,7 +616,7 @@ oldbt_iter_iseq(void *ptr, const rb_iseq_t *iseq, const VALUE *pc) } static void -oldbt_iter_cfunc(void *ptr, ID mid) +oldbt_iter_cfunc(void *ptr, const rb_control_frame_t *cfp, ID mid) { struct oldbt_arg *arg = (struct oldbt_arg *)ptr; VALUE file = arg->filename; @@ -846,3 +852,137 @@ Init_vm_backtrace(void) rb_define_global_function("caller", rb_f_caller, -1); rb_define_global_function("caller_locations", rb_f_caller_locations, -1); } + +/* debugger API */ + +#if defined __GNUC__ && __GNUC__ >= 4 +#pragma GCC visibility push(default) +#endif + +#if defined __GNUC__ && __GNUC__ >= 4 +#pragma GCC visibility pop +#endif + +struct rb_debug_inspector_struct { + rb_thread_t *th; + rb_control_frame_t *cfp; + VALUE backtrace; + VALUE contexts; /* [[klass, binding, iseq, cfp], ...] */ + int backtrace_size; +}; + +struct collect_caller_bindings_data { + rb_thread_t *th; + VALUE ary; +}; + +static void +collect_caller_bindings_init(void *arg, size_t size) +{ + /* */ +} + +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))); +} + +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)); +} + +static VALUE +collect_caller_bindings(rb_thread_t *th) +{ + struct collect_caller_bindings_data data; + 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); +} + +/* + * Note that the passed `rb_debug_inspector_t' will be disabled + * after `rb_debug_inspector_open'. + */ + +VALUE +rb_debug_inspector_open(rb_debug_inspector_func_t func, void *data) +{ + rb_debug_inspector_t dbg_context; + rb_thread_t *th = GET_THREAD(); + int state; + VALUE result; + + dbg_context.th = th; + dbg_context.cfp = dbg_context.th->cfp; + dbg_context.backtrace = vm_backtrace_location_ary(th, 0, 0); + dbg_context.backtrace_size = RARRAY_LEN(dbg_context.backtrace); + dbg_context.contexts = collect_caller_bindings(th); + + TH_PUSH_TAG(th); + if ((state = EXEC_TAG()) == 0) { + result = (*func)(&dbg_context, data); + } + TH_POP_TAG(); + + /* invalidate bindings? */ + + + if (state) { + JUMP_TAG(state); + } + + return result; +} + +static VALUE +frame_get(const rb_debug_inspector_t *dc, int index) +{ + if (index < 0 || index >= dc->backtrace_size) { + rb_raise(rb_eArgError, "no such frame"); + } + return rb_ary_entry(dc->contexts, index); +} + +VALUE +rb_debug_inspector_frame_class_get(const rb_debug_inspector_t *dc, int index) +{ + VALUE frame = frame_get(dc, index); + return rb_ary_entry(frame, 0); +} + +VALUE +rb_debug_inspector_frame_binding_get(const rb_debug_inspector_t *dc, int index) +{ + VALUE frame = frame_get(dc, index); + return rb_ary_entry(frame, 1); +} + +VALUE +rb_debug_inspector_frame_iseq_get(const rb_debug_inspector_t *dc, int index) +{ + VALUE frame = frame_get(dc, index); + return rb_ary_entry(frame, 2); +} + +VALUE +rb_debug_inspector_backtrace_locations(const rb_debug_inspector_t *dc) +{ + return dc->backtrace; +} + diff --git a/vm_core.h b/vm_core.h index b29df8eb9d..444dfe7c87 100644 --- a/vm_core.h +++ b/vm_core.h @@ -793,6 +793,7 @@ VALUE rb_vm_invoke_proc(rb_thread_t *th, rb_proc_t *proc, int argc, const VALUE *argv, const rb_block_t *blockptr); VALUE rb_vm_make_proc(rb_thread_t *th, const rb_block_t *block, VALUE klass); VALUE rb_vm_make_env_object(rb_thread_t *th, rb_control_frame_t *cfp); +VALUE rb_binding_new_with_cfp(rb_thread_t *th, const rb_control_frame_t *src_cfp); void rb_vm_inc_const_missing_count(void); void rb_vm_gvl_destroy(rb_vm_t *vm); VALUE rb_vm_call(rb_thread_t *th, VALUE recv, VALUE id, int argc, @@ -808,7 +809,7 @@ void rb_thread_wakeup_timer_thread(void); int ruby_thread_has_gvl_p(void); typedef int rb_backtrace_iter_func(void *, VALUE, int, VALUE); -rb_control_frame_t *rb_vm_get_ruby_level_next_cfp(rb_thread_t *th, rb_control_frame_t *cfp); +rb_control_frame_t *rb_vm_get_ruby_level_next_cfp(rb_thread_t *th, const rb_control_frame_t *cfp); 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); diff --git a/vm_trace.c b/vm_trace.c index 318f7287bd..211b36e969 100644 --- a/vm_trace.c +++ b/vm_trace.c @@ -644,9 +644,7 @@ rb_tracearg_event(rb_trace_arg_t *trace_arg) return ID2SYM(get_event_id(trace_arg->event)); } -rb_control_frame_t *rb_vm_get_ruby_level_next_cfp(rb_thread_t *th, rb_control_frame_t *cfp); int rb_vm_control_frame_id_and_class(rb_control_frame_t *cfp, ID *idp, VALUE *klassp); -VALUE rb_binding_new_with_cfp(rb_thread_t *th, rb_control_frame_t *src_cfp); static void fill_path_and_lineno(rb_trace_arg_t *trace_arg) -- cgit v1.2.3