Skip to content

Commit d936d4e

Browse files
authored
Merge pull request #2309 from Shopify/run-valgrind-on-ci
Run Valgrind on CI and fix memory leaks
2 parents c9db27a + 9a71cb1 commit d936d4e

File tree

6 files changed

+66
-1
lines changed

6 files changed

+66
-1
lines changed

.github/workflows/ruby.yml

+26
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,29 @@ jobs:
8080
- name: Run test
8181
run: |
8282
bundle exec rake ${{ matrix.job }}
83+
valgrind:
84+
runs-on: ubuntu-latest
85+
steps:
86+
- uses: actions/checkout@v2
87+
- uses: ruby/setup-ruby@v1
88+
with:
89+
ruby-version: "3.4"
90+
bundler-cache: none
91+
- name: Set working directory as safe
92+
run: git config --global --add safe.directory $(pwd)
93+
- name: Set up permission
94+
run: chmod -R o-w /opt/hostedtoolcache/Ruby
95+
- name: Install dependencies
96+
run: |
97+
sudo apt-get update
98+
sudo apt-get install -y libdb-dev curl autoconf automake m4 libtool python3 valgrind
99+
- name: Update rubygems & bundler
100+
run: |
101+
ruby -v
102+
gem update --system
103+
- name: bin/setup
104+
run: |
105+
bin/setup
106+
- run: bundle exec rake test:valgrind
107+
env:
108+
RUBY_FREE_AT_EXIT: 1

Gemfile

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ group :profilers do
3737
gem 'stackprof'
3838
gem 'memory_profiler'
3939
gem 'benchmark-ips'
40+
gem "ruby_memcheck", platform: :ruby
4041
end
4142

4243
# Test gems

Gemfile.lock

+7
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,17 @@ GEM
6060
logger (1.6.6)
6161
marcel (1.0.4)
6262
memory_profiler (1.1.0)
63+
mini_portile2 (2.8.8)
6364
minitest (5.25.4)
6465
mutex_m (0.3.0)
6566
net-protocol (0.2.2)
6667
timeout
6768
net-smtp (0.5.1)
6869
net-protocol
6970
nkf (0.2.0)
71+
nokogiri (1.18.3)
72+
mini_portile2 (~> 2.8.2)
73+
racc (~> 1.4)
7074
ostruct (0.6.1)
7175
parallel (1.26.3)
7276
parser (3.3.7.1)
@@ -126,6 +130,8 @@ GEM
126130
lint_roller (~> 1.1)
127131
rubocop (~> 1.72, >= 1.72.1)
128132
ruby-progressbar (1.13.0)
133+
ruby_memcheck (3.0.1)
134+
nokogiri
129135
securerandom (0.4.1)
130136
stackprof (0.2.27)
131137
steep (1.9.4)
@@ -193,6 +199,7 @@ DEPENDENCIES
193199
rubocop
194200
rubocop-on-rbs
195201
rubocop-rubycw
202+
ruby_memcheck
196203
stackprof
197204
steep
198205
tempfile

Rakefile

+11-1
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,24 @@ bin = File.join(__dir__, "bin")
1111

1212
Rake::ExtensionTask.new("rbs_extension")
1313

14-
Rake::TestTask.new(:test => :compile) do |t|
14+
test_config = lambda do |t|
1515
t.libs << "test"
1616
t.libs << "lib"
1717
t.test_files = FileList["test/**/*_test.rb"].reject do |path|
1818
path =~ %r{test/stdlib/}
1919
end
2020
end
2121

22+
Rake::TestTask.new(test: :compile, &test_config)
23+
24+
unless Gem.win_platform?
25+
require "ruby_memcheck"
26+
27+
namespace :test do
28+
RubyMemcheck::TestTask.new(valgrind: :compile, &test_config)
29+
end
30+
end
31+
2232
multitask :default => [:test, :stdlib_test, :typecheck_test, :rubocop, :validate, :test_doc]
2333

2434
task :lexer do

ext/rbs_extension/parserstate.c

+14
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ parserstate *alloc_parser(VALUE buffer, lexstate *lexer, int start_pos, int end_
363363

364364
if (!NIL_P(variables)) {
365365
if (!RB_TYPE_P(variables, T_ARRAY)) {
366+
free_parser(parser);
366367
rb_raise(rb_eTypeError,
367368
"wrong argument type %"PRIsVALUE" (must be array or nil)",
368369
rb_obj_class(variables));
@@ -387,11 +388,24 @@ parserstate *alloc_parser(VALUE buffer, lexstate *lexer, int start_pos, int end_
387388
return parser;
388389
}
389390

391+
void free_typevar_tables(id_table *table) {
392+
while (table != NULL) {
393+
id_table *next = table->next;
394+
if (table->ids != NULL) {
395+
free(table->ids);
396+
}
397+
free(table);
398+
table = next;
399+
}
400+
}
401+
390402
void free_parser(parserstate *parser) {
391403
free(parser->lexstate);
392404
if (parser->last_comment) {
393405
free_comment(parser->last_comment);
394406
}
407+
408+
free_typevar_tables(parser->vars);
395409
rbs_constant_pool_free(&parser->constant_pool);
396410
free(parser);
397411
}

test/rbs/test/runtime_test_test.rb

+7
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ def test_runtime_test_with_sample_size
2020
end
2121

2222
def test_runtime_test_error_with_invalid_sample_size
23+
# Skip this test if running under Valgrind because `RUBY_FREE_AT_EXIT` has a bug.
24+
# See: https://bugs.ruby-lang.org/issues/21173
25+
omit if ENV["RUBY_MEMCHECK_RUNNING"]
26+
2327
string_err_msg = refute_test_success(other_env: {"RBS_TEST_SAMPLE_SIZE" => 'yes'})
2428
assert_match(/E, .+ ERROR -- rbs: Sample size should be a positive integer: `.+`\n/, string_err_msg)
2529

@@ -93,6 +97,9 @@ def refute_test_success(other_env: {}, rbs_content: nil, ruby_content: nil)
9397
end
9498

9599
def test_test_target
100+
# Skip this test if running under Valgrind because `RUBY_FREE_AT_EXIT` has a bug.
101+
# See: https://bugs.ruby-lang.org/issues/21173
102+
omit if ENV["RUBY_MEMCHECK_RUNNING"]
96103
output = refute_test_success(other_env: { "RBS_TEST_TARGET" => nil })
97104
assert_match "test/setup handles the following environment variables:", output
98105
end

0 commit comments

Comments
 (0)