Ruby 如何从SystemStackError获取回溯:堆栈级别太深?

Ruby 如何从SystemStackError获取回溯:堆栈级别太深?,ruby,Ruby,在编写ruby时,我常常很难调试无限递归。有没有办法从SystemStackError中获取回溯,以找出无限循环的确切发生位置 例子 给定一些循环中相互调用的方法foo、bar和baz: def foo bar end def bar baz end def baz foo end foo 当我运行这段代码时,我只得到消息test.rb:6:堆栈级别太深(SystemStackError)。至少获得堆栈的最后100行是有用的,因此我可以立即看到这是foo、bar和baz之间的

在编写ruby时,我常常很难调试无限递归。有没有办法从
SystemStackError
中获取回溯,以找出无限循环的确切发生位置

例子 给定一些循环中相互调用的方法
foo
bar
baz

def foo
  bar
end

def bar
  baz
end

def baz
  foo
end

foo
当我运行这段代码时,我只得到消息
test.rb:6:堆栈级别太深(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'
  [...]
有没有办法做到这一点

编辑:

从下面的答案可以看出,鲁宾尼乌斯可以做到。不幸的是,有些人阻止我将它与我想调试的软件一起使用。准确地说,问题是:

如何使用MRI(默认的ruby)1.9获得回溯?这里:

begin
  foo
rescue SystemStackError
  puts $!
  puts caller[0..100]
end

方法
Kernel#caller
将堆栈回溯作为数组返回,因此这将打印回溯中的前0到100个条目。因为即使Ruby没有打印SystemStackErrors的回溯,调用方也可以在任何时候调用(并用于一些非常奇怪的事情),所以您仍然可以得到回溯

使用Ruby 1.8可以获得这种堆栈跟踪。如果存在1.9风格的语法(例如
{foo:42}
)是唯一的问题,那么编译。

这是我在调试ruby/rails时经常遇到的一个有点棘手的问题。我刚刚发现了一种可行的技术,可以在堆栈崩溃系统堆栈和真正的回溯丢失或混乱之前检测堆栈是否超出范围。基本模式是:

raise "crash me" if caller.length > 500

增加500,直到它不会过早触发,您将有一个很好的跟踪您不断增长的堆栈问题。

另一种方法,供稍后发现此问题的人使用。。。提供有关在控制台中启用跟踪功能以及将所有函数调用打印到文件的说明。刚在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()

我在这里尝试了很多东西,但找不到递归的位置(我使用的是Ruby 2.0)

然后,我尝试了“愚蠢的事情”。我在Terminal中运行了问题代码(在我的例子中是单元测试),然后在我认为攻击性测试正在运行时按下了
Ctrl-C
(一些
put
调用有帮助)。果然,我得到了完整的回溯:)


在2013年的Macbook Pro上,我有整整3到4秒钟的时间来响应。

如果你碰巧使用了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
}

结合来自多个答案的建议,当调用堆栈变得太深时,这将抛出一个错误(带有堆栈跟踪)。这将减慢所有方法调用的速度,因此您应该尽可能将其放在您认为无限循环正在发生的位置

set_trace_func proc {
  |event, file, line, id, binding, classname| 
  if event == "call"  && caller_locations.length > 500
    fail "stack level too deep"
  end
}

显然,这是在Ruby 2.2中跟踪和修复的

$ruby system-stack-error.rb
系统堆栈错误。rb:6:在'bar'中:堆栈级别太深(SystemStackError)
来自系统堆栈错误。rb:2:在'foo'中
来自系统堆栈错误。rb:10:in`baz'
来自系统堆栈错误。rb:6:在'bar'中
来自系统堆栈错误。rb:2:在'foo'中
来自系统堆栈错误。rb:10:in`baz'
来自系统堆栈错误。rb:6:在'bar'中
来自系统堆栈错误。rb:2:在'foo'中
来自系统堆栈错误。rb:10:in`baz'
... 10067级。。。
来自系统堆栈错误。rb:10:in`baz'
来自系统堆栈错误。rb:6:在'bar'中
来自系统堆栈错误。rb:2:在'foo'中
来自系统堆栈错误。rb:13:in`'

在具有以下代码的ruby 1.9.3-p194上不起作用:。它只输出递归。rb:6:堆栈级别太深(SystemStackError)。你自己试过吗?您使用的是哪个ruby版本?@iblue:请参阅编辑。这应该可以解决问题。给了我一个调用
foo
的堆栈跟踪,我的
foo
只是
def foo;傅;结束
。仍然不打印包含任何方法foo、bar或baz的回溯。它输出
堆栈级别太深
递归。rb:13:in
“`,这是开始块开始的行。没有帮助。@iblue:那么我怀疑有什么会有帮助。我会删除答案,这样问题就不会再次得到回答。然后我会得到异常的来源(
recursion.rb:6
),但仍然没有回溯。你能详细解释一下为什么这在1.9中不起作用吗?我找不到其他任何关于这种情况的参考,或者为什么…@bjeanes我真的记不起来了,但我可能一直在想,
assert\u nothing\u所提出的
在1.8和1.9之间的行为有什么不同,这可能是一个测试/单元对小型测试的问题。结合@gerry gleason的提示,这对我来说尤其有效。通过引发一个异常,我不会被非递归调用淹没。哈,非常愚蠢,当你最需要的时候,你无法获得有意义的堆栈跟踪。。。谢谢,这对meCombine起到了作用。下面我的答案测试了堆栈深度,并在一定程度上引发了一个异常,你有一种自动检测。哦,那是它自己的答案,我会投票赞成。问题是把它放在哪里。你不能把它放在程序的每一个步骤中。您可能可以在出现错误后通过反复试验来确定位置,但您不能在代码中预先确定位置。这是一种更快的查找方法:)使用
caller\u位置。length
更有效,特别是如果您使用@elliot nelson的tracing technology.sawa-将其放在set_trace_func proc中,并用if语句包围-'if event==“call”&&caller.length>500'有趣,没有尝试过它,但这是一种有趣的调试技术。因此,这与上面的类似,但您可以结束