From 4f54d4710e1edfbcb8b0d7d7feea1bd8c519229f Mon Sep 17 00:00:00 2001 From: ko1 Date: Sat, 26 May 2012 04:49:23 +0000 Subject: [PATCH] * vm.c (RubyVM::FrameInfo): add a class to access each frame information. You don't need to parse strings from caller(). FrameInfo has the following methods: FrameInfo#name: method name, class name, etc with decorations. FrameInfo#basename: name without decorations. FrameInfo#line_no: line number. FrameInfo#filename: file name. FrameInfo#filepath: full filepath. FrameInfo#iseq: iseq if it is iseq frame (defined by ruby script) FrameInfo#to_s: return caller() method style string. RubyVM::FrameInfoFrameInfo.caller(n, lev) returns array of FrameInfo objects. The name "RubyVM::FrameInfoFrameInfo.caller" is long and ambiguous (same as caller() method), we need to change the name before Ruby 2.0 release. Good names or comments are welcome. * test/ruby/test_backtrace.rb: add a test for above change. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@35801 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 20 +++ test/ruby/test_backtrace.rb | 10 ++ vm.c | 309 +++++++++++++++++++++++++++++------- vm_eval.c | 32 +++- 4 files changed, 310 insertions(+), 61 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2c3fd208c3..11d4a22182 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +Sat May 26 13:40:29 2012 Koichi Sasada + + * vm.c (RubyVM::FrameInfo): add a class to access each frame + information. You don't need to parse strings from caller(). + FrameInfo has the following methods: + FrameInfo#name: method name, class name, etc with decorations. + FrameInfo#basename: name without decorations. + FrameInfo#line_no: line number. + FrameInfo#filename: file name. + FrameInfo#filepath: full filepath. + FrameInfo#iseq: iseq if it is iseq frame (defined by ruby script) + FrameInfo#to_s: return caller() method style string. + RubyVM::FrameInfoFrameInfo.caller(n, lev) returns array of + FrameInfo objects. The name "RubyVM::FrameInfoFrameInfo.caller" + is long and ambiguous (same as caller() method), we need to change + the name before Ruby 2.0 release. + Good names or comments are welcome. + + * test/ruby/test_backtrace.rb: add a test for above change. + Sat May 26 12:18:09 2012 Koichi Sasada * vm.c (frame_info_to_str): add `break'. diff --git a/test/ruby/test_backtrace.rb b/test/ruby/test_backtrace.rb index c35b95c9e1..5eae64f325 100644 --- a/test/ruby/test_backtrace.rb +++ b/test/ruby/test_backtrace.rb @@ -84,4 +84,14 @@ class TestBacktrace < Test::Unit::TestCase } rec[m] end + + def test_caller_frame_info + fis = RubyVM::FrameInfo.caller(0); cs = caller(0) + assert_equal(cs.size, fis.size) + fis.zip(cs).each{|fi, s| + assert_match(/#{fi.name}/, s) + assert_match(/#{fi.filename}/, s) + assert_match(/#{fi.line_no}/, s) + } + end end diff --git a/vm.c b/vm.c index e0cf141b0d..0357a3d29b 100644 --- a/vm.c +++ b/vm.c @@ -36,10 +36,8 @@ VALUE rb_cRubyVM; VALUE rb_cThread; VALUE rb_cEnv; VALUE rb_mRubyVMFrozenCore; -VALUE rb_cBacktrace; -#if 0 -VALUE rb_cFrameInfo; -#endif +static VALUE rb_cBacktrace; +static VALUE rb_cFrameInfo; VALUE ruby_vm_const_missing_count = 0; char ruby_vm_redefined_flag[BOP_LAST_]; @@ -761,11 +759,6 @@ typedef struct rb_frame_info_struct { FRAME_INFO_TYPE_IFUNC, } type; - enum FRAME_INFO_STORAGE { - FRAME_INFO_STORAGE_BACKTRACE, - FRAME_INFO_STORAGE_VALUE, - } storage; - union { struct { const rb_iseq_t *iseq; @@ -781,29 +774,197 @@ typedef struct rb_frame_info_struct { } body; } rb_frame_info_t; +struct valued_frame_info { + rb_frame_info_t *fi; + VALUE btobj; +}; + static void frame_info_mark(void *ptr) { if (ptr) { - rb_frame_info_t *fi = (rb_frame_info_t *)ptr; - - switch (fi->type) { - case FRAME_INFO_TYPE_ISEQ: - case FRAME_INFO_TYPE_ISEQ_CALCED: - rb_gc_mark(fi->body.iseq.iseq->self); - break; - case FRAME_INFO_TYPE_CFUNC: - if (fi->body.cfunc.prev_fi && fi->body.cfunc.prev_fi->storage == FRAME_INFO_STORAGE_VALUE) { - /* TODO: rb_gc_mark(fi->body.cfunc.prev_fi->self) */ - } - break; - case FRAME_INFO_TYPE_IFUNC: - default: - break; - } + struct valued_frame_info *vfi = (struct valued_frame_info *)ptr; + rb_gc_mark(vfi->btobj); } } +static void +frame_info_mark_entry(rb_frame_info_t *fi) +{ + switch (fi->type) { + case FRAME_INFO_TYPE_ISEQ: + case FRAME_INFO_TYPE_ISEQ_CALCED: + rb_gc_mark(fi->body.iseq.iseq->self); + break; + case FRAME_INFO_TYPE_CFUNC: + case FRAME_INFO_TYPE_IFUNC: + default: + break; + } +} + +static void +frame_info_free(void *ptr) +{ + if (ptr) { + rb_frame_info_t *fi = (rb_frame_info_t *)ptr; + ruby_xfree(fi); + } +} + +static size_t +frame_info_memsize(const void *ptr) +{ + /* rb_frame_info_t *fi = (rb_frame_info_t *)ptr; */ + return sizeof(rb_frame_info_t); +} + +static const rb_data_type_t frame_info_data_type = { + "frame_info", + {frame_info_mark, frame_info_free, frame_info_memsize,}, +}; + +static inline rb_frame_info_t * +frame_info_ptr(VALUE fiobj) +{ + struct valued_frame_info *vfi; + GetCoreDataFromValue(fiobj, struct valued_frame_info, vfi); + return vfi->fi; +} + +static int +frame_info_line_no(rb_frame_info_t *fi) +{ + switch (fi->type) { + case FRAME_INFO_TYPE_ISEQ: + fi->type = FRAME_INFO_TYPE_ISEQ_CALCED; + return (fi->body.iseq.line_no.line_no = calc_line_no(fi->body.iseq.iseq, fi->body.iseq.line_no.pc)); + case FRAME_INFO_TYPE_ISEQ_CALCED: + return fi->body.iseq.line_no.line_no; + case FRAME_INFO_TYPE_CFUNC: + if (fi->body.cfunc.prev_fi) { + return frame_info_line_no(fi->body.cfunc.prev_fi); + } + return 0; + default: + rb_bug("frame_info_line_no: unreachable"); + } +} + +static VALUE +frame_info_line_no_m(VALUE self) +{ + return INT2FIX(frame_info_line_no(frame_info_ptr(self))); +} + +static VALUE +frame_info_name(rb_frame_info_t *fi) +{ + switch (fi->type) { + case FRAME_INFO_TYPE_ISEQ: + case FRAME_INFO_TYPE_ISEQ_CALCED: + return fi->body.iseq.iseq->location.name; + case FRAME_INFO_TYPE_CFUNC: + return rb_id2str(fi->body.cfunc.mid); + case FRAME_INFO_TYPE_IFUNC: + default: + rb_bug("frame_info_name: unreachable"); + } +} + +static VALUE +frame_info_name_m(VALUE self) +{ + return frame_info_name(frame_info_ptr(self)); +} + +static VALUE +frame_info_basename(rb_frame_info_t *fi) +{ + switch (fi->type) { + case FRAME_INFO_TYPE_ISEQ: + case FRAME_INFO_TYPE_ISEQ_CALCED: + return fi->body.iseq.iseq->location.basename; + case FRAME_INFO_TYPE_CFUNC: + return rb_sym_to_s(ID2SYM(fi->body.cfunc.mid)); + case FRAME_INFO_TYPE_IFUNC: + default: + rb_bug("frame_info_basename: unreachable"); + } +} + +static VALUE +frame_info_basename_m(VALUE self) +{ + return frame_info_basename(frame_info_ptr(self)); +} + +static VALUE +frame_info_filename(rb_frame_info_t *fi) +{ + switch (fi->type) { + case FRAME_INFO_TYPE_ISEQ: + case FRAME_INFO_TYPE_ISEQ_CALCED: + return fi->body.iseq.iseq->location.filename; + case FRAME_INFO_TYPE_CFUNC: + if (fi->body.cfunc.prev_fi) { + return frame_info_filename(fi->body.cfunc.prev_fi); + } + return Qnil; + case FRAME_INFO_TYPE_IFUNC: + default: + rb_bug("frame_info_filename: unreachable"); + } +} + +static VALUE +frame_info_filename_m(VALUE self) +{ + return frame_info_filename(frame_info_ptr(self)); +} + +static VALUE +frame_info_filepath(rb_frame_info_t *fi) +{ + switch (fi->type) { + case FRAME_INFO_TYPE_ISEQ: + case FRAME_INFO_TYPE_ISEQ_CALCED: + return fi->body.iseq.iseq->location.filepath; + case FRAME_INFO_TYPE_CFUNC: + if (fi->body.cfunc.prev_fi) { + return frame_info_filepath(fi->body.cfunc.prev_fi); + } + return Qnil; + case FRAME_INFO_TYPE_IFUNC: + default: + rb_bug("frame_info_filepath: unreachable"); + } +} + +static VALUE +frame_info_filepath_m(VALUE self) +{ + return frame_info_filepath(frame_info_ptr(self)); +} + +static VALUE +frame_info_iseq(rb_frame_info_t *fi) +{ + switch (fi->type) { + case FRAME_INFO_TYPE_ISEQ: + case FRAME_INFO_TYPE_ISEQ_CALCED: + return fi->body.iseq.iseq->self; + default: + return Qnil; + } +} + +static VALUE +frame_info_iseq_m(VALUE self) +{ + return frame_info_iseq(frame_info_ptr(self)); +} + static VALUE frame_info_format(VALUE file, int line_no, VALUE name) { @@ -817,22 +978,6 @@ frame_info_format(VALUE file, int line_no, VALUE name) } } -static int -frame_info_line_no(rb_frame_info_t *fi) -{ - switch (fi->type) { - case FRAME_INFO_TYPE_ISEQ: - fi->type = FRAME_INFO_TYPE_ISEQ_CALCED; - return (fi->body.iseq.line_no.line_no = calc_line_no(fi->body.iseq.iseq, fi->body.iseq.line_no.pc)); - case FRAME_INFO_TYPE_ISEQ_CALCED: - return fi->body.iseq.line_no.line_no; - case FRAME_INFO_TYPE_CFUNC: - case FRAME_INFO_TYPE_IFUNC: - break; - } - return 0; -} - static VALUE frame_info_to_str(rb_frame_info_t *fi) { @@ -872,6 +1017,12 @@ frame_info_to_str(rb_frame_info_t *fi) return frame_info_format(file, line_no, name); } +static VALUE +frame_info_to_str_m(VALUE self) +{ + return frame_info_to_str(frame_info_ptr(self)); +} + typedef struct rb_backtrace_struct { rb_frame_info_t *backtrace; rb_frame_info_t *backtrace_base; @@ -887,7 +1038,7 @@ backtrace_mark(void *ptr) size_t i, s = bt->backtrace_size; for (i=0; ibacktrace[i]); + frame_info_mark_entry(&bt->backtrace[i]); rb_gc_mark(bt->strary); } } @@ -1005,7 +1156,6 @@ bt_iter_iseq(void *ptr, const rb_iseq_t *iseq, const VALUE *pc) struct bt_iter_arg *arg = (struct bt_iter_arg *)ptr; rb_frame_info_t *fi = &arg->bt->backtrace[arg->bt->backtrace_size++]; fi->type = FRAME_INFO_TYPE_ISEQ; - fi->storage = FRAME_INFO_STORAGE_BACKTRACE; fi->body.iseq.iseq = iseq; fi->body.iseq.line_no.pc = pc; arg->prev_fi = fi; @@ -1017,7 +1167,6 @@ bt_iter_cfunc(void *ptr, ID mid) struct bt_iter_arg *arg = (struct bt_iter_arg *)ptr; rb_frame_info_t *fi = &arg->bt->backtrace[arg->bt->backtrace_size++]; fi->type = FRAME_INFO_TYPE_CFUNC; - fi->storage = FRAME_INFO_STORAGE_BACKTRACE; fi->body.cfunc.mid = mid; fi->body.cfunc.prev_fi = arg->prev_fi; } @@ -1044,7 +1193,7 @@ rb_vm_backtrace_object(void) } static VALUE -backtreace_collect(rb_backtrace_t *bt, int lev, int n, VALUE (*func)(rb_frame_info_t *)) +backtreace_collect(rb_backtrace_t *bt, int lev, int n, VALUE (*func)(rb_frame_info_t *, void *arg), void *arg) { VALUE btary; int i; @@ -1053,12 +1202,18 @@ backtreace_collect(rb_backtrace_t *bt, int lev, int n, VALUE (*func)(rb_frame_in for (i=0; i+lev<(int)bt->backtrace_size && ibacktrace[bt->backtrace_size - 1 - (lev+i)]; - rb_ary_push(btary, func(fi)); + rb_ary_push(btary, func(fi, arg)); } return btary; } +static VALUE +frame_info_to_str_dmyarg(rb_frame_info_t *fi, void *dmy) +{ + return frame_info_to_str(fi); +} + VALUE rb_backtrace_to_str_ary(VALUE self) { @@ -1069,7 +1224,7 @@ rb_backtrace_to_str_ary(VALUE self) return bt->strary; } else { - bt->strary = backtreace_collect(bt, 0, bt->backtrace_size, frame_info_to_str); + bt->strary = backtreace_collect(bt, 0, bt->backtrace_size, frame_info_to_str_dmyarg, 0); return bt->strary; } } @@ -1080,8 +1235,6 @@ backtrace_to_str_ary2(VALUE self, size_t lev, size_t n) rb_backtrace_t *bt; size_t size; GetCoreDataFromValue(self, rb_backtrace_t, bt); - /* fprintf(stderr, "btsize: %d, lev: %d, n: %d\n", (int)bt->backtrace_size, lev, n); */ - size = bt->backtrace_size; if (n == 0) { @@ -1091,22 +1244,39 @@ backtrace_to_str_ary2(VALUE self, size_t lev, size_t n) return Qnil; } - return backtreace_collect(bt, lev, n, frame_info_to_str); -} - -#if 0 -static VALUE -rb_backtrace_to_frame_ary(VALUE self) -{ - return backtreace_collect(self, frame_info_create); + return backtreace_collect(bt, lev, n, frame_info_to_str_dmyarg, 0); } static VALUE -backtrace_frame_ary(rb_thread_t *th, size_t lev, size_t n) +frame_info_create(rb_frame_info_t *srcfi, void *btobj) { - return rb_backtrace_to_frame_ary(backtrace_create(th, lev, n)); + VALUE obj; + struct valued_frame_info *vfi; + obj = TypedData_Make_Struct(rb_cFrameInfo, struct valued_frame_info, &frame_info_data_type, vfi); + + vfi->fi = srcfi; + vfi->btobj = (VALUE)btobj; + + return obj; +} + +static VALUE +backtrace_to_frame_ary(VALUE self, size_t lev, size_t n) +{ + rb_backtrace_t *bt; + size_t size; + GetCoreDataFromValue(self, rb_backtrace_t, bt); + size = bt->backtrace_size; + + if (n == 0) { + n = size; + } + if (lev > size) { + return Qnil; + } + + return backtreace_collect(bt, lev, n, frame_info_create, (void *)self); } -#endif static VALUE backtrace_dump_data(VALUE self) @@ -1130,6 +1300,12 @@ vm_backtrace_str_ary(rb_thread_t *th, size_t lev, size_t n) return backtrace_to_str_ary2(backtrace_object(th), lev, n); } +static VALUE +vm_backtrace_frame_ary(rb_thread_t *th, size_t lev, size_t n) +{ + return backtrace_to_frame_ary(backtrace_object(th), lev, n); +} + /* old style backtrace directly */ struct oldbt_arg { @@ -2587,6 +2763,19 @@ Init_VM(void) rb_undef_method(CLASS_OF(rb_cBacktrace), "new"); rb_marshal_define_compat(rb_cBacktrace, rb_cArray, backtrace_dump_data, backtrace_load_data); + /* ::RubyVM::FrameInfo */ + rb_cFrameInfo = rb_define_class_under(rb_cRubyVM, "FrameInfo", rb_cObject); + rb_undef_alloc_func(rb_cFrameInfo); + rb_undef_method(CLASS_OF(rb_cFrameInfo), "new"); + rb_define_method(rb_cFrameInfo, "line_no", frame_info_line_no_m, 0); + rb_define_method(rb_cFrameInfo, "name", frame_info_name_m, 0); + rb_define_method(rb_cFrameInfo, "basename", frame_info_basename_m, 0); + rb_define_method(rb_cFrameInfo, "filename", frame_info_filename_m, 0); + rb_define_method(rb_cFrameInfo, "filepath", frame_info_filepath_m, 0); + rb_define_method(rb_cFrameInfo, "iseq", frame_info_iseq_m, 0); + rb_define_method(rb_cFrameInfo, "to_s", frame_info_to_str_m, 0); + rb_define_singleton_method(rb_cFrameInfo, "caller", rb_f_caller_frame_info, -1); + /* ::RubyVM::USAGE_ANALYSIS_* */ rb_define_const(rb_cRubyVM, "USAGE_ANALYSIS_INSN", rb_hash_new()); rb_define_const(rb_cRubyVM, "USAGE_ANALYSIS_REGS", rb_hash_new()); diff --git a/vm_eval.c b/vm_eval.c index 0160b66af6..d620213fa0 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -21,6 +21,7 @@ static void vm_set_eval_stack(rb_thread_t * th, VALUE iseqval, const NODE *cref) static int vm_collect_local_variables_in_heap(rb_thread_t *th, VALUE *dfp, VALUE ary); static VALUE vm_backtrace_str_ary(rb_thread_t *th, size_t lev, size_t n); +static VALUE vm_backtrace_frame_ary(rb_thread_t *th, size_t lev, size_t n); static void vm_backtrace_print(FILE *fp); typedef enum call_type { @@ -1636,6 +1637,36 @@ rb_f_caller(int argc, VALUE *argv) return vm_backtrace_str_ary(GET_THREAD(), lev+1, n); } +static VALUE +rb_f_caller_frame_info(int argc, VALUE *argv) +{ + VALUE level, vn; + int lev, n; + + rb_scan_args(argc, argv, "02", &level, &vn); + + lev = NIL_P(level) ? 1 : NUM2INT(level); + + if (NIL_P(vn)) { + n = 0; + } + else { + n = NUM2INT(vn); + if (n == 0) { + return rb_ary_new(); + } + } + + if (lev < 0) { + rb_raise(rb_eArgError, "negative level (%d)", lev); + } + if (n < 0) { + rb_raise(rb_eArgError, "negative n (%d)", n); + } + + return vm_backtrace_frame_ary(GET_THREAD(), lev+1, n); +} + void rb_backtrace(void) { @@ -1806,4 +1837,3 @@ Init_vm_eval(void) rb_define_global_function("caller", rb_f_caller, -1); } -