зеркало из https://github.com/github/ruby.git
remove `trace` instruction. [Feature #14104]
* tool/instruction.rb: create `trace_` prefix instructions. * compile.c (ADD_TRACE): do not add `trace` instructions but add TRACE link elements. TRACE elements will be unified with a next instruction as instruction information. * vm_trace.c (update_global_event_hook): modify all ISeqs when hooks are enabled. * iseq.c (rb_iseq_trace_set): added to toggle `trace_` instructions. * vm_insnhelper.c (vm_trace): added. This function is a body of `trace_` prefix instructions. * vm_insnhelper.h (JUMP): save PC to a control frame. * insns.def (trace): removed. * vm_exec.h (INSN_ENTRY_SIG): add debug output (disabled). git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@60763 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
Родитель
fe3decb201
Коммит
665ba24b44
3
NEWS
3
NEWS
|
@ -264,6 +264,9 @@ with all sufficient information, see the ChangeLog file or Redmine
|
|||
* Performance of block passing using block parameters is improved by
|
||||
lazy Proc allocation [Feature #14045]
|
||||
|
||||
* Dynamic instrumentation for TracePoint hooks instead of using "trace"
|
||||
instruction to avoid overhead [Feature #14104]
|
||||
|
||||
=== Miscellaneous changes
|
||||
|
||||
* Print backtrace and error message in reverse order if STDERR is unchanged and a tty.
|
||||
|
|
|
@ -2904,6 +2904,7 @@ vm_trace.$(OBJEXT): {$(VPATH)}id.h
|
|||
vm_trace.$(OBJEXT): {$(VPATH)}intern.h
|
||||
vm_trace.$(OBJEXT): {$(VPATH)}internal.h
|
||||
vm_trace.$(OBJEXT): {$(VPATH)}io.h
|
||||
vm_trace.$(OBJEXT): {$(VPATH)}iseq.h
|
||||
vm_trace.$(OBJEXT): {$(VPATH)}method.h
|
||||
vm_trace.$(OBJEXT): {$(VPATH)}missing.h
|
||||
vm_trace.$(OBJEXT): {$(VPATH)}node.h
|
||||
|
|
200
compile.c
200
compile.c
|
@ -36,10 +36,10 @@
|
|||
|
||||
typedef struct iseq_link_element {
|
||||
enum {
|
||||
ISEQ_ELEMENT_NONE,
|
||||
ISEQ_ELEMENT_LABEL,
|
||||
ISEQ_ELEMENT_INSN,
|
||||
ISEQ_ELEMENT_ADJUST
|
||||
ISEQ_ELEMENT_ADJUST,
|
||||
ISEQ_ELEMENT_TRACE
|
||||
} type;
|
||||
struct iseq_link_element *next;
|
||||
struct iseq_link_element *prev;
|
||||
|
@ -77,6 +77,7 @@ typedef struct iseq_insn_data {
|
|||
VALUE *operands;
|
||||
struct {
|
||||
int line_no;
|
||||
rb_event_flag_t events;
|
||||
} insn_info;
|
||||
} INSN;
|
||||
|
||||
|
@ -86,6 +87,11 @@ typedef struct iseq_adjust_data {
|
|||
int line_no;
|
||||
} ADJUST;
|
||||
|
||||
typedef struct iseq_trace_data {
|
||||
LINK_ELEMENT link;
|
||||
rb_event_flag_t event;
|
||||
} TRACE;
|
||||
|
||||
struct ensure_range {
|
||||
LABEL *begin;
|
||||
LABEL *end;
|
||||
|
@ -242,6 +248,9 @@ struct iseq_compile_data_ensure_node_stack {
|
|||
#define ADD_SEND_R(seq, line, id, argc, block, flag, keywords) \
|
||||
ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_send(iseq, (line), (id), (VALUE)(argc), (block), (VALUE)(flag), (keywords)))
|
||||
|
||||
#define ADD_TRACE(seq, event) \
|
||||
ADD_ELEM((seq), (LINK_ELEMENT *)new_trace_body(iseq, (event)))
|
||||
|
||||
#define ADD_TRACE_LINE_COVERAGE(seq, line) \
|
||||
do { \
|
||||
if (ISEQ_COVERAGE(iseq) && \
|
||||
|
@ -295,13 +304,6 @@ struct iseq_compile_data_ensure_node_stack {
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
#define ADD_TRACE(seq, line, event) \
|
||||
do { \
|
||||
if (ISEQ_COMPILE_DATA(iseq)->option->trace_instruction) { \
|
||||
ADD_INSN1((seq), (line), trace, INT2FIX(event)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static void iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, int level);
|
||||
static void iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, int level);
|
||||
|
||||
|
@ -484,6 +486,8 @@ static int calc_sp_depth(int depth, INSN *iobj);
|
|||
static INSN *new_insn_body(rb_iseq_t *iseq, int line_no, enum ruby_vminsn_type insn_id, int argc, ...);
|
||||
static LABEL *new_label_body(rb_iseq_t *iseq, long line);
|
||||
static ADJUST *new_adjust_body(rb_iseq_t *iseq, LABEL *label, int line);
|
||||
static TRACE *new_trace_body(rb_iseq_t *iseq, rb_event_flag_t event);
|
||||
|
||||
|
||||
static int iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *anchor, const NODE *n, int);
|
||||
static int iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
|
||||
|
@ -639,11 +643,12 @@ rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node)
|
|||
start->rescued = LABEL_RESCUE_BEG;
|
||||
end->rescued = LABEL_RESCUE_END;
|
||||
|
||||
ADD_TRACE(ret, FIX2INT(iseq->body->location.first_lineno), RUBY_EVENT_B_CALL);
|
||||
ADD_TRACE(ret, RUBY_EVENT_B_CALL);
|
||||
ADD_INSN (ret, FIX2INT(iseq->body->location.first_lineno), nop);
|
||||
ADD_LABEL(ret, start);
|
||||
CHECK(COMPILE(ret, "block body", node->nd_body));
|
||||
ADD_LABEL(ret, end);
|
||||
ADD_TRACE(ret, nd_line(node), RUBY_EVENT_B_RETURN);
|
||||
ADD_TRACE(ret, RUBY_EVENT_B_RETURN);
|
||||
|
||||
/* wide range catch handler must put at last */
|
||||
ADD_CATCH_ENTRY(CATCH_TYPE_REDO, start, end, NULL, start);
|
||||
|
@ -652,17 +657,17 @@ rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node)
|
|||
}
|
||||
case ISEQ_TYPE_CLASS:
|
||||
{
|
||||
ADD_TRACE(ret, FIX2INT(iseq->body->location.first_lineno), RUBY_EVENT_CLASS);
|
||||
ADD_TRACE(ret, RUBY_EVENT_CLASS);
|
||||
CHECK(COMPILE(ret, "scoped node", node->nd_body));
|
||||
ADD_TRACE(ret, nd_line(node), RUBY_EVENT_END);
|
||||
ADD_TRACE(ret, RUBY_EVENT_END);
|
||||
break;
|
||||
}
|
||||
case ISEQ_TYPE_METHOD:
|
||||
{
|
||||
ADD_TRACE(ret, FIX2INT(iseq->body->location.first_lineno), RUBY_EVENT_CALL);
|
||||
ADD_TRACE(ret, RUBY_EVENT_CALL);
|
||||
ADD_TRACE_METHOD_COVERAGE(ret, FIX2INT(iseq->body->location.first_lineno), rb_intern_str(iseq->body->location.label));
|
||||
CHECK(COMPILE(ret, "scoped node", node->nd_body));
|
||||
ADD_TRACE(ret, nd_line(node), RUBY_EVENT_RETURN);
|
||||
ADD_TRACE(ret, RUBY_EVENT_RETURN);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
@ -897,6 +902,12 @@ compile_data_alloc_adjust(rb_iseq_t *iseq)
|
|||
return (ADJUST *)compile_data_alloc(iseq, sizeof(ADJUST));
|
||||
}
|
||||
|
||||
static TRACE *
|
||||
compile_data_alloc_trace(rb_iseq_t *iseq)
|
||||
{
|
||||
return (TRACE *)compile_data_alloc(iseq, sizeof(TRACE));
|
||||
}
|
||||
|
||||
/*
|
||||
* elem1, elemX => elem1, elem2, elemX
|
||||
*/
|
||||
|
@ -951,7 +962,7 @@ REMOVE_ELEM(LINK_ELEMENT *elem)
|
|||
}
|
||||
|
||||
static LINK_ELEMENT *
|
||||
FIRST_ELEMENT(LINK_ANCHOR *const anchor)
|
||||
FIRST_ELEMENT(const LINK_ANCHOR *const anchor)
|
||||
{
|
||||
return anchor->anchor.next;
|
||||
}
|
||||
|
@ -975,26 +986,42 @@ POP_ELEMENT(ISEQ_ARG_DECLARE LINK_ANCHOR *const anchor)
|
|||
#define POP_ELEMENT(anchor) POP_ELEMENT(iseq, (anchor))
|
||||
#endif
|
||||
|
||||
static int
|
||||
LIST_SIZE_ZERO(LINK_ANCHOR *const anchor)
|
||||
static LINK_ELEMENT *
|
||||
ELEM_FIRST_INSN(LINK_ELEMENT *elem)
|
||||
{
|
||||
if (anchor->anchor.next == 0) {
|
||||
return 1;
|
||||
while (elem) {
|
||||
switch (elem->type) {
|
||||
case ISEQ_ELEMENT_INSN:
|
||||
case ISEQ_ELEMENT_ADJUST:
|
||||
return elem;
|
||||
default:
|
||||
elem = elem->next;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
LIST_INSN_SIZE_ONE(const LINK_ANCHOR *const anchor)
|
||||
{
|
||||
LINK_ELEMENT *first_insn = ELEM_FIRST_INSN(FIRST_ELEMENT(anchor));
|
||||
if (first_insn != NULL &&
|
||||
ELEM_FIRST_INSN(first_insn->next) == NULL) {
|
||||
return TRUE;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
LIST_SIZE_ONE(const LINK_ANCHOR *const anchor)
|
||||
LIST_INSN_SIZE_ZERO(const LINK_ANCHOR *const anchor)
|
||||
{
|
||||
if (anchor->anchor.next != NULL &&
|
||||
anchor->anchor.next->next == NULL) {
|
||||
return 1;
|
||||
if (ELEM_FIRST_INSN(FIRST_ELEMENT(anchor)) == NULL) {
|
||||
return TRUE;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1073,6 +1100,18 @@ debug_list(ISEQ_ARG_DECLARE LINK_ANCHOR *const anchor)
|
|||
#define debug_list(anc) ((void)0)
|
||||
#endif
|
||||
|
||||
static TRACE *
|
||||
new_trace_body(rb_iseq_t *iseq, rb_event_flag_t event)
|
||||
{
|
||||
TRACE *trace = compile_data_alloc_trace(iseq);
|
||||
|
||||
trace->link.type = ISEQ_ELEMENT_TRACE;
|
||||
trace->link.next = NULL;
|
||||
trace->event = event;
|
||||
|
||||
return trace;
|
||||
}
|
||||
|
||||
static LABEL *
|
||||
new_label_body(rb_iseq_t *iseq, long line)
|
||||
{
|
||||
|
@ -1108,12 +1147,14 @@ new_insn_core(rb_iseq_t *iseq, int line_no,
|
|||
int insn_id, int argc, VALUE *argv)
|
||||
{
|
||||
INSN *iobj = compile_data_alloc_insn(iseq);
|
||||
|
||||
/* printf("insn_id: %d, line: %d\n", insn_id, line_no); */
|
||||
|
||||
iobj->link.type = ISEQ_ELEMENT_INSN;
|
||||
iobj->link.next = 0;
|
||||
iobj->insn_id = insn_id;
|
||||
iobj->insn_info.line_no = line_no;
|
||||
iobj->insn_info.events = 0;
|
||||
iobj->operands = argv;
|
||||
iobj->operand_size = argc;
|
||||
iobj->sc_state = 0;
|
||||
|
@ -1764,7 +1805,7 @@ fix_sp_depth(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case ISEQ_ELEMENT_NONE:
|
||||
case ISEQ_ELEMENT_TRACE:
|
||||
{
|
||||
/* ignore */
|
||||
break;
|
||||
|
@ -1798,11 +1839,12 @@ add_insn_info(struct iseq_insn_info_entry *insns_info, int insns_info_index, int
|
|||
{
|
||||
if (list->type == ISEQ_ELEMENT_INSN) {
|
||||
INSN *iobj = (INSN *)list;
|
||||
if (iobj->insn_info.line_no != 0 &&
|
||||
(insns_info_index == 0 ||
|
||||
insns_info[insns_info_index-1].line_no != iobj->insn_info.line_no)) {
|
||||
if (insns_info_index == 0 ||
|
||||
insns_info[insns_info_index-1].line_no != iobj->insn_info.line_no ||
|
||||
insns_info[insns_info_index-1].events != iobj->insn_info.events) {
|
||||
insns_info[insns_info_index].position = code_index;
|
||||
insns_info[insns_info_index].line_no = iobj->insn_info.line_no;
|
||||
insns_info[insns_info_index].events = iobj->insn_info.events;
|
||||
return TRUE;
|
||||
}
|
||||
else {
|
||||
|
@ -1815,6 +1857,7 @@ add_insn_info(struct iseq_insn_info_entry *insns_info, int insns_info_index, int
|
|||
insns_info[insns_info_index-1].line_no != adjust->line_no) {
|
||||
insns_info[insns_info_index].position = code_index;
|
||||
insns_info[insns_info_index].line_no = adjust->line_no;
|
||||
insns_info[insns_info_index].events = 0;
|
||||
return TRUE;
|
||||
}
|
||||
else {
|
||||
|
@ -1835,6 +1878,7 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
|
|||
struct iseq_insn_info_entry *insns_info;
|
||||
LINK_ELEMENT *list;
|
||||
VALUE *generated_iseq;
|
||||
rb_event_flag_t events = 0;
|
||||
|
||||
int insn_num, code_index, insns_info_index, sp = 0;
|
||||
int stack_max = fix_sp_depth(iseq, anchor);
|
||||
|
@ -1853,6 +1897,8 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
|
|||
sp = calc_sp_depth(sp, iobj);
|
||||
code_index += insn_data_length(iobj);
|
||||
insn_num++;
|
||||
iobj->insn_info.events |= events;
|
||||
events = 0;
|
||||
break;
|
||||
}
|
||||
case ISEQ_ELEMENT_LABEL:
|
||||
|
@ -1862,9 +1908,10 @@ iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
|
|||
sp = lobj->sp;
|
||||
break;
|
||||
}
|
||||
case ISEQ_ELEMENT_NONE:
|
||||
case ISEQ_ELEMENT_TRACE:
|
||||
{
|
||||
/* ignore */
|
||||
TRACE *trace = (TRACE *)list;
|
||||
events |= trace->event;
|
||||
break;
|
||||
}
|
||||
case ISEQ_ELEMENT_ADJUST:
|
||||
|
@ -2177,14 +2224,31 @@ get_destination_insn(INSN *iobj)
|
|||
{
|
||||
LABEL *lobj = (LABEL *)OPERAND_AT(iobj, 0);
|
||||
LINK_ELEMENT *list;
|
||||
rb_event_flag_t events = 0;
|
||||
|
||||
list = lobj->link.next;
|
||||
while (list) {
|
||||
if (IS_INSN(list) || IS_ADJUST(list)) {
|
||||
switch (list->type) {
|
||||
case ISEQ_ELEMENT_INSN:
|
||||
case ISEQ_ELEMENT_ADJUST:
|
||||
goto found;
|
||||
case ISEQ_ELEMENT_LABEL:
|
||||
/* ignore */
|
||||
break;
|
||||
case ISEQ_ELEMENT_TRACE:
|
||||
{
|
||||
TRACE *trace = (TRACE *)list;
|
||||
events |= trace->event;
|
||||
}
|
||||
break;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
found:
|
||||
if (list && IS_INSN(list)) {
|
||||
INSN *iobj = (INSN *)list;
|
||||
iobj->insn_info.events |= events;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
@ -2369,6 +2433,7 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
|
|||
unref_destination(iobj, 0);
|
||||
iobj->insn_id = BIN(leave);
|
||||
iobj->operand_size = 0;
|
||||
iobj->insn_info = diobj->insn_info;
|
||||
goto again;
|
||||
}
|
||||
else if ((piobj = (INSN *)get_prev_insn(iobj)) != 0 &&
|
||||
|
@ -2710,7 +2775,6 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
|
|||
}
|
||||
switch (INSN_OF(next)) {
|
||||
case BIN(nop):
|
||||
/*case BIN(trace):*/
|
||||
next = next->next;
|
||||
break;
|
||||
case BIN(jump):
|
||||
|
@ -2780,16 +2844,6 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal
|
|||
}
|
||||
}
|
||||
|
||||
#define IS_TRACE_LINE(insn) \
|
||||
(IS_INSN_ID(insn, trace) && \
|
||||
OPERAND_AT(insn, 0) == INT2FIX(RUBY_EVENT_LINE))
|
||||
if (IS_TRACE_LINE(iobj) && iobj->link.prev && IS_INSN(iobj->link.prev)) {
|
||||
INSN *piobj = (INSN *)iobj->link.prev;
|
||||
if (IS_TRACE_LINE(piobj)) {
|
||||
REMOVE_ELEM(iobj->link.prev);
|
||||
}
|
||||
}
|
||||
|
||||
return COMPILE_OK;
|
||||
}
|
||||
|
||||
|
@ -4360,12 +4414,12 @@ setup_args(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
|
|||
}
|
||||
}
|
||||
|
||||
if (!LIST_SIZE_ZERO(args_splat)) {
|
||||
if (!LIST_INSN_SIZE_ZERO(args_splat)) {
|
||||
ADD_SEQ(args, args_splat);
|
||||
}
|
||||
|
||||
if (*flag & VM_CALL_ARGS_BLOCKARG) {
|
||||
if (LIST_SIZE_ONE(arg_block)) {
|
||||
if (LIST_INSN_SIZE_ONE(arg_block)) {
|
||||
LINK_ELEMENT *elem = FIRST_ELEMENT(arg_block);
|
||||
if (elem->type == ISEQ_ELEMENT_INSN) {
|
||||
INSN *iobj = (INSN *)elem;
|
||||
|
@ -5208,7 +5262,7 @@ compile_ensure(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
|
|||
ADD_LABEL(ret, lstart);
|
||||
CHECK(COMPILE_(ret, "ensure head", node->nd_head, (popped | last_leave)));
|
||||
ADD_LABEL(ret, lend);
|
||||
if (ensr->anchor.next == NULL) {
|
||||
if (LIST_INSN_SIZE_ZERO(ensr)) {
|
||||
ADD_INSN(ret, line, nop);
|
||||
}
|
||||
else {
|
||||
|
@ -5272,7 +5326,7 @@ compile_return(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
|
|||
|
||||
if (type == ISEQ_TYPE_METHOD) {
|
||||
add_ensure_iseq(ret, iseq, 1);
|
||||
ADD_TRACE(ret, line, RUBY_EVENT_RETURN);
|
||||
ADD_TRACE(ret, RUBY_EVENT_RETURN);
|
||||
ADD_INSN(ret, line, leave);
|
||||
ADD_ADJUST_RESTORE(ret, splabel);
|
||||
|
||||
|
@ -5314,7 +5368,6 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *node, int poppe
|
|||
static int
|
||||
iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int popped)
|
||||
{
|
||||
LINK_ELEMENT *saved_last_element = 0;
|
||||
const int line = (int)nd_line(node);
|
||||
const enum node_type type = nd_type(node);
|
||||
|
||||
|
@ -5325,8 +5378,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, in
|
|||
if (node->flags & NODE_FL_NEWLINE) {
|
||||
ISEQ_COMPILE_DATA(iseq)->last_line = line;
|
||||
ADD_TRACE_LINE_COVERAGE(ret, line);
|
||||
ADD_TRACE(ret, line, RUBY_EVENT_LINE);
|
||||
saved_last_element = ret->last;
|
||||
ADD_TRACE(ret, RUBY_EVENT_LINE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6653,7 +6705,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, in
|
|||
INIT_ANCHOR(pref);
|
||||
INIT_ANCHOR(body);
|
||||
CHECK(compile_const_prefix(iseq, node, pref, body));
|
||||
if (LIST_SIZE_ZERO(pref)) {
|
||||
if (LIST_INSN_SIZE_ZERO(pref)) {
|
||||
if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
|
||||
ADD_INSN2(ret, line, getinlinecache, lend, INT2FIX(ic_index));
|
||||
}
|
||||
|
@ -6976,20 +7028,6 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, in
|
|||
return COMPILE_NG;
|
||||
}
|
||||
|
||||
/* check & remove redundant trace(line) */
|
||||
if (saved_last_element &&
|
||||
ret->last == saved_last_element &&
|
||||
((INSN *)saved_last_element)->insn_id == BIN(trace)) {
|
||||
POP_ELEMENT(ret);
|
||||
/* remove trace(coverage) */
|
||||
if (IS_INSN_ID(ret->last, trace2) &&
|
||||
(FIX2LONG(OPERAND_AT(ret->last, 0)) & RUBY_EVENT_COVERAGE) &&
|
||||
(FIX2LONG(OPERAND_AT(ret->last, 1)) == COVERAGE_INDEX_LINES)) {
|
||||
POP_ELEMENT(ret);
|
||||
RARRAY_ASET(ISEQ_LINE_COVERAGE(iseq), line - 1, Qnil);
|
||||
}
|
||||
}
|
||||
|
||||
debug_node_end();
|
||||
return COMPILE_OK;
|
||||
}
|
||||
|
@ -7154,9 +7192,10 @@ dump_disasm_list_with_cursor(const LINK_ELEMENT *link, const LINK_ELEMENT *curr,
|
|||
printf(LABEL_FORMAT"%s\n", lobj->label_no, dest == lobj ? " <---" : "");
|
||||
break;
|
||||
}
|
||||
case ISEQ_ELEMENT_NONE:
|
||||
case ISEQ_ELEMENT_TRACE:
|
||||
{
|
||||
printf("[none]\n");
|
||||
TRACE *trace = (TRACE *)link;
|
||||
printf("trace: %0x\n", trace->event);
|
||||
break;
|
||||
}
|
||||
case ISEQ_ELEMENT_ADJUST:
|
||||
|
@ -7355,6 +7394,21 @@ iseq_build_callinfo_from_hash(rb_iseq_t *iseq, VALUE op)
|
|||
return (VALUE)new_callinfo(iseq, mid, orig_argc, flag, kw_arg, (flag & VM_CALL_ARGS_SIMPLE) == 0);
|
||||
}
|
||||
|
||||
static rb_event_flag_t
|
||||
event_name_to_flag(VALUE sym)
|
||||
{
|
||||
#define CHECK_EVENT(ev) if (sym == ID2SYM(rb_intern(#ev))) return ev;
|
||||
CHECK_EVENT(RUBY_EVENT_LINE);
|
||||
CHECK_EVENT(RUBY_EVENT_CLASS);
|
||||
CHECK_EVENT(RUBY_EVENT_END);
|
||||
CHECK_EVENT(RUBY_EVENT_CALL);
|
||||
CHECK_EVENT(RUBY_EVENT_RETURN);
|
||||
CHECK_EVENT(RUBY_EVENT_B_CALL);
|
||||
CHECK_EVENT(RUBY_EVENT_B_RETURN);
|
||||
#undef CHECK_EVENT
|
||||
return RUBY_EVENT_NONE;
|
||||
}
|
||||
|
||||
static int
|
||||
iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor,
|
||||
VALUE body, VALUE labels_wrapper)
|
||||
|
@ -7380,8 +7434,14 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor,
|
|||
VALUE obj = ptr[i];
|
||||
|
||||
if (SYMBOL_P(obj)) {
|
||||
LABEL *label = register_label(iseq, labels_table, obj);
|
||||
ADD_LABEL(anchor, label);
|
||||
rb_event_flag_t event;
|
||||
if ((event = event_name_to_flag(obj)) != RUBY_EVENT_NONE) {
|
||||
ADD_TRACE(anchor, event);
|
||||
}
|
||||
else {
|
||||
LABEL *label = register_label(iseq, labels_table, obj);
|
||||
ADD_LABEL(anchor, label);
|
||||
}
|
||||
}
|
||||
else if (FIXNUM_P(obj)) {
|
||||
line_no = NUM2INT(obj);
|
||||
|
|
18
insns.def
18
insns.def
|
@ -776,24 +776,6 @@ checkkeyword
|
|||
ret = vm_check_keyword(kw_bits_index, keyword_index, GET_EP());
|
||||
}
|
||||
|
||||
/**
|
||||
@c setting
|
||||
@e trace
|
||||
@j trace 用の命令。
|
||||
*/
|
||||
DEFINE_INSN
|
||||
trace
|
||||
(rb_num_t nf)
|
||||
()
|
||||
()
|
||||
{
|
||||
rb_event_flag_t flag = (rb_event_flag_t)nf;
|
||||
|
||||
vm_dtrace(flag, ec);
|
||||
EXEC_EVENT_HOOK(ec, flag, GET_SELF(), 0, 0, 0 /* id and klass are resolved at callee */,
|
||||
(flag & (RUBY_EVENT_RETURN | RUBY_EVENT_B_RETURN)) ? TOPN(0) : Qundef);
|
||||
}
|
||||
|
||||
/**
|
||||
@c setting
|
||||
@e trace
|
||||
|
|
234
iseq.c
234
iseq.c
|
@ -336,8 +336,10 @@ prepare_iseq_build(rb_iseq_t *iseq,
|
|||
return Qtrue;
|
||||
}
|
||||
|
||||
static void rb_iseq_trace_set(const rb_iseq_t *iseq, rb_event_flag_t turnon_events);
|
||||
|
||||
static VALUE
|
||||
cleanup_iseq_build(rb_iseq_t *iseq)
|
||||
finish_iseq_build(rb_iseq_t *iseq)
|
||||
{
|
||||
struct iseq_compile_data *data = ISEQ_COMPILE_DATA(iseq);
|
||||
VALUE err = data->err_info;
|
||||
|
@ -350,6 +352,10 @@ cleanup_iseq_build(rb_iseq_t *iseq)
|
|||
rb_funcallv(err, rb_intern("set_backtrace"), 1, &path);
|
||||
rb_exc_raise(err);
|
||||
}
|
||||
|
||||
if (ruby_vm_event_flags) {
|
||||
rb_iseq_trace_set(iseq, ruby_vm_event_flags);
|
||||
}
|
||||
return Qtrue;
|
||||
}
|
||||
|
||||
|
@ -503,7 +509,7 @@ rb_iseq_new_with_opt(const NODE *node, VALUE name, VALUE path, VALUE realpath,
|
|||
prepare_iseq_build(iseq, name, path, realpath, first_lineno, parent, type, option);
|
||||
|
||||
rb_iseq_compile_node(iseq, node);
|
||||
cleanup_iseq_build(iseq);
|
||||
finish_iseq_build(iseq);
|
||||
|
||||
return iseq_translate(iseq);
|
||||
}
|
||||
|
@ -606,7 +612,7 @@ iseq_load(VALUE data, const rb_iseq_t *parent, VALUE opt)
|
|||
|
||||
rb_iseq_build_from_ary(iseq, misc, locals, params, exception, body);
|
||||
|
||||
cleanup_iseq_build(iseq);
|
||||
finish_iseq_build(iseq);
|
||||
|
||||
return iseqw_new(iseq);
|
||||
}
|
||||
|
@ -1265,6 +1271,18 @@ rb_iseq_line_no(const rb_iseq_t *iseq, size_t pos)
|
|||
}
|
||||
}
|
||||
|
||||
rb_event_flag_t
|
||||
rb_iseq_event_flags(const rb_iseq_t *iseq, size_t pos)
|
||||
{
|
||||
const struct iseq_insn_info_entry *entry = get_insn_info(iseq, pos);
|
||||
if (entry) {
|
||||
return entry->events;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static VALUE
|
||||
id_to_name(ID id, VALUE default_value)
|
||||
{
|
||||
|
@ -1477,6 +1495,22 @@ 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]",
|
||||
events & RUBY_EVENT_LINE ? "Li" : "",
|
||||
events & RUBY_EVENT_CLASS ? "Cl" : "",
|
||||
events & RUBY_EVENT_END ? "En" : "",
|
||||
events & RUBY_EVENT_CALL ? "Ca" : "",
|
||||
events & RUBY_EVENT_RETURN ? "Re" : "",
|
||||
events & RUBY_EVENT_C_CALL ? "Cc" : "",
|
||||
events & RUBY_EVENT_C_RETURN ? "Cr" : "",
|
||||
events & RUBY_EVENT_B_CALL ? "Bc" : "",
|
||||
events & RUBY_EVENT_B_RETURN ? "Br" : "");
|
||||
}
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
rb_str_cat2(str, "\n");
|
||||
rb_str_concat(ret, str);
|
||||
|
@ -1834,7 +1868,7 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
|
|||
long l;
|
||||
size_t ti;
|
||||
unsigned int pos;
|
||||
unsigned int line = 0;
|
||||
int last_line = 0;
|
||||
VALUE *seq, *iseq_original;
|
||||
|
||||
VALUE val = rb_ary_new();
|
||||
|
@ -2095,10 +2129,27 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
|
|||
rb_ary_push(body, (VALUE)label);
|
||||
}
|
||||
|
||||
if (ti < iseq->body->insns_info_size && iseq->body->insns_info[ti].position == pos) {
|
||||
line = iseq->body->insns_info[ti].line_no;
|
||||
rb_ary_push(body, INT2FIX(line));
|
||||
ti++;
|
||||
if (ti < iseq->body->insns_info_size) {
|
||||
const struct iseq_insn_info_entry *info = &iseq->body->insns_info[ti];
|
||||
if (info->position == pos) {
|
||||
int line = info->line_no;
|
||||
rb_event_flag_t events = info->events;
|
||||
|
||||
if (line > 0 && last_line != line) {
|
||||
rb_ary_push(body, INT2FIX(line));
|
||||
last_line = line;
|
||||
}
|
||||
#define CHECK_EVENT(ev) if (events & ev) rb_ary_push(body, ID2SYM(rb_intern(#ev)));
|
||||
CHECK_EVENT(RUBY_EVENT_LINE);
|
||||
CHECK_EVENT(RUBY_EVENT_CLASS);
|
||||
CHECK_EVENT(RUBY_EVENT_END);
|
||||
CHECK_EVENT(RUBY_EVENT_CALL);
|
||||
CHECK_EVENT(RUBY_EVENT_RETURN);
|
||||
CHECK_EVENT(RUBY_EVENT_B_CALL);
|
||||
CHECK_EVENT(RUBY_EVENT_B_RETURN);
|
||||
#undef CHECK_EVENT
|
||||
ti++;
|
||||
}
|
||||
}
|
||||
|
||||
rb_ary_push(body, ary);
|
||||
|
@ -2112,7 +2163,6 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
|
|||
rb_hash_aset(misc, ID2SYM(rb_intern("local_size")), INT2FIX(iseq->body->local_table_size));
|
||||
rb_hash_aset(misc, ID2SYM(rb_intern("stack_max")), INT2FIX(iseq->body->stack_max));
|
||||
|
||||
/* TODO: compatibility issue */
|
||||
/*
|
||||
* [:magic, :major_version, :minor_version, :format_type, :misc,
|
||||
* :name, :path, :absolute_path, :start_lineno, :type, :locals, :args,
|
||||
|
@ -2261,131 +2311,72 @@ rb_iseq_defined_string(enum defined_type type)
|
|||
return str;
|
||||
}
|
||||
|
||||
/* Experimental tracing support: trace(line) -> trace(specified_line)
|
||||
* MRI Specific.
|
||||
*/
|
||||
#define TRACE_INSN_P(insn) ((insn) >= VM_INSTRUCTION_SIZE/2)
|
||||
|
||||
int
|
||||
rb_iseqw_line_trace_each(VALUE iseqw, int (*func)(int line, rb_event_flag_t *events_ptr, void *d), void *data)
|
||||
#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
|
||||
#define INSN_CODE(insn) ((VALUE)table[insn])
|
||||
#else
|
||||
#define INSN_CODE(insn) (insn)
|
||||
#endif
|
||||
|
||||
static void
|
||||
rb_iseq_trace_set(const rb_iseq_t *iseq, rb_event_flag_t turnon_events)
|
||||
{
|
||||
int trace_num = 0;
|
||||
unsigned int pos;
|
||||
size_t insn;
|
||||
const rb_iseq_t *iseq = iseqw_check(iseqw);
|
||||
int cont = 1;
|
||||
VALUE *iseq_original;
|
||||
unsigned int i;
|
||||
VALUE *iseq_encoded = (VALUE *)iseq->body->iseq_encoded;
|
||||
#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
|
||||
VALUE *code = rb_iseq_original_iseq(iseq);
|
||||
const void * const *table = rb_vm_get_insns_address_table();
|
||||
#else
|
||||
const VALUE *code = iseq->body->iseq_encoded;
|
||||
#endif
|
||||
|
||||
iseq_original = rb_iseq_original_iseq(iseq);
|
||||
for (pos = 0; cont && pos < iseq->body->iseq_size; pos += insn_len(insn)) {
|
||||
insn = iseq_original[pos];
|
||||
for (i=0; i<iseq->body->iseq_size;) {
|
||||
int insn = (int)code[i];
|
||||
rb_event_flag_t events = rb_iseq_event_flags(iseq, i);
|
||||
|
||||
if (insn == BIN(trace)) {
|
||||
rb_event_flag_t current_events;
|
||||
|
||||
current_events = (rb_event_flag_t)iseq_original[pos+1];
|
||||
|
||||
if (current_events & RUBY_EVENT_LINE) {
|
||||
rb_event_flag_t events = current_events & RUBY_EVENT_SPECIFIED_LINE;
|
||||
trace_num++;
|
||||
|
||||
if (func) {
|
||||
int line = rb_iseq_line_no(iseq, pos);
|
||||
/* printf("line: %d\n", line); */
|
||||
cont = (*func)(line, &events, data);
|
||||
if (current_events != events) {
|
||||
VALUE *encoded = (VALUE *)iseq->body->iseq_encoded;
|
||||
iseq_original[pos+1] = encoded[pos+1] =
|
||||
(VALUE)(current_events | (events & RUBY_EVENT_SPECIFIED_LINE));
|
||||
}
|
||||
}
|
||||
if (events & turnon_events) {
|
||||
if (!TRACE_INSN_P(insn)) {
|
||||
iseq_encoded[i] = INSN_CODE(insn + VM_INSTRUCTION_SIZE/2);
|
||||
}
|
||||
else {
|
||||
/* OK */
|
||||
}
|
||||
}
|
||||
else if (TRACE_INSN_P(insn)) {
|
||||
VM_ASSERT(insn - VM_INSTRUCTION_SIZE/2 >= 0);
|
||||
iseq_encoded[i] = INSN_CODE(insn - VM_INSTRUCTION_SIZE/2);
|
||||
}
|
||||
i += insn_len(insn);
|
||||
}
|
||||
return trace_num;
|
||||
|
||||
/* clear for debugging: ISEQ_ORIGINAL_ISEQ_CLEAR(iseq); */
|
||||
}
|
||||
|
||||
static int
|
||||
collect_trace(int line, rb_event_flag_t *events_ptr, void *ptr)
|
||||
trace_set_i(void *vstart, void *vend, size_t stride, void *data)
|
||||
{
|
||||
VALUE result = (VALUE)ptr;
|
||||
rb_ary_push(result, INT2NUM(line));
|
||||
return 1;
|
||||
}
|
||||
rb_event_flag_t turnon_events = *(rb_event_flag_t *)data;
|
||||
|
||||
/*
|
||||
* <b>Experimental MRI specific feature, only available as C level api.</b>
|
||||
*
|
||||
* Returns all +specified_line+ events.
|
||||
*/
|
||||
VALUE
|
||||
rb_iseqw_line_trace_all(VALUE iseqw)
|
||||
{
|
||||
VALUE result = rb_ary_new();
|
||||
rb_iseqw_line_trace_each(iseqw, collect_trace, (void *)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
struct set_specifc_data {
|
||||
int pos;
|
||||
int set;
|
||||
int prev; /* 1: set, 2: unset, 0: not found */
|
||||
};
|
||||
|
||||
static int
|
||||
line_trace_specify(int line, rb_event_flag_t *events_ptr, void *ptr)
|
||||
{
|
||||
struct set_specifc_data *data = (struct set_specifc_data *)ptr;
|
||||
|
||||
if (data->pos == 0) {
|
||||
data->prev = *events_ptr & RUBY_EVENT_SPECIFIED_LINE ? 1 : 2;
|
||||
if (data->set) {
|
||||
*events_ptr = *events_ptr | RUBY_EVENT_SPECIFIED_LINE;
|
||||
VALUE v = (VALUE)vstart;
|
||||
for (; v != (VALUE)vend; v += stride) {
|
||||
if (rb_obj_is_iseq(v)) {
|
||||
rb_iseq_trace_set(rb_iseq_check((rb_iseq_t *)v), turnon_events);
|
||||
}
|
||||
else {
|
||||
*events_ptr = *events_ptr & ~RUBY_EVENT_SPECIFIED_LINE;
|
||||
}
|
||||
return 0; /* found */
|
||||
}
|
||||
else {
|
||||
data->pos--;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* <b>Experimental MRI specific feature, only available as C level api.</b>
|
||||
*
|
||||
* Set a +specified_line+ event at the given line position, if the +set+
|
||||
* parameter is +true+.
|
||||
*
|
||||
* This method is useful for building a debugger breakpoint at a specific line.
|
||||
*
|
||||
* A TypeError is raised if +set+ is not boolean.
|
||||
*
|
||||
* If +pos+ is a negative integer a TypeError exception is raised.
|
||||
*/
|
||||
VALUE
|
||||
rb_iseqw_line_trace_specify(VALUE iseqval, VALUE pos, VALUE set)
|
||||
void
|
||||
rb_iseq_trace_set_all(rb_event_flag_t turnon_events)
|
||||
{
|
||||
struct set_specifc_data data;
|
||||
rb_objspace_each_objects(trace_set_i, &turnon_events);
|
||||
}
|
||||
|
||||
data.prev = 0;
|
||||
data.pos = NUM2INT(pos);
|
||||
if (data.pos < 0) rb_raise(rb_eTypeError, "`pos' is negative");
|
||||
|
||||
switch (set) {
|
||||
case Qtrue: data.set = 1; break;
|
||||
case Qfalse: data.set = 0; break;
|
||||
default:
|
||||
rb_raise(rb_eTypeError, "`set' should be true/false");
|
||||
}
|
||||
|
||||
rb_iseqw_line_trace_each(iseqval, line_trace_specify, (void *)&data);
|
||||
|
||||
if (data.prev == 0) {
|
||||
rb_raise(rb_eTypeError, "`pos' is out of range.");
|
||||
}
|
||||
return data.prev == 1 ? Qtrue : Qfalse;
|
||||
void
|
||||
rb_iseq_trace_on_all(void)
|
||||
{
|
||||
rb_iseq_trace_set_all(RUBY_EVENT_TRACEPOINT_ALL);
|
||||
}
|
||||
|
||||
VALUE
|
||||
|
@ -2494,16 +2485,6 @@ Init_ISeq(void)
|
|||
rb_define_method(rb_cISeq, "base_label", iseqw_base_label, 0);
|
||||
rb_define_method(rb_cISeq, "first_lineno", iseqw_first_lineno, 0);
|
||||
|
||||
#if 0
|
||||
/* Now, it is experimental. No discussions, no tests. */
|
||||
/* They can be used from C level. Please give us feedback. */
|
||||
rb_define_method(rb_cISeq, "line_trace_all", rb_iseqw_line_trace_all, 0);
|
||||
rb_define_method(rb_cISeq, "line_trace_specify", rb_iseqw_line_trace_specify, 2);
|
||||
#else
|
||||
(void)rb_iseqw_line_trace_all;
|
||||
(void)rb_iseqw_line_trace_specify;
|
||||
#endif
|
||||
|
||||
#if 0 /* TBD */
|
||||
rb_define_private_method(rb_cISeq, "marshal_dump", iseqw_marshal_dump, 0);
|
||||
rb_define_private_method(rb_cISeq, "marshal_load", iseqw_marshal_load, 1);
|
||||
|
@ -2524,3 +2505,4 @@ Init_ISeq(void)
|
|||
rb_undef_method(CLASS_OF(rb_cISeq), "translate");
|
||||
rb_undef_method(CLASS_OF(rb_cISeq), "load_iseq");
|
||||
}
|
||||
|
||||
|
|
12
iseq.h
12
iseq.h
|
@ -71,6 +71,12 @@ ISEQ_ORIGINAL_ISEQ(const rb_iseq_t *iseq)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static inline void
|
||||
ISEQ_ORIGINAL_ISEQ_CLEAR(const rb_iseq_t *iseq)
|
||||
{
|
||||
RARRAY_ASET(ISEQ_MARK_ARY(iseq), ISEQ_MARK_ARY_ORIGINAL_ISEQ, Qnil);
|
||||
}
|
||||
|
||||
static inline VALUE *
|
||||
ISEQ_ORIGINAL_ISEQ_ALLOC(const rb_iseq_t *iseq, long size)
|
||||
{
|
||||
|
@ -110,10 +116,9 @@ VALUE rb_iseq_load(VALUE data, VALUE parent, VALUE opt);
|
|||
VALUE rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc);
|
||||
struct st_table *ruby_insn_make_insn_table(void);
|
||||
unsigned int rb_iseq_line_no(const rb_iseq_t *iseq, size_t pos);
|
||||
void rb_iseq_trace_set_all(rb_event_flag_t turnon_events);
|
||||
void rb_iseq_trace_on_all(void);
|
||||
|
||||
int rb_iseqw_line_trace_each(VALUE iseqval, int (*func)(int line, rb_event_flag_t *events_ptr, void *d), void *data);
|
||||
VALUE rb_iseqw_line_trace_all(VALUE iseqval);
|
||||
VALUE rb_iseqw_line_trace_specify(VALUE iseqval, VALUE pos, VALUE set);
|
||||
VALUE rb_iseqw_new(const rb_iseq_t *iseq);
|
||||
const rb_iseq_t *rb_iseqw_to_iseq(VALUE iseqw);
|
||||
|
||||
|
@ -145,6 +150,7 @@ struct rb_compile_option_struct {
|
|||
struct iseq_insn_info_entry {
|
||||
unsigned int position;
|
||||
int line_no;
|
||||
rb_event_flag_t events;
|
||||
};
|
||||
|
||||
struct iseq_catch_table_entry {
|
||||
|
|
|
@ -77,7 +77,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
|
|||
events.shift)
|
||||
assert_equal(["c-return", 5, :+, Integer],
|
||||
events.shift)
|
||||
assert_equal(["return", 6, :add, self.class],
|
||||
assert_equal(["return", 5, :add, self.class],
|
||||
events.shift)
|
||||
assert_equal(["line", 8, __method__, self.class],
|
||||
events.shift)
|
||||
|
@ -116,7 +116,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
|
|||
events.shift)
|
||||
assert_equal(["c-return", 5, :method_added, Module],
|
||||
events.shift)
|
||||
assert_equal(["end", 7, nil, nil],
|
||||
assert_equal(["end", 5, nil, nil],
|
||||
events.shift)
|
||||
assert_equal(["line", 8, __method__, self.class],
|
||||
events.shift)
|
||||
|
@ -130,7 +130,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
|
|||
events.shift)
|
||||
assert_equal(["call", 5, :bar, Foo],
|
||||
events.shift)
|
||||
assert_equal(["return", 6, :bar, Foo],
|
||||
assert_equal(["return", 5, :bar, Foo],
|
||||
events.shift)
|
||||
assert_equal(["line", 9, __method__, self.class],
|
||||
events.shift)
|
||||
|
@ -176,7 +176,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
|
|||
events.shift)
|
||||
assert_equal(["line", 5, :meth_return, self.class],
|
||||
events.shift)
|
||||
assert_equal(["return", 7, :meth_return, self.class],
|
||||
assert_equal(["return", 5, :meth_return, self.class],
|
||||
events.shift)
|
||||
assert_equal(["line", 10, :test_return, self.class],
|
||||
events.shift)
|
||||
|
@ -215,7 +215,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
|
|||
events.shift)
|
||||
assert_equal(["line", 6, :meth_return2, self.class],
|
||||
events.shift)
|
||||
assert_equal(["return", 7, :meth_return2, self.class],
|
||||
assert_equal(["return", 6, :meth_return2, self.class],
|
||||
events.shift)
|
||||
assert_equal(["line", 9, :test_return2, self.class],
|
||||
events.shift)
|
||||
|
@ -239,8 +239,6 @@ class TestSetTraceFunc < Test::Unit::TestCase
|
|||
EOF
|
||||
assert_equal(["c-return", 1, :set_trace_func, Kernel],
|
||||
events.shift)
|
||||
assert_equal(["line", 4, __method__, self.class],
|
||||
events.shift)
|
||||
assert_equal(["line", 5, __method__, self.class],
|
||||
events.shift)
|
||||
assert_equal(["c-call", 5, :raise, Kernel],
|
||||
|
@ -289,8 +287,8 @@ class TestSetTraceFunc < Test::Unit::TestCase
|
|||
["line", 4, __method__, self.class],
|
||||
["c-return", 4, :any?, Array],
|
||||
["line", 5, __method__, self.class],
|
||||
["c-call", 5, :set_trace_func, Kernel]].each{|e|
|
||||
assert_equal(e, events.shift)
|
||||
["c-call", 5, :set_trace_func, Kernel]].each.with_index{|e, i|
|
||||
assert_equal(e, events.shift, "mismatch on #{i}th trace")
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -345,7 +343,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
|
|||
["line", 4, nil, nil],
|
||||
["c-call", 4, :method_added, Module],
|
||||
["c-return", 4, :method_added, Module],
|
||||
["end", 7, nil, nil],
|
||||
["end", 4, nil, nil],
|
||||
["line", 8, __method__, self.class],
|
||||
["c-call", 8, :new, Class],
|
||||
["c-call", 8, :initialize, BasicObject],
|
||||
|
@ -355,7 +353,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
|
|||
["line", 5, :foo, ThreadTraceInnerClass],
|
||||
["c-call", 5, :+, Integer],
|
||||
["c-return", 5, :+, Integer],
|
||||
["return", 6, :foo, ThreadTraceInnerClass],
|
||||
["return", 5, :foo, ThreadTraceInnerClass],
|
||||
["line", 9, __method__, self.class],
|
||||
["c-call", 9, :set_trace_func, Thread]].each do |e|
|
||||
[:set, :add].each do |type|
|
||||
|
@ -489,7 +487,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
|
|||
[:line, 13, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
|
||||
[:c_call, 13, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, :nothing],
|
||||
[:c_return,13, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, nil],
|
||||
[:end, 17, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
|
||||
[:end, 13, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
|
||||
[:line, 18, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
|
||||
[:c_call, 18, "xyzzy", Class, :new, xyzzy.class, :outer, :nothing],
|
||||
[:c_call, 18, "xyzzy", BasicObject, :initialize, xyzzy, :outer, :nothing],
|
||||
|
@ -504,8 +502,8 @@ class TestSetTraceFunc < Test::Unit::TestCase
|
|||
[:line, 15, "xyzzy", xyzzy.class, :bar, xyzzy, :XYZZY_bar, :nothing],
|
||||
[:c_call, 15, "xyzzy", Kernel, :tap, xyzzy, :XYZZY_bar, :nothing],
|
||||
[:c_return,15, "xyzzy", Kernel, :tap, xyzzy, :XYZZY_bar, xyzzy],
|
||||
[:return, 16, "xyzzy", xyzzy.class, :bar, xyzzy, :XYZZY_bar, xyzzy],
|
||||
[:return, 12, "xyzzy", xyzzy.class, :foo, xyzzy, :XYZZY_foo, xyzzy],
|
||||
[:return, 15, "xyzzy", xyzzy.class, :bar, xyzzy, :XYZZY_bar, xyzzy],
|
||||
[:return, 11, "xyzzy", xyzzy.class, :foo, xyzzy, :XYZZY_foo, xyzzy],
|
||||
[:line, 20, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
|
||||
[:c_call, 20, "xyzzy", Kernel, :raise, self, :outer, :nothing],
|
||||
[:c_call, 20, "xyzzy", Exception, :exception, RuntimeError, :outer, :nothing],
|
||||
|
|
|
@ -33,6 +33,7 @@ class RubyVM
|
|||
@optimized = []
|
||||
@is_sc = false
|
||||
@sp_inc = sp_inc
|
||||
@trace = trace
|
||||
end
|
||||
|
||||
def add_sc sci
|
||||
|
@ -49,6 +50,7 @@ class RubyVM
|
|||
attr_reader :is_sc
|
||||
attr_reader :tvars
|
||||
attr_reader :sp_inc
|
||||
attr_accessor :trace
|
||||
|
||||
def set_sc
|
||||
@is_sc = true
|
||||
|
@ -116,6 +118,7 @@ class RubyVM
|
|||
load_opt_operand_def opts[:"opope.def"] || 'defs/opt_operand.def'
|
||||
load_insn_unification_def opts[:"unif.def"] || 'defs/opt_insn_unif.def'
|
||||
make_stackcaching_insns if vm_opt?('STACK_CACHING')
|
||||
make_trace_insns
|
||||
end
|
||||
|
||||
attr_reader :vpath
|
||||
|
@ -533,6 +536,21 @@ class RubyVM
|
|||
}
|
||||
end
|
||||
|
||||
def make_trace_insns
|
||||
@insns.dup.each{|insn|
|
||||
body = <<-EOS
|
||||
vm_trace(ec, GET_CFP(), GET_PC());
|
||||
goto start_of_#{insn.name};
|
||||
EOS
|
||||
|
||||
trace_insn = Instruction.new(name = "trace_#{insn.name}",
|
||||
insn.opes, insn.pops, insn.rets, insn.comm,
|
||||
body, insn.tvars, insn.sp_inc)
|
||||
trace_insn.trace = true
|
||||
add_insn trace_insn
|
||||
}
|
||||
end
|
||||
|
||||
def make_insn_sc orig_insn, name, opes, pops, rets, pushs, nextsc
|
||||
comm = orig_insn.comm.dup
|
||||
comm[:c] = 'optimize(sc)'
|
||||
|
@ -866,27 +884,32 @@ class RubyVM
|
|||
end
|
||||
|
||||
def make_header insn
|
||||
commit "INSN_ENTRY(#{insn.name}){"
|
||||
label = insn.trace ? '' : "start_of_#{insn.name}:;"
|
||||
commit "INSN_ENTRY(#{insn.name}){#{label}"
|
||||
make_header_prepare_stack insn
|
||||
commit "{"
|
||||
make_header_stack_val insn
|
||||
make_header_default_operands insn
|
||||
make_header_operands insn
|
||||
make_header_stack_pops insn
|
||||
make_header_temporary_vars insn
|
||||
#
|
||||
make_header_debug insn
|
||||
make_header_pc insn
|
||||
make_header_popn insn
|
||||
make_header_defines insn
|
||||
make_header_analysis insn
|
||||
unless insn.trace
|
||||
make_header_stack_val insn
|
||||
make_header_default_operands insn
|
||||
make_header_operands insn
|
||||
make_header_stack_pops insn
|
||||
make_header_temporary_vars insn
|
||||
#
|
||||
make_header_debug insn
|
||||
make_header_pc insn
|
||||
make_header_popn insn
|
||||
make_header_defines insn
|
||||
make_header_analysis insn
|
||||
end
|
||||
commit "{"
|
||||
end
|
||||
|
||||
def make_footer insn
|
||||
make_footer_stack_val insn
|
||||
make_footer_default_operands insn
|
||||
make_footer_undefs insn
|
||||
unless insn.trace
|
||||
make_footer_stack_val insn
|
||||
make_footer_default_operands insn
|
||||
make_footer_undefs insn
|
||||
end
|
||||
commit " END_INSN(#{insn.name});}}}"
|
||||
end
|
||||
|
||||
|
|
|
@ -40,7 +40,14 @@ int
|
|||
rb_vm_get_sourceline(const rb_control_frame_t *cfp)
|
||||
{
|
||||
if (VM_FRAME_RUBYFRAME_P(cfp) && cfp->iseq) {
|
||||
return calc_lineno(cfp->iseq, cfp->pc);
|
||||
const rb_iseq_t *iseq = cfp->iseq;
|
||||
int line = calc_lineno(iseq, cfp->pc);
|
||||
if (line != 0) {
|
||||
return line;
|
||||
}
|
||||
else {
|
||||
return FIX2INT(rb_iseq_first_lineno(iseq));
|
||||
}
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
|
|
|
@ -74,8 +74,12 @@ error !
|
|||
#define ELABEL(x) INSN_ELABEL_##x
|
||||
#define LABEL_PTR(x) &&LABEL(x)
|
||||
|
||||
#define INSN_ENTRY_SIG(insn)
|
||||
|
||||
#define INSN_ENTRY_SIG(insn) \
|
||||
if (0) fprintf(stderr, "exec: %s@(%d, %d)@%s:%d\n", #insn, \
|
||||
(int)(reg_pc - reg_cfp->iseq->body->iseq_encoded), \
|
||||
(int)(reg_cfp->pc - reg_cfp->iseq->body->iseq_encoded), \
|
||||
RSTRING_PTR(rb_iseq_path(reg_cfp->iseq)), \
|
||||
(int)(rb_iseq_line_no(reg_cfp->iseq, reg_pc - reg_cfp->iseq->body->iseq_encoded)));
|
||||
|
||||
#define INSN_DISPATCH_SIG(insn)
|
||||
|
||||
|
|
|
@ -3723,3 +3723,58 @@ vm_opt_regexpmatch2(VALUE recv, VALUE obj)
|
|||
return Qundef;
|
||||
}
|
||||
}
|
||||
|
||||
rb_event_flag_t rb_iseq_event_flags(const rb_iseq_t *iseq, size_t pos);
|
||||
|
||||
NOINLINE(static void vm_trace(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, const VALUE *pc));
|
||||
|
||||
static void
|
||||
vm_trace(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, const VALUE *pc)
|
||||
{
|
||||
const rb_iseq_t *iseq = reg_cfp->iseq;
|
||||
size_t pos = pc - iseq->body->iseq_encoded;
|
||||
rb_event_flag_t events = rb_iseq_event_flags(iseq, pos);
|
||||
rb_event_flag_t event;
|
||||
|
||||
if (ec->trace_arg != NULL) return;
|
||||
|
||||
if (0) {
|
||||
fprintf(stderr, "vm_trace>>%4d (%4x) - %s:%d %s\n",
|
||||
(int)pos,
|
||||
(int)events,
|
||||
RSTRING_PTR(rb_iseq_path(iseq)),
|
||||
(int)rb_iseq_line_no(iseq, pos),
|
||||
RSTRING_PTR(rb_iseq_label(iseq)));
|
||||
}
|
||||
|
||||
VM_ASSERT(reg_cfp->pc == pc);
|
||||
VM_ASSERT(events != 0);
|
||||
|
||||
/* increment PC because source line is calculated with PC-1 */
|
||||
if (events & ruby_vm_event_flags) {
|
||||
if (event = (events & (RUBY_EVENT_CLASS | RUBY_EVENT_CALL | RUBY_EVENT_B_CALL))) {
|
||||
VM_ASSERT(event == RUBY_EVENT_CLASS ||
|
||||
event == RUBY_EVENT_CALL ||
|
||||
event == RUBY_EVENT_B_CALL);
|
||||
reg_cfp->pc++;
|
||||
vm_dtrace(event, ec);
|
||||
EXEC_EVENT_HOOK(ec, event, GET_SELF(), 0, 0, 0, Qundef);
|
||||
reg_cfp->pc--;
|
||||
}
|
||||
if (events & RUBY_EVENT_LINE) {
|
||||
reg_cfp->pc++;
|
||||
vm_dtrace(RUBY_EVENT_LINE, ec);
|
||||
EXEC_EVENT_HOOK(ec, RUBY_EVENT_LINE, GET_SELF(), 0, 0, 0, Qundef);
|
||||
reg_cfp->pc--;
|
||||
}
|
||||
if (event = (events & (RUBY_EVENT_END | RUBY_EVENT_RETURN | RUBY_EVENT_B_RETURN))) {
|
||||
VM_ASSERT(event == RUBY_EVENT_END ||
|
||||
event == RUBY_EVENT_RETURN ||
|
||||
event == RUBY_EVENT_B_RETURN);
|
||||
reg_cfp->pc++;
|
||||
vm_dtrace(RUBY_EVENT_LINE, ec);
|
||||
EXEC_EVENT_HOOK(ec, event, GET_SELF(), 0, 0, 0, TOPN(0));
|
||||
reg_cfp->pc--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ enum vm_regan_acttype {
|
|||
#define GET_CURRENT_INSN() (*GET_PC())
|
||||
#define GET_OPERAND(n) (GET_PC()[(n)])
|
||||
#define ADD_PC(n) (SET_PC(VM_REG_PC + (n)))
|
||||
#define JUMP(dst) (VM_REG_PC += (dst))
|
||||
#define JUMP(dst) (SET_PC(VM_REG_PC + (dst)))
|
||||
|
||||
/* frame pointer, environment pointer */
|
||||
#define GET_CFP() (COLLECT_USAGE_REGISTER_HELPER(CFP, GET, VM_REG_CFP))
|
||||
|
|
65
vm_trace.c
65
vm_trace.c
|
@ -25,6 +25,7 @@
|
|||
#include "ruby/debug.h"
|
||||
|
||||
#include "vm_core.h"
|
||||
#include "iseq.h"
|
||||
#include "eval_intern.h"
|
||||
|
||||
/* (1) trace mechanisms */
|
||||
|
@ -58,36 +59,45 @@ rb_vm_trace_mark_event_hooks(rb_hook_list_t *hooks)
|
|||
|
||||
/* ruby_vm_event_flags management */
|
||||
|
||||
static void
|
||||
update_global_event_hook(rb_event_flag_t vm_events)
|
||||
{
|
||||
ruby_vm_event_flags = vm_events;
|
||||
rb_iseq_trace_set_all(vm_events);
|
||||
rb_objspace_set_event_hook(vm_events);
|
||||
}
|
||||
|
||||
static void
|
||||
recalc_add_ruby_vm_event_flags(rb_event_flag_t events)
|
||||
{
|
||||
int i;
|
||||
ruby_vm_event_flags = 0;
|
||||
rb_event_flag_t vm_events = 0;
|
||||
|
||||
for (i=0; i<MAX_EVENT_NUM; i++) {
|
||||
if (events & ((rb_event_flag_t)1 << i)) {
|
||||
ruby_event_flag_count[i]++;
|
||||
}
|
||||
ruby_vm_event_flags |= ruby_event_flag_count[i] ? (1<<i) : 0;
|
||||
vm_events |= ruby_event_flag_count[i] ? (1<<i) : 0;
|
||||
}
|
||||
|
||||
rb_objspace_set_event_hook(ruby_vm_event_flags);
|
||||
update_global_event_hook(vm_events);
|
||||
}
|
||||
|
||||
static void
|
||||
recalc_remove_ruby_vm_event_flags(rb_event_flag_t events)
|
||||
{
|
||||
int i;
|
||||
ruby_vm_event_flags = 0;
|
||||
rb_event_flag_t vm_events = 0;
|
||||
|
||||
for (i=0; i<MAX_EVENT_NUM; i++) {
|
||||
if (events & ((rb_event_flag_t)1 << i)) {
|
||||
VM_ASSERT(ruby_event_flag_count[i] > 0);
|
||||
ruby_event_flag_count[i]--;
|
||||
}
|
||||
ruby_vm_event_flags |= ruby_event_flag_count[i] ? (1<<i) : 0;
|
||||
vm_events |= ruby_event_flag_count[i] ? (1<<i) : 0;
|
||||
}
|
||||
|
||||
rb_objspace_set_event_hook(ruby_vm_event_flags);
|
||||
update_global_event_hook(vm_events);
|
||||
}
|
||||
|
||||
/* add/remove hooks */
|
||||
|
@ -467,7 +477,6 @@ static void call_trace_func(rb_event_flag_t, VALUE data, VALUE self, ID id, VALU
|
|||
static VALUE
|
||||
set_trace_func(VALUE obj, VALUE trace)
|
||||
{
|
||||
|
||||
rb_remove_event_hook(call_trace_func);
|
||||
|
||||
if (NIL_P(trace)) {
|
||||
|
@ -580,16 +589,41 @@ get_event_id(rb_event_flag_t event)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
get_path_and_lineno(const rb_execution_context_t *ec, const rb_control_frame_t *cfp, rb_event_flag_t event, VALUE *pathp, int *linep)
|
||||
{
|
||||
cfp = rb_vm_get_ruby_level_next_cfp(ec, cfp);
|
||||
|
||||
if (cfp) {
|
||||
const rb_iseq_t *iseq = cfp->iseq;
|
||||
*pathp = rb_iseq_path(iseq);
|
||||
|
||||
if (event & (RUBY_EVENT_CLASS |
|
||||
RUBY_EVENT_CALL |
|
||||
RUBY_EVENT_B_CALL)) {
|
||||
*linep = FIX2INT(rb_iseq_first_lineno(iseq));
|
||||
}
|
||||
else {
|
||||
*linep = rb_vm_get_sourceline(cfp);
|
||||
}
|
||||
}
|
||||
else {
|
||||
*pathp = Qnil;
|
||||
*linep = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
call_trace_func(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klass)
|
||||
{
|
||||
int line;
|
||||
const char *srcfile = rb_source_loc(&line);
|
||||
VALUE filename;
|
||||
VALUE eventname = rb_str_new2(get_event_name(event));
|
||||
VALUE filename = srcfile ? rb_str_new2(srcfile) : Qnil;
|
||||
VALUE argv[6];
|
||||
const rb_execution_context_t *ec = GET_EC();
|
||||
|
||||
get_path_and_lineno(ec, ec->cfp, event, &filename, &line);
|
||||
|
||||
if (!klass) {
|
||||
rb_ec_frame_method_id_and_class(ec, &id, 0, &klass);
|
||||
}
|
||||
|
@ -607,7 +641,7 @@ call_trace_func(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klas
|
|||
argv[1] = filename;
|
||||
argv[2] = INT2FIX(line);
|
||||
argv[3] = id ? ID2SYM(id) : Qnil;
|
||||
argv[4] = (self && srcfile) ? rb_binding_new() : Qnil;
|
||||
argv[4] = (self && (filename != Qnil)) ? rb_binding_new() : Qnil;
|
||||
argv[5] = klass ? klass : Qnil;
|
||||
|
||||
rb_proc_call_with_block(proc, 6, argv, Qnil);
|
||||
|
@ -725,16 +759,7 @@ static void
|
|||
fill_path_and_lineno(rb_trace_arg_t *trace_arg)
|
||||
{
|
||||
if (trace_arg->path == Qundef) {
|
||||
rb_control_frame_t *cfp = rb_vm_get_ruby_level_next_cfp(trace_arg->ec, trace_arg->cfp);
|
||||
|
||||
if (cfp) {
|
||||
trace_arg->path = rb_iseq_path(cfp->iseq);
|
||||
trace_arg->lineno = rb_vm_get_sourceline(cfp);
|
||||
}
|
||||
else {
|
||||
trace_arg->path = Qnil;
|
||||
trace_arg->lineno = 0;
|
||||
}
|
||||
get_path_and_lineno(trace_arg->ec, trace_arg->cfp, trace_arg->event, &trace_arg->path, &trace_arg->lineno);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче