summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test/ruby/test_backtrace.rb28
-rw-r--r--vm_backtrace.c476
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);