Skip to content

Commit 1def0d8

Browse files
authored
Add tests for MMTk (#23)
1 parent b45c3c8 commit 1def0d8

File tree

2 files changed

+174
-8
lines changed

2 files changed

+174
-8
lines changed

gc.c

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ static const char *mmtk_env_plan = NULL;
160160
static const char *mmtk_pre_arg_plan = NULL;
161161
static const char *mmtk_post_arg_plan = NULL;
162162
static const char *mmtk_chosen_plan = NULL;
163+
static size_t mmtk_pre_max_heap_size = 0;
164+
static size_t mmtk_post_max_heap_size = 0;
163165

164166
static bool mmtk_max_heap_parse_error = false;
165167
static size_t mmtk_max_heap_size = 0;
@@ -15463,13 +15465,26 @@ size_t rb_mmtk_heap_limit(void) {
1546315465
}
1546415466

1546515467
void rb_mmtk_pre_process_opts(int argc, char **argv) {
15468+
/*
15469+
* Processing these arguments is a mess - we have to process them before
15470+
* Ruby is set up, when arguments are normally processed, because we need
15471+
* the GC up and running to set up Ruby. We have to kind of rough parsing
15472+
* and then re-parse them properly later and compare against our rough
15473+
* parsing. We also can't report errors using exceptions. Needs tidying
15474+
* up in general, but may always be a bit awkward.
15475+
*/
15476+
1546615477
bool enable_rubyopt = true;
1546715478

1546815479
mmtk_env_plan = getenv("MMTK_PLAN");
1546915480
if (mmtk_env_plan) {
1547015481
mmtk_enable = true;
1547115482
}
1547215483

15484+
if (getenv("THIRD_PARTY_HEAP_LIMIT")) {
15485+
mmtk_enable = true;
15486+
}
15487+
1547315488
for (int n = 1; n < argc; n++) {
1547415489
if (strcmp(argv[n], "--") == 0) {
1547515490
break;
@@ -15493,13 +15508,23 @@ void rb_mmtk_pre_process_opts(int argc, char **argv) {
1549315508
|| strcmp(argv[n], "--disable=mmtk") == 0) {
1549415509
mmtk_enable = false;
1549515510
}
15496-
else if (strncmp(argv[n], "--mmtk-plan=", strlen("--mmtk-plan=")) == 0) {
15511+
else if (strncmp(argv[n], "--mmtk-plan", strlen("--mmtk-plan")) == 0) {
1549715512
mmtk_enable = true;
1549815513
mmtk_pre_arg_plan = argv[n] + strlen("--mmtk-plan=");
15514+
if (argv[n][strlen("--mmtk-plan")] != '=' || strlen(mmtk_pre_arg_plan) == 0) {
15515+
fputs("[FATAL] --mmtk-plan needs an argument\n", stderr);
15516+
exit(EXIT_FAILURE);
15517+
}
1549915518
}
15500-
else if (strncmp(argv[n], "--mmtk-max-heap=", strlen("--mmtk-max-heap=")) == 0) {
15519+
else if (strncmp(argv[n], "--mmtk-max-heap", strlen("--mmtk-max-heap")) == 0) {
1550115520
mmtk_enable = true;
15502-
mmtk_max_heap_size = rb_mmtk_parse_heap_limit(argv[n] + strlen("--mmtk-max-heap="), &mmtk_max_heap_parse_error);
15521+
char *mmtk_max_heap_size_arg = argv[n] + strlen("--mmtk-max-heap=");
15522+
if (argv[n][strlen("--mmtk-max-heap")] != '=' || strlen(mmtk_max_heap_size_arg) == 0) {
15523+
fputs("[FATAL] --mmtk-max-heap needs an argument\n", stderr);
15524+
exit(EXIT_FAILURE);
15525+
}
15526+
mmtk_pre_max_heap_size = rb_mmtk_parse_heap_limit(mmtk_max_heap_size_arg, &mmtk_max_heap_parse_error);
15527+
mmtk_max_heap_size = mmtk_pre_max_heap_size;
1550315528
}
1550415529
}
1550515530

@@ -15516,12 +15541,41 @@ void rb_mmtk_pre_process_opts(int argc, char **argv) {
1551615541
length++;
1551715542
}
1551815543

15519-
if (strncmp(env_args, "--mmtk-plan=", strlen("--mmtk-plan=")) == 0) {
15544+
if (strncmp(env_args, "--mmtk", strlen("--mmtk")) == 0) {
15545+
mmtk_enable = true;
15546+
} else if (strncmp(env_args, "--enable-mmtk", strlen("--enable-mmtk")) == 0) {
1552015547
mmtk_enable = true;
15548+
} else if (strncmp(env_args, "--enable=mmtk", strlen("--enable=mmtk")) == 0) {
15549+
mmtk_enable = true;
15550+
}
15551+
15552+
if (strncmp(env_args, "--mmtk-plan", strlen("--mmtk-plan")) == 0) {
15553+
if (env_args[strlen("--mmtk-plan")] != '=') {
15554+
fputs("[FATAL] --mmtk-plan needs an argument\n", stderr);
15555+
exit(EXIT_FAILURE);
15556+
}
1552115557
mmtk_pre_arg_plan = strndup(env_args + strlen("--mmtk-plan="), length - strlen("--mmtk-plan="));
1552215558
if (mmtk_pre_arg_plan == NULL) {
1552315559
rb_bug("could not allocate space for argument");
1552415560
}
15561+
if (strlen(mmtk_pre_arg_plan) == 0) {
15562+
fputs("[FATAL] --mmtk-plan needs an argument\n", stderr);
15563+
exit(EXIT_FAILURE);
15564+
}
15565+
} else if (strncmp(env_args, "--mmtk-max-heap", strlen("--mmtk-max-heap")) == 0) {
15566+
if (env_args[strlen("--mmtk-max-heap")] != '=') {
15567+
fputs("[FATAL] --mmtk-max-heap needs an argument\n", stderr);
15568+
exit(EXIT_FAILURE);
15569+
}
15570+
char *mmtk_max_heap_size_arg = strndup(env_args + strlen("--mmtk-max-heap="), length - strlen("--mmtk-max-heap="));
15571+
if (mmtk_max_heap_size_arg == NULL) {
15572+
rb_bug("could not allocate space for argument");
15573+
}
15574+
if (strlen(mmtk_max_heap_size_arg) == 0) {
15575+
fputs("[FATAL] --mmtk-max-heap needs an argument\n", stderr);
15576+
exit(EXIT_FAILURE);
15577+
}
15578+
mmtk_pre_max_heap_size = rb_mmtk_parse_heap_limit(mmtk_max_heap_size_arg, &mmtk_max_heap_parse_error);
1552515579
}
1552615580

1552715581
env_args += length;
@@ -15532,7 +15586,7 @@ void rb_mmtk_pre_process_opts(int argc, char **argv) {
1553215586

1553315587
if (enable_rubyopt && mmtk_env_plan && mmtk_pre_arg_plan && strcmp(mmtk_env_plan, mmtk_pre_arg_plan) != 0) {
1553415588
fputs("[FATAL] MMTK_PLAN and --mmtk-plan do not agree\n", stderr);
15535-
exit(EXIT_FAILURE);
15589+
exit(EXIT_FAILURE);
1553615590
}
1553715591

1553815592
if (enable_rubyopt && mmtk_env_plan) {
@@ -15546,8 +15600,6 @@ void rb_mmtk_pre_process_opts(int argc, char **argv) {
1554615600
}
1554715601
}
1554815602

15549-
#define opt_match_noarg(s, l, name) \
15550-
opt_match(s, l, name) && (*(s) ? (rb_warn("argument to --mmtk-" name " is ignored"), 1) : 1)
1555115603
#define opt_match_arg(s, l, name) \
1555215604
opt_match(s, l, name) && (*(s) ? 1 : (rb_raise(rb_eRuntimeError, "--mmtk-" name " needs an argument"), 0))
1555315605

@@ -15560,7 +15612,7 @@ void rb_mmtk_post_process_opts(const char *s) {
1556015612
mmtk_post_arg_plan = s + 1;
1556115613
}
1556215614
else if (opt_match_arg(s, l, "max-heap")) {
15563-
// no-op
15615+
mmtk_post_max_heap_size = rb_mmtk_parse_heap_limit((char *) (s + 1), &mmtk_max_heap_parse_error);
1556415616
}
1556515617
else {
1556615618
rb_raise(rb_eRuntimeError,
@@ -15577,6 +15629,10 @@ void rb_mmtk_post_process_opts_finish(bool feature_enable) {
1557715629
rb_raise(rb_eRuntimeError, "--mmtk-plan values disagree");
1557815630
}
1557915631

15632+
if (mmtk_pre_max_heap_size != 0 && mmtk_post_max_heap_size != 0 && mmtk_pre_max_heap_size != mmtk_post_max_heap_size) {
15633+
rb_raise(rb_eRuntimeError, "--mmtk-max-heap values disagree");
15634+
}
15635+
1558015636
if (mmtk_max_heap_parse_error) {
1558115637
rb_raise(rb_eRuntimeError,
1558215638
"--mmtk-max-heap Invalid. Valid values positive integers, with optional KiB, MiB, GiB, TiB suffixes.");

test/ruby/test_mmtk.rb

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# frozen_string_literal: true
2+
#
3+
# This set of tests can be run with:
4+
# make test-all TESTS='test/ruby/test_mmtk.rb' RUN_OPTS="--mmtk"
5+
6+
require 'test/unit'
7+
require 'envutil'
8+
require 'tmpdir'
9+
10+
return unless defined?(GC::MMTk.enabled?) && GC::MMTk.enabled?
11+
12+
class TestMMTk < Test::Unit::TestCase
13+
def test_description
14+
assert_includes(RUBY_DESCRIPTION, '+MMTk')
15+
end
16+
17+
ENABLE_OPTIONS = [
18+
['--mmtk'],
19+
["--mmtk-plan=#{GC::MMTk.plan_name}"],
20+
['--mmtk-max-heap=1024000'],
21+
['--enable-mmtk'],
22+
['--enable=mmtk'],
23+
24+
['--disable-mmtk', '--mmtk'],
25+
['--disable-mmtk', '--enable-mmtk'],
26+
['--disable-mmtk', '--enable=mmtk'],
27+
['--disable-mmtk', "--mmtk-plan=#{GC::MMTk.plan_name}"],
28+
['--disable-mmtk', '--mmtk-max-heap=1024000'],
29+
30+
['--disable=mmtk', '--mmtk'],
31+
['--disable=mmtk', '--enable-mmtk'],
32+
['--disable=mmtk', '--enable=mmtk'],
33+
['--disable=mmtk', "--mmtk-plan=#{GC::MMTk.plan_name}"],
34+
['--disable=mmtk', '--mmtk-max-heap=1024000']
35+
]
36+
37+
def test_enable
38+
ENABLE_OPTIONS.each do |version_args|
39+
assert_in_out_err(['--version'] + version_args) do |stdout, stderr|
40+
assert_equal(RUBY_DESCRIPTION, stdout.first)
41+
assert_equal([], stderr)
42+
end
43+
end
44+
end
45+
46+
def test_enable_from_rubyopt
47+
ENABLE_OPTIONS.each do |version_args|
48+
mmtk_child_env = {'RUBYOPT' => version_args.join(' ')}
49+
assert_in_out_err([mmtk_child_env, '--version'], '') do |stdout, stderr|
50+
assert_equal(RUBY_DESCRIPTION, stdout.first)
51+
assert_equal([], stderr)
52+
end
53+
end
54+
end
55+
56+
def test_invalid_flags
57+
assert_in_out_err('--mmtk-', '', [], /invalid option --mmtk-/)
58+
assert_in_out_err('--mmtkhello', '', [], /invalid option --mmtkhello/)
59+
end
60+
61+
def test_args
62+
assert_in_out_err('--mmtk-plan', '', [], /--mmtk-plan needs an argument/)
63+
assert_in_out_err('--mmtk-plan=', '', [], /--mmtk-plan needs an argument/)
64+
assert_in_out_err('--mmtk-max-heap', '', [], /--mmtk-max-heap needs an argument/)
65+
assert_in_out_err('--mmtk-max-heap=', '', [], /--mmtk-max-heap needs an argument/)
66+
end
67+
68+
def test_arg_after_script
69+
Tempfile.create(["test_ignore_after_script", ".rb"]) do |t|
70+
t.puts "p ARGV"
71+
t.close
72+
assert_in_out_err([t.path, '--mmtk'], '', [["--mmtk"].inspect])
73+
end
74+
end
75+
76+
def test_mmtk_plan_env_var
77+
assert_in_out_err([{'MMTK_PLAN' => 'NoGC'}, '-e puts GC::MMTk.plan_name'], '', ['NoGC'])
78+
end
79+
80+
def test_third_party_max_heap_env_var
81+
assert_in_out_err([{'THIRD_PARTY_HEAP_LIMIT' => '1024000'}, '-e p GC.stat(:mmtk_total_bytes)'], '', ['1024000'])
82+
end
83+
84+
def test_enabled
85+
assert_in_out_err(['-e p GC::MMTk.enabled?'], '', ['false'])
86+
assert_in_out_err(['--mmtk', '-e p GC::MMTk.enabled?'], '', ['true'])
87+
end
88+
89+
def test_plan_name
90+
assert_in_out_err(['--mmtk-plan=NoGC', '-e puts GC::MMTk.plan_name'], '', ['NoGC'])
91+
assert_in_out_err(['--mmtk-plan=MarkSweep', '-e puts GC::MMTk.plan_name'], '', ['MarkSweep'])
92+
end
93+
94+
def test_max_heap
95+
assert_in_out_err(['--mmtk-max-heap=1024000', '-e p GC.stat(:mmtk_total_bytes)'], '', ['1024000'])
96+
assert_in_out_err(['--mmtk-max-heap=1000KiB', '-e p GC.stat(:mmtk_total_bytes)'], '', ['1024000'])
97+
assert_in_out_err(['--mmtk-max-heap=1MiB', '-e p GC.stat(:mmtk_total_bytes)'], '', ['1048576'])
98+
end
99+
100+
def test_gc_stat
101+
assert_equal(GC.stat(:mmtk_free_bytes).class, Integer)
102+
assert_equal(GC.stat(:mmtk_total_bytes).class, Integer)
103+
assert_equal(GC.stat(:mmtk_used_bytes).class, Integer)
104+
assert_equal(GC.stat(:mmtk_starting_heap_address).class, Integer)
105+
assert_equal(GC.stat(:mmtk_last_heap_address).class, Integer)
106+
assert_operator(GC.stat(:mmtk_last_heap_address), :>, GC.stat(:mmtk_starting_heap_address))
107+
assert_operator(GC.stat(:mmtk_free_bytes), :<=, GC.stat(:mmtk_total_bytes))
108+
assert_operator(GC.stat(:mmtk_used_bytes), :<=, GC.stat(:mmtk_total_bytes))
109+
end
110+
end

0 commit comments

Comments
 (0)