diff options
author | Yusuke Endoh <[email protected]> | 2025-05-02 17:12:15 +0900 |
---|---|---|
committer | Yusuke Endoh <[email protected]> | 2025-06-18 14:51:56 +0900 |
commit | 10767283dd0277a1d780790ce6bde67cf2c832a2 () | |
tree | a4c98d6d1f8a46d80c2f3aa1cbc3c4d9d9f3d57c /vm_backtrace.c | |
parent | ca10c521ff748bded89e481ab3f1767a8e56a71c (diff) |
Exclude internal frames from backtrace
This changeset suppresses backtrace locations like `<internal:array>:211` as much as possible. Before the : ``` $ ruby -e '[1].fetch_values(42)' <internal:array>:211:in 'Array#fetch': index 42 outside of array bounds: -1...1 (IndexError) from <internal:array>:211:in 'block in Array#fetch_values' from <internal:array>:211:in 'Array#map!' from <internal:array>:211:in 'Array#fetch_values' from -e:1:in '<main>' ``` After the : ``` $ ./miniruby -e '[1].fetch_values(42)' -e:1:in 'Array#fetch_values': index 42 outside of array bounds: -1...1 (IndexError) from -e:1:in '<main>' ``` Specifically: * The special backtrace handling of BUILTIN_ATTR_C_TRACE is now always applied to frames with `<internal:...>`. * When multiple consecutive internal frames appear, all but the bottom (caller-side) frame are removed. [Misc #20968]
Notes: Merged: https://.com/ruby/ruby/pull/13238
-rw-r--r-- | vm_backtrace.c | 42 |
1 files changed, 26 insertions, 16 deletions
@@ -262,6 +262,15 @@ retry: } } // Return true if a given location is a C method or supposed to behave like one. static inline bool location_cfunc_p(rb_backtrace_location_t *loc) @@ -272,7 +281,7 @@ location_cfunc_p(rb_backtrace_location_t *loc) case VM_METHOD_TYPE_CFUNC: return true; case VM_METHOD_TYPE_ISEQ: - return rb_iseq_attr_p(loc->cme->def->body.iseq.iseqptr, BUILTIN_ATTR_C_TRACE); default: return false; } @@ -605,15 +614,6 @@ backtrace_size(const rb_execution_context_t *ec) } static bool -is_internal_location(const rb_control_frame_t *cfp) -{ - static const char prefix[] = "<internal:"; - const size_t prefix_len = sizeof(prefix) - 1; - VALUE file = rb_iseq_path(cfp->iseq); - return strncmp(prefix, RSTRING_PTR(file), prefix_len) == 0; -} - -static bool is_rescue_or_ensure_frame(const rb_control_frame_t *cfp) { enum rb_iseq_type type = ISEQ_BODY(cfp->iseq)->type; @@ -691,16 +691,26 @@ rb_ec_partial_backtrace_object(const rb_execution_context_t *ec, long start_fram if (start_frame > 0) { start_frame--; } - else if (!(skip_internal && is_internal_location(cfp))) { if (!skip_next_frame) { const rb_iseq_t *iseq = cfp->iseq; const VALUE *pc = cfp->pc; loc = &bt->backtrace[bt->backtrace_size++]; RB_OBJ_WRITE(btobj, &loc->cme, rb_vm_frame_method_entry(cfp)); - // Ruby methods with `Primitive.attr! :c_trace` should behave like C methods - if (rb_iseq_attr_p(cfp->iseq, BUILTIN_ATTR_C_TRACE)) { - loc->iseq = NULL; - loc->pc = NULL; back_counter++; } else { @@ -736,7 +746,7 @@ 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 (back_counter > 0) { for (; cfp != end_cfp; cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)) { - if (cfp->iseq && cfp->pc && !(skip_internal && is_internal_location(cfp))) { VM_ASSERT(!skip_next_frame); // ISEQ_TYPE_RESCUE/ISEQ_TYPE_ENSURE should have a caller Ruby ISEQ, not a cfunc bt_back_loc(back_counter, loc, cfp->iseq, cfp->pc); RB_OBJ_WRITTEN(btobj, Qundef, cfp->iseq); |