Project

General

Profile

Actions

Bug #1674

closed

set_trace_func with 1line block

Added by wanabe (_ wanabe) about 13 years ago. Updated about 11 years ago.

Status:
Closed
Priority:
Normal
Target version:
ruby -v:
ruby 1.9.2dev (2009-06-21 trunk 23809) [i386-mingw32]
Backport:
[ruby-dev:38698]

Description

=begin
内容が1行だけのブロックを実行すると
set_trace_funcで期待通りの行番号が得られません。

$ ruby -ve '
set_trace_func(proc{|type, file, line, *a|
p [file, line] if type == "line"
})
iseq = RubyVM::InstructionSequence.compile("\n1.upto(2) {\n\np true\n\n}")
iseq.eval
puts iseq.disasm
'
ruby 1.9.2dev (2009-06-21 trunk 23809) [i386-mingw32]
["-e", 5]
["-e", 6]
["", 2]
["", 2]
true
["", 2]
true
["-e", 7]
== disasm: <RubyVM::InstructionSequence:@>==========
== catch table

catch type: break st: 0002 ed: 0012 sp: 0000 cont: 0012
0000 trace 1 ( 2)
0002 putobject 1
0004 putobject 2
0006 send :upto, 1, block in , 0,
0012 leave
== disasm: <RubyVM::InstructionSequence:block in @>=
== catch table
catch type: redo st: 0000 ed: 0011 sp: 0000 cont: 0000
catch type: next st: 0000 ed: 0011 sp: 0000 cont: 0011
------------------------------------------------------------------------
0000 trace 1 ( 2)
0002 putnil
0003 putobject true ( 4)
0005 send :p, 1, nil, 8, ( 2)
0011 leave ( 4)
=end
Actions #1

Updated by wanabe (_ wanabe) about 13 years ago

=begin
set_trace_funcだけでなく例外のバックトレース表示でも同様でした。

$ ruby -e '1.upto(2) {
raise
}'
-e:1:in block in <main>': unhandled exception from -e:1:in upto'
from -e:1:in `'

また、文頭にセミコロンを付けると正しい行番号になりました。

$ ruby -e '1.upto(2) {
;raise
}'
-e:2:in block in <main>': unhandled exception from -e:1:in upto'
from -e:1:in `'

=end

Actions #2

Updated by mame (Yusuke Endoh) about 13 years ago

=begin
遠藤です。

2009/06/22 15:15 に _ wanabe さんは書きました:

set_trace_funcだけでなく例外のバックトレース表示でも同様でした。

$ ruby -e '1.upto(2) {
raise
}'
-e:1:in block in <main>': unhandled exception from -e:1:in upto'
from -e:1:in `'

カバレッジ測定機能にも影響があったので調べてみました。

r19592 で Proc#source_location などが導入されたとき、
brace_block で行番号をいじるようになったのが原因のようです。

1: f = proc {
2: x
3: }
4: p f.source_location #=> 1

という挙動を実現するために、ブロックの先頭あたりのノードの
行番号を無理やり '{' のある行の番号に書き換えるようですが、
ちょっと ad-hoc だと思います。また、カバレッジの測定が変に
なるので実際問題としても困ります。

rb_iseq_struct に行番号を持たせるようにしてみました。
おまけの効果として、set_trace_func の class/end イベントや
call/return イベントの行番号をruby 1.8 と同じにできました。

Index: vm_core.h

--- vm_core.h (revision 23832)
+++ vm_core.h (working copy)
@@ -132,6 +132,7 @@
unsigned long iseq_size;
VALUE mark_ary; /* Array: includes operands which should be GC marked /
VALUE coverage; /
coverage array */

  • unsigned short line_no;

    /* insn info, must be freed /
    struct iseq_insn_info_entry insn_info_table;
    @@ -433,8 +434,8 @@
    VALUE rb_iseq_new(NODE
    , VALUE, VALUE, VALUE, VALUE);
    VALUE rb_iseq_new_top(NODE node, VALUE name, VALUE filename, VALUE parent);
    VALUE rb_iseq_new_main(NODE node, VALUE filename);
    -VALUE rb_iseq_new_with_bopt(NODE
    , VALUE, VALUE, VALUE, VALUE, VALUE);
    -VALUE rb_iseq_new_with_opt(NODE
    , VALUE, VALUE, VALUE, VALUE, const
    rb_compile_option_t
    );
    +VALUE rb_iseq_new_with_bopt(NODE*, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE);
    +VALUE rb_iseq_new_with_opt(NODE*, VALUE, VALUE, VALUE, VALUE, VALUE,
    const rb_compile_option_t*);
    VALUE rb_iseq_compile(VALUE src, VALUE file, VALUE line);
    VALUE rb_iseq_disasm(VALUE self);
    VALUE rb_iseq_disasm_insn(VALUE str, VALUE *iseqval, int pos,
    rb_iseq_t *iseq, VALUE child);
    Index: iseq.c
    ===================================================================
    --- iseq.c (revision 23832)
    +++ iseq.c (working copy)
    @@ -202,7 +202,7 @@

static VALUE
prepare_iseq_build(rb_iseq_t *iseq,

  •     VALUE name, VALUE filename,
    
  •     VALUE name, VALUE filename, VALUE line_no,
        VALUE parent, VALUE type, VALUE block_opt,
        const rb_compile_option_t *option)
    

{
@@ -211,6 +211,7 @@

  iseq->name = name;
  iseq->filename = filename;
  • iseq->line_no = line_no;
    iseq->defined_method_id = 0;
    iseq->mark_ary = rb_ary_new();
    RBASIC(iseq->mark_ary)->klass = 0;
    @@ -354,14 +355,14 @@
    rb_iseq_new(NODE *node, VALUE name, VALUE filename,
    VALUE parent, VALUE type)
    {
  • return rb_iseq_new_with_opt(node, name, filename, parent, type,
  • return rb_iseq_new_with_opt(node, name, filename, INT2FIX(0), parent, type,
    &COMPILE_OPTION_DEFAULT);
    }

VALUE
rb_iseq_new_top(NODE *node, VALUE name, VALUE filename, VALUE parent)
{

  • return rb_iseq_new_with_opt(node, name, filename, parent, ISEQ_TYPE_TOP,
  • return rb_iseq_new_with_opt(node, name, filename, INT2FIX(0),
    parent, ISEQ_TYPE_TOP,
    &COMPILE_OPTION_DEFAULT);
    }

@@ -370,12 +371,12 @@
{
rb_thread_t *th = GET_THREAD();
VALUE parent = th->base_block->iseq->self;

  • return rb_iseq_new_with_opt(node, rb_str_new2(""), filename,
  • return rb_iseq_new_with_opt(node, rb_str_new2(""),
    filename, INT2FIX(0),
    parent, ISEQ_TYPE_MAIN, &COMPILE_OPTION_DEFAULT);
    }

static VALUE
-rb_iseq_new_with_bopt_and_opt(NODE *node, VALUE name, VALUE filename,
+rb_iseq_new_with_bopt_and_opt(NODE *node, VALUE name, VALUE filename,
VALUE line_no,
VALUE parent, VALUE type, VALUE bopt,
const rb_compile_option_t *option)
{
@@ -385,28 +386,28 @@
GetISeqPtr(self, iseq);
iseq->self = self;

  • prepare_iseq_build(iseq, name, filename, parent, type, bopt, option);
  • prepare_iseq_build(iseq, name, filename, line_no, parent, type,
    bopt, option);
    rb_iseq_compile_node(self, node);
    cleanup_iseq_build(iseq);
    return self;
    }

VALUE
-rb_iseq_new_with_opt(NODE *node, VALUE name, VALUE filename,
+rb_iseq_new_with_opt(NODE *node, VALUE name, VALUE filename, VALUE line_no,
VALUE parent, VALUE type,
const rb_compile_option_t option)
{
/
TODO: argument check */

  • return rb_iseq_new_with_bopt_and_opt(node, name, filename, parent, type,
  • return rb_iseq_new_with_bopt_and_opt(node, name, filename,
    line_no, parent, type,
    Qfalse, option);
    }

VALUE
-rb_iseq_new_with_bopt(NODE *node, VALUE name, VALUE filename,
+rb_iseq_new_with_bopt(NODE node, VALUE name, VALUE filename, VALUE line_no,
VALUE parent, VALUE type, VALUE bopt)
{
/
TODO: argument check */

  • return rb_iseq_new_with_bopt_and_opt(node, name, filename, parent, type,
  • return rb_iseq_new_with_bopt_and_opt(node, name, filename,
    line_no, parent, type,
    bopt, &COMPILE_OPTION_DEFAULT);
    }

@@ -420,7 +421,7 @@
VALUE iseqval = iseq_alloc(self);

  VALUE magic, version1, version2, format_type, misc;
  • VALUE name, filename;
  • VALUE name, filename, line_no;
    VALUE type, body, locals, args, exception;

    VALUE iseq_type;
    @@ -430,7 +431,7 @@
    int i = 0;

    /* [magic, major_version, minor_version, format_type, misc,

  • *  name, filename,
    
  • *  name, filename, line_no,
    *  type, locals, args, exception_table, body]
    */
    

@@ -444,6 +445,7 @@

  name        = CHECK_STRING(rb_ary_entry(data, i++));
  filename    = CHECK_STRING(rb_ary_entry(data, i++));
  • line_no = CHECK_INTEGER(rb_ary_entry(data, i++));

    type = CHECK_SYMBOL(rb_ary_entry(data, i++));
    locals = CHECK_ARRAY(rb_ary_entry(data, i++));
    @@ -485,7 +487,7 @@
    }

    make_compile_option(&option, opt);

  • prepare_iseq_build(iseq, name, filename,
  • prepare_iseq_build(iseq, name, filename, line_no,
    parent, iseq_type, 0, &option);

    rb_iseq_build_from_ary(iseq, locals, args, exception, body);
    @@ -533,11 +535,11 @@

    if (th->base_block && th->base_block->iseq) {
    return rb_iseq_new_with_opt(node, th->base_block->iseq->name,

  •  		    file, th->base_block->iseq->self,
    
  •  		    file, line, th->base_block->iseq->self,
     		    ISEQ_TYPE_EVAL, &option);
    
    }
    else {
  • return rb_iseq_new_with_opt(node, rb_str_new2(""), file, Qfalse,
  • return rb_iseq_new_with_opt(node, rb_str_new2(""), file,
    line, Qfalse,
    ISEQ_TYPE_TOP, &option);
    }
    }
    @@ -582,7 +584,7 @@
    parser = rb_parser_new();
    node = rb_parser_compile_file(parser, fname, f, NUM2INT(line));
    make_compile_option(&option, opt);
  • return rb_iseq_new_with_opt(node, rb_str_new2(""), file, Qfalse,
  • return rb_iseq_new_with_opt(node, rb_str_new2(""), file,
    line, Qfalse,
    ISEQ_TYPE_TOP, &option);
    }

@@ -648,7 +650,7 @@
int
rb_iseq_first_lineno(rb_iseq_t *iseq)
{

  • return iseq->insn_info_table[0].line_no;
  • return FIX2INT(iseq->line_no);
    }

/* TODO: search algorithm is brute force.
@@ -1290,7 +1292,7 @@

  /*
   * [:magic, :major_version, :minor_version, :format_type, :misc,
  • *  :name, :filename, :type, :locals, :args,
    
  • *  :name, :filename, :line_no, :type, :locals, :args,
    *  :catch_table, :bytecode]
    */
    
    rb_ary_push(val, rb_str_new2("YARVInstructionSequence/SimpleDataFormat"));
    @@ -1300,6 +1302,7 @@
    rb_ary_push(val, misc);
    rb_ary_push(val, iseq->name);
    rb_ary_push(val, iseq->filename);
  • rb_ary_push(val, iseq->line_no);
    rb_ary_push(val, type);
    rb_ary_push(val, locals);
    rb_ary_push(val, args);
    @@ -1406,7 +1409,8 @@
    const VALUE *arg_opt_table,
    const struct iseq_catch_table_entry *catch_table,
    const char *name,
  • const char *filename)
  • const char *filename,
  • const unsigned short line_no)
    {
    int i;
    VALUE iseqval = iseq_alloc(rb_cISeq);
    @@ -1417,6 +1421,7 @@
    *iseq = *iseq_template;
    iseq->name = rb_str_new2(name);
    iseq->filename = rb_str_new2(filename);
  • iseq->line_no = line_no;
    iseq->mark_ary = rb_ary_tmp_new(3);
    iseq->self = iseqval;

Index: compile.c

--- compile.c (revision 23832)
+++ compile.c (working copy)
@@ -167,15 +167,12 @@
#define iseq_filename(iseq)
(((rb_iseq_t*)DATA_PTR(iseq))->filename)

-#define NEW_ISEQVAL(node, name, type) \

  • new_child_iseq(iseq, node, name, 0, type)
    +#define NEW_ISEQVAL(node, name, type, line_no) \
  • new_child_iseq(iseq, node, name, 0, type, line_no)

-#define NEW_CHILD_ISEQVAL(node, name, type) \

  • new_child_iseq(iseq, node, name, iseq->self, type)
    +#define NEW_CHILD_ISEQVAL(node, name, type, line_no) \
  • new_child_iseq(iseq, node, name, iseq->self, type, line_no)

-#define NEW_SPECIAQL_BLOCK_ISEQVAL(iseq, sym) \

  • new_child_iseq(iseq, iseq->node, iseq->name, iseq->parent_iseq,
    iseq->type, sym)

/* add instructions */
#define ADD_SEQ(seq1, seq2)
APPEND_LIST(seq1, seq2)
@@ -448,13 +445,13 @@
break;
}
case ISEQ_TYPE_CLASS: {

  •  ADD_TRACE(ret, nd_line(node), RUBY_EVENT_CLASS);
    
  •  ADD_TRACE(ret, FIX2INT(iseq->line_no), RUBY_EVENT_CLASS);
     COMPILE(ret, "scoped node", node->nd_body);
     ADD_TRACE(ret, nd_line(node), RUBY_EVENT_END);
     break;
    
    }
    case ISEQ_TYPE_METHOD: {
  •  ADD_TRACE(ret, nd_line(node), RUBY_EVENT_CALL);
    
  •  ADD_TRACE(ret, FIX2INT(iseq->line_no), RUBY_EVENT_CALL);
     COMPILE(ret, "scoped node", node->nd_body);
     ADD_TRACE(ret, nd_line(node), RUBY_EVENT_RETURN);
     break;
    

@@ -911,12 +908,12 @@

static VALUE
new_child_iseq(rb_iseq_t *iseq, NODE *node,

  •     VALUE name, VALUE parent, VALUE type)
    
  •     VALUE name, VALUE parent, VALUE type, int line_no)
    

{
VALUE ret;

  debugs("[new_child_iseq]> ---------------------------------------\n");
  • ret = rb_iseq_new_with_opt(node, name, iseq_filename(iseq->self),
  • ret = rb_iseq_new_with_opt(node, name, iseq_filename(iseq->self),
    INT2FIX(line_no),
    parent, type, iseq->compile_data->option);
    debugs("[new_child_iseq]< ---------------------------------------\n");
    iseq_add_mark_object(iseq, ret);
    @@ -2686,7 +2683,7 @@
    rb_str_concat(rb_str_new2
    ("defined guard in "),
    iseq->name),
  •  			     ISEQ_TYPE_DEFINED_GUARD);
    
  •  			     ISEQ_TYPE_DEFINED_GUARD, 0);
    
     defined_expr(iseq, ret, node->nd_recv, lfinish, Qfalse);
     ADD_INSNL(ret, nd_line(node), branchunless, lfinish[1]);
    

@@ -3264,7 +3261,7 @@

    iseq->compile_data->current_block =
	NEW_CHILD_ISEQVAL(node->nd_body, make_name_for_block(iseq),
  •  		  ISEQ_TYPE_BLOCK);
    
  •  		  ISEQ_TYPE_BLOCK, nd_line(node));
    
     mid = idEach;
     ADD_SEND_R(ret, nd_line(node), ID2SYM(idEach), INT2FIX(0),
    

@@ -3273,7 +3270,7 @@
else {
iseq->compile_data->current_block =
NEW_CHILD_ISEQVAL(node->nd_body, make_name_for_block(iseq),

  •  		  ISEQ_TYPE_BLOCK);
    
  •  		  ISEQ_TYPE_BLOCK, nd_line(node));
     COMPILE(ret, "iter caller", node->nd_iter);
    
    }
    ADD_LABEL(ret, retry_end_l);
    @@ -3507,7 +3504,7 @@
    VALUE rescue = NEW_CHILD_ISEQVAL(
    node->nd_resq,
    rb_str_concat(rb_str_new2("rescue in "), iseq->name),
  •  ISEQ_TYPE_RESCUE);
    
  •  ISEQ_TYPE_RESCUE, nd_line(node));
    

    ADD_LABEL(ret, lstart);
    COMPILE(ret, "rescue head", node->nd_head);
    @@ -3589,7 +3586,7 @@
    rb_str_concat(rb_str_new2
    ("ensure in "),
    iseq->name),

  •  			 ISEQ_TYPE_ENSURE);
    
  •  			 ISEQ_TYPE_ENSURE, nd_line(node));
    
    LABEL *lstart = NEW_LABEL(nd_line(node));
    LABEL *lend = NEW_LABEL(nd_line(node));
    LABEL *lcont = NEW_LABEL(nd_line(node));
    @@ -4503,7 +4500,7 @@
    case NODE_DEFN:{
    VALUE iseqval = NEW_ISEQVAL(node->nd_defn,
    rb_str_dup(rb_id2str(node->nd_mid)),
  •  		    ISEQ_TYPE_METHOD);
    
  •  		    ISEQ_TYPE_METHOD, nd_line(node));
    

    debugp_param("defn/iseq", iseqval);

@@ -4523,7 +4520,7 @@
case NODE_DEFS:{
VALUE iseqval = NEW_ISEQVAL(node->nd_defn,
rb_str_dup(rb_id2str(node->nd_mid)),

  •  		    ISEQ_TYPE_METHOD);
    
  •  		    ISEQ_TYPE_METHOD, nd_line(node));
    

    debugp_param("defs/iseq", iseqval);

@@ -4577,7 +4574,7 @@
NEW_CHILD_ISEQVAL(
node->nd_body,
rb_sprintf("class:%s", rb_id2name(node->nd_cpath->nd_mid)),

  •  ISEQ_TYPE_CLASS);
    
  •  ISEQ_TYPE_CLASS, nd_line(node));
    
    compile_cpath(ret, iseq, node->nd_cpath);
    COMPILE(ret, "super", node->nd_super);
    ADD_INSN3(ret, nd_line(node), defineclass,
    @@ -4592,7 +4589,7 @@
    VALUE iseqval = NEW_CHILD_ISEQVAL(
    node->nd_body,
    rb_sprintf("module:%s", rb_id2name(node->nd_cpath->nd_mid)),
  •  ISEQ_TYPE_CLASS);
    
  •  ISEQ_TYPE_CLASS, nd_line(node));
    

    compile_cpath(ret, iseq, node->nd_cpath);
    ADD_INSN (ret, nd_line(node), putnil); /* dummy */
    @@ -4607,7 +4604,7 @@
    ID singletonclass;
    VALUE iseqval =
    NEW_ISEQVAL(node->nd_body, rb_str_new2("singletonclass"),

  •  	ISEQ_TYPE_CLASS);
    
  •  	ISEQ_TYPE_CLASS, nd_line(node));
    

    COMPILE(ret, "sclass#recv", node->nd_recv);
    ADD_INSN (ret, nd_line(node), putnil);
    @@ -4810,7 +4807,7 @@
    case NODE_POSTEXE:{
    LABEL *lstart = NEW_LABEL(nd_line(node));
    LABEL *lend = NEW_LABEL(nd_line(node));

  • VALUE block = NEW_CHILD_ISEQVAL(node->nd_body,
    make_name_for_block(iseq), ISEQ_TYPE_BLOCK);
  • VALUE block = NEW_CHILD_ISEQVAL(node->nd_body,
    make_name_for_block(iseq), ISEQ_TYPE_BLOCK, nd_line(node));

    ADD_LABEL(ret, lstart);
    ADD_INSN2(ret, nd_line(node), onceinlinecache, 0, lend);
    @@ -4903,7 +4900,7 @@
    }
    case NODE_LAMBDA:{
    /* compile same as lambda{...} */

  • VALUE block = NEW_CHILD_ISEQVAL(node->nd_body,
    make_name_for_block(iseq), ISEQ_TYPE_BLOCK);
  • VALUE block = NEW_CHILD_ISEQVAL(node->nd_body,
    make_name_for_block(iseq), ISEQ_TYPE_BLOCK, nd_line(node));
    VALUE argc = INT2FIX(0);
    ADD_CALL_RECEIVER(ret, nd_line(node));
    ADD_CALL_WITH_BLOCK(ret, nd_line(node), ID2SYM(idLambda), argc, block);
    Index: parse.y
    ===================================================================
    --- parse.y (revision 23832)
    +++ parse.y (working copy)
    @@ -2897,7 +2897,6 @@
    reduce_nodes(&body);
    $$ = NEW_DEFN($2, $4, body, NOEX_PRIVATE);
    fixpos($$, $4);
  •  	fixpos($$->nd_defn, $4);
     	local_pop();
         /*%
     	$$ = dispatch3(def, $2, $4, $5);
    

@@ -2923,7 +2922,6 @@
reduce_nodes(&body);
$$ = NEW_DEFS($2, $5, $7, body);
fixpos($$, $2);

  •  	fixpos($$->nd_defn, $2);
     	local_pop();
         /*%
     	$$ = dispatch5(defs, $2, $3, $5, $7, $8);
    

@@ -3637,8 +3635,6 @@
/%%%/
$$ = NEW_ITER($3,$4);
nd_set_line($$, $2);

  •  	nd_set_line($$->nd_body, $<num>2);
    
  •  	nd_set_line($$->nd_body->nd_body, $<num>2);
     	dyna_pop();
         /*%
     	$$ = dispatch2(brace_block, escape_Qundef($3), $4);
    

Index: test/ruby/test_settracefunc.rb

--- test/ruby/test_settracefunc.rb (revision 23832)
+++ test/ruby/test_settracefunc.rb (working copy)
@@ -68,7 +68,7 @@
events.shift)
assert_equal(["c-return", 5, :+, Fixnum],
events.shift)

  • assert_equal(["return", 4, :add, self.class],
  • assert_equal(["return", 6, :add, self.class],
    events.shift)
    assert_equal(["line", 8, method, self.class],
    events.shift)
    @@ -98,7 +98,7 @@
    events.shift)
    assert_equal(["c-return", 4, :inherited, Class],
    events.shift)
  • assert_equal(["class", 7, nil, nil],
  • assert_equal(["class", 4, nil, nil],
    events.shift)
    assert_equal(["line", 5, nil, nil],
    events.shift)
    @@ -120,7 +120,7 @@
    events.shift)
    assert_equal(["call", 5, :bar, Foo],
    events.shift)
  • assert_equal(["return", 5, :bar, Foo],
  • assert_equal(["return", 6, :bar, Foo],
    events.shift)
    assert_equal(["line", 9, method, self.class],
    events.shift)

--
Yusuke ENDOH

=end

Actions #3

Updated by mame (Yusuke Endoh) almost 13 years ago

  • Assignee set to ko1 (Koichi Sasada)
  • Priority changed from 3 to Normal

=begin

=end

Actions #4

Updated by yugui (Yuki Sonoda) almost 13 years ago

  • Target version set to 1.9.2

=begin

=end

Actions #5

Updated by mame (Yusuke Endoh) almost 13 years ago

  • Status changed from Open to Closed
  • % Done changed from 0 to 100

=begin
Applied in changeset r24243.
=end

Actions

Also available in: Atom PDF