add debug context APIs for debuggers (frame depth)

The following new debug context APIs are for implementing debugger's
`next` (step over) and similar functionality.

* `rb_debug_inspector_frame_depth(dc, index)` returns `index`-th
  frame's depth.
* `rb_debug_inspector_current_depth()` returns current frame depth.

The frame depth is not related to the frame index because debug
context API skips some special frames but proposed `_depth()` APIs
returns the count of all frames (raw depth).
This commit is contained in:
Koichi Sasada 2022-11-25 11:51:35 +09:00
Родитель 571d21fd4a
Коммит 67766cd55c
2 изменённых файлов: 62 добавлений и 14 удалений

Просмотреть файл

@ -206,6 +206,17 @@ typedef VALUE (*rb_debug_inspector_func_t)(const rb_debug_inspector_t *dc, void
*/
VALUE rb_debug_inspector_open(rb_debug_inspector_func_t func, void *data);
/**
* Queries the backtrace object of the context. This is as if you call
* `caller_locations` at the point of debugger.
*
* @param[in] dc A debug context.
* @return An array of `Thread::Backtrace::Location` which represents the
* current point of execution at `dc`.
*/
VALUE rb_debug_inspector_backtrace_locations(const rb_debug_inspector_t *dc);
/**
* Queries the current receiver of the passed context's upper frame.
*
@ -250,15 +261,27 @@ VALUE rb_debug_inspector_frame_binding_get(const rb_debug_inspector_t *dc, long
VALUE rb_debug_inspector_frame_iseq_get(const rb_debug_inspector_t *dc, long index);
/**
* Queries the backtrace object of the context. This is as if you call
* `caller_locations` at the point of debugger.
* Queries the depth of the passed context's upper frame.
*
* @param[in] dc A debug context.
* @return An array of `Thread::Backtrace::Location` which represents the
* current point of execution at `dc`.
* Note that the depth is not same as the frame index because debug_inspector
* skips some special frames but the depth counts all frames.
*
* @param[in] dc A debug context.
* @param[in] index Index of the frame from top to bottom.
* @exception rb_eArgError `index` out of range.
* @retval The depth at `index`-th frame in Integer.
*/
VALUE rb_debug_inspector_backtrace_locations(const rb_debug_inspector_t *dc);
VALUE rb_debug_inspector_frame_depth(const rb_debug_inspector_t *dc, long index);
// A macro to recognize `rb_debug_inspector_frame_depth()` is available or not
#define RB_DEBUG_INSPECTOR_FRAME_DEPTH(dc, index) rb_debug_inspector_frame_depth(dc, index)
/**
* Return current frmae depth.
*
* @retval The depth of the current frame in Integer.
*/
VALUE rb_debug_inspector_current_depth(void);
/** @} */

Просмотреть файл

@ -1374,11 +1374,13 @@ enum {
CALLER_BINDING_CLASS,
CALLER_BINDING_BINDING,
CALLER_BINDING_ISEQ,
CALLER_BINDING_CFP
CALLER_BINDING_CFP,
CALLER_BINDING_DEPTH,
};
struct collect_caller_bindings_data {
VALUE ary;
const rb_execution_context_t *ec;
};
static void
@ -1404,17 +1406,25 @@ get_klass(const rb_control_frame_t *cfp)
}
}
static int
frame_depth(const rb_execution_context_t *ec, const rb_control_frame_t *cfp)
{
VM_ASSERT(RUBY_VM_END_CONTROL_FRAME(ec) >= cfp);
return (int)(RUBY_VM_END_CONTROL_FRAME(ec) - cfp);
}
static void
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(5);
VALUE frame = rb_ary_new2(6);
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_CFP, GC_GUARDED_PTR(cfp));
rb_ary_store(frame, CALLER_BINDING_DEPTH, INT2FIX(frame_depth(data->ec, cfp)));
rb_ary_push(data->ary, frame);
}
@ -1423,13 +1433,14 @@ static void
collect_caller_bindings_cfunc(void *arg, const rb_control_frame_t *cfp, ID mid)
{
struct collect_caller_bindings_data *data = (struct collect_caller_bindings_data *)arg;
VALUE frame = rb_ary_new2(5);
VALUE frame = rb_ary_new2(6);
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, 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);
}
@ -1437,11 +1448,11 @@ collect_caller_bindings_cfunc(void *arg, const rb_control_frame_t *cfp, ID mid)
static VALUE
collect_caller_bindings(const rb_execution_context_t *ec)
{
struct collect_caller_bindings_data data;
VALUE result;
int i;
data.ary = rb_ary_new();
VALUE result;
struct collect_caller_bindings_data data = {
rb_ary_new(), ec
};
backtrace_each(ec,
collect_caller_bindings_init,
@ -1541,6 +1552,20 @@ rb_debug_inspector_frame_iseq_get(const rb_debug_inspector_t *dc, long index)
return RTEST(iseq) ? rb_iseqw_new((rb_iseq_t *)iseq) : Qnil;
}
VALUE
rb_debug_inspector_frame_depth(const rb_debug_inspector_t *dc, long index)
{
VALUE frame = frame_get(dc, index);
return rb_ary_entry(frame, CALLER_BINDING_DEPTH);
}
VALUE
rb_debug_inspector_current_depth(void)
{
rb_execution_context_t *ec = GET_EC();
return INT2FIX(frame_depth(ec, ec->cfp));
}
VALUE
rb_debug_inspector_backtrace_locations(const rb_debug_inspector_t *dc)
{