Bug #22228
closedDo a major GC in YJIT test helper `assert_compiles` before running the test code
Description
I was debugging why a YJIT test added from backport to 3.3.11 with GC.stress fails downstream. This revealed an assumption in the assert_compiles, that the environment is "clean", or as default as possible.
assert_compiles already sanitizes environment with --disable-gems and reset_stats! call.
I'd like to propose the assert_compiles method to trigger a GC.start before executing the test proc, and before the stats reset.
While the issue described below does not happen on default Rubies 3.4 and 4.0, it could enhance the YJIT test environment.
I was investigating downstream test failure on Ruby 3.3.11 (and later 3.3.12) [0] that appeared after backport of: https://bugs.ruby-lang.org/issues/21266
Where the tests in YJIT expects no exits by default:
Provided as a default here:
https://github.com/ruby/ruby/blob/3e646aee/test/ruby/test_yjit.rb#L2015
Expected as part of condition here:
https://github.com/ruby/ruby/blob/3e646aee/test/ruby/test_yjit.rb#L2080
Downstream we insert hook for optional ABRT error reporting for Ruby:
https://src.fedoraproject.org/rpms/ruby/blob/11eb00c7bf5c87033a73301ca8dad515fc925e76/f/ruby.spec#_290
Relevant patchfile:
https://src.fedoraproject.org/rpms/ruby/blob/11eb00c7bf5c87033a73301ca8dad515fc925e76/f/ruby-2.7.0-Initialize-ABRT-hook.patch
We create empty abrt.rb to test with later on:
https://src.fedoraproject.org/rpms/ruby/blob/11eb00c7bf5c87033a73301ca8dad515fc925e76/f/ruby.spec#_1178
To reproduce the failure in 3.3, both the patch and the file are required to exist for the failure at [0] to appear.
Later I found that parse.y opens a file to load the abrt.rb and registers it with Ruby GC, which causes zombie and interrupt on GC.stress=true,
since prism parser became default, these failures no longer appear as the parser does not copy parse.y's approach of registering the opened file with Ruby GC.
The note on #19921 https://bugs.ruby-lang.org/issues/19921#note-20 regarding this was correct and it's also the earliest known appearance of the issue.
[0] Test failure result:
TestYJIT#test_proc_block_with_kwrest = 0.08 s
- Failure:
TestYJIT#test_proc_block_with_kwrest [/builddir/build/BUILD/ruby-3.3.11/test/ruby/test_yjit.rb:1531]:
Expected no exits, but got:
{:send=>1}
Reasons:
method call fallback reasons:
iseq_has_kwrest: 1 (100.0%)
invokeblock fallback reasons:
proc: 2 (100.0%)
invokesuper fallback reasons:
(all relevant counters are zero)
method call exit reasons:
interrupted: 1 (100.0%)
invokeblock exit reasons:
(all relevant counters are zero)
invokesuper exit reasons:
(all relevant counters are zero)
getblockparamproxy exit reasons:
(all relevant counters are zero)
getinstancevariable exit reasons:
(all relevant counters are zero)
setinstancevariable exit reasons:
(all relevant counters are zero)
leave exit reasons:
interp_return: 6 (100.0%)
left shift (ltlt) exit reasons:
(all relevant counters are zero)
invalidation reasons:
constant_ic_fill: 2 (100.0%)