2015-12-16 08:07:31 +03:00
|
|
|
|
# frozen_string_literal: false
|
2010-08-14 13:01:12 +04:00
|
|
|
|
require "test/unit"
|
|
|
|
|
require "objspace"
|
2016-06-11 08:04:22 +03:00
|
|
|
|
begin
|
|
|
|
|
require "json"
|
|
|
|
|
rescue LoadError
|
|
|
|
|
end
|
2010-08-14 13:01:12 +04:00
|
|
|
|
|
|
|
|
|
class TestObjSpace < Test::Unit::TestCase
|
|
|
|
|
def test_memsize_of
|
|
|
|
|
assert_equal(0, ObjectSpace.memsize_of(true))
|
2010-10-27 22:16:39 +04:00
|
|
|
|
assert_equal(0, ObjectSpace.memsize_of(nil))
|
|
|
|
|
assert_equal(0, ObjectSpace.memsize_of(1))
|
2010-08-14 13:01:12 +04:00
|
|
|
|
assert_kind_of(Integer, ObjectSpace.memsize_of(Object.new))
|
|
|
|
|
assert_kind_of(Integer, ObjectSpace.memsize_of(Class))
|
|
|
|
|
assert_kind_of(Integer, ObjectSpace.memsize_of(""))
|
|
|
|
|
assert_kind_of(Integer, ObjectSpace.memsize_of([]))
|
|
|
|
|
assert_kind_of(Integer, ObjectSpace.memsize_of({}))
|
|
|
|
|
assert_kind_of(Integer, ObjectSpace.memsize_of(//))
|
|
|
|
|
f = File.new(__FILE__)
|
|
|
|
|
assert_kind_of(Integer, ObjectSpace.memsize_of(f))
|
|
|
|
|
f.close
|
|
|
|
|
assert_kind_of(Integer, ObjectSpace.memsize_of(/a/.match("a")))
|
|
|
|
|
assert_kind_of(Integer, ObjectSpace.memsize_of(Struct.new(:a)))
|
2011-12-15 08:15:54 +04:00
|
|
|
|
|
|
|
|
|
assert_operator(ObjectSpace.memsize_of(Regexp.new("(a)"*1000).match("a"*1000)),
|
|
|
|
|
:>,
|
|
|
|
|
ObjectSpace.memsize_of(//.match("")))
|
2010-08-14 13:01:12 +04:00
|
|
|
|
end
|
|
|
|
|
|
2013-11-26 21:27:01 +04:00
|
|
|
|
def test_memsize_of_root_shared_string
|
2021-08-26 17:06:32 +03:00
|
|
|
|
a = "a" * GC::INTERNAL_CONSTANTS[:RVARGC_MAX_ALLOCATE_SIZE]
|
2013-11-26 21:27:01 +04:00
|
|
|
|
b = a.dup
|
|
|
|
|
c = nil
|
|
|
|
|
ObjectSpace.each_object(String) {|x| break c = x if x == a and x.frozen?}
|
2022-02-01 17:25:12 +03:00
|
|
|
|
rv_size = GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE]
|
2021-08-26 17:06:32 +03:00
|
|
|
|
assert_equal([rv_size, rv_size, a.length + 1 + rv_size], [a, b, c].map {|x| ObjectSpace.memsize_of(x)})
|
2013-11-26 21:27:01 +04:00
|
|
|
|
end
|
|
|
|
|
|
2011-12-15 12:57:01 +04:00
|
|
|
|
def test_argf_memsize
|
|
|
|
|
size = ObjectSpace.memsize_of(ARGF)
|
|
|
|
|
assert_kind_of(Integer, size)
|
|
|
|
|
assert_operator(size, :>, 0)
|
|
|
|
|
argf = ARGF.dup
|
|
|
|
|
argf.inplace_mode = nil
|
|
|
|
|
size = ObjectSpace.memsize_of(argf)
|
|
|
|
|
argf.inplace_mode = "inplace_mode_suffix"
|
2017-10-10 15:30:42 +03:00
|
|
|
|
assert_equal(size, ObjectSpace.memsize_of(argf))
|
2011-12-15 12:57:01 +04:00
|
|
|
|
end
|
|
|
|
|
|
2010-10-29 00:23:21 +04:00
|
|
|
|
def test_memsize_of_all
|
|
|
|
|
assert_kind_of(Integer, a = ObjectSpace.memsize_of_all)
|
|
|
|
|
assert_kind_of(Integer, b = ObjectSpace.memsize_of_all(String))
|
2015-04-01 09:02:45 +03:00
|
|
|
|
assert_operator(a, :>, b)
|
|
|
|
|
assert_operator(a, :>, 0)
|
|
|
|
|
assert_operator(b, :>, 0)
|
2010-10-29 00:23:21 +04:00
|
|
|
|
assert_raise(TypeError) {ObjectSpace.memsize_of_all('error')}
|
2010-10-27 22:16:39 +04:00
|
|
|
|
end
|
|
|
|
|
|
2010-08-14 13:01:12 +04:00
|
|
|
|
def test_count_objects_size
|
|
|
|
|
res = ObjectSpace.count_objects_size
|
2015-03-13 08:12:43 +03:00
|
|
|
|
assert_not_empty(res)
|
|
|
|
|
assert_operator(res[:TOTAL], :>, 0)
|
2017-09-18 02:56:37 +03:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def test_count_objects_size_with_hash
|
2010-08-14 13:01:12 +04:00
|
|
|
|
arg = {}
|
|
|
|
|
ObjectSpace.count_objects_size(arg)
|
2015-03-13 08:12:43 +03:00
|
|
|
|
assert_not_empty(arg)
|
2017-09-18 02:56:37 +03:00
|
|
|
|
arg = {:TOTAL => 1 }
|
|
|
|
|
ObjectSpace.count_objects_size(arg)
|
|
|
|
|
assert_not_empty(arg)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def test_count_objects_size_with_wrong_type
|
|
|
|
|
assert_raise(TypeError) { ObjectSpace.count_objects_size(0) }
|
2010-08-14 13:01:12 +04:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def test_count_nodes
|
|
|
|
|
res = ObjectSpace.count_nodes
|
2015-03-13 08:12:43 +03:00
|
|
|
|
assert_not_empty(res)
|
2010-08-14 13:01:12 +04:00
|
|
|
|
arg = {}
|
|
|
|
|
ObjectSpace.count_nodes(arg)
|
2013-03-05 05:40:52 +04:00
|
|
|
|
assert_not_empty(arg)
|
|
|
|
|
bug8014 = '[ruby-core:53130] [Bug #8014]'
|
|
|
|
|
assert_empty(arg.select {|k, v| !(Symbol === k && Integer === v)}, bug8014)
|
2015-04-01 09:02:45 +03:00
|
|
|
|
end if false
|
2010-08-14 13:01:12 +04:00
|
|
|
|
|
|
|
|
|
def test_count_tdata_objects
|
|
|
|
|
res = ObjectSpace.count_tdata_objects
|
2015-03-13 08:12:43 +03:00
|
|
|
|
assert_not_empty(res)
|
2010-08-14 13:01:12 +04:00
|
|
|
|
arg = {}
|
|
|
|
|
ObjectSpace.count_tdata_objects(arg)
|
2015-03-13 08:12:43 +03:00
|
|
|
|
assert_not_empty(arg)
|
2010-08-14 13:01:12 +04:00
|
|
|
|
end
|
2012-10-05 12:14:09 +04:00
|
|
|
|
|
2015-04-10 13:38:13 +03:00
|
|
|
|
def test_count_imemo_objects
|
|
|
|
|
res = ObjectSpace.count_imemo_objects
|
|
|
|
|
assert_not_empty(res)
|
|
|
|
|
assert_not_nil(res[:imemo_cref])
|
2019-02-13 07:24:59 +03:00
|
|
|
|
assert_not_empty res.inspect
|
|
|
|
|
|
2015-04-10 13:38:13 +03:00
|
|
|
|
arg = {}
|
|
|
|
|
res = ObjectSpace.count_imemo_objects(arg)
|
|
|
|
|
assert_not_empty(res)
|
|
|
|
|
end
|
|
|
|
|
|
2015-08-12 15:18:51 +03:00
|
|
|
|
def test_memsize_of_iseq
|
2019-06-30 03:34:41 +03:00
|
|
|
|
iseqw = RubyVM::InstructionSequence.compile('def a; a = :b; a; end')
|
2021-05-12 11:35:57 +03:00
|
|
|
|
# Use anonymous class as a basic object size because size of Object.new can be increased
|
2021-05-12 11:40:52 +03:00
|
|
|
|
base_obj_size = ObjectSpace.memsize_of(Class.new.new)
|
2015-08-12 15:18:51 +03:00
|
|
|
|
assert_operator(ObjectSpace.memsize_of(iseqw), :>, base_obj_size)
|
|
|
|
|
end
|
|
|
|
|
|
2012-10-05 12:14:09 +04:00
|
|
|
|
def test_reachable_objects_from
|
2019-06-23 01:42:11 +03:00
|
|
|
|
opts = %w[--disable-gem --disable=frozen-string-literal -robjspace]
|
|
|
|
|
assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}"
|
2017-01-04 03:30:45 +03:00
|
|
|
|
begin;
|
2022-09-23 20:54:42 +03:00
|
|
|
|
assert_equal(nil, ObjectSpace.reachable_objects_from(nil))
|
2022-09-27 02:09:50 +03:00
|
|
|
|
assert_equal([Array, 'a', 'b', 'c'], ObjectSpace.reachable_objects_from(['a', 'b', 'c']))
|
|
|
|
|
|
|
|
|
|
assert_equal([Array, 'a', 'a', 'a'], ObjectSpace.reachable_objects_from(['a', 'a', 'a']))
|
|
|
|
|
assert_equal([Array, 'a', 'a'], ObjectSpace.reachable_objects_from(['a', v = 'a', v]))
|
|
|
|
|
assert_equal([Array, 'a'], ObjectSpace.reachable_objects_from([v = 'a', v, v]))
|
2017-01-04 03:30:45 +03:00
|
|
|
|
|
|
|
|
|
long_ary = Array.new(1_000){''}
|
|
|
|
|
max = 0
|
|
|
|
|
|
|
|
|
|
ObjectSpace.each_object{|o|
|
|
|
|
|
refs = ObjectSpace.reachable_objects_from(o)
|
|
|
|
|
max = [refs.size, max].max
|
|
|
|
|
|
|
|
|
|
unless refs.nil?
|
|
|
|
|
refs.each_with_index {|ro, i|
|
|
|
|
|
assert_not_nil(ro, "#{i}: this referenced object is internal object")
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
}
|
|
|
|
|
assert_operator(max, :>=, long_ary.size+1, "1000 elems + Array class")
|
|
|
|
|
end;
|
2012-10-05 12:14:09 +04:00
|
|
|
|
end
|
2012-12-08 04:18:42 +04:00
|
|
|
|
|
2022-02-09 23:14:51 +03:00
|
|
|
|
def test_reachable_objects_during_iteration
|
2023-12-07 20:41:06 +03:00
|
|
|
|
omit 'flaky on Visual Studio with: [BUG] Unnormalized Fixnum value' if /mswin/ =~ RUBY_PLATFORM
|
2022-02-09 23:14:51 +03:00
|
|
|
|
opts = %w[--disable-gem --disable=frozen-string-literal -robjspace]
|
|
|
|
|
assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}"
|
|
|
|
|
begin;
|
|
|
|
|
ObjectSpace.each_object{|o|
|
|
|
|
|
o.inspect
|
|
|
|
|
ObjectSpace.reachable_objects_from(Class)
|
|
|
|
|
}
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
2013-10-16 10:13:41 +04:00
|
|
|
|
def test_reachable_objects_from_root
|
|
|
|
|
root_objects = ObjectSpace.reachable_objects_from_root
|
|
|
|
|
|
|
|
|
|
assert_operator(root_objects.size, :>, 0)
|
|
|
|
|
|
|
|
|
|
root_objects.each{|category, objects|
|
|
|
|
|
assert_kind_of(String, category)
|
|
|
|
|
assert_kind_of(Array, objects)
|
|
|
|
|
assert_operator(objects.size, :>, 0)
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
2012-12-08 04:18:42 +04:00
|
|
|
|
def test_reachable_objects_size
|
2017-01-04 03:30:45 +03:00
|
|
|
|
assert_separately %w[--disable-gem -robjspace], "#{<<~"begin;"}\n#{<<~'end;'}"
|
|
|
|
|
begin;
|
|
|
|
|
ObjectSpace.each_object{|o|
|
|
|
|
|
ObjectSpace.reachable_objects_from(o).each{|reached_obj|
|
|
|
|
|
size = ObjectSpace.memsize_of(reached_obj)
|
|
|
|
|
assert_kind_of(Integer, size)
|
|
|
|
|
assert_operator(size, :>=, 0)
|
|
|
|
|
}
|
2012-12-08 04:18:42 +04:00
|
|
|
|
}
|
2017-01-04 03:30:45 +03:00
|
|
|
|
end;
|
2012-12-08 04:18:42 +04:00
|
|
|
|
end
|
2013-05-27 14:01:45 +04:00
|
|
|
|
|
2020-04-03 16:28:06 +03:00
|
|
|
|
def test_trace_object_allocations_stop_first
|
|
|
|
|
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
|
|
|
|
|
begin;
|
|
|
|
|
require "objspace"
|
2021-04-25 18:10:39 +03:00
|
|
|
|
# Make sure stopping before the tracepoints are initialized doesn't raise. See [Bug #17020]
|
2020-04-03 16:28:06 +03:00
|
|
|
|
ObjectSpace.trace_object_allocations_stop
|
|
|
|
|
end;
|
|
|
|
|
end
|
|
|
|
|
|
2013-09-30 12:17:30 +04:00
|
|
|
|
def test_trace_object_allocations
|
2020-08-19 05:56:42 +03:00
|
|
|
|
ObjectSpace.trace_object_allocations_clear # clear object_table to get rid of erroneous detection for c0
|
2013-12-10 06:26:09 +04:00
|
|
|
|
Class.name
|
2013-05-27 14:01:45 +04:00
|
|
|
|
o0 = Object.new
|
|
|
|
|
ObjectSpace.trace_object_allocations{
|
2013-05-28 07:36:34 +04:00
|
|
|
|
o1 = Object.new; line1 = __LINE__; c1 = GC.count
|
|
|
|
|
o2 = "xyzzy" ; line2 = __LINE__; c2 = GC.count
|
|
|
|
|
o3 = [1, 2] ; line3 = __LINE__; c3 = GC.count
|
2013-05-27 14:01:45 +04:00
|
|
|
|
|
|
|
|
|
assert_equal(nil, ObjectSpace.allocation_sourcefile(o0))
|
|
|
|
|
assert_equal(nil, ObjectSpace.allocation_sourceline(o0))
|
2013-05-28 07:36:34 +04:00
|
|
|
|
assert_equal(nil, ObjectSpace.allocation_generation(o0))
|
|
|
|
|
|
2013-05-27 14:01:45 +04:00
|
|
|
|
assert_equal(line1, ObjectSpace.allocation_sourceline(o1))
|
2013-05-28 07:36:34 +04:00
|
|
|
|
assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(o1))
|
|
|
|
|
assert_equal(c1, ObjectSpace.allocation_generation(o1))
|
|
|
|
|
assert_equal(Class.name, ObjectSpace.allocation_class_path(o1))
|
|
|
|
|
assert_equal(:new, ObjectSpace.allocation_method_id(o1))
|
|
|
|
|
|
2013-05-27 14:01:45 +04:00
|
|
|
|
assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(o2))
|
|
|
|
|
assert_equal(line2, ObjectSpace.allocation_sourceline(o2))
|
2013-05-28 07:36:34 +04:00
|
|
|
|
assert_equal(c2, ObjectSpace.allocation_generation(o2))
|
|
|
|
|
assert_equal(self.class.name, ObjectSpace.allocation_class_path(o2))
|
|
|
|
|
assert_equal(__method__, ObjectSpace.allocation_method_id(o2))
|
|
|
|
|
|
2013-05-27 14:01:45 +04:00
|
|
|
|
assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(o3))
|
|
|
|
|
assert_equal(line3, ObjectSpace.allocation_sourceline(o3))
|
2013-05-28 07:36:34 +04:00
|
|
|
|
assert_equal(c3, ObjectSpace.allocation_generation(o3))
|
|
|
|
|
assert_equal(self.class.name, ObjectSpace.allocation_class_path(o3))
|
|
|
|
|
assert_equal(__method__, ObjectSpace.allocation_method_id(o3))
|
2023-02-24 17:20:14 +03:00
|
|
|
|
|
|
|
|
|
# [Bug #19456]
|
|
|
|
|
o4 =
|
|
|
|
|
# This line intentionally left blank
|
|
|
|
|
# This line intentionally left blank
|
2023-05-10 17:49:44 +03:00
|
|
|
|
1.0 / 0.0; line4 = __LINE__; _c4 = GC.count
|
2023-02-24 17:20:14 +03:00
|
|
|
|
assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(o4))
|
|
|
|
|
assert_equal(line4, ObjectSpace.allocation_sourceline(o4))
|
2023-03-07 17:50:30 +03:00
|
|
|
|
|
2023-08-17 06:28:33 +03:00
|
|
|
|
# The line number should be based on newarray instead of getinstancevariable.
|
|
|
|
|
line5 = __LINE__; o5 = [ # newarray (leaf)
|
|
|
|
|
@ivar, # getinstancevariable (not leaf)
|
|
|
|
|
]
|
|
|
|
|
assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(o5))
|
|
|
|
|
assert_equal(line5, ObjectSpace.allocation_sourceline(o5))
|
|
|
|
|
|
2023-03-07 17:50:30 +03:00
|
|
|
|
# [Bug #19482]
|
|
|
|
|
EnvUtil.under_gc_stress do
|
|
|
|
|
100.times do
|
|
|
|
|
Class.new
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-05-27 14:01:45 +04:00
|
|
|
|
}
|
2020-08-19 03:34:03 +03:00
|
|
|
|
end
|
2013-09-30 12:17:30 +04:00
|
|
|
|
|
|
|
|
|
def test_trace_object_allocations_start_stop_clear
|
2015-10-15 17:37:02 +03:00
|
|
|
|
ObjectSpace.trace_object_allocations_clear # clear object_table to get rid of erroneous detection for obj3
|
2015-10-14 20:02:56 +03:00
|
|
|
|
GC.disable # suppress potential object reuse. see [Bug #11271]
|
2013-09-30 12:17:30 +04:00
|
|
|
|
begin
|
|
|
|
|
ObjectSpace.trace_object_allocations_start
|
|
|
|
|
begin
|
|
|
|
|
ObjectSpace.trace_object_allocations_start
|
|
|
|
|
begin
|
|
|
|
|
ObjectSpace.trace_object_allocations_start
|
|
|
|
|
obj0 = Object.new
|
|
|
|
|
ensure
|
|
|
|
|
ObjectSpace.trace_object_allocations_stop
|
|
|
|
|
obj1 = Object.new
|
|
|
|
|
end
|
|
|
|
|
ensure
|
|
|
|
|
ObjectSpace.trace_object_allocations_stop
|
|
|
|
|
obj2 = Object.new
|
|
|
|
|
end
|
|
|
|
|
ensure
|
|
|
|
|
ObjectSpace.trace_object_allocations_stop
|
|
|
|
|
obj3 = Object.new
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(obj0))
|
|
|
|
|
assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(obj1))
|
|
|
|
|
assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(obj2))
|
|
|
|
|
assert_equal(nil , ObjectSpace.allocation_sourcefile(obj3)) # after tracing
|
|
|
|
|
|
|
|
|
|
ObjectSpace.trace_object_allocations_clear
|
|
|
|
|
assert_equal(nil, ObjectSpace.allocation_sourcefile(obj0))
|
|
|
|
|
assert_equal(nil, ObjectSpace.allocation_sourcefile(obj1))
|
|
|
|
|
assert_equal(nil, ObjectSpace.allocation_sourcefile(obj2))
|
|
|
|
|
assert_equal(nil, ObjectSpace.allocation_sourcefile(obj3))
|
2015-10-14 20:02:56 +03:00
|
|
|
|
ensure
|
|
|
|
|
GC.enable
|
2013-09-30 12:17:30 +04:00
|
|
|
|
end
|
2013-10-10 22:36:54 +04:00
|
|
|
|
|
2021-03-31 11:39:40 +03:00
|
|
|
|
def test_trace_object_allocations_gc_stress
|
2021-03-31 16:14:15 +03:00
|
|
|
|
EnvUtil.under_gc_stress do
|
|
|
|
|
ObjectSpace.trace_object_allocations{
|
|
|
|
|
proc{}
|
|
|
|
|
}
|
|
|
|
|
end
|
2021-03-31 11:39:40 +03:00
|
|
|
|
assert true # success
|
|
|
|
|
end
|
|
|
|
|
|
2013-12-10 06:26:09 +04:00
|
|
|
|
def test_dump_flags
|
2023-05-31 21:01:11 +03:00
|
|
|
|
# Ensure that the fstring is promoted to old generation
|
|
|
|
|
4.times { GC.start }
|
2013-12-10 06:26:09 +04:00
|
|
|
|
info = ObjectSpace.dump("foo".freeze)
|
2019-06-01 14:44:24 +03:00
|
|
|
|
assert_match(/"wb_protected":true, "old":true/, info)
|
|
|
|
|
assert_match(/"fstring":true/, info)
|
2016-06-11 08:04:22 +03:00
|
|
|
|
JSON.parse(info) if defined?(JSON)
|
2013-12-10 06:26:09 +04:00
|
|
|
|
end
|
|
|
|
|
|
2023-05-04 07:41:30 +03:00
|
|
|
|
if defined?(RubyVM::Shape)
|
2023-05-21 04:03:28 +03:00
|
|
|
|
class TooComplex; end
|
|
|
|
|
|
2023-05-04 07:41:30 +03:00
|
|
|
|
def test_dump_too_complex_shape
|
2023-05-21 10:44:10 +03:00
|
|
|
|
omit "flaky test"
|
2023-05-21 04:03:28 +03:00
|
|
|
|
|
2022-12-15 21:42:24 +03:00
|
|
|
|
RubyVM::Shape::SHAPE_MAX_VARIATIONS.times do
|
2023-04-03 21:46:43 +03:00
|
|
|
|
TooComplex.new.instance_variable_set(:"@a#{_1}", 1)
|
2022-12-15 21:42:24 +03:00
|
|
|
|
end
|
|
|
|
|
|
2023-04-03 21:46:43 +03:00
|
|
|
|
tc = TooComplex.new
|
|
|
|
|
info = ObjectSpace.dump(tc)
|
|
|
|
|
assert_not_match(/"too_complex_shape"/, info)
|
2022-12-15 21:42:24 +03:00
|
|
|
|
tc.instance_variable_set(:@new_ivar, 1)
|
|
|
|
|
info = ObjectSpace.dump(tc)
|
|
|
|
|
assert_match(/"too_complex_shape":true/, info)
|
|
|
|
|
if defined?(JSON)
|
|
|
|
|
assert_true(JSON.parse(info)["too_complex_shape"])
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
class NotTooComplex ; end
|
|
|
|
|
|
|
|
|
|
def test_dump_not_too_complex_shape
|
|
|
|
|
tc = NotTooComplex.new
|
|
|
|
|
tc.instance_variable_set(:@new_ivar, 1)
|
|
|
|
|
info = ObjectSpace.dump(tc)
|
|
|
|
|
|
|
|
|
|
assert_not_match(/"too_complex_shape"/, info)
|
|
|
|
|
if defined?(JSON)
|
|
|
|
|
assert_nil(JSON.parse(info)["too_complex_shape"])
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2013-11-14 20:06:09 +04:00
|
|
|
|
def test_dump_to_default
|
|
|
|
|
line = nil
|
2013-11-08 21:06:55 +04:00
|
|
|
|
info = nil
|
|
|
|
|
ObjectSpace.trace_object_allocations do
|
2013-11-14 20:06:09 +04:00
|
|
|
|
line = __LINE__ + 1
|
2022-11-08 23:35:31 +03:00
|
|
|
|
str = "hello w"
|
2013-11-08 21:06:55 +04:00
|
|
|
|
info = ObjectSpace.dump(str)
|
|
|
|
|
end
|
2013-11-14 20:06:09 +04:00
|
|
|
|
assert_dump_object(info, line)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def test_dump_to_io
|
|
|
|
|
line = nil
|
|
|
|
|
info = IO.pipe do |r, w|
|
|
|
|
|
th = Thread.start {r.read}
|
|
|
|
|
ObjectSpace.trace_object_allocations do
|
|
|
|
|
line = __LINE__ + 1
|
2022-11-08 23:35:31 +03:00
|
|
|
|
str = "hello w"
|
2013-11-14 20:06:09 +04:00
|
|
|
|
ObjectSpace.dump(str, output: w)
|
|
|
|
|
end
|
|
|
|
|
w.close
|
|
|
|
|
th.value
|
|
|
|
|
end
|
|
|
|
|
assert_dump_object(info, line)
|
|
|
|
|
end
|
2013-11-08 21:06:55 +04:00
|
|
|
|
|
2013-11-14 20:06:09 +04:00
|
|
|
|
def assert_dump_object(info, line)
|
|
|
|
|
loc = caller_locations(1, 1)[0]
|
2019-06-01 14:44:24 +03:00
|
|
|
|
assert_match(/"type":"STRING"/, info)
|
2022-11-08 23:35:31 +03:00
|
|
|
|
assert_match(/"embedded":true, "bytesize":7, "value":"hello w", "encoding":"UTF-8"/, info)
|
2019-06-01 14:44:24 +03:00
|
|
|
|
assert_match(/"file":"#{Regexp.escape __FILE__}", "line":#{line}/, info)
|
|
|
|
|
assert_match(/"method":"#{loc.base_label}"/, info)
|
2016-06-11 08:04:22 +03:00
|
|
|
|
JSON.parse(info) if defined?(JSON)
|
2013-11-08 21:06:55 +04:00
|
|
|
|
end
|
|
|
|
|
|
2022-02-28 17:39:16 +03:00
|
|
|
|
def test_dump_array
|
|
|
|
|
# Empty array
|
|
|
|
|
info = ObjectSpace.dump([])
|
|
|
|
|
assert_include(info, '"length":0, "embedded":true')
|
|
|
|
|
assert_not_include(info, '"shared":true')
|
|
|
|
|
|
|
|
|
|
# Non-embed array
|
|
|
|
|
arr = (1..10).to_a
|
|
|
|
|
info = ObjectSpace.dump(arr)
|
|
|
|
|
assert_include(info, '"length":10')
|
|
|
|
|
assert_not_include(info, '"embedded":true')
|
|
|
|
|
assert_not_include(info, '"shared":true')
|
|
|
|
|
|
|
|
|
|
# Shared array
|
|
|
|
|
arr1 = (1..10).to_a
|
|
|
|
|
arr = []
|
|
|
|
|
arr.replace(arr1)
|
|
|
|
|
info = ObjectSpace.dump(arr)
|
|
|
|
|
assert_include(info, '"length":10, "shared":true')
|
|
|
|
|
assert_not_include(info, '"embedded":true')
|
|
|
|
|
end
|
|
|
|
|
|
2023-01-05 18:55:44 +03:00
|
|
|
|
def test_dump_object
|
|
|
|
|
klass = Class.new
|
|
|
|
|
|
|
|
|
|
# Empty object
|
|
|
|
|
info = ObjectSpace.dump(klass.new)
|
|
|
|
|
assert_include(info, '"embedded":true')
|
|
|
|
|
assert_include(info, '"ivars":0')
|
|
|
|
|
|
|
|
|
|
# Non-embed object
|
|
|
|
|
obj = klass.new
|
|
|
|
|
5.times { |i| obj.instance_variable_set("@ivar#{i}", 0) }
|
|
|
|
|
info = ObjectSpace.dump(obj)
|
|
|
|
|
assert_not_include(info, '"embedded":true')
|
|
|
|
|
assert_include(info, '"ivars":5')
|
|
|
|
|
end
|
|
|
|
|
|
2020-08-17 16:29:51 +03:00
|
|
|
|
def test_dump_control_char
|
|
|
|
|
assert_include(ObjectSpace.dump("\x0f"), '"value":"\u000f"')
|
2020-08-17 16:36:48 +03:00
|
|
|
|
assert_include(ObjectSpace.dump("\C-?"), '"value":"\u007f"')
|
2020-08-17 16:29:51 +03:00
|
|
|
|
end
|
|
|
|
|
|
2015-06-20 23:50:20 +03:00
|
|
|
|
def test_dump_special_consts
|
|
|
|
|
# [ruby-core:69692] [Bug #11291]
|
2015-10-19 09:25:01 +03:00
|
|
|
|
assert_equal('null', ObjectSpace.dump(nil))
|
|
|
|
|
assert_equal('true', ObjectSpace.dump(true))
|
|
|
|
|
assert_equal('false', ObjectSpace.dump(false))
|
|
|
|
|
assert_equal('0', ObjectSpace.dump(0))
|
|
|
|
|
assert_equal('{"type":"SYMBOL", "value":"foo"}', ObjectSpace.dump(:foo))
|
|
|
|
|
end
|
|
|
|
|
|
2021-01-21 11:45:11 +03:00
|
|
|
|
def test_dump_singleton_class
|
|
|
|
|
assert_include(ObjectSpace.dump(Object), '"name":"Object"')
|
|
|
|
|
assert_include(ObjectSpace.dump(Kernel), '"name":"Kernel"')
|
|
|
|
|
assert_include(ObjectSpace.dump(Object.new.singleton_class), '"real_class_name":"Object"')
|
|
|
|
|
|
|
|
|
|
singleton = Object.new.singleton_class
|
|
|
|
|
singleton_dump = ObjectSpace.dump(singleton)
|
|
|
|
|
assert_include(singleton_dump, '"singleton":true')
|
|
|
|
|
if defined?(JSON)
|
|
|
|
|
assert_equal(Object, singleton.superclass)
|
|
|
|
|
superclass_address = JSON.parse(ObjectSpace.dump(Object)).fetch('address')
|
|
|
|
|
assert_equal(superclass_address, JSON.parse(singleton_dump).fetch('superclass'))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2020-08-18 10:41:38 +03:00
|
|
|
|
def test_dump_special_floats
|
|
|
|
|
assert_match(/"value":"NaN"/, ObjectSpace.dump(Float::NAN))
|
|
|
|
|
assert_match(/"value":"Inf"/, ObjectSpace.dump(Float::INFINITY))
|
|
|
|
|
assert_match(/"value":"\-Inf"/, ObjectSpace.dump(-Float::INFINITY))
|
|
|
|
|
end
|
|
|
|
|
|
2015-10-19 09:25:01 +03:00
|
|
|
|
def test_dump_dynamic_symbol
|
|
|
|
|
dump = ObjectSpace.dump(("foobar%x" % rand(0x10000)).to_sym)
|
2019-06-01 14:44:24 +03:00
|
|
|
|
assert_match(/"type":"SYMBOL"/, dump)
|
|
|
|
|
assert_match(/"value":"foobar\h+"/, dump)
|
2015-06-20 23:50:20 +03:00
|
|
|
|
end
|
|
|
|
|
|
2017-01-31 20:46:51 +03:00
|
|
|
|
def test_dump_includes_imemo_type
|
|
|
|
|
assert_in_out_err(%w[-robjspace], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error|
|
|
|
|
|
begin;
|
|
|
|
|
def dump_my_heap_please
|
|
|
|
|
ObjectSpace.dump_all(output: :stdout)
|
|
|
|
|
end
|
|
|
|
|
|
2020-10-02 12:19:04 +03:00
|
|
|
|
p dump_my_heap_please
|
2017-01-31 20:46:51 +03:00
|
|
|
|
end;
|
2020-10-02 12:19:04 +03:00
|
|
|
|
assert_equal 'nil', output.pop
|
2017-01-31 20:46:51 +03:00
|
|
|
|
heap = output.find_all { |l|
|
|
|
|
|
obj = JSON.parse(l)
|
|
|
|
|
obj['type'] == "IMEMO" && obj['imemo_type']
|
|
|
|
|
}
|
|
|
|
|
assert_operator heap.length, :>, 0
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2017-01-04 01:42:10 +03:00
|
|
|
|
def test_dump_all_full
|
2017-01-04 03:30:45 +03:00
|
|
|
|
assert_in_out_err(%w[-robjspace], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error|
|
|
|
|
|
begin;
|
|
|
|
|
def dump_my_heap_please
|
|
|
|
|
ObjectSpace.dump_all(output: :stdout, full: true)
|
|
|
|
|
end
|
2017-01-04 01:42:10 +03:00
|
|
|
|
|
2020-10-02 12:19:04 +03:00
|
|
|
|
p dump_my_heap_please
|
2017-01-04 03:30:45 +03:00
|
|
|
|
end;
|
2020-10-02 12:19:04 +03:00
|
|
|
|
assert_equal 'nil', output.pop
|
2017-01-04 03:30:45 +03:00
|
|
|
|
heap = output.find_all { |l| JSON.parse(l)['type'] == "NONE" }
|
|
|
|
|
assert_operator heap.length, :>, 0
|
2017-01-04 01:42:10 +03:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2020-07-28 13:58:49 +03:00
|
|
|
|
def test_dump_all_single_generation
|
|
|
|
|
assert_in_out_err(%w[-robjspace], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error|
|
|
|
|
|
begin;
|
|
|
|
|
def dump_my_heap_please
|
|
|
|
|
GC.start
|
|
|
|
|
ObjectSpace.trace_object_allocations_start
|
|
|
|
|
gc_gen = GC.count
|
|
|
|
|
puts gc_gen
|
|
|
|
|
@obj1 = Object.new
|
|
|
|
|
GC.start
|
|
|
|
|
@obj2 = Object.new
|
2022-12-06 14:56:51 +03:00
|
|
|
|
ObjectSpace.dump_all(output: :stdout, since: gc_gen, shapes: false)
|
2020-07-28 13:58:49 +03:00
|
|
|
|
end
|
|
|
|
|
|
2020-10-02 12:19:04 +03:00
|
|
|
|
p dump_my_heap_please
|
2020-07-28 13:58:49 +03:00
|
|
|
|
end;
|
2020-10-02 12:19:04 +03:00
|
|
|
|
assert_equal 'nil', output.pop
|
2020-07-28 13:58:49 +03:00
|
|
|
|
since = output.shift.to_i
|
|
|
|
|
assert_operator output.size, :>, 0
|
2022-12-06 14:56:51 +03:00
|
|
|
|
generations = output.map { |l| JSON.parse(l) }.map { |o| o["generation"] }.uniq.sort
|
2020-07-28 13:58:49 +03:00
|
|
|
|
assert_equal [since, since + 1], generations
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2018-10-09 06:21:57 +03:00
|
|
|
|
def test_dump_addresses_match_dump_all_addresses
|
|
|
|
|
assert_in_out_err(%w[-robjspace], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error|
|
|
|
|
|
begin;
|
|
|
|
|
def dump_my_heap_please
|
|
|
|
|
obj = Object.new
|
|
|
|
|
puts ObjectSpace.dump(obj)
|
|
|
|
|
ObjectSpace.dump_all(output: $stdout)
|
|
|
|
|
end
|
|
|
|
|
|
2020-10-02 12:19:04 +03:00
|
|
|
|
p $stdout == dump_my_heap_please
|
2018-10-09 06:21:57 +03:00
|
|
|
|
end;
|
2020-10-02 12:19:04 +03:00
|
|
|
|
assert_equal 'true', output.pop
|
2018-10-09 06:21:57 +03:00
|
|
|
|
needle = JSON.parse(output.first)
|
|
|
|
|
addr = needle['address']
|
|
|
|
|
found = output.drop(1).find { |l| JSON.parse(l)['address'] == addr }
|
|
|
|
|
assert found, "object #{addr} should be findable in full heap dump"
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def test_dump_class_addresses_match_dump_all_addresses
|
|
|
|
|
assert_in_out_err(%w[-robjspace], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error|
|
|
|
|
|
begin;
|
|
|
|
|
def dump_my_heap_please
|
|
|
|
|
obj = Object.new
|
|
|
|
|
puts ObjectSpace.dump(obj)
|
|
|
|
|
ObjectSpace.dump_all(output: $stdout)
|
|
|
|
|
end
|
|
|
|
|
|
2020-10-02 12:19:04 +03:00
|
|
|
|
p $stdout == dump_my_heap_please
|
2018-10-09 06:21:57 +03:00
|
|
|
|
end;
|
2020-10-02 12:19:04 +03:00
|
|
|
|
assert_equal 'true', output.pop
|
2018-10-09 06:21:57 +03:00
|
|
|
|
needle = JSON.parse(output.first)
|
|
|
|
|
addr = needle['class']
|
|
|
|
|
found = output.drop(1).find { |l| JSON.parse(l)['address'] == addr }
|
|
|
|
|
assert found, "object #{addr} should be findable in full heap dump"
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-02-02 00:36:29 +03:00
|
|
|
|
def test_dump_objects_dumps_page_slot_sizes
|
|
|
|
|
assert_in_out_err(%w[-robjspace], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error|
|
|
|
|
|
begin;
|
|
|
|
|
def dump_my_heap_please
|
|
|
|
|
ObjectSpace.dump_all(output: $stdout)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
p $stdout == dump_my_heap_please
|
|
|
|
|
end;
|
|
|
|
|
assert_equal 'true', output.pop
|
|
|
|
|
assert(output.count > 1)
|
|
|
|
|
output.each { |l|
|
|
|
|
|
obj = JSON.parse(l)
|
|
|
|
|
next if obj["type"] == "ROOT"
|
2022-12-06 14:56:51 +03:00
|
|
|
|
next if obj["type"] == "SHAPE"
|
2022-02-02 00:36:29 +03:00
|
|
|
|
|
2022-11-18 18:07:06 +03:00
|
|
|
|
assert_not_nil obj["slot_size"]
|
|
|
|
|
assert_equal 0, obj["slot_size"] % GC::INTERNAL_CONSTANTS[:RVALUE_SIZE]
|
2022-02-02 00:36:29 +03:00
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2023-10-11 21:09:04 +03:00
|
|
|
|
def test_dump_callinfo_includes_mid
|
2023-11-22 11:16:43 +03:00
|
|
|
|
assert_in_out_err(%w[-robjspace --disable-gems], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error|
|
2023-10-11 21:09:04 +03:00
|
|
|
|
begin;
|
|
|
|
|
class Foo
|
|
|
|
|
def foo
|
|
|
|
|
super(bar: 123) # should not crash on 0 mid
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def bar
|
|
|
|
|
baz(bar: 123) # mid: baz
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
ObjectSpace.dump_all(output: $stdout)
|
|
|
|
|
end;
|
|
|
|
|
assert_empty error
|
|
|
|
|
assert(output.count > 1)
|
2023-11-22 11:16:43 +03:00
|
|
|
|
assert_includes output.grep(/"imemo_type":"callinfo"/).join("\n"), '"mid":"baz"'
|
2023-10-11 21:09:04 +03:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-06-29 16:21:11 +03:00
|
|
|
|
def test_dump_string_coderange
|
|
|
|
|
assert_includes ObjectSpace.dump("TEST STRING"), '"coderange":"7bit"'
|
2023-11-08 16:05:52 +03:00
|
|
|
|
unknown = "TEST STRING".dup.force_encoding(Encoding::UTF_16BE)
|
2022-06-29 16:21:11 +03:00
|
|
|
|
2.times do # ensure that dumping the string doesn't mutate it
|
|
|
|
|
assert_includes ObjectSpace.dump(unknown), '"coderange":"unknown"'
|
|
|
|
|
end
|
|
|
|
|
assert_includes ObjectSpace.dump("Fée"), '"coderange":"valid"'
|
|
|
|
|
assert_includes ObjectSpace.dump("\xFF"), '"coderange":"broken"'
|
|
|
|
|
end
|
|
|
|
|
|
2020-07-10 00:59:59 +03:00
|
|
|
|
def test_dump_escapes_method_name
|
|
|
|
|
method_name = "foo\"bar"
|
|
|
|
|
klass = Class.new do
|
|
|
|
|
define_method(method_name) { "TEST STRING" }
|
|
|
|
|
end
|
|
|
|
|
ObjectSpace.trace_object_allocations_start
|
|
|
|
|
|
|
|
|
|
obj = klass.new.send(method_name)
|
|
|
|
|
|
|
|
|
|
dump = ObjectSpace.dump(obj)
|
|
|
|
|
assert_includes dump, '"method":"foo\"bar"'
|
|
|
|
|
|
|
|
|
|
parsed = JSON.parse(dump)
|
|
|
|
|
assert_equal "foo\"bar", parsed["method"]
|
|
|
|
|
ensure
|
|
|
|
|
ObjectSpace.trace_object_allocations_stop
|
|
|
|
|
end
|
|
|
|
|
|
2022-02-02 00:36:29 +03:00
|
|
|
|
def test_dump_includes_slot_size
|
|
|
|
|
str = "TEST"
|
|
|
|
|
dump = ObjectSpace.dump(str)
|
|
|
|
|
|
|
|
|
|
assert_includes dump, "\"slot_size\":#{GC::INTERNAL_CONSTANTS[:BASE_SLOT_SIZE]}"
|
|
|
|
|
end
|
|
|
|
|
|
2018-10-09 06:21:57 +03:00
|
|
|
|
def test_dump_reference_addresses_match_dump_all_addresses
|
|
|
|
|
assert_in_out_err(%w[-robjspace], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error|
|
|
|
|
|
begin;
|
|
|
|
|
def dump_my_heap_please
|
|
|
|
|
obj = Object.new
|
|
|
|
|
obj2 = Object.new
|
|
|
|
|
obj2.instance_variable_set(:@ref, obj)
|
|
|
|
|
puts ObjectSpace.dump(obj)
|
|
|
|
|
ObjectSpace.dump_all(output: $stdout)
|
|
|
|
|
end
|
|
|
|
|
|
2020-10-02 12:19:04 +03:00
|
|
|
|
p $stdout == dump_my_heap_please
|
2018-10-09 06:21:57 +03:00
|
|
|
|
end;
|
2020-10-02 12:19:04 +03:00
|
|
|
|
assert_equal 'true', output.pop
|
2018-10-09 06:21:57 +03:00
|
|
|
|
needle = JSON.parse(output.first)
|
|
|
|
|
addr = needle['address']
|
|
|
|
|
found = output.drop(1).find { |l| (JSON.parse(l)['references'] || []).include? addr }
|
|
|
|
|
assert found, "object #{addr} should be findable in full heap dump"
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-03-29 11:37:46 +03:00
|
|
|
|
def assert_test_string_entry_correct_in_dump_all(output)
|
|
|
|
|
# `TEST STRING` appears twice in the output of `ObjectSpace.dump_all`
|
|
|
|
|
# 1. To create the T_STRING object for the literal string "TEST STRING"
|
|
|
|
|
# 2. When it is assigned to the `str` variable with a new encoding
|
|
|
|
|
#
|
|
|
|
|
# This test makes assertions on the assignment to `str`, so we look for
|
|
|
|
|
# the second appearance of /TEST STRING/ in the output
|
2022-11-08 23:35:31 +03:00
|
|
|
|
test_string_in_dump_all = output.grep(/TEST2/)
|
2022-12-28 09:46:16 +03:00
|
|
|
|
|
|
|
|
|
begin
|
|
|
|
|
assert_equal(2, test_string_in_dump_all.size, "number of strings")
|
Skip unfixed assertion about objspace/dump_all
```
{"address":"0x7f8c03e9fcf0", "type":"STRING", "shape_id":10, "slot_size":40, "class":"0x7f8c00dbed98", "frozen":true, "embedded":true, "fstring":true, "bytesize":5, "value":"TEST2", "encoding":"US-ASCII", "coderange":"7bit", "memsize":40, "flags":{"wb_protected":true}}
{"address":"0x7f8c03e9ffc0", "type":"STRING", "shape_id":0, "slot_size":40, "class":"0x7f8c00dbed98", "embedded":true, "bytesize":5, "value":"TEST2", "encoding":"US-ASCII", "coderange":"7bit", "memsize":40, "flags":{"wb_protected":true}}
{"address":"0x7f8c03e487c0", "type":"STRING", "shape_id":0, "slot_size":40, "class":"0x7f8c00dbed98", "embedded":true, "bytesize":5, "value":"TEST2", "encoding":"UTF-8", "coderange":"unknown", "file":"-", "line":4, "method":"dump_my_heap_please", "generation":1, "memsize":40, "flags":{"wb_protected":true}}
1) Failure:
TestObjSpace#test_dump_all [/tmp/ruby/src/trunk-gc-asserts/test/objspace/test_objspace.rb:622]:
number of strings.
<2> expected but was
<3>.
```
This failure only occurred on a ruby built with `DEFS=\"-DRGENGC_CHECK_MODE=2\""`
and only on a specific machine (Docker container) and difficult to reproduce,
so skip this failure to check other failures.
2023-01-11 11:00:30 +03:00
|
|
|
|
rescue Test::Unit::AssertionFailedError => e
|
2022-12-28 09:46:16 +03:00
|
|
|
|
STDERR.puts e.inspect
|
|
|
|
|
STDERR.puts test_string_in_dump_all
|
Skip unfixed assertion about objspace/dump_all
```
{"address":"0x7f8c03e9fcf0", "type":"STRING", "shape_id":10, "slot_size":40, "class":"0x7f8c00dbed98", "frozen":true, "embedded":true, "fstring":true, "bytesize":5, "value":"TEST2", "encoding":"US-ASCII", "coderange":"7bit", "memsize":40, "flags":{"wb_protected":true}}
{"address":"0x7f8c03e9ffc0", "type":"STRING", "shape_id":0, "slot_size":40, "class":"0x7f8c00dbed98", "embedded":true, "bytesize":5, "value":"TEST2", "encoding":"US-ASCII", "coderange":"7bit", "memsize":40, "flags":{"wb_protected":true}}
{"address":"0x7f8c03e487c0", "type":"STRING", "shape_id":0, "slot_size":40, "class":"0x7f8c00dbed98", "embedded":true, "bytesize":5, "value":"TEST2", "encoding":"UTF-8", "coderange":"unknown", "file":"-", "line":4, "method":"dump_my_heap_please", "generation":1, "memsize":40, "flags":{"wb_protected":true}}
1) Failure:
TestObjSpace#test_dump_all [/tmp/ruby/src/trunk-gc-asserts/test/objspace/test_objspace.rb:622]:
number of strings.
<2> expected but was
<3>.
```
This failure only occurred on a ruby built with `DEFS=\"-DRGENGC_CHECK_MODE=2\""`
and only on a specific machine (Docker container) and difficult to reproduce,
so skip this failure to check other failures.
2023-01-11 11:00:30 +03:00
|
|
|
|
if test_string_in_dump_all.size == 3
|
|
|
|
|
STDERR.puts "This test is skipped because it seems hard to fix."
|
|
|
|
|
else
|
|
|
|
|
raise
|
|
|
|
|
end
|
2022-12-28 09:46:16 +03:00
|
|
|
|
end
|
2022-03-29 11:37:46 +03:00
|
|
|
|
|
2023-07-18 18:00:58 +03:00
|
|
|
|
strs = test_string_in_dump_all.reject do |s|
|
|
|
|
|
s.include?("fstring")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
assert_equal(1, strs.length)
|
|
|
|
|
|
|
|
|
|
entry_hash = JSON.parse(strs[0])
|
2022-03-29 11:37:46 +03:00
|
|
|
|
|
2022-11-08 23:35:31 +03:00
|
|
|
|
assert_equal(5, entry_hash["bytesize"], "bytesize is wrong")
|
|
|
|
|
assert_equal("TEST2", entry_hash["value"], "value is wrong")
|
|
|
|
|
assert_equal("UTF-8", entry_hash["encoding"], "encoding is wrong")
|
|
|
|
|
assert_equal("-", entry_hash["file"], "file is wrong")
|
|
|
|
|
assert_equal(4, entry_hash["line"], "line is wrong")
|
|
|
|
|
assert_equal("dump_my_heap_please", entry_hash["method"], "method is wrong")
|
2022-03-29 11:37:46 +03:00
|
|
|
|
assert_not_nil(entry_hash["generation"])
|
|
|
|
|
end
|
|
|
|
|
|
2013-11-08 21:06:55 +04:00
|
|
|
|
def test_dump_all
|
2019-06-23 01:42:11 +03:00
|
|
|
|
opts = %w[--disable-gem --disable=frozen-string-literal -robjspace]
|
2013-11-24 04:22:19 +04:00
|
|
|
|
|
2019-06-23 01:42:11 +03:00
|
|
|
|
assert_in_out_err(opts, "#{<<-"begin;"}#{<<-'end;'}") do |output, error|
|
2017-01-04 03:30:45 +03:00
|
|
|
|
begin;
|
|
|
|
|
def dump_my_heap_please
|
|
|
|
|
ObjectSpace.trace_object_allocations_start
|
|
|
|
|
GC.start
|
2022-11-08 23:35:31 +03:00
|
|
|
|
str = "TEST2".force_encoding("UTF-8")
|
2017-01-04 03:30:45 +03:00
|
|
|
|
ObjectSpace.dump_all(output: :stdout)
|
|
|
|
|
end
|
2013-11-08 21:06:55 +04:00
|
|
|
|
|
2020-10-02 12:19:04 +03:00
|
|
|
|
p dump_my_heap_please
|
2017-01-04 03:30:45 +03:00
|
|
|
|
end;
|
2022-03-29 11:37:46 +03:00
|
|
|
|
|
|
|
|
|
assert_test_string_entry_correct_in_dump_all(output)
|
2013-11-24 04:22:19 +04:00
|
|
|
|
end
|
2013-11-09 17:35:01 +04:00
|
|
|
|
|
2017-01-04 03:30:45 +03:00
|
|
|
|
assert_in_out_err(%w[-robjspace], "#{<<-"begin;"}#{<<-'end;'}") do |(output), (error)|
|
|
|
|
|
begin;
|
|
|
|
|
def dump_my_heap_please
|
|
|
|
|
ObjectSpace.trace_object_allocations_start
|
|
|
|
|
GC.start
|
2022-11-08 23:35:31 +03:00
|
|
|
|
(str = "TEST2").force_encoding("UTF-8")
|
2017-01-04 03:30:45 +03:00
|
|
|
|
ObjectSpace.dump_all().path
|
|
|
|
|
end
|
2013-11-09 17:35:01 +04:00
|
|
|
|
|
2017-01-04 03:30:45 +03:00
|
|
|
|
puts dump_my_heap_please
|
|
|
|
|
end;
|
2019-06-28 06:02:33 +03:00
|
|
|
|
assert_nil(error)
|
|
|
|
|
dump = File.readlines(output)
|
2013-11-14 20:06:09 +04:00
|
|
|
|
File.unlink(output)
|
2022-03-29 11:37:46 +03:00
|
|
|
|
|
|
|
|
|
assert_test_string_entry_correct_in_dump_all(dump)
|
2013-11-09 17:35:01 +04:00
|
|
|
|
end
|
2016-06-11 08:04:22 +03:00
|
|
|
|
|
|
|
|
|
if defined?(JSON)
|
2016-06-12 17:20:50 +03:00
|
|
|
|
args = [
|
|
|
|
|
"-rjson", "-",
|
|
|
|
|
EnvUtil.rubybin,
|
|
|
|
|
"--disable=gems", "-robjspace", "-eObjectSpace.dump_all(output: :stdout)",
|
|
|
|
|
]
|
|
|
|
|
assert_ruby_status(args, "#{<<~"begin;"}\n#{<<~"end;"}")
|
2016-06-11 08:04:22 +03:00
|
|
|
|
begin;
|
2016-06-12 17:20:50 +03:00
|
|
|
|
IO.popen(ARGV) do |f|
|
2016-09-16 20:44:45 +03:00
|
|
|
|
f.each_line.map { |x| JSON.load(x) }
|
2016-06-12 17:20:50 +03:00
|
|
|
|
end
|
2016-06-11 08:04:22 +03:00
|
|
|
|
end;
|
|
|
|
|
end
|
2013-11-08 21:06:55 +04:00
|
|
|
|
end
|
2014-03-11 01:55:51 +04:00
|
|
|
|
|
|
|
|
|
def test_dump_uninitialized_file
|
2014-03-11 17:23:43 +04:00
|
|
|
|
assert_in_out_err(%[-robjspace], <<-RUBY) do |(output), (error)|
|
2014-03-11 01:55:51 +04:00
|
|
|
|
puts ObjectSpace.dump(File.allocate)
|
|
|
|
|
RUBY
|
2014-03-11 17:23:43 +04:00
|
|
|
|
assert_nil error
|
2019-06-01 14:44:24 +03:00
|
|
|
|
assert_match(/"type":"FILE"/, output)
|
|
|
|
|
assert_not_match(/"fd":/, output)
|
2014-03-11 01:55:51 +04:00
|
|
|
|
end
|
|
|
|
|
end
|
2015-05-28 22:40:04 +03:00
|
|
|
|
|
|
|
|
|
def traverse_classes klass
|
|
|
|
|
h = {}
|
|
|
|
|
while klass && !h.has_key?(klass)
|
|
|
|
|
h[klass] = true
|
|
|
|
|
klass = ObjectSpace.internal_class_of(klass)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def test_internal_class_of
|
|
|
|
|
i = 0
|
|
|
|
|
ObjectSpace.each_object{|o|
|
|
|
|
|
traverse_classes ObjectSpace.internal_class_of(o)
|
|
|
|
|
i += 1
|
|
|
|
|
}
|
|
|
|
|
assert_operator i, :>, 0
|
|
|
|
|
end
|
|
|
|
|
|
2020-09-02 06:13:54 +03:00
|
|
|
|
def test_internal_class_of_on_ast
|
|
|
|
|
children = ObjectSpace.reachable_objects_from(RubyVM::AbstractSyntaxTree.parse("kadomatsu"))
|
|
|
|
|
children.each {|child| ObjectSpace.internal_class_of(child).itself} # this used to crash
|
|
|
|
|
end
|
|
|
|
|
|
2021-02-01 12:01:10 +03:00
|
|
|
|
def test_name_error_message
|
|
|
|
|
begin
|
|
|
|
|
bar
|
|
|
|
|
rescue => err
|
|
|
|
|
_, m = ObjectSpace.reachable_objects_from(err)
|
|
|
|
|
end
|
|
|
|
|
assert_equal(m, m.clone)
|
|
|
|
|
end
|
|
|
|
|
|
2015-05-28 22:40:04 +03:00
|
|
|
|
def traverse_super_classes klass
|
|
|
|
|
while klass
|
|
|
|
|
klass = ObjectSpace.internal_super_of(klass)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def all_super_classes klass
|
|
|
|
|
klasses = []
|
|
|
|
|
while klass
|
|
|
|
|
klasses << klass
|
|
|
|
|
klass = ObjectSpace.internal_super_of(klass)
|
|
|
|
|
end
|
|
|
|
|
klasses
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def test_internal_super_of
|
|
|
|
|
klasses = all_super_classes(String)
|
|
|
|
|
String.ancestors.each{|k|
|
|
|
|
|
case k
|
|
|
|
|
when Class
|
|
|
|
|
assert_equal(true, klasses.include?(k), k.inspect)
|
|
|
|
|
when Module
|
|
|
|
|
assert_equal(false, klasses.include?(k), k.inspect) # Internal object (T_ICLASS)
|
|
|
|
|
end
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
i = 0
|
|
|
|
|
ObjectSpace.each_object(Module){|o|
|
|
|
|
|
traverse_super_classes ObjectSpace.internal_super_of(o)
|
|
|
|
|
i += 1
|
|
|
|
|
}
|
|
|
|
|
assert_operator i, :>, 0
|
|
|
|
|
end
|
2015-08-21 14:30:24 +03:00
|
|
|
|
|
|
|
|
|
def test_count_symbols
|
2017-06-22 15:53:05 +03:00
|
|
|
|
assert_separately(%w[-robjspace], "#{<<~';;;'}")
|
|
|
|
|
h0 = ObjectSpace.count_symbols
|
|
|
|
|
|
2017-06-22 11:33:33 +03:00
|
|
|
|
syms = (1..128).map{|i| ("xyzzy#{i}_#{Process.pid}_#{rand(1_000_000)}_" * 128).to_sym}
|
2017-06-22 15:53:05 +03:00
|
|
|
|
syms << Class.new{define_method(syms[-1]){}}
|
2015-08-21 14:30:24 +03:00
|
|
|
|
|
|
|
|
|
h = ObjectSpace.count_symbols
|
2017-06-22 15:53:05 +03:00
|
|
|
|
m = proc {h0.inspect + "\n" + h.inspect}
|
|
|
|
|
assert_equal 127, h[:mortal_dynamic_symbol] - h0[:mortal_dynamic_symbol], m
|
|
|
|
|
assert_equal 1, h[:immortal_dynamic_symbol] - h0[:immortal_dynamic_symbol], m
|
|
|
|
|
assert_operator h[:immortal_static_symbol], :>=, Object.methods.size, m
|
|
|
|
|
assert_equal h[:immortal_symbol], h[:immortal_dynamic_symbol] + h[:immortal_static_symbol], m
|
|
|
|
|
;;;
|
2015-08-21 14:30:24 +03:00
|
|
|
|
end
|
2020-07-22 13:47:14 +03:00
|
|
|
|
|
|
|
|
|
def test_anonymous_class_name
|
|
|
|
|
assert_not_include ObjectSpace.dump(Class.new), '"name"'
|
|
|
|
|
assert_not_include ObjectSpace.dump(Module.new), '"name"'
|
|
|
|
|
end
|
2021-05-14 07:40:32 +03:00
|
|
|
|
|
|
|
|
|
def test_objspace_trace
|
|
|
|
|
assert_in_out_err(%w[-robjspace/trace], "#{<<-"begin;"}\n#{<<-'end;'}") do |out, err|
|
|
|
|
|
begin;
|
|
|
|
|
a = "foo"
|
|
|
|
|
b = "b" + "a" + "r"
|
|
|
|
|
c = 42
|
|
|
|
|
p a, b, c
|
|
|
|
|
end;
|
2021-05-14 12:07:58 +03:00
|
|
|
|
assert_equal ["objspace/trace is enabled"], err
|
2021-05-14 07:40:32 +03:00
|
|
|
|
assert_equal 3, out.size
|
|
|
|
|
assert_equal '"foo" @ -:2', out[0]
|
|
|
|
|
assert_equal '"bar" @ -:3', out[1]
|
|
|
|
|
assert_equal '42', out[2]
|
|
|
|
|
end
|
|
|
|
|
end
|
2022-07-22 03:54:57 +03:00
|
|
|
|
|
2022-10-17 11:50:42 +03:00
|
|
|
|
def load_allocation_path_helper method, to_binary: false
|
|
|
|
|
|
|
|
|
|
Tempfile.create(["test_ruby_load_allocation_path", ".rb"]) do |t|
|
|
|
|
|
path = t.path
|
|
|
|
|
str = "#{Time.now.to_f.to_s}_#{rand.to_s}"
|
|
|
|
|
t.puts script = <<~RUBY
|
|
|
|
|
# frozen-string-literal: true
|
|
|
|
|
return if Time.now.to_i > 0
|
|
|
|
|
$gv = 'rnd-#{str}' # unreachable, but the string literal was written
|
|
|
|
|
RUBY
|
|
|
|
|
|
|
|
|
|
t.close
|
|
|
|
|
|
|
|
|
|
if to_binary
|
|
|
|
|
bin = RubyVM::InstructionSequence.compile_file(t.path).to_binary
|
|
|
|
|
bt = Tempfile.new(['test_ruby_load_allocation_path', '.yarb'], mode: File::Constants::WRONLY)
|
|
|
|
|
bt.write bin
|
|
|
|
|
bt.close
|
|
|
|
|
|
|
|
|
|
path = bt.path
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
assert_separately(%w[-robjspace -rtempfile], <<~RUBY)
|
|
|
|
|
GC.disable
|
|
|
|
|
path = "#{path}"
|
|
|
|
|
ObjectSpace.trace_object_allocations do
|
|
|
|
|
#{method}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
n = 0
|
|
|
|
|
dump = ObjectSpace.dump_all(output: :string)
|
|
|
|
|
dump.each_line do |line|
|
|
|
|
|
if /"value":"rnd-#{str}"/ =~ line && /"frozen":true/ =~ line
|
|
|
|
|
assert Regexp.new('"file":"' + "#{path}") =~ line
|
|
|
|
|
assert Regexp.new('"line":') !~ line
|
|
|
|
|
n += 1
|
|
|
|
|
end
|
|
|
|
|
rescue ArgumentError
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
assert_equal(1, n)
|
|
|
|
|
RUBY
|
|
|
|
|
ensure
|
|
|
|
|
bt.unlink if bt
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def test_load_allocation_path_load
|
|
|
|
|
load_allocation_path_helper 'load(path)'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def test_load_allocation_path_compile_file
|
|
|
|
|
load_allocation_path_helper 'RubyVM::InstructionSequence.compile_file(path)'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def test_load_allocation_path_load_from_binary
|
|
|
|
|
# load_allocation_path_helper 'iseq = RubyVM::InstructionSequence.load_from_binary(File.binread(path))', to_binary: true
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-22 03:54:57 +03:00
|
|
|
|
def test_utf8_method_names
|
|
|
|
|
name = "utf8_❨╯°□°❩╯︵┻━┻"
|
|
|
|
|
obj = ObjectSpace.trace_object_allocations do
|
|
|
|
|
__send__(name)
|
|
|
|
|
end
|
|
|
|
|
dump = ObjectSpace.dump(obj)
|
|
|
|
|
assert_equal name, JSON.parse(dump)["method"], dump
|
|
|
|
|
end
|
|
|
|
|
|
2022-12-06 14:56:51 +03:00
|
|
|
|
def test_dump_shapes
|
|
|
|
|
json = ObjectSpace.dump_shapes(output: :string)
|
|
|
|
|
json.each_line do |line|
|
|
|
|
|
assert_include(line, '"type":"SHAPE"')
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
assert_empty ObjectSpace.dump_shapes(output: :string, since: RubyVM.stat(:next_shape_id))
|
|
|
|
|
assert_equal 2, ObjectSpace.dump_shapes(output: :string, since: RubyVM.stat(:next_shape_id) - 2).lines.size
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-22 03:54:57 +03:00
|
|
|
|
private
|
|
|
|
|
|
|
|
|
|
def utf8_❨╯°□°❩╯︵┻━┻
|
|
|
|
|
"1#{2}"
|
|
|
|
|
end
|
2010-08-14 13:01:12 +04:00
|
|
|
|
end
|