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

时间:2012-07-18 15:05:14

标签: ruby

编码ruby时,我常常难以调试无限递归。有没有办法从SystemStackError中找到一个回溯来找出无限循环的确切位置?

实施例

鉴于某些方法foobarbaz在循环中相互调用:

def foo
  bar
end

def bar
  baz
end

def baz
  foo
end

foo

当我运行此代码时,我只收到消息test.rb:6: stack level too deep (SystemStackError)。至少获得堆栈的最后100行是有用的,所以我可以立即看到这是foobarbaz之间的循环,如下所示:

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 bugs阻止我使用我想要调试的软件。所以准确的问题是:

如何获得MRI(默认红宝石)1.9的回溯?

8 个答案:

答案 0 :(得分:42)

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

答案 1 :(得分:20)

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

raise "crash me" if caller.length > 500

提升500,直到它不会过早开火,你会对你不断增长的堆栈问题有一个很好的追踪。

答案 2 :(得分:12)

下面:

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

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

答案 3 :(得分:6)

结合来自多个答案的建议,当您的调用堆栈太深时,这将抛出错误(带有堆栈跟踪)。这会降低所有方法调用的速度,所以你应该尝试尽可能接近你认为无限循环发生的地方。

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

答案 4 :(得分: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
}

答案 5 :(得分:3)

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

答案 6 :(得分:3)

显然,这被跟踪为feature 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>'

答案 7 :(得分:1)

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

然后,我尝试了#34;愚蠢的事情&#34;。我在终端中运行了问题代码(在我的情况下,单元测试),然后在我认为攻击性测试正在运行时按下Ctrl-C(一些puts调用帮助)。果然,我得到了完整的回溯:)

我有一个完整的3或4秒钟来响应2013 Macbook Pro。