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.
This commit is contained in:
parent
8d14d6ea2d
commit
caa6ba1a46
Notes:
git
2025-06-04 10:53:32 +00:00
@ -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;
|
||||
VALUE raw_backtrace;
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -1504,18 +1503,22 @@ enum {
|
||||
CALLER_BINDING_BINDING,
|
||||
CALLER_BINDING_ISEQ,
|
||||
CALLER_BINDING_CFP,
|
||||
CALLER_BINDING_LOC,
|
||||
CALLER_BINDING_DEPTH,
|
||||
};
|
||||
|
||||
struct collect_caller_bindings_data {
|
||||
VALUE ary;
|
||||
const rb_execution_context_t *ec;
|
||||
VALUE btobj;
|
||||
rb_backtrace_t *bt;
|
||||
};
|
||||
|
||||
static void
|
||||
collect_caller_bindings_init(void *arg, size_t size)
|
||||
collect_caller_bindings_init(void *arg, size_t num_frames)
|
||||
{
|
||||
/* */
|
||||
struct collect_caller_bindings_data *data = (struct collect_caller_bindings_data *)arg;
|
||||
data->btobj = backtrace_alloc_capa(num_frames, &data->bt);
|
||||
}
|
||||
|
||||
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_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;
|
||||
VALUE vloc = location_create(loc, (void *)data->btobj);
|
||||
rb_ary_store(frame, CALLER_BINDING_LOC, vloc);
|
||||
|
||||
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_backtrace_location_t *loc = &data->bt->backtrace[data->bt->backtrace_size++];
|
||||
RB_OBJ_WRITE(data->btobj, &loc->cme, rb_vm_frame_method_entry(cfp));
|
||||
loc->iseq = NULL;
|
||||
loc->pc = NULL;
|
||||
VALUE vloc = location_create(loc, (void *)data->btobj);
|
||||
rb_ary_store(frame, CALLER_BINDING_LOC, vloc);
|
||||
|
||||
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);
|
||||
int i;
|
||||
|
||||
/* 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);
|
||||
dbg_context.raw_backtrace = rb_ary_new();
|
||||
for (i=0; i<RARRAY_LEN(dbg_context.contexts); i++) {
|
||||
VALUE frame = rb_ary_entry(dbg_context.contexts, i);
|
||||
rb_ary_push(dbg_context.raw_backtrace, rb_ary_entry(frame, CALLER_BINDING_LOC));
|
||||
}
|
||||
|
||||
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) {
|
||||
if (index < 0 || index >= RARRAY_LEN(dc->contexts)) {
|
||||
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;
|
||||
return dc->raw_backtrace;
|
||||
}
|
||||
|
||||
static int
|
||||
|
Loading…
x
Reference in New Issue
Block a user