diff options
-rw-r--r-- | test/ruby/test_backtrace.rb | 28 | ||||
-rw-r--r-- | vm_backtrace.c | 476 |
2 files changed, 209 insertions, 295 deletions
@@ -170,6 +170,34 @@ class TestBacktrace < Test::Unit::TestCase end end def test_caller_locations cs = caller(0); locs = caller_locations(0).map{|loc| loc.to_s @@ -119,16 +119,9 @@ typedef struct rb_backtrace_location_struct { LOCATION_TYPE_CFUNC, } type; - union { - struct { - const rb_iseq_t *iseq; - const VALUE *pc; - } iseq; - struct { - ID mid; - struct rb_backtrace_location_struct *prev_loc; - } cfunc; - } body; } rb_backtrace_location_t; struct valued_frame_info { @@ -148,9 +141,13 @@ location_mark_entry(rb_backtrace_location_t *fi) { switch (fi->type) { case LOCATION_TYPE_ISEQ: - rb_gc_mark_movable((VALUE)fi->body.iseq.iseq); break; case LOCATION_TYPE_CFUNC: default: break; } @@ -188,10 +185,10 @@ location_lineno(rb_backtrace_location_t *loc) { switch (loc->type) { case LOCATION_TYPE_ISEQ: - return calc_lineno(loc->body.iseq.iseq, loc->body.iseq.pc); case LOCATION_TYPE_CFUNC: - if (loc->body.cfunc.prev_loc) { - return location_lineno(loc->body.cfunc.prev_loc); } return 0; default: @@ -219,9 +216,9 @@ location_label(rb_backtrace_location_t *loc) { switch (loc->type) { case LOCATION_TYPE_ISEQ: - return loc->body.iseq.iseq->body->location.label; case LOCATION_TYPE_CFUNC: - return rb_id2str(loc->body.cfunc.mid); default: rb_bug("location_label: unreachable"); UNREACHABLE; @@ -266,9 +263,9 @@ location_base_label(rb_backtrace_location_t *loc) { switch (loc->type) { case LOCATION_TYPE_ISEQ: - return loc->body.iseq.iseq->body->location.base_label; case LOCATION_TYPE_CFUNC: - return rb_id2str(loc->body.cfunc.mid); default: rb_bug("location_base_label: unreachable"); UNREACHABLE; @@ -291,10 +288,10 @@ location_path(rb_backtrace_location_t *loc) { switch (loc->type) { case LOCATION_TYPE_ISEQ: - return rb_iseq_path(loc->body.iseq.iseq); case LOCATION_TYPE_CFUNC: - if (loc->body.cfunc.prev_loc) { - return location_path(loc->body.cfunc.prev_loc); } return Qnil; default: @@ -325,10 +322,10 @@ location_node_id(rb_backtrace_location_t *loc) { switch (loc->type) { case LOCATION_TYPE_ISEQ: - return calc_node_id(loc->body.iseq.iseq, loc->body.iseq.pc); case LOCATION_TYPE_CFUNC: - if (loc->body.cfunc.prev_loc) { - return location_node_id(loc->body.cfunc.prev_loc); } return -1; default: @@ -355,10 +352,10 @@ location_realpath(rb_backtrace_location_t *loc) { switch (loc->type) { case LOCATION_TYPE_ISEQ: - return rb_iseq_realpath(loc->body.iseq.iseq); case LOCATION_TYPE_CFUNC: - if (loc->body.cfunc.prev_loc) { - return location_realpath(loc->body.cfunc.prev_loc); } return Qnil; default: @@ -404,21 +401,21 @@ location_to_str(rb_backtrace_location_t *loc) switch (loc->type) { case LOCATION_TYPE_ISEQ: - file = rb_iseq_path(loc->body.iseq.iseq); - name = loc->body.iseq.iseq->body->location.label; - lineno = calc_lineno(loc->body.iseq.iseq, loc->body.iseq.pc); break; case LOCATION_TYPE_CFUNC: - if (loc->body.cfunc.prev_loc) { - file = rb_iseq_path(loc->body.cfunc.prev_loc->body.iseq.iseq); - lineno = location_lineno(loc->body.cfunc.prev_loc); } else { file = GET_VM()->progname; lineno = 0; } - name = rb_id2str(loc->body.cfunc.mid); break; default: rb_bug("location_to_str: unreachable"); @@ -479,9 +476,13 @@ location_update_entry(rb_backtrace_location_t *fi) { switch (fi->type) { case LOCATION_TYPE_ISEQ: - fi->body.iseq.iseq = (rb_iseq_t*)rb_gc_location((VALUE)fi->body.iseq.iseq); break; case LOCATION_TYPE_CFUNC: default: break; } @@ -548,157 +549,6 @@ backtrace_size(const rb_execution_context_t *ec) return start_cfp - last_cfp + 1; } -static int -backtrace_each(const rb_execution_context_t *ec, - ptrdiff_t from_last, - long num_frames, - void (*init)(void *arg, size_t size), - 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 (*iter_skip)(void *arg, const rb_control_frame_t *cfp), - void *arg) -{ - const rb_control_frame_t *last_cfp = ec->cfp; - const rb_control_frame_t *start_cfp = RUBY_VM_END_CONTROL_FRAME(ec); - const rb_control_frame_t *cfp; - ptrdiff_t size, real_size, i, j, last, start = 0; - int ret = 0; - - // In the case the thread vm_stack or cfp is not initialized, there is no backtrace. - if (start_cfp == NULL) { - init(arg, 0); - return ret; - } - - /* <- start_cfp (end control frame) - * top frame (dummy) - * top frame (dummy) - * top frame <- start_cfp - * top frame - * ... - * 2nd frame <- lev:0 - * current frame <- ec->cfp - */ - - start_cfp = - RUBY_VM_NEXT_CONTROL_FRAME( - RUBY_VM_NEXT_CONTROL_FRAME(start_cfp)); /* skip top frames */ - - if (start_cfp < last_cfp) { - real_size = size = last = 0; - } - else { - /* Ensure we don't look at frames beyond the ones requested */ - for (; from_last > 0 && start_cfp >= last_cfp; from_last--) { - last_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(last_cfp); - } - - real_size = size = start_cfp - last_cfp + 1; - - if (from_last > size) { - size = last = 0; - ret = 1; - } - else if (num_frames >= 0 && num_frames < size) { - if (from_last + num_frames > size) { - size -= from_last; - last = size; - } - else { - start = size - from_last - num_frames; - size = num_frames; - last = start + size; - } - } - else { - size -= from_last; - last = size; - } - } - - init(arg, size); - - /* If a limited number of frames is requested, scan the VM stack for - * ignored frames (iseq without pc). Then adjust the start for the - * backtrace to account for skipped frames. - */ - if (start > 0 && num_frames >= 0 && num_frames < real_size) { - ptrdiff_t ignored_frames; - bool ignored_frames_before_start = false; - for (i=0, j=0, cfp = start_cfp; i<last && j<real_size; i++, j++, cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) { - if (cfp->iseq && !cfp->pc) { - if (j < start) - ignored_frames_before_start = true; - else - i--; - } - } - ignored_frames = j - i; - - if (ignored_frames) { - if (ignored_frames_before_start) { - /* There were ignored frames before start. So just decrementing - * start for ignored frames could still result in not all desired - * frames being captured. - * - * First, scan to the CFP of the desired start frame. - * - * Then scan backwards to previous frames, skipping the number of - * frames ignored after start and any additional ones before start, - * so the number of desired frames will be correctly captured. - */ - for (i=0, j=0, cfp = start_cfp; i<last && j<real_size && j < start; i++, j++, cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) { - /* nothing */ - } - for (; start > 0 && ignored_frames > 0 && j > 0; j--, ignored_frames--, start--, cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)) { - if (cfp->iseq && !cfp->pc) { - ignored_frames++; - } - } - } - else { - /* No ignored frames before start frame, just decrement start */ - start -= ignored_frames; - } - } - } - - for (i=0, j=0, cfp = start_cfp; i<last && j<real_size; i++, j++, cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) { - if (j < start) { - if (iter_skip) { - iter_skip(arg, cfp); - } - continue; - } - - /* fprintf(stderr, "cfp: %d\n", (rb_control_frame_t *)(ec->vm_stack + ec->vm_stack_size) - cfp); */ - if (cfp->iseq) { - if (cfp->pc) { - iter_iseq(arg, cfp); - } - else { - i--; - } - } - else if (RUBYVM_CFUNC_FRAME_P(cfp)) { - const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp); - ID mid = me->def->original_id; - - iter_cfunc(arg, cfp, mid); - } - } - - return ret; -} - -struct bt_iter_arg { - rb_backtrace_t *bt; - VALUE btobj; - rb_backtrace_location_t *prev_loc; - const rb_control_frame_t *prev_cfp; - rb_backtrace_location_t *init_loc; -}; - static bool is_internal_location(const rb_control_frame_t *cfp) { @@ -709,113 +559,103 @@ is_internal_location(const rb_control_frame_t *cfp) } static void -bt_init(void *ptr, size_t size) { - struct bt_iter_arg *arg = (struct bt_iter_arg *)ptr; - arg->btobj = backtrace_alloc(rb_cBacktrace); - GetCoreDataFromValue(arg->btobj, rb_backtrace_t, arg->bt); - arg->bt->backtrace = ZALLOC_N(rb_backtrace_location_t, size+1); - arg->bt->backtrace_size = 1; - arg->prev_cfp = NULL; - arg->init_loc = &arg->bt->backtrace[size]; -} - -static void -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++-1]; - loc->type = LOCATION_TYPE_ISEQ; - loc->body.iseq.iseq = iseq; - loc->body.iseq.pc = pc; - arg->prev_loc = loc; } -static void -bt_iter_iseq_skip_internal(void *ptr, const rb_control_frame_t *cfp) { - struct bt_iter_arg *arg = (struct bt_iter_arg *)ptr; - rb_backtrace_location_t *loc = &arg->bt->backtrace[arg->bt->backtrace_size++-1]; - if (!is_internal_location(cfp)) { - loc->type = LOCATION_TYPE_ISEQ; - loc->body.iseq.iseq = cfp->iseq; - loc->body.iseq.pc = cfp->pc; - arg->prev_loc = loc; - } - else if (arg->prev_cfp) { - loc->type = LOCATION_TYPE_ISEQ; - loc->body.iseq.iseq = arg->prev_cfp->iseq; - loc->body.iseq.pc = arg->prev_cfp->pc; - arg->prev_loc = loc; } else { - rb_bug("No non-internal backtrace entry before an <internal: backtrace entry"); } -} -static void -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++-1]; - loc->type = LOCATION_TYPE_CFUNC; - loc->body.cfunc.mid = mid; - if (arg->prev_loc) { - loc->body.cfunc.prev_loc = arg->prev_loc; - } - else if (arg->prev_cfp) { - const rb_iseq_t *iseq = arg->prev_cfp->iseq; - const VALUE *pc = arg->prev_cfp->pc; - arg->init_loc->type = LOCATION_TYPE_ISEQ; - arg->init_loc->body.iseq.iseq = iseq; - arg->init_loc->body.iseq.pc = pc; - loc->body.cfunc.prev_loc = arg->prev_loc = arg->init_loc; } - else { - loc->body.cfunc.prev_loc = NULL; - } -} -static void -bt_iter_skip(void *ptr, const rb_control_frame_t *cfp) -{ - if (cfp->iseq && cfp->pc) { - ((struct bt_iter_arg *)ptr)->prev_cfp = cfp; } -} -static void -bt_iter_skip_skip_internal(void *ptr, const rb_control_frame_t *cfp) -{ - if (cfp->iseq && cfp->pc) { - if (!is_internal_location(cfp)) { - ((struct bt_iter_arg *) ptr)->prev_cfp = cfp; } } -} - -static VALUE -rb_ec_partial_backtrace_object(const rb_execution_context_t *ec, long lev, long n, int* level_too_large, bool skip_internal) -{ - struct bt_iter_arg arg; - int too_large; - arg.prev_loc = 0; - too_large = backtrace_each(ec, - lev, - n, - bt_init, - skip_internal ? bt_iter_iseq_skip_internal : bt_iter_iseq, - bt_iter_cfunc, - skip_internal ? bt_iter_skip_skip_internal : bt_iter_skip, - &arg); - - if (level_too_large) *level_too_large = too_large; - - return arg.btobj; } MJIT_FUNC_EXPORTED VALUE @@ -830,10 +670,10 @@ backtrace_collect(rb_backtrace_t *bt, VALUE (*func)(rb_backtrace_location_t *, v VALUE btary; int i; - btary = rb_ary_new2(bt->backtrace_size-1); - for (i=0; i<bt->backtrace_size-1; i++) { - rb_backtrace_location_t *loc = &bt->backtrace[bt->backtrace_size - 2 - i]; rb_ary_push(btary, func(loc, arg)); } @@ -876,13 +716,13 @@ rb_backtrace_use_iseq_first_lineno_for_last_location(VALUE self) rb_backtrace_location_t *loc; GetCoreDataFromValue(self, rb_backtrace_t, bt); - VM_ASSERT(bt->backtrace_size > 1); - loc = &bt->backtrace[bt->backtrace_size - 2]; VM_ASSERT(loc->type == LOCATION_TYPE_ISEQ); - loc->body.iseq.pc = NULL; // means location.first_lineno } static VALUE @@ -957,6 +797,64 @@ rb_ec_backtrace_location_ary(const rb_execution_context_t *ec, long lev, long n, /* make old style backtrace directly */ struct oldbt_arg { VALUE filename; int lineno; @@ -1019,12 +917,9 @@ vm_backtrace_print(FILE *fp) arg.func = oldbt_print; arg.data = (void *)fp; backtrace_each(GET_EC(), - BACKTRACE_START, - ALL_BACKTRACE_LINES, oldbt_init, oldbt_iter_iseq, oldbt_iter_cfunc, - NULL, &arg); } @@ -1055,12 +950,9 @@ rb_backtrace_print_as_bugreport(void) arg.data = (int *)&i; backtrace_each(GET_EC(), - BACKTRACE_START, - ALL_BACKTRACE_LINES, oldbt_init, oldbt_iter_iseq, oldbt_iter_cfunc, - NULL, &arg); } @@ -1101,12 +993,9 @@ rb_backtrace_each(VALUE (*iter)(VALUE recv, VALUE str), VALUE output) arg.func = oldbt_print_to; arg.data = &parg; backtrace_each(GET_EC(), - BACKTRACE_START, - ALL_BACKTRACE_LINES, oldbt_init, oldbt_iter_iseq, oldbt_iter_cfunc, - NULL, &arg); } @@ -1458,12 +1347,9 @@ collect_caller_bindings(const rb_execution_context_t *ec) data.ary = rb_ary_new(); backtrace_each(ec, - BACKTRACE_START, - ALL_BACKTRACE_LINES, collect_caller_bindings_init, collect_caller_bindings_iseq, collect_caller_bindings_cfunc, - NULL, &data); result = rb_ary_reverse(data.ary); |