support `rescue` event for TracePoint

fix [Feature #19572]
This commit is contained in:
Koichi Sasada 2023-08-01 17:25:20 +09:00
Родитель f11ac06337
Коммит d68c01fd31
8 изменённых файлов: 86 добавлений и 9 удалений

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

@ -7748,7 +7748,18 @@ compile_resbody(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
}
ADD_INSNL(ret, line_node, jump, label_miss);
ADD_LABEL(ret, label_hit);
CHECK(COMPILE(ret, "resbody body", resq->nd_body));
ADD_TRACE(ret, RUBY_EVENT_RESCUE);
if (nd_type(resq->nd_body) == NODE_BEGIN && resq->nd_body->nd_body == NULL) {
// empty body
int lineno = nd_line(resq->nd_body);
NODE dummy_line_node = generate_dummy_line_node(lineno, -1);
ADD_INSN(ret, &dummy_line_node, putnil);
}
else {
CHECK(COMPILE(ret, "resbody body", resq->nd_body));
}
if (ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization) {
ADD_INSN(ret, line_node, nop);
}
@ -10406,6 +10417,7 @@ event_name_to_flag(VALUE sym)
CHECK_EVENT(RUBY_EVENT_RETURN);
CHECK_EVENT(RUBY_EVENT_B_CALL);
CHECK_EVENT(RUBY_EVENT_B_RETURN);
CHECK_EVENT(RUBY_EVENT_RESCUE);
#undef CHECK_EVENT
return RUBY_EVENT_NONE;
}

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

@ -58,6 +58,7 @@
#define RUBY_EVENT_THREAD_END 0x0800 /**< Encountered an end of a thread. */
#define RUBY_EVENT_FIBER_SWITCH 0x1000 /**< Encountered a `Fiber#yield`. */
#define RUBY_EVENT_SCRIPT_COMPILED 0x2000 /**< Encountered an `eval`. */
#define RUBY_EVENT_RESCUE 0x4000 /**< Encountered a `rescue` statement. */
#define RUBY_EVENT_TRACEPOINT_ALL 0xffff /**< Bitmask of extended events. */
/** @} */

5
iseq.c
Просмотреть файл

@ -2219,7 +2219,7 @@ rb_iseq_disasm_insn(VALUE ret, const VALUE *code, size_t pos,
{
rb_event_flag_t events = rb_iseq_event_flags(iseq, pos);
if (events) {
str = rb_str_catf(str, "[%s%s%s%s%s%s%s%s%s%s%s]",
str = rb_str_catf(str, "[%s%s%s%s%s%s%s%s%s%s%s%s]",
events & RUBY_EVENT_LINE ? "Li" : "",
events & RUBY_EVENT_CLASS ? "Cl" : "",
events & RUBY_EVENT_END ? "En" : "",
@ -2229,6 +2229,7 @@ rb_iseq_disasm_insn(VALUE ret, const VALUE *code, size_t pos,
events & RUBY_EVENT_C_RETURN ? "Cr" : "",
events & RUBY_EVENT_B_CALL ? "Bc" : "",
events & RUBY_EVENT_B_RETURN ? "Br" : "",
events & RUBY_EVENT_RESCUE ? "Rs" : "",
events & RUBY_EVENT_COVERAGE_LINE ? "Cli" : "",
events & RUBY_EVENT_COVERAGE_BRANCH ? "Cbr" : "");
}
@ -2573,6 +2574,7 @@ push_event_info(const rb_iseq_t *iseq, rb_event_flag_t events, int line, VALUE a
C(RUBY_EVENT_END, "end", INT2FIX(line));
C(RUBY_EVENT_RETURN, "return", INT2FIX(line));
C(RUBY_EVENT_B_RETURN, "b_return", INT2FIX(line));
C(RUBY_EVENT_RESCUE, "rescue", INT2FIX(line));
#undef C
}
@ -3090,6 +3092,7 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
CHECK_EVENT(RUBY_EVENT_RETURN);
CHECK_EVENT(RUBY_EVENT_B_CALL);
CHECK_EVENT(RUBY_EVENT_B_RETURN);
CHECK_EVENT(RUBY_EVENT_RESCUE);
#undef CHECK_EVENT
prev_insn_info = info;
}

7
iseq.h
Просмотреть файл

@ -83,9 +83,10 @@ ISEQ_ORIGINAL_ISEQ_ALLOC(const rb_iseq_t *iseq, long size)
RUBY_EVENT_CALL | \
RUBY_EVENT_RETURN| \
RUBY_EVENT_C_CALL| \
RUBY_EVENT_C_RETURN| \
RUBY_EVENT_B_CALL| \
RUBY_EVENT_B_RETURN| \
RUBY_EVENT_C_RETURN | \
RUBY_EVENT_B_CALL | \
RUBY_EVENT_B_RETURN | \
RUBY_EVENT_RESCUE | \
RUBY_EVENT_COVERAGE_LINE| \
RUBY_EVENT_COVERAGE_BRANCH)

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

@ -497,7 +497,8 @@ class TestISeq < Test::Unit::TestCase
[7, :line],
[9, :return]]],
[["ensure in foo@2", [[7, :line]]]],
[["rescue in foo@4", [[5, :line]]]]]],
[["rescue in foo@4", [[5, :line],
[5, :rescue]]]]]],
[["<class:D>@17", [[17, :class],
[18, :end]]]]], collect_iseq.call(sample_iseq)
end

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

@ -2749,4 +2749,51 @@ CODE
raise_line = lines.shift
assert_equal [], lines
end
def helper_can_rescue
begin
raise __LINE__.to_s
rescue SyntaxError
:ng
rescue
:ok
end
end
def helper_can_rescue_empty_body
begin
raise __LINE__.to_s
rescue SyntaxError
:ng
rescue
end
end
def test_tp_rescue_event
lines = []
TracePoint.new(:rescue){|tp|
next unless target_thread?
lines << [tp.lineno, tp.raised_exception]
}.enable{
helper_can_rescue
}
line, err, = lines.pop
assert_equal [], lines
assert err.kind_of?(RuntimeError)
assert_equal err.message.to_i + 4, line
lines = []
TracePoint.new(:rescue){|tp|
next unless target_thread?
lines << [tp.lineno, tp.raised_exception]
}.enable{
helper_can_rescue_empty_body
}
line, err, = lines.pop
assert_equal [], lines
assert err.kind_of?(RuntimeError)
assert_equal err.message.to_i + 3, line
end
end

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

@ -6483,6 +6483,14 @@ rb_vm_opt_cfunc_p(CALL_CACHE cc, int insn)
} \
} while (0)
static VALUE
rescue_errinfo(rb_execution_context_t *ec, rb_control_frame_t *cfp)
{
VM_ASSERT(VM_FRAME_RUBYFRAME_P(cfp));
VM_ASSERT(ISEQ_BODY(cfp->iseq)->type == ISEQ_TYPE_RESCUE);
return cfp->ep[VM_ENV_INDEX_LAST_LVAR];
}
static void
vm_trace(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp)
{
@ -6559,6 +6567,7 @@ vm_trace(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp)
vm_trace_hook(ec, reg_cfp, pc, RUBY_EVENT_CALL, RUBY_EVENT_CALL, global_hooks, bmethod_local_hooks_ptr, Qundef);
}
VM_TRACE_HOOK(RUBY_EVENT_CLASS | RUBY_EVENT_CALL | RUBY_EVENT_B_CALL, Qundef);
VM_TRACE_HOOK(RUBY_EVENT_RESCUE, rescue_errinfo(ec, reg_cfp));
VM_TRACE_HOOK(RUBY_EVENT_LINE, Qundef);
VM_TRACE_HOOK(RUBY_EVENT_COVERAGE_LINE, Qundef);
VM_TRACE_HOOK(RUBY_EVENT_COVERAGE_BRANCH, Qundef);

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

@ -681,6 +681,7 @@ get_event_id(rb_event_flag_t event)
C(thread_end, THREAD_END);
C(fiber_switch, FIBER_SWITCH);
C(script_compiled, SCRIPT_COMPILED);
C(rescue, RESCUE);
#undef C
default:
return 0;
@ -697,8 +698,8 @@ get_path_and_lineno(const rb_execution_context_t *ec, const rb_control_frame_t *
*pathp = rb_iseq_path(iseq);
if (event & (RUBY_EVENT_CLASS |
RUBY_EVENT_CALL |
RUBY_EVENT_B_CALL)) {
RUBY_EVENT_CALL |
RUBY_EVENT_B_CALL)) {
*linep = FIX2INT(rb_iseq_first_lineno(iseq));
}
else {
@ -823,6 +824,7 @@ symbol2event_flag(VALUE v)
C(thread_end, THREAD_END);
C(fiber_switch, FIBER_SWITCH);
C(script_compiled, SCRIPT_COMPILED);
C(rescue, RESCUE);
/* joke */
C(a_call, A_CALL);
@ -943,6 +945,7 @@ rb_tracearg_parameters(rb_trace_arg_t *trace_arg)
case RUBY_EVENT_CLASS:
case RUBY_EVENT_END:
case RUBY_EVENT_SCRIPT_COMPILED:
case RUBY_EVENT_RESCUE:
rb_raise(rb_eRuntimeError, "not supported by this event");
break;
}
@ -1013,7 +1016,7 @@ rb_tracearg_return_value(rb_trace_arg_t *trace_arg)
VALUE
rb_tracearg_raised_exception(rb_trace_arg_t *trace_arg)
{
if (trace_arg->event & (RUBY_EVENT_RAISE)) {
if (trace_arg->event & (RUBY_EVENT_RAISE | RUBY_EVENT_RESCUE)) {
/* ok */
}
else {