diff options
author | ko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-11-14 12:58:36 +0000 |
---|---|---|
committer | ko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-11-14 12:58:36 +0000 |
commit | 665ba24b446971fdf652a9c57c32b176d0018636 (patch) | |
tree | 31357897b60fbef63947d89ad7cd0f0ae95025bc | |
parent | fe3decb2017af2092d4b2181e27e4cd8f6bbf4fa (diff) | |
download | ruby-665ba24b446971fdf652a9c57c32b176d0018636.tar.gz |
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
-rw-r--r-- | NEWS | 3 | ||||
-rw-r--r-- | common.mk | 1 | ||||
-rw-r--r-- | compile.c | 198 | ||||
-rw-r--r-- | insns.def | 18 | ||||
-rw-r--r-- | iseq.c | 236 | ||||
-rw-r--r-- | iseq.h | 12 | ||||
-rw-r--r-- | test/ruby/test_settracefunc.rb | 26 | ||||
-rwxr-xr-x | tool/instruction.rb | 53 | ||||
-rw-r--r-- | vm_backtrace.c | 9 | ||||
-rw-r--r-- | vm_exec.h | 8 | ||||
-rw-r--r-- | vm_insnhelper.c | 55 | ||||
-rw-r--r-- | vm_insnhelper.h | 2 | ||||
-rw-r--r-- | vm_trace.c | 65 |
13 files changed, 416 insertions, 270 deletions
@@ -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 @@ -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 LINK_ELEMENT * +ELEM_FIRST_INSN(LINK_ELEMENT *elem) +{ + while (elem) { + switch (elem->type) { + case ISEQ_ELEMENT_INSN: + case ISEQ_ELEMENT_ADJUST: + return elem; + default: + elem = elem->next; + } + } + return NULL; +} + static int -LIST_SIZE_ZERO(LINK_ANCHOR *const anchor) +LIST_INSN_SIZE_ONE(const LINK_ANCHOR *const anchor) { - if (anchor->anchor.next == 0) { - return 1; + 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); @@ -782,24 +782,6 @@ checkkeyword @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 - @j trace 用の命令。 - */ -DEFINE_INSN trace2 (rb_num_t nf, VALUE data) () @@ -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; - - iseq_original = rb_iseq_original_iseq(iseq); - for (pos = 0; cont && pos < iseq->body->iseq_size; pos += insn_len(insn)) { - insn = iseq_original[pos]; - - 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)); - } - } + 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 + + 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 (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; -} -static int -collect_trace(int line, rb_event_flag_t *events_ptr, void *ptr) -{ - VALUE result = (VALUE)ptr; - rb_ary_push(result, INT2NUM(line)); - return 1; -} - -/* - * <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; + /* clear for debugging: ISEQ_ORIGINAL_ISEQ_CLEAR(iseq); */ } -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) +trace_set_i(void *vstart, void *vend, size_t stride, void *data) { - struct set_specifc_data *data = (struct set_specifc_data *)ptr; + rb_event_flag_t turnon_events = *(rb_event_flag_t *)data; - 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; - - 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); + rb_objspace_each_objects(trace_set_i, &turnon_events); +} - 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"); } + @@ -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 { diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb index 74c092c460..c2e02afe59 100644 --- a/test/ruby/test_settracefunc.rb +++ b/test/ruby/test_settracefunc.rb @@ -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], diff --git a/tool/instruction.rb b/tool/instruction.rb index b2fb037655..3354933da9 100755 --- a/tool/instruction.rb +++ b/tool/instruction.rb @@ -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 diff --git a/vm_backtrace.c b/vm_backtrace.c index fa9295daab..0418b6c4d0 100644 --- a/vm_backtrace.c +++ b/vm_backtrace.c @@ -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) diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 7625cdbcac..1a7000c964 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -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--; + } + } +} diff --git a/vm_insnhelper.h b/vm_insnhelper.h index f89039e3c8..c76f0cad22 100644 --- a/vm_insnhelper.h +++ b/vm_insnhelper.h @@ -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)) diff --git a/vm_trace.c b/vm_trace.c index beea1ecb8f..3b9f025d89 100644 --- a/vm_trace.c +++ b/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 */ @@ -59,35 +60,44 @@ 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)) { @@ -581,15 +590,40 @@ 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); } } |