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

It is often difficult for me to debug endless recursions when ruby ​​coding. Is there a way to get backtracking from a SystemStackError to find out exactly where the infinite loop happens?

Example

For some methods foo , bar and baz that 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 that this is a loop between foo , bar and baz , for example:

 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 do this?

EDIT:

As you can see from the answer below, Rubinius can do this. Unfortunately, some rubinius errors will prevent me from using it with software that I would like to debug. Therefore, to be precise, the question is:

How to get back trace with MRI (default ruby) 1.9?

+47
ruby
Jul 18 '12 at 15:05
source share
8 answers

This seems to have been tracked as a function of 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 `<main>' 
+3
Aug 05 '15 at 14:13
source share

Another method for those who find this question later ... an excellent gist contains instructions for including 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, if the entity disappears 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() 
+38
Feb 05 '14 at 18:52
source share

This was a rather unpleasant problem that occasionally occurred while debugging rubies / rails. I just discovered a workable technique for detecting a stack growing outside before it crashed the system stack and the real return path was lost or distorted. The main template:

 raise "crash me" if caller.length > 500 

increase the number 500 until it fires ahead of schedule and you have a good trace of your growing stack problem.

+20
Nov 13 '12 at 15:03
source share

Here:

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

The Kernel#caller method returns the backtrace of the stack as an array, so it prints the first 0 to 100 entries in the opposite direction. Since caller can be called at any time (and used for some rather strange things), even if Ruby does not print backtrace for SystemStackErrors, you can still get backtrace.

+12
Jul 18 2018-12-18T00:
source share

Combining sentences from multiple answers will cause an error (with a trace stack) when your call stack gets too deep. This will slow down all method calls, so you should try to set them as close as possible to where you think the infinite loop is going on 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 } 
+5
Feb 23 '15 at 17:47
source share

If you use pry, it will actually allow you to break into a chain of method calls that is 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 } 
+4
Aug 28 '14 at 20:42 on
source share

You can get this stack trace with Ruby 1.8. If having 1.9 style syntax (like {foo: 42} ) is the only problem, then compile the Ruby 1.8 header .

+3
Jul 18 '12 at 23:14
source share

I tried a lot of things here, but couldn't find where the recursion was (I am using Ruby 2.0).

Then I tried dumb. I ran the problem code (in my case, unit tests) in the terminal, and then pressed Ctrl-C when I thought the attack test was running (several puts calls helped). Of course I have a full back trace :)

I had a full 3 or 4 seconds to answer the 2013 Macbook Pro.

+1
May 1 '14 at 16:50
source share



All Articles