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

在编码ruby时,我经常很难调试无限递归。 有没有办法从SystemStackError中获取回溯以找出无限循环到底发生在哪里?

给定一些方法test.rb:6: stack level too deep (SystemStackError)foobar,它们在一个循环中相互调用:

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错误阻止了我将其与我想调试的软件一起使用。 因此,确切地说,问题是:

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

iblue asked 2020-02-21T12:29:50Z
8个解决方案
42 votes

后来找到这个问题的另一种方法...优秀的要旨提供有关在控制台中启用跟踪功能以及将所有函数调用打印到文件的说明。 刚刚在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()
Elliot Nelson answered 2020-02-21T12:30:28Z
20 votes

这是我在调试ruby / rails时不时遇到的一个令人烦恼的问题。 我刚刚发现了一种可行的技术,可以在堆栈崩溃导致系统堆栈崩溃和真实回溯丢失或出现乱码之前检测出堆栈超出范围。 基本模式是:

raise "crash me" if caller.length > 500

提高500,直到它不会过早点火,您将很好地了解不断增长的堆栈问题。

Gerry Gleason answered 2020-02-21T12:30:54Z
13 votes

这里:

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

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

Linuxios answered 2020-02-21T12:31:19Z
6 votes

结合来自多个答案的建议,这将在您的调用堆栈太深时引发错误(带有堆栈跟踪)。 这将降低所有方法的调用速度,因此您应尽量将其置于您认为无限循环正在发生的位置。

set_trace_func proc {
  |event, file, line, id, binding, classname| 
  if event == "call"  && caller_locations.length > 500
    fail "stack level too deep"
  end
}
Jonathan Swartz answered 2020-02-21T12:31:39Z
4 votes

如果您碰巧使用了撬,这实际上将使您陷入太深的方法调用链中。

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
}
Paul Alexander answered 2020-02-21T12:32:00Z
3 votes

显然,此功能已作为功能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>'
Martin Vidner answered 2020-02-21T12:30:03Z
3 votes

您可以使用Ruby 1.8获得这种堆栈跟踪。 如果仅存在1.9样式语法(例如{foo: 42}),则编译Ruby 1.8 head。

Andrew Grimm answered 2020-02-21T12:32:20Z
1 votes

我在这里尝试了许多事情,但是找不到递归的位置(我正在使用Ruby 2.0)。

然后,我尝试了“愚蠢的事情”。 我在终端中运行了问题代码(在我的情况下,是单元测试),然后当我认为攻击性测试正在运行时,按了Ctrl-C(有一些puts呼叫帮助了)。 果然,我得到了完整的追溯:)

我花了整整3或4秒钟来回应2013 Macbook Pro。

makdad answered 2020-02-21T12:32:53Z
translate from https://stackoverflow.com:/questions/11544460/how-to-get-a-backtrace-from-a-systemstackerror-stack-level-too-deep