fix it?

Maybe this works

I started to see the setup work when I ran it in the testcase directory using what looked like the same gemset - the only difference appears to be missing the nokogiri versions for mac (since I started with no lockfile this makes sense):

djuber@forem:~/src/testcase38666$ diff -u ../forem/Gemfile.lock Gemfile.lock
--- ../forem/Gemfile.lock       2021-04-08 11:31:56.620181461 -0500
+++ Gemfile.lock        2021-04-08 11:33:08.199653760 -0500
@@ -459,10 +459,6 @@
       http-2 (~> 0.11)
     netrc (0.11.0)
     nio4r (2.5.7)
-    nokogiri (1.11.3-arm64-darwin)
-      racc (~> 1.4)
-    nokogiri (1.11.3-x86_64-darwin)
-      racc (~> 1.4)
     nokogiri (1.11.3-x86_64-linux)
       racc (~> 1.4)
     notiffany (0.1.3)
bundle update chartkick ahoy_email marcel oauth omniauth parser sidekiq 

This gets the forem gemfile.lock looking very like the one in my environment - however I'm still getting the same issue (well, almost the same - it's stilli happening when trying to get the class information for Hash, but the location moved, this could be completely related to the initializers and their ordering):

require [c function] - (unknown):0
<main> - /home/djuber/src/forem/vendor/cache/ruby/3.0.0/gems/rspec-core-3.10.1/lib/rspec/core/metadata.rb:1
<module:RSpec> - /home/djuber/src/forem/vendor/cache/ruby/3.0.0/gems/rspec-core-3.10.1/lib/rspec/core/metadata.rb:498
<module:Core> - /home/djuber/src/forem/vendor/cache/ruby/3.0.0/gems/rspec-core-3.10.1/lib/rspec/core/metadata.rb:497
<module:HashImitatable> - /home/djuber/src/forem/vendor/cache/ruby/3.0.0/gems/rspec-core-3.10.1/lib/rspec/core/metadata.rb:445
block (2 levels) in <class:Class> - /home/djuber/src/forem/vendor/cache/ruby/3.0.0/gems/amazing_print-1.3.0/lib/amazing_print/core_ext/class.rb:23
call [c function] - (unknown):0
public_instance_methods [c function] - (unknown):0

When all else fails - debug it!

The critical path that's causing the issue is this for loop in search_method

The debug counters are in debug_counter.h (and are guarded by a compile time define so might just be a cast to void(0);

Meanwhile in ruby

I added a pry breakpoint to the define_method for AmazingPrint::Class core extension

Inside protected_instance_methodshere - and even show-method Hash was sufficient to get the system locked in a loop (the backtrace points to public_instance_methods but that's not any different.) Will kill and get back here in a second and be very gentle.

So much for gentle - cd Hash worked - but pry's ls command calls public_instance_methods as part of the introspection. Re-attach and find the problem I guess - time to learn about inspecting stack frames.

Here me is a method entry or rb_method_entry_t

One hint if you're trying this again - pointers are hex values and VALUE's (which I assume are lookup table keys?) are integer types.

This is looking at the class's methods (we were passed in a method entry for a defined class value pointer) super class, if there is no super we return 0 - if there is we call search_method_protect

I set a breakpoint on search method protect - the first call passed null defined class, id = 140079, and klass = 94240761825200

We were in resolve_refined_method (refinements=8, me=0x55b6264afec0, defined_class_ptr=0x0) so this makes sense (null was forwarded to the next method).

We can see the loop happening :

Single stepping shows the places this goes - it's possible rb_id_table_lookup having an optimized out table is suspicious.

Detached and re-attached while directory was set and I get a killed process when I attach.

Restepping through on the ruby side - with the breakpoint in define method in amazing print.

I noticed we're wrapping object's methods here. That sounds sneaky and hopefully avoidable. In any case - I can call Object.public_instance_methods after this is defined - it looks like something else is happening to hash before we get to the wrapper that fetches the original method (the wrapper is hitting a loop that was already there). As I understand it - the inheritance structure for Hash is Hash -> Object -> BasicObject -> HereBeDragons. I did not see BasicObject ever getting wrapped here.

Tighter loop just breaking on rb id table lookup hits two tables with the same key:

ID SCOPE SHIFT is RUBY_ID_SCOPE_SHIFT is 4

This is notop id is a macro (so gdb doesn't understand it) but we can add the original definition:

it looks like those are just the "real real constant ruby method ids" defined in id.h (there's an enum, with t preserved id begin = 150 and t preserved id end followed by tToken local begin = end - 1. All of this is generated during build from the template, which in turn looks like it's tied to parse.y/parse.c where TOKEN2ID is defined - basically these are the "operator tokens" and our method is not one of them.

Loading the ruby macros defined in https://github.com/ruby/ruby/blob/master/.gdbinit helps a lot here

so we have Hash, Object, "ActiveSupport::ToJsonWithActiveSupportEncoder" all in play here. The 70039 called_id is a fixnum (it's 140079 >> 1)

Last updated

Was this helpful?