summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYusuke Endoh <[email protected]>2025-06-04 16:44:28 +0900
committerYusuke Endoh <[email protected]>2025-06-04 19:53:16 +0900
commitcaa6ba1a46afa1bc696adc5fe91ee992f9570c89 ()
tree3d4503775c02a1f3e51394360da27babe3cee6f0
parent8d14d6ea2d9e278a04ebe7e5805221f4cd4cd950 (diff)
Make `rb_debug_inspector_backtrace_locations` return a raw backtrace
Previously, a user of the debug inspector API was supposed to use `rb_debug_inspector_backtrace_locations` to get an array of `Thread::Backtrace::Location`, and then used its index to get more information from `rb_debug_inspector _frame_binding_get(index)`, etc. However, `rb_debug_inspector_backtrace_locations` returned an array of backtraces excluding rescue/ensure frames. On the other hand, `rb_debug_inspector_frame_binding_get(index)` interprets index with rescue/ensure frames. This led to inconsistency of the index, and it was very difficult to correctly use the debug inspector API. This is a minimal fix for the issue by making `rb_debug_inspector_backtrace_locations` return a raw backtrace including rescue/ensure frames.
Notes: Merged: https://.com/ruby/ruby/pull/13510
-rw-r--r--vm_backtrace.c39
1 files changed, 31 insertions, 8 deletions
@@ -1493,9 +1493,8 @@ RUBY_SYMBOL_EXPORT_END
struct rb_debug_inspector_struct {
rb_execution_context_t *ec;
rb_control_frame_t *cfp;
- VALUE backtrace;
VALUE contexts; /* [[klass, binding, iseq, cfp], ...] */
- long backtrace_size;
};
enum {
@@ -1504,18 +1503,22 @@ enum {
CALLER_BINDING_BINDING,
CALLER_BINDING_ISEQ,
CALLER_BINDING_CFP,
CALLER_BINDING_DEPTH,
};
struct collect_caller_bindings_data {
VALUE ary;
const rb_execution_context_t *ec;
};
static void
-collect_caller_bindings_init(void *arg, size_t size)
{
- /* */
}
static VALUE
@@ -1553,6 +1556,14 @@ collect_caller_bindings_iseq(void *arg, const rb_control_frame_t *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_CFP, GC_GUARDED_PTR(cfp));
rb_ary_store(frame, CALLER_BINDING_DEPTH, INT2FIX(frame_depth(data->ec, cfp)));
rb_ary_push(data->ary, frame);
@@ -1569,6 +1580,14 @@ collect_caller_bindings_cfunc(void *arg, const rb_control_frame_t *cfp, ID mid)
rb_ary_store(frame, CALLER_BINDING_BINDING, Qnil); /* not available */
rb_ary_store(frame, CALLER_BINDING_ISEQ, Qnil); /* not available */
rb_ary_store(frame, CALLER_BINDING_CFP, GC_GUARDED_PTR(cfp));
rb_ary_store(frame, CALLER_BINDING_DEPTH, INT2FIX(frame_depth(data->ec, cfp)));
rb_ary_push(data->ary, frame);
@@ -1617,15 +1636,19 @@ rb_debug_inspector_open(rb_debug_inspector_func_t func, void *data)
rb_execution_context_t *ec = GET_EC();
enum ruby_tag_type state;
volatile VALUE MAYBE_UNUSED(result);
/* escape all env to heap */
rb_vm_stack_to_heap(ec);
dbg_context.ec = ec;
dbg_context.cfp = dbg_context.ec->cfp;
- dbg_context.backtrace = rb_ec_backtrace_location_ary(ec, RUBY_BACKTRACE_START, RUBY_ALL_BACKTRACE_LINES, FALSE);
- dbg_context.backtrace_size = RARRAY_LEN(dbg_context.backtrace);
dbg_context.contexts = collect_caller_bindings(ec);
EC_PUSH_TAG(ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
@@ -1645,7 +1668,7 @@ rb_debug_inspector_open(rb_debug_inspector_func_t func, void *data)
static VALUE
frame_get(const rb_debug_inspector_t *dc, long index)
{
- if (index < 0 || index >= dc->backtrace_size) {
rb_raise(rb_eArgError, "no such frame");
}
return rb_ary_entry(dc->contexts, index);
@@ -1698,7 +1721,7 @@ rb_debug_inspector_current_depth(void)
VALUE
rb_debug_inspector_backtrace_locations(const rb_debug_inspector_t *dc)
{
- return dc->backtrace;
}
static int