Skip to content

Instantly share code, notes, and snippets.

@rkh
Created January 1, 2009 16:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rkh/42307 to your computer and use it in GitHub Desktop.
Save rkh/42307 to your computer and use it in GitHub Desktop.
konstantin:~/Workspace/misc/rubinius-copy (git: master) $ uname --all
Linux localtoast 2.6.27-10-generic #1 SMP Fri Nov 21 12:00:22 UTC 2008 i686 GNU/Linux
konstantin:~/Workspace/misc/rubinius-copy (git: master) $ gdb --args bin/rbx -e "puts ObjectSpace.each_object { }"
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
(gdb) run
Starting program: /home/konstantin/Workspace/misc/rubinius-copy/bin/rbx -e puts\ ObjectSpace.each_object\ \{\ \}
[Thread debugging using libthread_db enabled]
[New Thread 0xb7c3f6c0 (LWP 31539)]
[New Thread 0xb793bb90 (LWP 31542)]
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xb7c3f6c0 (LWP 31539)]
rubinius::LookupTable::store (this=0x0, state=0x99de428, key=0x34e, val=0x1ba9) at vm/builtin/lookuptable.cpp:102
102 num_entries = entries_->to_native();
(gdb) bt
#0 rubinius::LookupTable::store (this=0x0, state=0x99de428, key=0x34e, val=0x1ba9) at vm/builtin/lookuptable.cpp:102
#1 0x080d551d in rubinius::Object::set_ivar (this=0xa8e0f2c, state=0x99de428, sym=0x34e, val=0x1ba9) at vm/builtin/object.cpp:396
#2 0x080d5aa3 in rubinius::Object::id (this=0xa8e0f2c, state=0x99de428) at vm/builtin/object.cpp:240
#3 0x080ad665 in rubinius::Primitives::object_id (state=0x99de428, task=0xa8845ec, msg=@0xa2a9940) at vm/gen/primitives_glue.gen.cpp:4865
#4 0x080d6679 in rubinius::performer::basic_performer (state=0x99de428, task=0xa8845ec, msg=@0xa2a9940) at vm/builtin/sendsite.cpp:82
#5 0x081118b9 in rubinius::VMMethod::interpreter (vmm=0x9ab6170, task=0xa8845ec, ctx=0xb79dd3e4) at vm/gen/instructions.cpp:2532
#6 0x080db324 in rubinius::Task::execute (this=0xa8845ec) at vm/builtin/task.cpp:575
#7 0x080b8f85 in rubinius::VM::run_and_monitor (this=0x99de428) at vm/vm.cpp:371
#8 0x080a6e5b in rubinius::CompiledFile::execute (this=0xa4111e8, state=0x99de428) at vm/compiled_file.cpp:58
#9 0x080c122c in rubinius::Environment::run_file (this=0xbff591cc, file=
{static npos = 4294967295, _M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0xbff591bc "\214\232�t�N�\t�N�\t�N�\t(�235\t"}}) at vm/environment.cpp:91
#10 0x08114da8 in main (argc=180292172, argv=0x5c0a) at vm/drivers/cli.cpp:62
the important part is line 167 - 169 in objectspace.rb
# depends on: module.rb
module ObjectSpace
# HACK: Check out ObjectSpace.known_to
# Set this to false to have a not correctly working,
# but not segfaulting version of each_object.
#
# Imagine:
# a_string = "foo"
# an_array = ["bar"]
# ObjectSpace.each_object(String) { ... }
#
# If FIND_ALL_OBJECTS_BUT_SEGFAULT is false, each_object will
# only find "foo". If it is set to true, it will find "bar", too
# but segfault at some point. (You can uncomment the part where
# each_object(nil) is traveling all constants to see that this
# realy works).
FIND_ALL_OBJECTS_BUT_SEGFAULT = true
# This does loop through all instances of what.
# FIXME: In most cases this method will be horribly slow.
def self.each_object(what = nil, &block)
raise TypeError, "class or module required" if what and not what.is_a? Module
# This loops through all objects by looping through all constants, class vars, ivars
# and local vars (the latter by looping through all contexts).
# We will only find "living" objects that way, so this is not exactly what MRI does.
if what == nil
skip_list = []
recursive_loop(Object, block, skip_list) { |an_object| known_to(an_object)}
each_object(MethodContext) do |context|
unless context.current_scope == ObjectSpace
local_vars = []
if list = context.method.local_names
local_vars += list.collect { |lvar| context.get_eval_local lvar }
end
local_vars += list.values if list = context.dynamic_locals
local_vars.each do |lvar|
recursive_loop(lvar, block, skip_list) { |an_object| known_to(an_object) }
end
end
end
return skip_list.size
end
# Finds all classes by recursivly looping through subclasses.
if what == Class
return recursive_loop(Object, block) { |a_class| a_class.__subclasses__ }
end
# Finds all modules by looping through the constants of all classes (hence
# __subclasses__) and checking whether those are modules.
if what == Module
return recursive_loop(Object, block) do |a_module|
# Get all modules that are helt in a_module.constants plus
# subclasses of a_module if a_module is a class.
list = get_constants(a_module).find_all { |const| const.is_a? Module }
list += a_module.__subclasses__ if a_module.is_a? Class
list
end
end
# This is what MRI does:
return 0 if [Fixnum, Symbol, TrueClass, FalseClass, NilClass].include? what
# All threads are kept in Thread.list.
if what == Thread
Thread.list.each(&block)
return Thread.list.size
end
# Looping through all methods by looping through all modules and
# their meta classes.
if what == CompiledMethod
count = 0
each_object(Module) do |o|
[o, o.__metaclass__].each do |a_module|
a_module.method_table.values.each do |a_method|
if a_method.is_a? CompiledMethod
yield a_method
count += 1
end
end
end
end
return count
end
# This is used by ObjectSpace.each_object(nil)-
if what == MethodContext
list = []
Thread.list.each { |t| list += t.current_context.context_stack }
list.uniq!
list.each(&block)
return list.size
end
# This is a singelton pattern, too.
if what.is_a? MetaClass
yield what.attached_instance
return 1
end
# If this is a Singelton, check whether it already has an instance.
if defined?(Singleton) and what.ancestors.include?(Singleton)
return 0 unless what.instance_eval { _instantiate? }
yield what.instance
return 1
end
# Simply loop through all instances of superclass and check.
count = 0
each_object(what.superclass) do |obj|
if obj.is_a? what
count += 1
yield obj
end
end
count
end
protected
# This is a helper method for ObjectSpace.each_object. It does expect the grepper block
# to return an array of objects and will call the each block for each one of those, than calling
# recursive_loop on each of those with the same grepper block. It will maintain a skip list to
# not call it for any object twice (and thus prevent loops).
# Note: This is no longer recursive, for speed reason.
def self.recursive_loop(start, each_block, skip = [])
list = yield start
count = 0
until list.empty?
an_object = list.shift
unless skip.include? an_object
each_block.call(an_object)
skip << an_object
yld = yield(an_object)
list += yld
count += 1
end
end
count
end
# This is a helper method for ObjectSpace.each_object returning an Array of objects
# known to an_object.
def self.known_to(an_object)
known_objects = []
case an_object
when nil, true, false, Symbol, Fixnum
return []
# I assume that any other Enumerable will be based on those two or otherwise
# use ivars / cvars / ... to reference it's values. Thus I keep the skip
# list shorter and do not get trapped in infinite lists.
when Array, Tuple
# FIXME: HACK HACK HACK HACK HACK
if FIND_ALL_OBJECTS_BUT_SEGFAULT
# HACK: Tuple#to_a does raise a VM Assertion error from time to time.
# As we'll probably meet a lot of Tuples from time to time turns out
# to be very often.
an_object.each do |value|
known_objects << value
end
end
return known_objects
when Module
known_objects += get_constants(an_object)
known_objects += an_object.__subclasses__ if an_object.is_a? Class
end
known_objects << an_object
known_objects << an_object.instance_variables.collect { |ivar| an_object.instance_variable_get(ivar) rescue nil }
known_objects << an_object.class_variables.collect { |cvar| an_object.class_variable_get(cvar) rescue nil }
end
# This is a helper method for ObjectSpace.each_object and ObjectSpace.known_to.
def self.get_constants(a_module)
a_module.constants.inject([]) do |list, const_name|
begin
list << a_module.const_get(const_name) unless const_name == "fatal"
rescue LoadError # Handles malicious autoloading.
end
list
end
end
public
# Finalizer support. Uses WeakRef to detect object death.
# WeakRef uses the GC to do all the real work.
@finalizers = Hash.new
def self.define_finalizer(obj, prc=nil, &block)
prc ||= block
if prc.nil? or !prc.respond_to?(:call)
raise ArgumentError, "action must respond to call"
end
@finalizers[obj.object_id] = [WeakRef.new(obj), prc]
return nil
end
def self.undefine_finalizer(obj)
@finalizers.delete(obj.object_id)
end
def self.run_finalizers
@finalizers.each_pair do |key, val|
unless val[0].weakref_alive?
@finalizers.delete key
val[1].call(key)
end
end
end
def self.garbage_collect
GC.start
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment