summaryrefslogtreecommitdiff
path: root/test/objspace/test_objspace.rb
AgeCommit message (Collapse)Author
2025-05-01Skip test affected by TracePoint-dependent allocation_class_pathYusuke Endoh
These assertions fail when TracePoint is enabled due to differing allocation context. Commented out for now until behavior is fixed. See [Bug #21298] Notes: Merged: https://github.com/ruby/ruby/pull/13228
2025-04-25Inline Class#new.Aaron Patterson
This commit inlines instructions for Class#new. To make this work, we added a new YARV instructions, `opt_new`. `opt_new` checks whether or not the `new` method is the default allocator method. If it is, it allocates the object, and pushes the instance on the stack. If not, the instruction jumps to the "slow path" method call instructions. Old instructions: ``` > ruby --dump=insns -e'Object.new' == disasm: #<ISeq:<main>@-e:1 (1,0)-(1,10)> 0000 opt_getconstant_path <ic:0 Object> ( 1)[Li] 0002 opt_send_without_block <calldata!mid:new, argc:0, ARGS_SIMPLE> 0004 leave ``` New instructions: ``` > ./miniruby --dump=insns -e'Object.new' == disasm: #<ISeq:<main>@-e:1 (1,0)-(1,10)> 0000 opt_getconstant_path <ic:0 Object> ( 1)[Li] 0002 putnil 0003 swap 0004 opt_new <calldata!mid:new, argc:0, ARGS_SIMPLE>, 11 0007 opt_send_without_block <calldata!mid:initialize, argc:0, FCALL|ARGS_SIMPLE> 0009 jump 14 0011 opt_send_without_block <calldata!mid:new, argc:0, ARGS_SIMPLE> 0013 swap 0014 pop 0015 leave ``` This commit speeds up basic object allocation (`Foo.new`) by 60%, but classes that take keyword parameters see an even bigger benefit because no hash is allocated when instantiating the object (3x to 6x faster). Here is an example that uses `Hash.new(capacity: 0)`: ``` > hyperfine "ruby --disable-gems -e'i = 0; while i < 10_000_000; Hash.new(capacity: 0); i += 1; end'" "./ruby --disable-gems -e'i = 0; while i < 10_000_000; Hash.new(capacity: 0); i += 1; end'" Benchmark 1: ruby --disable-gems -e'i = 0; while i < 10_000_000; Hash.new(capacity: 0); i += 1; end' Time (mean ± σ): 1.082 s ± 0.004 s [User: 1.074 s, System: 0.008 s] Range (min … max): 1.076 s … 1.088 s 10 runs Benchmark 2: ./ruby --disable-gems -e'i = 0; while i < 10_000_000; Hash.new(capacity: 0); i += 1; end' Time (mean ± σ): 627.9 ms ± 3.5 ms [User: 622.7 ms, System: 4.8 ms] Range (min … max): 622.7 ms … 633.2 ms 10 runs Summary ./ruby --disable-gems -e'i = 0; while i < 10_000_000; Hash.new(capacity: 0); i += 1; end' ran 1.72 ± 0.01 times faster than ruby --disable-gems -e'i = 0; while i < 10_000_000; Hash.new(capacity: 0); i += 1; end' ``` This commit changes the backtrace for `initialize`: ``` aaron@tc ~/g/ruby (inline-new)> cat test.rb class Foo def initialize puts caller end end def hello Foo.new end hello aaron@tc ~/g/ruby (inline-new)> ruby -v test.rb ruby 3.4.2 (2025-02-15 revision d2930f8e7a) +PRISM [arm64-darwin24] test.rb:8:in 'Class#new' test.rb:8:in 'Object#hello' test.rb:11:in '<main>' aaron@tc ~/g/ruby (inline-new)> ./miniruby -v test.rb ruby 3.5.0dev (2025-03-28T23:59:40Z inline-new c4157884e4) +PRISM [arm64-darwin24] test.rb:8:in 'Object#hello' test.rb:11:in '<main>' ``` It also increases memory usage for calls to `new` by 122 bytes: ``` aaron@tc ~/g/ruby (inline-new)> cat test.rb require "objspace" class Foo def initialize puts caller end end def hello Foo.new end puts ObjectSpace.memsize_of(RubyVM::InstructionSequence.of(method(:hello))) aaron@tc ~/g/ruby (inline-new)> make runruby RUBY_ON_BUG='gdb -x ./.gdbinit -p' ./miniruby -I./lib -I. -I.ext/common ./tool/runruby.rb --extout=.ext -- --disable-gems ./test.rb 656 aaron@tc ~/g/ruby (inline-new)> ruby -v test.rb ruby 3.4.2 (2025-02-15 revision d2930f8e7a) +PRISM [arm64-darwin24] 544 ``` Thanks to @ko1 for coming up with this idea! Co-Authored-By: John Hawthorn <[email protected]>
2025-04-24objspace_dump: Include `shareable` flagJean Boussier
Given that the currently planned ractor local GC implementation performance will heavilly be influenced by the number of shareable objects it would be valuable to be able to know how many of them are in the heap.
2025-03-06Harden `TestObjSpace#test_memsize_of_root_shared_string`Jean Boussier
This test occasionally fail because it runs into a String instance that had its `==` method removed. I couldn't identify where this String comes from, but in general when using `each_object` it's best to not assume returned objectd are functional. By just inverting the operands of `==` we ensure it's always `String#==` that is called. ``` 1) Error: TestObjSpace#test_memsize_of_root_shared_string: NoMethodError: undefined method '==' for #<String:0x00007f9b50e8c978> /tmp/ruby/src/trunk-random1/test/objspace/test_objspace.rb:35:in 'block in TestObjSpace#test_memsize_of_root_shared_string' /tmp/ruby/src/trunk-random1/test/objspace/test_objspace.rb:35:in 'ObjectSpace.each_object' /tmp/ruby/src/trunk-random1/test/objspace/test_objspace.rb:35:in 'TestObjSpace#test_memsize_of_root_shared_string' ``` Notes: Merged: https://github.com/ruby/ruby/pull/12870
2025-02-19Add age to rb_gc_object_metadataPeter Zhu
This will allow ObjectSpace.dump to output the age of the object. Notes: Merged: https://github.com/ruby/ruby/pull/12777
2025-01-30Output object_id in ObjectSpace.dumpPeter Zhu
Outputs the object ID in the dump for objects that have it seen. Notes: Merged: https://github.com/ruby/ruby/pull/12657
2024-12-16Check whether object is valid in allocation_info_tracer_compactPeter Zhu
When reference updating ObjectSpace.trace_object_allocations, we need to check whether the object is valid or not because it does not mark the object so the object may be dead. This can cause a segmentation fault if the object is on a free heap page. For example, the following script crashes: require "objspace" objs = [] ObjectSpace.trace_object_allocations do 1_000_000.times do objs << Object.new end end objs = nil # Free pages that the objs were on GC.start # Run compaction and check that it doesn't crash GC.compact Notes: Merged: https://github.com/ruby/ruby/pull/12360
2024-12-16Fix ObjectSpace.trace_object_allocations for compactionPeter Zhu
We need to reinsert into the ST table when an object moves because it is a numtable that hashes on the object address, so when an object moves we need to reinsert it rather than just updating the key. Notes: Merged: https://github.com/ruby/ruby/pull/12339
2024-11-12ObjectSpace.dump: handle Module#set_temporary_nameJean Boussier
[Bug #20892] Until the introduction of that method, it was impossible for a Module name not to be valid JSON, hence it wasn't going through the slower escaping function. This assumption no longer hold. Notes: Merged: https://github.com/ruby/ruby/pull/12067
2024-09-17Replace all GC.disable with EnvUtil.without_gcPeter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/11635
2024-07-03[Feature #20470] Split GC into gc_impl.cPeter Zhu
This commit splits gc.c into two files: - gc.c now only contains code not specific to Ruby GC. This includes code to mark objects (which the GC implementation may choose not to use) and wrappers for internal APIs that the implementation may need to use (e.g. locking the VM). - gc_impl.c now contains the implementation of Ruby's GC. This includes marking, sweeping, compaction, and statistics. Most importantly, gc_impl.c only uses public APIs in Ruby and a limited set of functions exposed in gc.c. This allows us to build gc_impl.c independently of Ruby and plug Ruby's GC into itself.
2024-05-09Stabilize TestObjSpace#test_dump_special_constsJean Boussier
The test assumes `:foo` is a static symbol, but that is only true if a literal `:foo` was parsed before `"foo".to_sym` was evaled: ```ruby require 'objspace' foo_sym = "foo".to_sym puts ObjectSpace.dump(eval(":foo")) ``` ``` {"address":"0x100fb46d0", "type":"SYMBOL", "shape_id":10, "slot_size":40, "class":"0x100d3e9c8", "frozen":true, "bytesize":3, "value":"foo", "memsize":40, "flags":{"wb_protected":true, "marking":true, "marked":true}} ```
2024-03-14Ensure test suite is compatible with --frozen-string-literalJean Boussier
As preparation for https://bugs.ruby-lang.org/issues/20205 making sure the test suite is compatible with frozen string literals is making things easier.
2023-12-07Skip a flaky objspace test on Visual StudioTakashi Kokubun
This seems to happen only on VisualStudio: https://github.com/ruby/ruby/actions/runs/7130917319/job/19418375386 It fails relatively frequently. Nobody seems actively working on it, so let's skip it until somebody starts working on it.
2023-11-22objspace_dump.c: dump call cache ids with dump_append_idJean Boussier
Not all `ID` have an associated string. Fixes a SEGFAULT in ObjectSpace.dump_all spec.
2023-11-13Revert "Fix crash caused by concurrent ObjectSpace.dump_all calls"Jean Boussier
This reverts commit 9a62fd3cbae2ebb60e2f9cad782af1ad18db4319.
2023-11-12Fix crash caused by concurrent ObjectSpace.dump_all callsKJ Tsanaktsidis
Since the callback defined in the objspace module might give up the GVL, we need to make sure the right cr->mfd value is set back after the GVL is re-obtained.
2023-11-09String#force_encoding don't clear coderange if encoding is unchangedJean Boussier
Some code out there blind calls `force_encoding` without checking what the original encoding was, which clears the coderange uselessly. If the String is big, it can be a rather costly mistake. For instance the `rack-utf8_sanitizer` gem does this on request bodies.
2023-10-12Fix ObjectSpace.dump with super() callinfoJohn Hawthorn
super() uses 0 as mid for its callinfo, so we need to check for that to avoid a segfault when using dump_all.
2023-08-16Move the PC regardless of the leaf flag (#8232)Takashi Kokubun
Co-authored-by: Alan Wu <[email protected]> Notes: Merged-By: k0kubun <[email protected]>
2023-07-18Remove assumption about object orderPeter Zhu
The address of objects can't be assumed since a later object may be allocate in a swept slot. Notes: Merged: https://github.com/ruby/ruby/pull/8092
2023-05-31Fix flaky test in test_objspace.rbPeter Zhu
Ensure that the frozen string is promoted to the old generation by running the GC 4 times.
2023-05-21Skip too-complex-shape test which is always flaky regardless JITNobuyoshi Nakada
2023-05-21Skip first if flaky [ci skip]Nobuyoshi Nakada
2023-05-19The too-complex test isn't stablefor RJIT eitherTakashi Kokubun
https://github.com/ruby/ruby/actions/runs/5020231516
2023-05-19Skip test_dump_too_complex_shape for YJIT for nowTakashi Kokubun
It fails too often with YJIT: * https://github.com/ruby/ruby/actions/runs/5015976941/jobs/8992254690 * https://github.com/ruby/ruby/actions/runs/5017310353/jobs/8995281395 * https://github.com/ruby/ruby/actions/runs/5019625711/jobs/9000322487 * https://github.com/ruby/ruby/actions/runs/5019883965/jobs/9000836915 ref: https://github.com/ruby/ruby/pull/7646
2023-05-18change to test/objectspace, don't rely on Object's shape not being "too complex"lukeg
2023-05-10Prevent warning: assigned but unused variableYusuke Endoh
http://rubyci.s3.amazonaws.com/debian10/ruby-master/log/20230510T123003Z.log.html.gz ``` /home/chkbuild/chkbuild/tmp/build/20230510T123003Z/ruby/test/objspace/test_objspace.rb:224: warning: assigned but unused variable - c4 /home/chkbuild/chkbuild/tmp/build/20230510T123003Z/ruby/test/ruby/test_class.rb:362: warning: assigned but unused variable - e /home/chkbuild/chkbuild/tmp/build/20230510T123003Z/ruby/test/ruby/test_process.rb:2602: warning: assigned but unused variable - parent_pid ```
2023-05-04Define `RubyVM::Shape` dependent test only if availableNobuyoshi Nakada
2023-03-08Fix crash when allocating classes with newobj hookPeter Zhu
We need to zero out the whole slot when running the newobj hook for a newly allocated class because the slot could be filled with garbage, which would cause a crash if a GC runs inside of the newobj hook. For example, the following script crashes: ``` require "objspace" GC.stress = true ObjectSpace.trace_object_allocations { 100.times do Class.new end } ``` [Bug #19482] Notes: Merged: https://github.com/ruby/ruby/pull/7464
2023-02-24Fix incorrect line numbers in GC hookPeter Zhu
If the previous instruction is not a leaf instruction, then the PC was incremented before the instruction was ran (meaning the currently executing instruction is actually the previous instruction), so we should not increment the PC otherwise we will calculate the source line for the next instruction. This bug can be reproduced in the following script: ``` require "objspace" ObjectSpace.trace_object_allocations_start a = 1.0 / 0.0 p [ObjectSpace.allocation_sourceline(a), ObjectSpace.allocation_sourcefile(a)] ``` Which outputs: [4, "test.rb"] This is incorrect because the object was allocated on line 10 and not line 4. The behaviour is correct when we use a leaf instruction (e.g. if we replaced `1.0 / 0.0` with `"hello"`), then the output is: [10, "test.rb"]. [Bug #19456] Notes: Merged: https://github.com/ruby/ruby/pull/7357
2023-01-11Skip unfixed assertion about objspace/dump_allKoichi Sasada
``` {"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. Notes: Merged: https://github.com/ruby/ruby/pull/7100
2023-01-05Add embedded status to dumps of T_OBJECTPeter Zhu
This commit adds `"embedded":true` in ObjectSpace.dump for T_OBJECTs that are embedded. Notes: Merged: https://github.com/ruby/ruby/pull/7068
2022-12-28add debug print for the failureKoichi Sasada
http://ci.rvm.jp/results/trunk-gc-asserts@ruby-sp2-docker/4364584 ``` 1) Failure: TestObjSpace#test_dump_all [/tmp/ruby/src/trunk-gc-asserts/test/objspace/test_objspace.rb:599]: number of strings. <2> expected but was <3>. ```
2022-12-15Indicate if a shape is too_complex in ObjectSpace#dumpJemma Issroff
Notes: Merged: https://github.com/ruby/ruby/pull/6939
2022-12-08ObjectSpace.dump_all: dump shapes as wellJean Boussier
I see several arguments in doing so. First they use a non trivial amount of memory, so for various memory profiling/mapping tools it is relevant to have visibility of the space occupied by shapes. Then, some pathological code can create a tons of shape, so it is valuable to have a way to have a way to observe shapes without having to compile Ruby with `SHAPE_DEBUG=1`. And additionally it's likely much faster to dump then this way than to use `RubyVM::Shape`. There are however a few open questions: - Shapes can't respect the `since:` argument. Not sure what to do when it is provided. Would probably make sense to not dump them. - Maybe it would make more sense to have a separate `ObjectSpace.dump_shapes`? - Maybe instead `dump_all` should take a `shapes: false` argument? Additionally, `ObjectSpace.dump_shapes` is added for the use case of debugging the evolution of the shape tree. Notes: Merged: https://github.com/ruby/ruby/pull/6868
2022-11-21Add RVALUE_OVERHEAD and move ractor_belonging_idPeter Zhu
This commit adds RVALUE_OVERHEAD for storing metadata at the end of the slot. This commit moves the ractor_belonging_id in debug builds from the flags to RVALUE_OVERHEAD which frees the 16 bits in the headers for object shapes. Notes: Merged: https://github.com/ruby/ruby/pull/6763
2022-11-10Transition shape when object's capacity changesJemma Issroff
This commit adds a `capacity` field to shapes, and adds shape transitions whenever an object's capacity changes. Objects which are allocated out of a bigger size pool will also make a transition from the root shape to the shape with the correct capacity for their size pool when they are allocated. This commit will allow us to remove numiv from objects completely, and will also mean we can guarantee that if two objects share shapes, their IVs are in the same positions (an embedded and extended object cannot share shapes). This will enable us to implement ivar sets in YJIT using object shapes. Co-Authored-By: Aaron Patterson <[email protected]> Notes: Merged: https://github.com/ruby/ruby/pull/6699
2022-10-20push dummy frame for loading processKoichi Sasada
This patch pushes dummy frames when loading code for the profiling purpose. The following methods push a dummy frame: * `Kernel#require` * `Kernel#load` * `RubyVM::InstructionSequence.compile_file` * `RubyVM::InstructionSequence.load_from_binary` https://bugs.ruby-lang.org/issues/18559 Notes: Merged: https://github.com/ruby/ruby/pull/6572
2022-09-26Revert this until we can figure out WB issues or remove shapes from GCAaron Patterson
Revert "* expand tabs. [ci skip]" This reverts commit 830b5b5c351c5c6efa5ad461ae4ec5085e5f0275. Revert "This commit implements the Object Shapes technique in CRuby." This reverts commit 9ddfd2ca004d1952be79cf1b84c52c79a55978f4.
2022-09-26This commit implements the Object Shapes technique in CRuby.Jemma Issroff
Object Shapes is used for accessing instance variables and representing the "frozenness" of objects. Object instances have a "shape" and the shape represents some attributes of the object (currently which instance variables are set and the "frozenness"). Shapes form a tree data structure, and when a new instance variable is set on an object, that object "transitions" to a new shape in the shape tree. Each shape has an ID that is used for caching. The shape structure is independent of class, so objects of different types can have the same shape. For example: ```ruby class Foo def initialize # Starts with shape id 0 @a = 1 # transitions to shape id 1 @b = 1 # transitions to shape id 2 end end class Bar def initialize # Starts with shape id 0 @a = 1 # transitions to shape id 1 @b = 1 # transitions to shape id 2 end end foo = Foo.new # `foo` has shape id 2 bar = Bar.new # `bar` has shape id 2 ``` Both `foo` and `bar` instances have the same shape because they both set instance variables of the same name in the same order. This technique can help to improve inline cache hits as well as generate more efficient machine code in JIT compilers. This commit also adds some methods for debugging shapes on objects. See `RubyVM::Shape` for more details. For more context on Object Shapes, see [Feature: #18776] Co-Authored-By: Aaron Patterson <[email protected]> Co-Authored-By: Eileen M. Uchitelle <[email protected]> Co-Authored-By: John Hawthorn <[email protected]> Notes: Merged: https://github.com/ruby/ruby/pull/6386
2022-07-22Dump non-ASCII char as unsignedNobuyoshi Nakada
Non-ASCII code may be negative on platforms plain char is signed. Notes: Merged: https://github.com/ruby/ruby/pull/6166
2022-07-21Revert "objspace_dump.c: skip dumping method name if not pure ASCII"Jean byroot Boussier
This reverts commit 79406e3600862bbb6dcdd7c5ef8de1978e6f916c. Notes: Merged: https://github.com/ruby/ruby/pull/6165
2022-07-21objspace_dump.c: skip dumping method name if not pure ASCIIJean Boussier
Sidekiq has a method named `❨╯°□°❩╯︵┻━┻`which corrupts heap dumps. Normally we could just dump is as is since it's valid UTF-8 and need no escaping. But our code to escape control characters isn't UTF-8 aware so it's more complicated than it seems. Ultimately since the overwhelming majority of method names are pure ASCII, it's not a big loss to just skip it. Notes: Merged: https://github.com/ruby/ruby/pull/6161
2022-07-04ObjectSpace.dump: Include string coderangeJean Boussier
I suspect that some shared pages are invalidated because some static string don't have their coderange set eagerly. So the first time they are scanned, the entire memory page is invalidated. Being able to see the coderange in `ObjectSpace` would help debug this. And in addition `dump` currently call `is_broken_string()` and `is_ascii_string()` which both end up scanning the string and assigning coderange. I think it's undesirable as `dump` should be read only. Notes: Merged: https://github.com/ruby/ruby/pull/6076
2022-03-29Refactor test_dump_all to make assertions about the contents of the dumped hashJemma Issroff
Notes: Merged: https://github.com/ruby/ruby/pull/5728
2022-03-01Show embed status of array when len is 0 in objspace dumpPeter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/5609
2022-02-09objspace: Hide identhash containing internal objsJohn Hawthorn
Inside ObjectSpace.reachable_objects_from we keep an internal identhash in order to de-duplicate reachable objects when wrapping them as InternalObject. Previously this hash was not hidden, making it possible to leak references to those internal objects to Ruby if using ObjectSpace.each_object. This commit solves this by hiding the hash. To simplify collection of values, we instead now just use the hash as a set of visited objects, and collect an Array (not hidden) of values to be returned. Notes: Merged: https://github.com/ruby/ruby/pull/5542
2022-02-03Add the size pool slot size to the output of ObjectSpace.dump/dump_allMatt Valentine-House
Notes: Merged: https://github.com/ruby/ruby/pull/5520
2022-02-02Decouple GC slot sizes from RVALUEPeter Zhu
Add a new macro BASE_SLOT_SIZE that determines the slot size. For Variable Width Allocation (compiled with USE_RVARGC=1), all slot sizes are powers-of-2 multiples of BASE_SLOT_SIZE. For USE_RVARGC=0, BASE_SLOT_SIZE is set to sizeof(RVALUE). Notes: Merged: https://github.com/ruby/ruby/pull/5517