在编写ruby时,我常常难以调试无限递归.有没有办法从a SystemStackError中找到一个回溯来找出,究竟是无限循环发生在哪里?
给定一些方法foo,bar并baz在循环中相互调用:
def foo
bar
end
def bar
baz
end
def baz
foo
end
foo
Run Code Online (Sandbox Code Playgroud)
当我运行此代码时,我只是收到消息test.rb:6: stack level too deep (SystemStackError).至少得到堆栈的最后100行是有用的,所以我可以立即看到这是一个循环foo,bar并且baz,像这样:
test.rb:6: stack level too deep (SystemStackError)
test.rb:2:in `foo'
test.rb:10:in `baz'
test.rb:6:in `bar'
test.rb:2:in `foo'
test.rb:10:in `baz'
test.rb:6:in `bar'
test.rb:2:in `foo'
[...]
Run Code Online (Sandbox Code Playgroud)
有没有办法实现这个目标?
编辑:
从下面的答案可以看出,Rubinius可以做到.不幸的是,一些rubinius错误阻止我使用我想要调试的软件.所以准确的问题是:
如何获得MRI(默认红宝石)1.9的回溯?
Ell*_*son 42
后来发现这个问题的另一种方法......一个优秀的要点提供了在控制台中启用跟踪功能并将所有函数调用打印到文件的说明.刚刚在1.9.3-p194上测试过,效果很好.
包括这里,以防有一天要点消失:
$enable_tracing = false
$trace_out = open('trace.txt', 'w')
set_trace_func proc { |event, file, line, id, binding, classname|
if $enable_tracing && event == 'call'
$trace_out.puts "#{file}:#{line} #{classname}##{id}"
end
}
$enable_tracing = true
a_method_that_causes_infinite_recursion_in_a_not_obvious_way()
Run Code Online (Sandbox Code Playgroud)
Ger*_*son 20
这是一个有点棘手的问题,我不时在调试ruby/rails时遇到过这个问题.我刚刚发现了一种可行的技术,可以在崩溃系统堆栈之前检测堆栈的超出范围,并且真正的回溯会丢失或出现乱码.基本模式是:
raise "crash me" if caller.length > 500
Run Code Online (Sandbox Code Playgroud)
提高500,直到它不会过早开火,你将有一个很好的跟踪你不断增长的堆栈问题.
Lin*_*ios 13
这里:
begin
foo
rescue SystemStackError
puts $!
puts caller[0..100]
end
Run Code Online (Sandbox Code Playgroud)
该方法Kernel#caller将堆栈回溯作为数组返回,因此这将在回溯中打印前0到100个条目.因为caller可以随时调用(并用于一些非常奇怪的事情),即使Ruby没有为SystemStackErrors打印回溯,你仍然可以获得回溯.
结合几个答案的建议,当你的调用堆栈太深时,这会抛出一个错误(带有堆栈跟踪).这会降低所有方法调用的速度,所以你应该尝试尽可能地将它放在你认为无限循环发生的地方.
set_trace_func proc {
|event, file, line, id, binding, classname|
if event == "call" && caller_locations.length > 500
fail "stack level too deep"
end
}
Run Code Online (Sandbox Code Playgroud)
如果您碰巧使用 pry,这实际上会让您侵入太深的方法调用链。
set_trace_func proc { |event, file, line, id, proc_binding, classname|
if !$pried && proc_binding && proc_binding.eval( "caller.size" ) > 200
$pried = true
proc_binding.pry
end
}
Run Code Online (Sandbox Code Playgroud)
显然,这被跟踪为功能 6216并在 Ruby 2.2 中修复。
$ ruby system-stack-error.rb
system-stack-error.rb:6:in `bar': stack level too deep (SystemStackError)
from system-stack-error.rb:2:in `foo'
from system-stack-error.rb:10:in `baz'
from system-stack-error.rb:6:in `bar'
from system-stack-error.rb:2:in `foo'
from system-stack-error.rb:10:in `baz'
from system-stack-error.rb:6:in `bar'
from system-stack-error.rb:2:in `foo'
from system-stack-error.rb:10:in `baz'
... 10067 levels...
from system-stack-error.rb:10:in `baz'
from system-stack-error.rb:6:in `bar'
from system-stack-error.rb:2:in `foo'
from system-stack-error.rb:13:in `<main>'
Run Code Online (Sandbox Code Playgroud)