如何从SystemStackError:堆栈级别太深回溯?
编码ruby时,经常遇到难以debugging的无限recursion。 有没有办法从SystemStackError
中找回一个回溯,找出无限循环发生的地方?
例
给定一些方法foo
, bar
和baz
在循环中相互调用:
def foo bar end def bar baz end def baz foo end foo
当我运行这个代码,我只是得到了消息test.rb:6: stack level too deep (SystemStackError)
。 至less得到堆栈的最后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' [...]
有什么办法可以做到这一点?
编辑:
从下面的答案可以看出,Rubinius可以做到这一点。 不幸的是一些rubinius错误使我无法使用它与我想debugging的软件。 所以确切的问题是:
如何用MRI(默认ruby)1.9进行回溯?
显然这被追踪为function6216和固定在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>'
另一种方法是稍后发现这个问题…一个出色的要点提供了关于在控制台中启用跟踪function以及将所有函数调用打印到文件的说明。 刚刚在1.9.3-p194上testing过,效果很好。
包括在这里,以防有一天主旨消失:
$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()
这是一个有点令人头疼的问题,我不时在debuggingruby / rails。 我刚刚发现了一种可行的技术,在崩溃系统堆栈之前检测堆栈越界越远,真正的回溯就会丢失或出现乱码。 基本模式是:
raise "crash me" if caller.length > 500
颠簸500,直到它不会过早起火,你将有一个很好的跟踪你的堆栈问题。
这里:
begin foo rescue SystemStackError puts $! puts caller[0..100] end
Kernel#caller
方法返回一个栈回溯作为一个数组,所以这会打印回溯中的前0到100个条目。 由于caller
可以在任何时候调用(并且用于一些非常奇怪的事情),即使Ruby不打印SystemStackErrors的回溯,仍然可以获得回溯。
结合几个答案的build议,当你的调用堆栈变得太深时,这将会抛出一个错误(带有堆栈跟踪)。 这会减慢所有的方法调用,所以你应该尽可能地把它放在你认为无限循环正在发生的地方。
set_trace_func proc { |event, file, line, id, binding, classname| if event == "call" && caller_locations.length > 500 fail "stack level too deep" end }
如果你碰巧使用撬,这实际上会让你陷入太深的方法调用链。
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 }
你可以用Ruby 1.8来获得这种堆栈跟踪。 如果存在1.9样式的语法(例如{foo: 42}
)是唯一的问题,那么编译Ruby 1.8头 。
我在这里尝试了很多东西,但是找不到recursion的地方(我正在使用Ruby 2.0)。
然后,我尝试了“愚蠢的东西”。 我在terminal中运行了问题代码(在我的情况下,unit testing),然后当我认为冒犯性testing正在运行的时候按下了Ctrl-C
(几个puts
帮助)。 果然,我得到了一个完整的回溯:)
我有一个完整的3或4秒,在2013年的Macbook Pro回应。