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

ibl*_*lue 48 ruby

在编写ruby时,我常常难以调试无限递归.有没有办法从a SystemStackError中找到一个回溯来找出,究竟是无限循环发生在哪里?

给定一些方法foo,barbaz在循环中相互调用:

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)

  • 嗯,非常愚蠢,当你最需要它时,你无法获得有意义的堆栈跟踪...谢谢,这对我有用 (3认同)
  • 这与@ gerry-gleason的提示一起对我特别有用.通过引发异常,我不会被不递归的调用所淹没. (2认同)

Ger*_*son 20

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

raise "crash me" if caller.length > 500
Run Code Online (Sandbox Code Playgroud)

提高500,直到它不会过早开火,你将有一个很好的跟踪你不断增长的堆栈问题.

  • sawa - 把它放在一个set_trace_func proc中,用if语句包围 - 'if event =="call"&& caller.length> 500' (3认同)

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打印回溯,你仍然可以获得回溯.

  • 使用以下代码在我的ruby 1.9.3-p194上不起作用:https://gist.github.com/3136778.它只输出`recursion.rb:6:堆栈级别太深(SystemStackError)`.你自己尝试过吗?您使用的是哪种红宝石版本? (4认同)
  • 仍然不打印包含任何方法foo,bar或baz的回溯.它输出`stack level too deep`和`recursion.rb:13:在`<main>'`,这是begin块开始的行.没有帮助. (3认同)

Jon*_*rtz 6

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

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)


Pau*_*der 5

如果您碰巧使用 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)


Mar*_*ner 3

显然,这被跟踪为功能 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)