Skip to content

Instantly share code, notes, and snippets.

@JoshCheek
Last active May 6, 2021 02:17
Show Gist options
  • Save JoshCheek/0d217bcdb2d2f8c6497b609d873c9838 to your computer and use it in GitHub Desktop.
Save JoshCheek/0d217bcdb2d2f8c6497b609d873c9838 to your computer and use it in GitHub Desktop.
Finding an object's referrers
require 'objspace'
require 'fiddle'
require 'json'
def heap
$heap ||= ObjectSpace
.dump_all(output: :string)
.lines.map { JSON.parse _1, symbolize_names: true }
.reject { |obj| %w[ROOT IMEMO DATA].include? obj[:type] } # probably overzealous
end
public def referrers
my_addr = JSON.parse(ObjectSpace.dump self, output: :string)['address']
heap.select { |obj| obj[:references]&.include? my_addr } # find referrers
.filter_map { |obj| obj[:address] } # get their hex address if they have one
.map { |addr| addr[2..-1].to_i(16) } # convert to int address
.map { |addr| Fiddle::Pointer.new(addr).to_value } # convert to Ruby reference
end
User = Struct.new :id, :name
Users = Struct.new :all
user1 = User.new 123, "Josh"
user2 = User.new 456, "Sal"
@users = Users.new [user1, user2]
# "Josh" shows up somewhere in the app
# We can see it came from User 123
# Which came from an array
# Stored by Users#all
# stored by main (in `@users`, but main's inspect doesn't show its ivars)
user1.name # => "Josh"
.referrers.first # => #<struct User id=123, name="Josh">
.referrers.first # => [#<struct User id=123, name="Josh">, #<struct User id=456, name="Sal">]
.referrers.first # => #<struct Users all=[#<struct User id=123, name="Josh">, #<struct User id=456, name="Sal">]>
.referrers.first # => main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment