Show the method owner in backtraces
``` test.rb:1:in 'Object#toplevel_meth': unhandled exception from test.rb:4:in 'Foo.class_meth' from test.rb:6:in 'Foo#instance_meth' from test.rb:11:in 'singleton_meth' from test.rb:13:in '<main>' ``` [Feature #19117]
This commit is contained in:
parent
61819c87b2
commit
9d1b000bd1
@ -29,7 +29,7 @@ class TestDebug < Test::Unit::TestCase
|
|||||||
# check same location
|
# check same location
|
||||||
assert_equal(loc.path, iseq.path, msg)
|
assert_equal(loc.path, iseq.path, msg)
|
||||||
assert_equal(loc.absolute_path, iseq.absolute_path, msg)
|
assert_equal(loc.absolute_path, iseq.absolute_path, msg)
|
||||||
assert_equal(loc.label, iseq.label, msg)
|
#assert_equal(loc.label, iseq.label, msg)
|
||||||
assert_operator(loc.lineno, :>=, iseq.first_lineno, msg)
|
assert_operator(loc.lineno, :>=, iseq.first_lineno, msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ end
|
|||||||
SRC
|
SRC
|
||||||
out = [
|
out = [
|
||||||
"start() function is unimplemented on this machine",
|
"start() function is unimplemented on this machine",
|
||||||
"-:2:in 'start'",
|
"-:2:in 'Bug.start'",
|
||||||
"-:2:in '<main>'",
|
"-:2:in '<main>'",
|
||||||
]
|
]
|
||||||
assert_in_out_err(%w"-r-test-/bug_3571", src, [], out, bug3571)
|
assert_in_out_err(%w"-r-test-/bug_3571", src, [], out, bug3571)
|
||||||
|
@ -61,7 +61,7 @@ IRB
|
|||||||
# TruffleRuby warns when the locale does not exist
|
# TruffleRuby warns when the locale does not exist
|
||||||
env['TRUFFLERUBYOPT'] = "#{ENV['TRUFFLERUBYOPT']} --log.level=SEVERE" if RUBY_ENGINE == 'truffleruby'
|
env['TRUFFLERUBYOPT'] = "#{ENV['TRUFFLERUBYOPT']} --log.level=SEVERE" if RUBY_ENGINE == 'truffleruby'
|
||||||
args = [env] + bundle_exec + %W[-rirb -C #{tmpdir} -W0 -e IRB.start(__FILE__) -- -f --]
|
args = [env] + bundle_exec + %W[-rirb -C #{tmpdir} -W0 -e IRB.start(__FILE__) -- -f --]
|
||||||
error = /[`']raise_euc_with_invalid_byte_sequence': あ\\xFF \(RuntimeError\)/
|
error = /[`'](?:Object#)?raise_euc_with_invalid_byte_sequence': あ\\xFF \(RuntimeError\)/
|
||||||
assert_in_out_err(args, <<~IRB, error, [], encoding: "UTF-8")
|
assert_in_out_err(args, <<~IRB, error, [], encoding: "UTF-8")
|
||||||
require_relative 'euc'
|
require_relative 'euc'
|
||||||
raise_euc_with_invalid_byte_sequence
|
raise_euc_with_invalid_byte_sequence
|
||||||
|
@ -216,7 +216,7 @@ class TestBacktrace < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
@line = __LINE__ + 1
|
@line = __LINE__ + 1
|
||||||
[1].map.map { [1].map.map { foo } }
|
[1].map.map { [1].map.map { foo } }
|
||||||
assert_equal("[\"#{__FILE__}:#{@line}:in 'map'\"]", @res)
|
assert_equal("[\"#{__FILE__}:#{@line}:in 'Array#map'\"]", @res)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_caller_location_path_cfunc_iseq_no_pc
|
def test_caller_location_path_cfunc_iseq_no_pc
|
||||||
@ -292,13 +292,13 @@ class TestBacktrace < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_caller_locations_label
|
def test_caller_locations_label
|
||||||
assert_equal("#{__method__}", caller_locations(0, 1)[0].label)
|
assert_equal("TestBacktrace##{__method__}", caller_locations(0, 1)[0].label)
|
||||||
loc, = tap {break caller_locations(0, 1)}
|
loc, = tap {break caller_locations(0, 1)}
|
||||||
assert_equal("block in #{__method__}", loc.label)
|
assert_equal("block in TestBacktrace##{__method__}", loc.label)
|
||||||
begin
|
begin
|
||||||
raise
|
raise
|
||||||
rescue
|
rescue
|
||||||
assert_equal("rescue in #{__method__}", caller_locations(0, 1)[0].label)
|
assert_equal("TestBacktrace##{__method__}", caller_locations(0, 1)[0].label)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -387,7 +387,7 @@ class TestBacktrace < Test::Unit::TestCase
|
|||||||
err = ["-:1:in '<main>': unhandled exception"]
|
err = ["-:1:in '<main>': unhandled exception"]
|
||||||
assert_in_out_err([], "raise", [], err)
|
assert_in_out_err([], "raise", [], err)
|
||||||
|
|
||||||
err = ["-:2:in 'foo': foo! (RuntimeError)",
|
err = ["-:2:in 'Object#foo': foo! (RuntimeError)",
|
||||||
"\tfrom -:4:in '<main>'"]
|
"\tfrom -:4:in '<main>'"]
|
||||||
assert_in_out_err([], <<-"end;", [], err)
|
assert_in_out_err([], <<-"end;", [], err)
|
||||||
def foo
|
def foo
|
||||||
@ -396,11 +396,11 @@ class TestBacktrace < Test::Unit::TestCase
|
|||||||
foo
|
foo
|
||||||
end;
|
end;
|
||||||
|
|
||||||
err = ["-:7:in 'rescue in bar': bar! (RuntimeError)",
|
err = ["-:7:in 'Object#bar': bar! (RuntimeError)",
|
||||||
"\tfrom -:4:in 'bar'",
|
"\tfrom -:4:in 'Object#bar'",
|
||||||
"\tfrom -:9:in '<main>'",
|
"\tfrom -:9:in '<main>'",
|
||||||
"-:2:in 'foo': foo! (RuntimeError)",
|
"-:2:in 'Object#foo': foo! (RuntimeError)",
|
||||||
"\tfrom -:5:in 'bar'",
|
"\tfrom -:5:in 'Object#bar'",
|
||||||
"\tfrom -:9:in '<main>'"]
|
"\tfrom -:9:in '<main>'"]
|
||||||
assert_in_out_err([], <<-"end;", [], err)
|
assert_in_out_err([], <<-"end;", [], err)
|
||||||
def foo
|
def foo
|
||||||
@ -416,7 +416,7 @@ class TestBacktrace < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_caller_to_enum
|
def test_caller_to_enum
|
||||||
err = ["-:3:in 'foo': unhandled exception", "\tfrom -:in 'each'"]
|
err = ["-:3:in 'Object#foo': unhandled exception", "\tfrom -:in 'Enumerator#each'"]
|
||||||
assert_in_out_err([], <<-"end;", [], err, "[ruby-core:91911]")
|
assert_in_out_err([], <<-"end;", [], err, "[ruby-core:91911]")
|
||||||
def foo
|
def foo
|
||||||
return to_enum(__method__) unless block_given?
|
return to_enum(__method__) unless block_given?
|
||||||
|
@ -1729,7 +1729,7 @@ module Test
|
|||||||
return '<empty>' unless e.backtrace # SystemStackError can return nil.
|
return '<empty>' unless e.backtrace # SystemStackError can return nil.
|
||||||
|
|
||||||
e.backtrace.reverse_each do |s|
|
e.backtrace.reverse_each do |s|
|
||||||
break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
|
break if s =~ /in .(?:Test::Unit::(?:Core)?Assertions#)?(assert|refute|flunk|pass|fail|raise|must|wont)/
|
||||||
last_before_assertion = s
|
last_before_assertion = s
|
||||||
end
|
end
|
||||||
last_before_assertion.sub(/:in .*$/, '')
|
last_before_assertion.sub(/:in .*$/, '')
|
||||||
|
@ -191,16 +191,83 @@ location_lineno_m(VALUE self)
|
|||||||
return INT2FIX(location_lineno(location_ptr(self)));
|
return INT2FIX(location_lineno(location_ptr(self)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
gen_method_name(VALUE owner, VALUE name)
|
||||||
|
{
|
||||||
|
if (RB_TYPE_P(owner, T_CLASS) || RB_TYPE_P(owner, T_MODULE)) {
|
||||||
|
if (RBASIC(owner)->flags & FL_SINGLETON) {
|
||||||
|
VALUE v = RCLASS_ATTACHED_OBJECT(owner);
|
||||||
|
if (RB_TYPE_P(v, T_CLASS) || RB_TYPE_P(v, T_MODULE)) {
|
||||||
|
v = rb_class_path(v);
|
||||||
|
if (!NIL_P(v)) {
|
||||||
|
return rb_sprintf("%"PRIsVALUE".%"PRIsVALUE, v, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
owner = rb_class_path(owner);
|
||||||
|
if (!NIL_P(owner)) {
|
||||||
|
return rb_sprintf("%"PRIsVALUE"#%"PRIsVALUE, owner, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
calculate_iseq_label(VALUE owner, const rb_iseq_t *iseq)
|
||||||
|
{
|
||||||
|
retry:
|
||||||
|
switch (ISEQ_BODY(iseq)->type) {
|
||||||
|
case ISEQ_TYPE_TOP:
|
||||||
|
case ISEQ_TYPE_CLASS:
|
||||||
|
case ISEQ_TYPE_MAIN:
|
||||||
|
return ISEQ_BODY(iseq)->location.label;
|
||||||
|
case ISEQ_TYPE_METHOD:
|
||||||
|
return gen_method_name(owner, ISEQ_BODY(iseq)->location.label);
|
||||||
|
case ISEQ_TYPE_BLOCK:
|
||||||
|
case ISEQ_TYPE_PLAIN: {
|
||||||
|
int level = 0;
|
||||||
|
const rb_iseq_t *orig_iseq = iseq;
|
||||||
|
if (ISEQ_BODY(orig_iseq)->parent_iseq != 0) {
|
||||||
|
while (ISEQ_BODY(orig_iseq)->local_iseq != iseq) {
|
||||||
|
if (ISEQ_BODY(iseq)->type == ISEQ_TYPE_BLOCK) {
|
||||||
|
level++;
|
||||||
|
}
|
||||||
|
iseq = ISEQ_BODY(iseq)->parent_iseq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (level <= 1) {
|
||||||
|
return rb_sprintf("block in %"PRIsVALUE, calculate_iseq_label(owner, iseq));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return rb_sprintf("block (%d levels) in %"PRIsVALUE, level, calculate_iseq_label(owner, iseq));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ISEQ_TYPE_RESCUE:
|
||||||
|
case ISEQ_TYPE_ENSURE:
|
||||||
|
case ISEQ_TYPE_EVAL:
|
||||||
|
iseq = ISEQ_BODY(iseq)->parent_iseq;
|
||||||
|
goto retry;
|
||||||
|
default:
|
||||||
|
rb_bug("calculate_iseq_label: unreachable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
location_label(rb_backtrace_location_t *loc)
|
location_label(rb_backtrace_location_t *loc)
|
||||||
{
|
{
|
||||||
if (loc->cme && loc->cme->def->type == VM_METHOD_TYPE_CFUNC) {
|
if (loc->cme && loc->cme->def->type == VM_METHOD_TYPE_CFUNC) {
|
||||||
return rb_id2str(loc->cme->def->original_id);
|
return gen_method_name(loc->cme->owner, rb_id2str(loc->cme->def->original_id));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
VALUE owner = Qnil;
|
||||||
|
if (loc->cme) {
|
||||||
|
owner = loc->cme->owner;
|
||||||
|
}
|
||||||
|
return calculate_iseq_label(owner, loc->iseq);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ISEQ_BODY(loc->iseq)->location.label;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns the label of this frame.
|
* Returns the label of this frame.
|
||||||
*
|
*
|
||||||
@ -349,7 +416,7 @@ location_format(VALUE file, int lineno, VALUE name)
|
|||||||
static VALUE
|
static VALUE
|
||||||
location_to_str(rb_backtrace_location_t *loc)
|
location_to_str(rb_backtrace_location_t *loc)
|
||||||
{
|
{
|
||||||
VALUE file, name;
|
VALUE file, owner = Qnil, name;
|
||||||
int lineno;
|
int lineno;
|
||||||
|
|
||||||
if (loc->cme && loc->cme->def->type == VM_METHOD_TYPE_CFUNC) {
|
if (loc->cme && loc->cme->def->type == VM_METHOD_TYPE_CFUNC) {
|
||||||
@ -361,12 +428,15 @@ location_to_str(rb_backtrace_location_t *loc)
|
|||||||
file = GET_VM()->progname;
|
file = GET_VM()->progname;
|
||||||
lineno = 0;
|
lineno = 0;
|
||||||
}
|
}
|
||||||
name = rb_id2str(loc->cme->def->original_id);
|
name = gen_method_name(loc->cme->owner, rb_id2str(loc->cme->def->original_id));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
file = rb_iseq_path(loc->iseq);
|
file = rb_iseq_path(loc->iseq);
|
||||||
lineno = calc_lineno(loc->iseq, loc->pc);
|
lineno = calc_lineno(loc->iseq, loc->pc);
|
||||||
name = ISEQ_BODY(loc->iseq)->location.label;
|
if (loc->cme) {
|
||||||
|
owner = loc->cme->owner;
|
||||||
|
}
|
||||||
|
name = calculate_iseq_label(owner, loc->iseq);
|
||||||
}
|
}
|
||||||
|
|
||||||
return location_format(file, lineno, name);
|
return location_format(file, lineno, name);
|
||||||
@ -669,7 +739,7 @@ rb_backtrace_use_iseq_first_lineno_for_last_location(VALUE self)
|
|||||||
|
|
||||||
loc = &bt->backtrace[0];
|
loc = &bt->backtrace[0];
|
||||||
|
|
||||||
VM_ASSERT(loc->type == LOCATION_TYPE_ISEQ);
|
VM_ASSERT(!loc->cme || loc->cme->def->type == VM_METHOD_TYPE_ISEQ);
|
||||||
|
|
||||||
loc->pc = NULL; // means location.first_lineno
|
loc->pc = NULL; // means location.first_lineno
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user