[ruby-core:76948] [Ruby trunk Bug#6783] Infinite loop in inspect, not overriding inspect, to_s, and no known circular references. Stepping into inspect in debugger locks it up with 100% CPU.

From: stefan.kroes@...
Date: 2016-08-17 13:15:39 UTC
List: ruby-core #76948
Issue #6783 has been updated by Stefan Kroes.


I would like to reopen discussion on this subject. I think the default implementation of #inspect tends to hang/explode for complex/large object graphs with lots of cycles. In 10 years of programming Ruby I ran into this twice and had to waste several hours before finding the problem twice (today and several years ago if I remember correctly). Inspect is often used for generating error messages which will hang in turn, misdirecting debugging efforts to the original error.

To clarify: I don't really think this is a bug, just an aspect of Ruby that may cause grief (especially to new users) and can be improved.

A simple script to reproduce:

```
class Base
  attr_accessor :foos, :bars, :bazs
end

class Foo < Base; end
class Bar < Base; end
class Baz < Base; end

foos = Array.new(100) { Foo.new }
bars = Array.new(100) { Bar.new }
bazs = Array.new(100) { Baz.new }

[*foos, *bars, *bazs].each do |base|
  base.foos = foos
  base.bars = bars
  base.bazs = bazs
end

puts foos.inspect.size
```

```
{14:54}[2.3.1]~ 筐ュ time ruby test.rb
127165300
ruby test.rb  7.77s user 0.29s system 97% cpu 8.237 total
```

This example seems somewhat contrived and 7 seconds doesn't seem long but I just had a real-life object graph of a large state machine hang my process for at least 20 minutes (broke it off).

I know the documentation for Object#inspect says `User defined classes should override this method to make better representation of obj.` but I don't think many people do this, especially as the default implementation is very useful.

Possible solutions include:

* Further limiting recursion of default inspect
* Limiting number of elements shown in inspect for Hash, Array, etc.
* Putting a timeout on the default inspect, informing the user he/she should override inspect with something sensible for a certain class
  * Timeout should be nested so it triggers for the deepest inspect that takes too long

----------------------------------------
Bug #6783: Infinite loop in inspect, not overriding inspect, to_s, and no known circular references. Stepping into inspect in debugger locks it up with 100% CPU.
https://bugs.ruby-lang.org/issues/6783#change-60177

* Author: Gary Weaver
* Status: Closed
* Priority: Normal
* Assignee: 
* ruby -v: ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-darwin11.4.0]
* Backport: 2.1: UNKNOWN, 2.2: UNKNOWN, 2.3: UNKNOWN
----------------------------------------
In Ruby 1.9.3p194 in Rails 3.2.6 in rails console, in my script I'm calling inspect on a Ruby object and even though I'm not overriding inspect, to_s, and there are no known circular references, inspect is going into an infinite loop locking Ruby up with 100% CPU usage.

At first, I would think this problem is probably outside of Ruby and either in my code or in a gem that I'm using, however the problem is that using the Debugger gem, if I set a breakpoint above the issue and use "s" (by itself) to step into the line where it calls inspect, it locks up there, so I can't debug the issue. When I do that I hit ctrl-c, I'm in .../.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/irb.rb:

       64      trap("SIGINT") do
    => 65        irb.signal_handle
       66      end

and breaking out of that, or if I don't step into it and I break out of it, I see:

	path_to_script/script_name.rb:739:in `call'
	path_to_script/script_name.rb:739:in `inspect'
	path_to_script/script_name.rb:739:in `inspect'
	...
        (~100 times)
        path_to_script/script_name.rb:739:in `block (2 levels) in my_method_name'

In a situation like this, how can I debug the issue? Is there anything in the inspect method that could causing this behavior?

I think the most likely culprit is some bad code on my part in the script, but unfortunately I can't debug it when the debugger can't step into inspect.

Thanks for any help you can provide.



-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>

In This Thread