PageRenderTime 69ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/test/objspace/test_objspace.rb

http://github.com/ruby/ruby
Ruby | 506 lines | 442 code | 62 blank | 2 comment | 17 complexity | 5cda825e3234a2aa24caf326658c08b8 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, AGPL-3.0
  1. # frozen_string_literal: false
  2. require "test/unit"
  3. require "objspace"
  4. begin
  5. require "json"
  6. rescue LoadError
  7. end
  8. class TestObjSpace < Test::Unit::TestCase
  9. def test_memsize_of
  10. assert_equal(0, ObjectSpace.memsize_of(true))
  11. assert_equal(0, ObjectSpace.memsize_of(nil))
  12. assert_equal(0, ObjectSpace.memsize_of(1))
  13. assert_kind_of(Integer, ObjectSpace.memsize_of(Object.new))
  14. assert_kind_of(Integer, ObjectSpace.memsize_of(Class))
  15. assert_kind_of(Integer, ObjectSpace.memsize_of(""))
  16. assert_kind_of(Integer, ObjectSpace.memsize_of([]))
  17. assert_kind_of(Integer, ObjectSpace.memsize_of({}))
  18. assert_kind_of(Integer, ObjectSpace.memsize_of(//))
  19. f = File.new(__FILE__)
  20. assert_kind_of(Integer, ObjectSpace.memsize_of(f))
  21. f.close
  22. assert_kind_of(Integer, ObjectSpace.memsize_of(/a/.match("a")))
  23. assert_kind_of(Integer, ObjectSpace.memsize_of(Struct.new(:a)))
  24. assert_operator(ObjectSpace.memsize_of(Regexp.new("(a)"*1000).match("a"*1000)),
  25. :>,
  26. ObjectSpace.memsize_of(//.match("")))
  27. end
  28. def test_memsize_of_root_shared_string
  29. a = "hello" * 5
  30. b = a.dup
  31. c = nil
  32. ObjectSpace.each_object(String) {|x| break c = x if x == a and x.frozen?}
  33. rv_size = GC::INTERNAL_CONSTANTS[:RVALUE_SIZE]
  34. assert_equal([rv_size, rv_size, 26 + rv_size], [a, b, c].map {|x| ObjectSpace.memsize_of(x)})
  35. end
  36. def test_argf_memsize
  37. size = ObjectSpace.memsize_of(ARGF)
  38. assert_kind_of(Integer, size)
  39. assert_operator(size, :>, 0)
  40. argf = ARGF.dup
  41. argf.inplace_mode = nil
  42. size = ObjectSpace.memsize_of(argf)
  43. argf.inplace_mode = "inplace_mode_suffix"
  44. assert_equal(size, ObjectSpace.memsize_of(argf))
  45. end
  46. def test_memsize_of_all
  47. assert_kind_of(Integer, a = ObjectSpace.memsize_of_all)
  48. assert_kind_of(Integer, b = ObjectSpace.memsize_of_all(String))
  49. assert_operator(a, :>, b)
  50. assert_operator(a, :>, 0)
  51. assert_operator(b, :>, 0)
  52. assert_raise(TypeError) {ObjectSpace.memsize_of_all('error')}
  53. end
  54. def test_count_objects_size
  55. res = ObjectSpace.count_objects_size
  56. assert_not_empty(res)
  57. assert_operator(res[:TOTAL], :>, 0)
  58. end
  59. def test_count_objects_size_with_hash
  60. arg = {}
  61. ObjectSpace.count_objects_size(arg)
  62. assert_not_empty(arg)
  63. arg = {:TOTAL => 1 }
  64. ObjectSpace.count_objects_size(arg)
  65. assert_not_empty(arg)
  66. end
  67. def test_count_objects_size_with_wrong_type
  68. assert_raise(TypeError) { ObjectSpace.count_objects_size(0) }
  69. end
  70. def test_count_nodes
  71. res = ObjectSpace.count_nodes
  72. assert_not_empty(res)
  73. arg = {}
  74. ObjectSpace.count_nodes(arg)
  75. assert_not_empty(arg)
  76. bug8014 = '[ruby-core:53130] [Bug #8014]'
  77. assert_empty(arg.select {|k, v| !(Symbol === k && Integer === v)}, bug8014)
  78. end if false
  79. def test_count_tdata_objects
  80. res = ObjectSpace.count_tdata_objects
  81. assert_not_empty(res)
  82. arg = {}
  83. ObjectSpace.count_tdata_objects(arg)
  84. assert_not_empty(arg)
  85. end
  86. def test_count_imemo_objects
  87. res = ObjectSpace.count_imemo_objects
  88. assert_not_empty(res)
  89. assert_not_nil(res[:imemo_cref])
  90. assert_not_empty res.inspect
  91. arg = {}
  92. res = ObjectSpace.count_imemo_objects(arg)
  93. assert_not_empty(res)
  94. end
  95. def test_memsize_of_iseq
  96. iseqw = RubyVM::InstructionSequence.compile('def a; a = :b; a; end')
  97. base_obj_size = ObjectSpace.memsize_of(Object.new)
  98. assert_operator(ObjectSpace.memsize_of(iseqw), :>, base_obj_size)
  99. end
  100. def test_reachable_objects_from
  101. opts = %w[--disable-gem --disable=frozen-string-literal -robjspace]
  102. assert_separately opts, "#{<<-"begin;"}\n#{<<-'end;'}"
  103. begin;
  104. assert_equal(nil, ObjectSpace.reachable_objects_from(nil))
  105. assert_equal([Array, 'a', 'b', 'c'], ObjectSpace.reachable_objects_from(['a', 'b', 'c']))
  106. assert_equal([Array, 'a', 'a', 'a'], ObjectSpace.reachable_objects_from(['a', 'a', 'a']))
  107. assert_equal([Array, 'a', 'a'], ObjectSpace.reachable_objects_from(['a', v = 'a', v]))
  108. assert_equal([Array, 'a'], ObjectSpace.reachable_objects_from([v = 'a', v, v]))
  109. long_ary = Array.new(1_000){''}
  110. max = 0
  111. ObjectSpace.each_object{|o|
  112. refs = ObjectSpace.reachable_objects_from(o)
  113. max = [refs.size, max].max
  114. unless refs.nil?
  115. refs.each_with_index {|ro, i|
  116. assert_not_nil(ro, "#{i}: this referenced object is internal object")
  117. }
  118. end
  119. }
  120. assert_operator(max, :>=, long_ary.size+1, "1000 elems + Array class")
  121. end;
  122. end
  123. def test_reachable_objects_from_root
  124. root_objects = ObjectSpace.reachable_objects_from_root
  125. assert_operator(root_objects.size, :>, 0)
  126. root_objects.each{|category, objects|
  127. assert_kind_of(String, category)
  128. assert_kind_of(Array, objects)
  129. assert_operator(objects.size, :>, 0)
  130. }
  131. end
  132. def test_reachable_objects_size
  133. assert_separately %w[--disable-gem -robjspace], "#{<<~"begin;"}\n#{<<~'end;'}"
  134. begin;
  135. ObjectSpace.each_object{|o|
  136. ObjectSpace.reachable_objects_from(o).each{|reached_obj|
  137. size = ObjectSpace.memsize_of(reached_obj)
  138. assert_kind_of(Integer, size)
  139. assert_operator(size, :>=, 0)
  140. }
  141. }
  142. end;
  143. end
  144. def test_trace_object_allocations
  145. Class.name
  146. o0 = Object.new
  147. ObjectSpace.trace_object_allocations{
  148. o1 = Object.new; line1 = __LINE__; c1 = GC.count
  149. o2 = "xyzzy" ; line2 = __LINE__; c2 = GC.count
  150. o3 = [1, 2] ; line3 = __LINE__; c3 = GC.count
  151. assert_equal(nil, ObjectSpace.allocation_sourcefile(o0))
  152. assert_equal(nil, ObjectSpace.allocation_sourceline(o0))
  153. assert_equal(nil, ObjectSpace.allocation_generation(o0))
  154. assert_equal(line1, ObjectSpace.allocation_sourceline(o1))
  155. assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(o1))
  156. assert_equal(c1, ObjectSpace.allocation_generation(o1))
  157. assert_equal(Class.name, ObjectSpace.allocation_class_path(o1))
  158. assert_equal(:new, ObjectSpace.allocation_method_id(o1))
  159. assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(o2))
  160. assert_equal(line2, ObjectSpace.allocation_sourceline(o2))
  161. assert_equal(c2, ObjectSpace.allocation_generation(o2))
  162. assert_equal(self.class.name, ObjectSpace.allocation_class_path(o2))
  163. assert_equal(__method__, ObjectSpace.allocation_method_id(o2))
  164. assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(o3))
  165. assert_equal(line3, ObjectSpace.allocation_sourceline(o3))
  166. assert_equal(c3, ObjectSpace.allocation_generation(o3))
  167. assert_equal(self.class.name, ObjectSpace.allocation_class_path(o3))
  168. assert_equal(__method__, ObjectSpace.allocation_method_id(o3))
  169. }
  170. end
  171. def test_trace_object_allocations_start_stop_clear
  172. ObjectSpace.trace_object_allocations_clear # clear object_table to get rid of erroneous detection for obj3
  173. GC.disable # suppress potential object reuse. see [Bug #11271]
  174. begin
  175. ObjectSpace.trace_object_allocations_start
  176. begin
  177. ObjectSpace.trace_object_allocations_start
  178. begin
  179. ObjectSpace.trace_object_allocations_start
  180. obj0 = Object.new
  181. ensure
  182. ObjectSpace.trace_object_allocations_stop
  183. obj1 = Object.new
  184. end
  185. ensure
  186. ObjectSpace.trace_object_allocations_stop
  187. obj2 = Object.new
  188. end
  189. ensure
  190. ObjectSpace.trace_object_allocations_stop
  191. obj3 = Object.new
  192. end
  193. assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(obj0))
  194. assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(obj1))
  195. assert_equal(__FILE__, ObjectSpace.allocation_sourcefile(obj2))
  196. assert_equal(nil , ObjectSpace.allocation_sourcefile(obj3)) # after tracing
  197. ObjectSpace.trace_object_allocations_clear
  198. assert_equal(nil, ObjectSpace.allocation_sourcefile(obj0))
  199. assert_equal(nil, ObjectSpace.allocation_sourcefile(obj1))
  200. assert_equal(nil, ObjectSpace.allocation_sourcefile(obj2))
  201. assert_equal(nil, ObjectSpace.allocation_sourcefile(obj3))
  202. ensure
  203. GC.enable
  204. end
  205. def test_dump_flags
  206. info = ObjectSpace.dump("foo".freeze)
  207. assert_match(/"wb_protected":true, "old":true/, info)
  208. assert_match(/"fstring":true/, info)
  209. JSON.parse(info) if defined?(JSON)
  210. end
  211. def test_dump_to_default
  212. line = nil
  213. info = nil
  214. ObjectSpace.trace_object_allocations do
  215. line = __LINE__ + 1
  216. str = "hello world"
  217. info = ObjectSpace.dump(str)
  218. end
  219. assert_dump_object(info, line)
  220. end
  221. def test_dump_to_io
  222. line = nil
  223. info = IO.pipe do |r, w|
  224. th = Thread.start {r.read}
  225. ObjectSpace.trace_object_allocations do
  226. line = __LINE__ + 1
  227. str = "hello world"
  228. ObjectSpace.dump(str, output: w)
  229. end
  230. w.close
  231. th.value
  232. end
  233. assert_dump_object(info, line)
  234. end
  235. def assert_dump_object(info, line)
  236. loc = caller_locations(1, 1)[0]
  237. assert_match(/"type":"STRING"/, info)
  238. assert_match(/"embedded":true, "bytesize":11, "value":"hello world", "encoding":"UTF-8"/, info)
  239. assert_match(/"file":"#{Regexp.escape __FILE__}", "line":#{line}/, info)
  240. assert_match(/"method":"#{loc.base_label}"/, info)
  241. JSON.parse(info) if defined?(JSON)
  242. end
  243. def test_dump_special_consts
  244. # [ruby-core:69692] [Bug #11291]
  245. assert_equal('null', ObjectSpace.dump(nil))
  246. assert_equal('true', ObjectSpace.dump(true))
  247. assert_equal('false', ObjectSpace.dump(false))
  248. assert_equal('0', ObjectSpace.dump(0))
  249. assert_equal('{"type":"SYMBOL", "value":"foo"}', ObjectSpace.dump(:foo))
  250. end
  251. def test_dump_dynamic_symbol
  252. dump = ObjectSpace.dump(("foobar%x" % rand(0x10000)).to_sym)
  253. assert_match(/"type":"SYMBOL"/, dump)
  254. assert_match(/"value":"foobar\h+"/, dump)
  255. end
  256. def test_dump_includes_imemo_type
  257. assert_in_out_err(%w[-robjspace], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error|
  258. begin;
  259. def dump_my_heap_please
  260. ObjectSpace.dump_all(output: :stdout)
  261. end
  262. dump_my_heap_please
  263. end;
  264. heap = output.find_all { |l|
  265. obj = JSON.parse(l)
  266. obj['type'] == "IMEMO" && obj['imemo_type']
  267. }
  268. assert_operator heap.length, :>, 0
  269. end
  270. end
  271. def test_dump_all_full
  272. assert_in_out_err(%w[-robjspace], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error|
  273. begin;
  274. def dump_my_heap_please
  275. ObjectSpace.dump_all(output: :stdout, full: true)
  276. end
  277. dump_my_heap_please
  278. end;
  279. heap = output.find_all { |l| JSON.parse(l)['type'] == "NONE" }
  280. assert_operator heap.length, :>, 0
  281. end
  282. end
  283. def test_dump_addresses_match_dump_all_addresses
  284. assert_in_out_err(%w[-robjspace], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error|
  285. begin;
  286. def dump_my_heap_please
  287. obj = Object.new
  288. puts ObjectSpace.dump(obj)
  289. ObjectSpace.dump_all(output: $stdout)
  290. end
  291. dump_my_heap_please
  292. end;
  293. needle = JSON.parse(output.first)
  294. addr = needle['address']
  295. found = output.drop(1).find { |l| JSON.parse(l)['address'] == addr }
  296. assert found, "object #{addr} should be findable in full heap dump"
  297. end
  298. end
  299. def test_dump_class_addresses_match_dump_all_addresses
  300. assert_in_out_err(%w[-robjspace], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error|
  301. begin;
  302. def dump_my_heap_please
  303. obj = Object.new
  304. puts ObjectSpace.dump(obj)
  305. ObjectSpace.dump_all(output: $stdout)
  306. end
  307. dump_my_heap_please
  308. end;
  309. needle = JSON.parse(output.first)
  310. addr = needle['class']
  311. found = output.drop(1).find { |l| JSON.parse(l)['address'] == addr }
  312. assert found, "object #{addr} should be findable in full heap dump"
  313. end
  314. end
  315. def test_dump_reference_addresses_match_dump_all_addresses
  316. assert_in_out_err(%w[-robjspace], "#{<<-"begin;"}\n#{<<-'end;'}") do |output, error|
  317. begin;
  318. def dump_my_heap_please
  319. obj = Object.new
  320. obj2 = Object.new
  321. obj2.instance_variable_set(:@ref, obj)
  322. puts ObjectSpace.dump(obj)
  323. ObjectSpace.dump_all(output: $stdout)
  324. end
  325. dump_my_heap_please
  326. end;
  327. needle = JSON.parse(output.first)
  328. addr = needle['address']
  329. found = output.drop(1).find { |l| (JSON.parse(l)['references'] || []).include? addr }
  330. assert found, "object #{addr} should be findable in full heap dump"
  331. end
  332. end
  333. def test_dump_all
  334. entry = /"bytesize":11, "value":"TEST STRING", "encoding":"UTF-8", "file":"-", "line":4, "method":"dump_my_heap_please", "generation":/
  335. opts = %w[--disable-gem --disable=frozen-string-literal -robjspace]
  336. assert_in_out_err(opts, "#{<<-"begin;"}#{<<-'end;'}") do |output, error|
  337. begin;
  338. def dump_my_heap_please
  339. ObjectSpace.trace_object_allocations_start
  340. GC.start
  341. str = "TEST STRING".force_encoding("UTF-8")
  342. ObjectSpace.dump_all(output: :stdout)
  343. end
  344. dump_my_heap_please
  345. end;
  346. assert_match(entry, output.grep(/TEST STRING/).join("\n"))
  347. end
  348. assert_in_out_err(%w[-robjspace], "#{<<-"begin;"}#{<<-'end;'}") do |(output), (error)|
  349. begin;
  350. def dump_my_heap_please
  351. ObjectSpace.trace_object_allocations_start
  352. GC.start
  353. (str = "TEST STRING").force_encoding("UTF-8")
  354. ObjectSpace.dump_all().path
  355. end
  356. puts dump_my_heap_please
  357. end;
  358. assert_nil(error)
  359. dump = File.readlines(output)
  360. File.unlink(output)
  361. assert_match(entry, dump.grep(/TEST STRING/).join("\n"))
  362. end
  363. if defined?(JSON)
  364. args = [
  365. "-rjson", "-",
  366. EnvUtil.rubybin,
  367. "--disable=gems", "-robjspace", "-eObjectSpace.dump_all(output: :stdout)",
  368. ]
  369. assert_ruby_status(args, "#{<<~"begin;"}\n#{<<~"end;"}")
  370. begin;
  371. IO.popen(ARGV) do |f|
  372. f.each_line.map { |x| JSON.load(x) }
  373. end
  374. end;
  375. end
  376. end
  377. def test_dump_uninitialized_file
  378. assert_in_out_err(%[-robjspace], <<-RUBY) do |(output), (error)|
  379. puts ObjectSpace.dump(File.allocate)
  380. RUBY
  381. assert_nil error
  382. assert_match(/"type":"FILE"/, output)
  383. assert_not_match(/"fd":/, output)
  384. end
  385. end
  386. def traverse_classes klass
  387. h = {}
  388. while klass && !h.has_key?(klass)
  389. h[klass] = true
  390. klass = ObjectSpace.internal_class_of(klass)
  391. end
  392. end
  393. def test_internal_class_of
  394. i = 0
  395. ObjectSpace.each_object{|o|
  396. traverse_classes ObjectSpace.internal_class_of(o)
  397. i += 1
  398. }
  399. assert_operator i, :>, 0
  400. end
  401. def traverse_super_classes klass
  402. while klass
  403. klass = ObjectSpace.internal_super_of(klass)
  404. end
  405. end
  406. def all_super_classes klass
  407. klasses = []
  408. while klass
  409. klasses << klass
  410. klass = ObjectSpace.internal_super_of(klass)
  411. end
  412. klasses
  413. end
  414. def test_internal_super_of
  415. klasses = all_super_classes(String)
  416. String.ancestors.each{|k|
  417. case k
  418. when Class
  419. assert_equal(true, klasses.include?(k), k.inspect)
  420. when Module
  421. assert_equal(false, klasses.include?(k), k.inspect) # Internal object (T_ICLASS)
  422. end
  423. }
  424. i = 0
  425. ObjectSpace.each_object(Module){|o|
  426. traverse_super_classes ObjectSpace.internal_super_of(o)
  427. i += 1
  428. }
  429. assert_operator i, :>, 0
  430. end
  431. def test_count_symbols
  432. assert_separately(%w[-robjspace], "#{<<~';;;'}")
  433. h0 = ObjectSpace.count_symbols
  434. syms = (1..128).map{|i| ("xyzzy#{i}_#{Process.pid}_#{rand(1_000_000)}_" * 128).to_sym}
  435. syms << Class.new{define_method(syms[-1]){}}
  436. h = ObjectSpace.count_symbols
  437. m = proc {h0.inspect + "\n" + h.inspect}
  438. assert_equal 127, h[:mortal_dynamic_symbol] - h0[:mortal_dynamic_symbol], m
  439. assert_equal 1, h[:immortal_dynamic_symbol] - h0[:immortal_dynamic_symbol], m
  440. assert_operator h[:immortal_static_symbol], :>=, Object.methods.size, m
  441. assert_equal h[:immortal_symbol], h[:immortal_dynamic_symbol] + h[:immortal_static_symbol], m
  442. ;;;
  443. end
  444. end