diff options
Diffstat (limited to 'vm_backtrace.c')
| -rw-r--r-- | vm_backtrace.c | 147 |
1 files changed, 104 insertions, 43 deletions
diff --git a/vm_backtrace.c b/vm_backtrace.c index 12e4b771e2..85a0658612 100644 --- a/vm_backtrace.c +++ b/vm_backtrace.c @@ -13,11 +13,13 @@ #include "internal.h" #include "internal/class.h" #include "internal/error.h" +#include "internal/object.h" #include "internal/vm.h" #include "iseq.h" #include "ruby/debug.h" #include "ruby/encoding.h" #include "vm_core.h" +#include "zjit.h" static VALUE rb_cBacktrace; static VALUE rb_cBacktraceLocation; @@ -44,7 +46,7 @@ calc_pos(const rb_iseq_t *iseq, const VALUE *pc, int *lineno, int *node_id) } if (lineno) *lineno = ISEQ_BODY(iseq)->location.first_lineno; #ifdef USE_ISEQ_NODE_ID - if (node_id) *node_id = -1; + if (node_id) *node_id = ISEQ_BODY(iseq)->location.node_id; #endif return 1; } @@ -101,9 +103,9 @@ calc_node_id(const rb_iseq_t *iseq, const VALUE *pc) int rb_vm_get_sourceline(const rb_control_frame_t *cfp) { - if (VM_FRAME_RUBYFRAME_P(cfp) && cfp->iseq) { - const rb_iseq_t *iseq = cfp->iseq; - int line = calc_lineno(iseq, cfp->pc); + if (VM_FRAME_RUBYFRAME_P(cfp) && CFP_ISEQ(cfp)) { + const rb_iseq_t *iseq = CFP_ISEQ(cfp); + int line = calc_lineno(iseq, CFP_PC(cfp)); if (line != 0) { return line; } @@ -400,7 +402,7 @@ location_path_m(VALUE self) static int location_node_id(rb_backtrace_location_t *loc) { - if (loc->iseq && loc->pc) { + if (loc->iseq) { return calc_node_id(loc->iseq, loc->pc); } return -1; @@ -576,14 +578,6 @@ rb_backtrace_p(VALUE obj) } static VALUE -backtrace_alloc(VALUE klass) -{ - rb_backtrace_t *bt; - VALUE obj = TypedData_Make_Struct(klass, rb_backtrace_t, &backtrace_data_type, bt); - return obj; -} - -static VALUE backtrace_alloc_capa(long num_frames, rb_backtrace_t **backtrace) { size_t memsize = offsetof(rb_backtrace_t, backtrace) + num_frames * sizeof(rb_backtrace_location_t); @@ -617,7 +611,7 @@ backtrace_size(const rb_execution_context_t *ec) static bool is_rescue_or_ensure_frame(const rb_control_frame_t *cfp) { - enum rb_iseq_type type = ISEQ_BODY(cfp->iseq)->type; + enum rb_iseq_type type = ISEQ_BODY(CFP_ISEQ(cfp))->type; return type == ISEQ_TYPE_RESCUE || type == ISEQ_TYPE_ENSURE; } @@ -687,17 +681,17 @@ rb_ec_partial_backtrace_object(const rb_execution_context_t *ec, long start_fram } for (; cfp != end_cfp && (bt->backtrace_size < num_frames); cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)) { - if (cfp->iseq) { - if (cfp->pc) { + if (CFP_ISEQ(cfp)) { + if (CFP_PC(cfp)) { if (start_frame > 0) { start_frame--; } else { - bool internal = is_internal_location(cfp->iseq); + bool internal = is_internal_location(CFP_ISEQ(cfp)); if (skip_internal && internal) continue; if (!skip_next_frame) { - const rb_iseq_t *iseq = cfp->iseq; - const VALUE *pc = cfp->pc; + const rb_iseq_t *iseq = CFP_ISEQ(cfp); + const VALUE *pc = CFP_PC(cfp); if (internal && backpatch_counter > 0) { // To keep only one internal frame, discard the previous backpatch frames bt->backtrace_size -= backpatch_counter; @@ -752,12 +746,12 @@ rb_ec_partial_backtrace_object(const rb_execution_context_t *ec, long start_fram // is the one of the caller Ruby frame, so if the last entry is a C frame we find the caller Ruby frame here. if (backpatch_counter > 0) { for (; cfp != end_cfp; cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)) { - if (cfp->iseq && cfp->pc && !(skip_internal && is_internal_location(cfp->iseq))) { + if (CFP_ISEQ(cfp) && CFP_PC(cfp) && !(skip_internal && is_internal_location(CFP_ISEQ(cfp)))) { VM_ASSERT(!skip_next_frame); // ISEQ_TYPE_RESCUE/ISEQ_TYPE_ENSURE should have a caller Ruby ISEQ, not a cfunc - bt_backpatch_loc(backpatch_counter, loc, cfp->iseq, cfp->pc); - RB_OBJ_WRITTEN(btobj, Qundef, cfp->iseq); + bt_backpatch_loc(backpatch_counter, loc, CFP_ISEQ(cfp), CFP_PC(cfp)); + RB_OBJ_WRITTEN(btobj, Qundef, CFP_ISEQ(cfp)); if (do_yield) { - bt_yield_loc(loc - backpatch_counter, backpatch_counter, btobj); + bt_yield_loc(loc - backpatch_counter + 1, backpatch_counter, btobj); } break; } @@ -858,7 +852,7 @@ rb_backtrace_to_location_ary(VALUE self) VALUE rb_location_ary_to_backtrace(VALUE ary) { - if (!RB_TYPE_P(ary, T_ARRAY) || !rb_frame_info_p(RARRAY_AREF(ary, 0))) { + if (!RB_TYPE_P(ary, T_ARRAY) || RARRAY_LEN(ary) == 0 || !rb_frame_info_p(RARRAY_AREF(ary, 0))) { return Qfalse; } @@ -961,6 +955,47 @@ backtrace_limit(VALUE self) return LONG2NUM(rb_backtrace_length_limit); } +/* :nodoc: */ +static VALUE +backtrace_clone(VALUE self) +{ + rb_backtrace_t *bt; + TypedData_Get_Struct(self, rb_backtrace_t, &backtrace_data_type, bt); + + rb_backtrace_t *other_bt; + VALUE clone = backtrace_alloc_capa(bt->backtrace_size, &other_bt); + + rb_obj_clone_setup(self, clone, Qfalse); + + return clone; +} + +/* :nodoc: */ +static VALUE +backtrace_dup(VALUE self) +{ + rb_notimplement(); + + UNREACHABLE_RETURN(Qnil); +} + +/* :nodoc: */ +static VALUE +backtrace_initialize_copy(VALUE self, VALUE original) +{ + rb_backtrace_t *bt; + TypedData_Get_Struct(self, rb_backtrace_t, &backtrace_data_type, bt); + + rb_backtrace_t *original_bt; + TypedData_Get_Struct(original, rb_backtrace_t, &backtrace_data_type, original_bt); + + bt->backtrace_size = original_bt->backtrace_size; + MEMCPY(bt->backtrace, original_bt->backtrace, rb_backtrace_location_t, original_bt->backtrace_size); + rb_gc_writebarrier_remember(self); + + return Qnil; +} + VALUE rb_ec_backtrace_str_ary(const rb_execution_context_t *ec, long lev, long n) { @@ -1019,8 +1054,8 @@ backtrace_each(const rb_execution_context_t *ec, /* SDR(); */ for (i=0, cfp = start_cfp; i<size; i++, cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) { /* fprintf(stderr, "cfp: %d\n", (rb_control_frame_t *)(ec->vm_stack + ec->vm_stack_size) - cfp); */ - if (cfp->iseq) { - if (cfp->pc) { + if (CFP_ISEQ(cfp)) { + if (CFP_PC(cfp)) { iter_iseq(arg, cfp); } } @@ -1052,8 +1087,8 @@ oldbt_init(void *ptr, size_t dmy) static void oldbt_iter_iseq(void *ptr, const rb_control_frame_t *cfp) { - const rb_iseq_t *iseq = cfp->iseq; - const VALUE *pc = cfp->pc; + const rb_iseq_t *iseq = CFP_ISEQ(cfp); + const VALUE *pc = CFP_PC(cfp); struct oldbt_arg *arg = (struct oldbt_arg *)ptr; VALUE file = arg->filename = rb_iseq_path(iseq); VALUE name = ISEQ_BODY(iseq)->location.label; @@ -1405,6 +1440,14 @@ each_caller_location(int argc, VALUE *argv, VALUE _) return Qnil; } +static VALUE +backtrace_no_allocator(VALUE klass) +{ + rb_notimplement(); + + UNREACHABLE_RETURN(Qnil); +} + /* called from Init_vm() in vm.c */ void Init_vm_backtrace(void) @@ -1415,11 +1458,18 @@ Init_vm_backtrace(void) * settings of the current session. */ rb_cBacktrace = rb_define_class_under(rb_cThread, "Backtrace", rb_cObject); - rb_define_alloc_func(rb_cBacktrace, backtrace_alloc); - rb_undef_method(CLASS_OF(rb_cBacktrace), "new"); + + // Can't undefine the allocator, as it's needed as a key by Marshal + rb_define_alloc_func(rb_cBacktrace, backtrace_no_allocator); rb_marshal_define_compat(rb_cBacktrace, rb_cArray, backtrace_dump_data, backtrace_load_data); + + rb_undef_method(CLASS_OF(rb_cBacktrace), "new"); rb_define_singleton_method(rb_cBacktrace, "limit", backtrace_limit, 0); + rb_define_method(rb_cBacktrace, "clone", backtrace_clone, 0); + rb_define_method(rb_cBacktrace, "dup", backtrace_dup, 0); + rb_define_method(rb_cBacktrace, "initialize_copy", backtrace_initialize_copy, 1); + /* * An object representation of a stack frame, initialized by * Kernel#caller_locations. @@ -1550,17 +1600,18 @@ collect_caller_bindings_iseq(void *arg, const rb_control_frame_t *cfp) { struct collect_caller_bindings_data *data = (struct collect_caller_bindings_data *)arg; VALUE frame = rb_ary_new2(6); + const rb_iseq_t *iseq = CFP_ISEQ(cfp); 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, GC_GUARDED_PTR(cfp)); /* create later */ - rb_ary_store(frame, CALLER_BINDING_ISEQ, cfp->iseq ? (VALUE)cfp->iseq : Qnil); + rb_ary_store(frame, CALLER_BINDING_ISEQ, iseq ? (VALUE)iseq : Qnil); rb_ary_store(frame, CALLER_BINDING_CFP, GC_GUARDED_PTR(cfp)); rb_backtrace_location_t *loc = &data->bt->backtrace[data->bt->backtrace_size++]; RB_OBJ_WRITE(data->btobj, &loc->cme, rb_vm_frame_method_entry(cfp)); - RB_OBJ_WRITE(data->btobj, &loc->iseq, cfp->iseq); - loc->pc = cfp->pc; + RB_OBJ_WRITE(data->btobj, &loc->iseq, iseq); + loc->pc = CFP_PC(cfp); VALUE vloc = location_create(loc, (void *)data->btobj); rb_ary_store(frame, CALLER_BINDING_LOC, vloc); @@ -1745,38 +1796,48 @@ thread_profile_frames(rb_execution_context_t *ec, int start, int limit, VALUE *b end_cfp = RUBY_VM_NEXT_CONTROL_FRAME(end_cfp); for (i=0; i<limit && cfp != end_cfp; cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)) { - if (VM_FRAME_RUBYFRAME_P(cfp) && cfp->pc != 0) { + if (VM_FRAME_RUBYFRAME_P_UNCHECKED(cfp) && CFP_PC(cfp)) { if (start > 0) { start--; continue; } /* record frame info */ - cme = rb_vm_frame_method_entry(cfp); + cme = rb_vm_frame_method_entry_unchecked(cfp); + const rb_iseq_t *iseq = CFP_ISEQ(cfp); if (cme && cme->def->type == VM_METHOD_TYPE_ISEQ) { buff[i] = (VALUE)cme; } else { - buff[i] = (VALUE)cfp->iseq; + buff[i] = (VALUE)iseq; } if (lines) { - // The topmost frame may not have an updated PC because the JIT - // may not have set one. The JIT compiler will update the PC - // before entering a new function (so that `caller` will work), - // so only the topmost frame could possibly have an out of date PC - if (cfp == top && cfp->jit_return) { + const VALUE *pc = CFP_PC(cfp); + VALUE *iseq_encoded = ISEQ_BODY(iseq)->iseq_encoded; + VALUE *pc_end = iseq_encoded + ISEQ_BODY(iseq)->iseq_size; + + // The topmost frame may have an invalid PC because the JIT + // may leave it uninitialized for speed. JIT code must update the PC + // before entering a non-leaf method (so that `caller` will work), + // so only the topmost frame could possibly have an out-of-date PC. + // ZJIT doesn't set `cfp->jit_return`, so it's not a reliable signal. + // TODO(zjit): lightweight frames potentially makes more than + // the top most frame invalid. + // + // Avoid passing invalid PC to calc_lineno() to avoid crashing. + if (cfp == top && (pc < iseq_encoded || pc > pc_end)) { lines[i] = 0; } else { - lines[i] = calc_lineno(cfp->iseq, cfp->pc); + lines[i] = calc_lineno(iseq, pc); } } i++; } else { - cme = rb_vm_frame_method_entry(cfp); + cme = rb_vm_frame_method_entry_unchecked(cfp); if (cme && cme->def->type == VM_METHOD_TYPE_CFUNC) { if (start > 0) { start--; |
