How to get a backtrace from a SystemStackError: stack level too deep?

匿名 (未验证) 提交于 2019-12-03 00:52:01

问题:

Often I get hard to debug infinite recursions when coding ruby. Is there a way to get a backtrace out of a SystemStackError to find out, where exactly the infinite loop occurs?

Example

Given some methods foo, bar and baz which call each other in a loop:

def foo   bar end  def bar   baz end  def baz   foo end  foo 

When I run this code, I just get the message test.rb:6: stack level too deep (SystemStackError). It would be useful to get at least the last 100 lines of the stack, so I could immediately see this is a loop between foo, bar and baz, like this:

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'   [...] 

Is there any way to accomplish this?

EDIT:

As you may see from the answer below, Rubinius can do it. Unfortunately some rubinius bugs prevent me from using it with the software I'd like to debug. So to be precise the question is:

How do I get a backtrace with MRI (the default ruby) 1.9?

回答1:

Apparently this was tracked as feature 6216 and fixed in 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 `
'


回答2:

Another method for those finding this question later... An excellent gist provides instructions on enabling a trace function in the console and printing all function calls to a file. Just tested on 1.9.3-p194 and it worked great.

Including here in case the gist goes away someday:

$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() 


回答3:

This has been a somewhat vexing problem that I've had from time to time in debugging ruby/rails. I just discovered a workable technique to detect the stack growing out of bounds before it crashed the system stack and the real backtrace gets lost or garbled. The basic pattern is:

raise "crash me" if caller.length > 500 

bump up the 500 until it doesn't fire prematurely and you will have a nice trace of your growing stack problem.



回答4:

Here:

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

The method Kernel#caller returns a stack backtrace as an array, so this prints the first 0 to 100 entries in the backtrace. Because caller can be called at any time (and used for some pretty weird things) even if Ruby doesn't print backtrace for SystemStackErrors, you can still get a backtrace.



回答5:

Combining suggestions from several answers, this will throw an error (with stack trace) when your call stack gets too deep. This will slow down all method calls, so you should try to put it as close to where you think the infinite loop is happening as you can.

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


回答6:

If you happen to use pry, this will actually let you break into the method call chain that's gone too deep.

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 } 


回答7:

You can get this kind of stack trace with Ruby 1.8. If the presence of 1.9 style syntax (eg {foo: 42}) is the only issue, then compile Ruby 1.8 head.



回答8:

I tried many of the things here, but could not find where the recursion was (I am using Ruby 2.0).

Then, I tried "the stupid thing". I ran the problem code (in my case, unit tests) in Terminal, and then pressed Ctrl-C when I thought the offensive test was running (a few puts calls helped). Sure enough, I got a full backtrace :)

I had a full 3 or 4 seconds to respond on a 2013 Macbook Pro.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!