Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/test/hotspot/gtest/metaspace/test_virtualspacenode.cpp
41144 views
1
/*
2
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
3
* Copyright (c) 2020, 2021 SAP SE. All rights reserved.
4
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5
*
6
* This code is free software; you can redistribute it and/or modify it
7
* under the terms of the GNU General Public License version 2 only, as
8
* published by the Free Software Foundation.
9
*
10
* This code is distributed in the hope that it will be useful, but WITHOUT
11
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13
* version 2 for more details (a copy is included in the LICENSE file that
14
* accompanied this code).
15
*
16
* You should have received a copy of the GNU General Public License version
17
* 2 along with this work; if not, write to the Free Software Foundation,
18
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19
*
20
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21
* or visit www.oracle.com if you need additional information or have any
22
* questions.
23
*
24
*/
25
26
#include "precompiled.hpp"
27
#include "memory/metaspace/chunklevel.hpp"
28
#include "memory/metaspace/commitLimiter.hpp"
29
#include "memory/metaspace/counters.hpp"
30
#include "memory/metaspace/freeChunkList.hpp"
31
#include "memory/metaspace/metachunk.hpp"
32
#include "memory/metaspace/metachunkList.hpp"
33
#include "memory/metaspace/metaspaceSettings.hpp"
34
#include "memory/metaspace/virtualSpaceNode.hpp"
35
#include "runtime/mutexLocker.hpp"
36
#include "utilities/debug.hpp"
37
//#define LOG_PLEASE
38
#include "metaspaceGtestCommon.hpp"
39
#include "metaspaceGtestRangeHelpers.hpp"
40
41
using metaspace::chunklevel_t;
42
using metaspace::CommitLimiter;
43
using metaspace::FreeChunkListVector;
44
using metaspace::Metachunk;
45
using metaspace::MetachunkList;
46
using metaspace::VirtualSpaceNode;
47
using metaspace::Settings;
48
using metaspace::SizeCounter;
49
50
class VirtualSpaceNodeTest {
51
52
// These counters are updated by the Node.
53
SizeCounter _counter_reserved_words;
54
SizeCounter _counter_committed_words;
55
CommitLimiter _commit_limiter;
56
VirtualSpaceNode* _node;
57
58
// These are my checks and counters.
59
const size_t _vs_word_size;
60
const size_t _commit_limit;
61
62
MetachunkList _root_chunks;
63
64
void verify() const {
65
66
ASSERT_EQ(_root_chunks.count() * metaspace::chunklevel::MAX_CHUNK_WORD_SIZE,
67
_node->used_words());
68
69
ASSERT_GE(_commit_limit, _counter_committed_words.get());
70
ASSERT_EQ(_commit_limiter.committed_words(), _counter_committed_words.get());
71
72
// Since we know _counter_committed_words serves our single node alone, the counter has to
73
// match the number of bits in the node internal commit mask.
74
ASSERT_EQ(_counter_committed_words.get(), _node->committed_words());
75
76
ASSERT_EQ(_counter_reserved_words.get(), _vs_word_size);
77
ASSERT_EQ(_counter_reserved_words.get(), _node->word_size());
78
79
}
80
81
void lock_and_verify_node() {
82
#ifdef ASSERT
83
MutexLocker fcl(Metaspace_lock, Mutex::_no_safepoint_check_flag);
84
_node->verify_locked();
85
#endif
86
}
87
88
Metachunk* alloc_root_chunk() {
89
90
verify();
91
92
const bool node_is_full = _node->used_words() == _node->word_size();
93
Metachunk* c = NULL;
94
{
95
MutexLocker fcl(Metaspace_lock, Mutex::_no_safepoint_check_flag);
96
c = _node->allocate_root_chunk();
97
}
98
99
lock_and_verify_node();
100
101
if (node_is_full) {
102
103
EXPECT_NULL(c);
104
105
} else {
106
107
DEBUG_ONLY(c->verify();)
108
EXPECT_NOT_NULL(c);
109
EXPECT_TRUE(c->is_root_chunk());
110
EXPECT_TRUE(c->is_free());
111
EXPECT_EQ(c->word_size(), metaspace::chunklevel::MAX_CHUNK_WORD_SIZE);
112
113
EXPECT_TRUE(c->is_fully_uncommitted());
114
115
EXPECT_TRUE(_node->contains(c->base()));
116
117
_root_chunks.add(c);
118
119
}
120
121
verify();
122
123
return c;
124
125
}
126
127
bool commit_root_chunk(Metachunk* c, size_t request_commit_words) {
128
129
verify();
130
131
const size_t committed_words_before = _counter_committed_words.get();
132
133
bool rc = c->ensure_committed(request_commit_words);
134
135
verify();
136
DEBUG_ONLY(c->verify();)
137
138
lock_and_verify_node();
139
140
if (rc == false) {
141
142
// We must have hit the commit limit.
143
EXPECT_GE(committed_words_before + request_commit_words, _commit_limit);
144
145
} else {
146
147
// We should not have hit the commit limit.
148
EXPECT_LE(_counter_committed_words.get(), _commit_limit);
149
150
// We do not know how much we really committed - maybe nothing if the
151
// chunk had been committed before - but we know the numbers should have
152
// risen or at least stayed equal.
153
EXPECT_GE(_counter_committed_words.get(), committed_words_before);
154
155
// The chunk should be as far committed as was requested
156
EXPECT_GE(c->committed_words(), request_commit_words);
157
158
// Zap committed portion.
159
DEBUG_ONLY(zap_range(c->base(), c->committed_words());)
160
161
}
162
163
verify();
164
165
return rc;
166
167
} // commit_root_chunk
168
169
void uncommit_chunk(Metachunk* c) {
170
171
verify();
172
173
const size_t committed_words_before = _counter_committed_words.get();
174
const size_t available_words_before = _commit_limiter.possible_expansion_words();
175
176
c->uncommit();
177
178
DEBUG_ONLY(c->verify();)
179
180
lock_and_verify_node();
181
182
EXPECT_EQ(c->committed_words(), (size_t)0);
183
184
// Commit counter should have gone down (by exactly the size of the chunk) if chunk
185
// is larger than a commit granule.
186
// For smaller chunks, we do not know, but at least we know the commit size should not
187
// have gone up.
188
if (c->word_size() >= Settings::commit_granule_words()) {
189
190
EXPECT_EQ(_counter_committed_words.get(), committed_words_before - c->word_size());
191
192
// also, commit number in commit limiter should have gone down, so we have more space
193
EXPECT_EQ(_commit_limiter.possible_expansion_words(),
194
available_words_before + c->word_size());
195
196
} else {
197
198
EXPECT_LE(_counter_committed_words.get(), committed_words_before);
199
200
}
201
202
verify();
203
204
} // uncommit_chunk
205
206
Metachunk* split_chunk_with_checks(Metachunk* c, chunklevel_t target_level, FreeChunkListVector* freelist) {
207
208
DEBUG_ONLY(c->verify();)
209
210
const chunklevel_t orig_level = c->level();
211
assert(orig_level < target_level, "Sanity");
212
DEBUG_ONLY(metaspace::chunklevel::check_valid_level(target_level);)
213
214
const int total_num_chunks_in_freelist_before = freelist->num_chunks();
215
const size_t total_word_size_in_freelist_before = freelist->word_size();
216
217
// freelist->print_on(tty);
218
219
// Split...
220
{
221
MutexLocker fcl(Metaspace_lock, Mutex::_no_safepoint_check_flag);
222
_node->split(target_level, c, freelist);
223
}
224
225
// freelist->print_on(tty);
226
227
EXPECT_NOT_NULL(c);
228
EXPECT_EQ(c->level(), target_level);
229
EXPECT_TRUE(c->is_free());
230
231
// ... check that we get the proper amount of splinters. For every chunk split we expect one
232
// buddy chunk to appear of level + 1 (aka, half size).
233
size_t expected_wordsize_increase = 0;
234
int expected_num_chunks_increase = 0;
235
for (chunklevel_t l = orig_level + 1; l <= target_level; l++) {
236
expected_wordsize_increase += metaspace::chunklevel::word_size_for_level(l);
237
expected_num_chunks_increase++;
238
}
239
240
const int total_num_chunks_in_freelist_after = freelist->num_chunks();
241
const size_t total_word_size_in_freelist_after = freelist->word_size();
242
243
EXPECT_EQ(total_num_chunks_in_freelist_after, total_num_chunks_in_freelist_before + expected_num_chunks_increase);
244
EXPECT_EQ(total_word_size_in_freelist_after, total_word_size_in_freelist_before + expected_wordsize_increase);
245
246
return c;
247
248
} // end: split_chunk_with_checks
249
250
Metachunk* merge_chunk_with_checks(Metachunk* c, chunklevel_t expected_target_level, FreeChunkListVector* freelist) {
251
252
const chunklevel_t orig_level = c->level();
253
assert(expected_target_level < orig_level, "Sanity");
254
255
const int total_num_chunks_in_freelist_before = freelist->num_chunks();
256
const size_t total_word_size_in_freelist_before = freelist->word_size();
257
258
//freelist->print_on(tty);
259
260
Metachunk* result = NULL;
261
{
262
MutexLocker fcl(Metaspace_lock, Mutex::_no_safepoint_check_flag);
263
result = _node->merge(c, freelist);
264
}
265
EXPECT_NOT_NULL(result);
266
EXPECT_TRUE(result->level() == expected_target_level);
267
268
//freelist->print_on(tty);
269
270
// ... check that we merged in the proper amount of chunks. For every decreased level
271
// of the original chunk (each size doubling) we should see one buddy chunk swallowed up.
272
size_t expected_wordsize_decrease = 0;
273
int expected_num_chunks_decrease = 0;
274
for (chunklevel_t l = orig_level; l > expected_target_level; l --) {
275
expected_wordsize_decrease += metaspace::chunklevel::word_size_for_level(l);
276
expected_num_chunks_decrease++;
277
}
278
279
const int total_num_chunks_in_freelist_after = freelist->num_chunks();
280
const size_t total_word_size_in_freelist_after = freelist->word_size();
281
282
EXPECT_EQ(total_num_chunks_in_freelist_after, total_num_chunks_in_freelist_before - expected_num_chunks_decrease);
283
EXPECT_EQ(total_word_size_in_freelist_after, total_word_size_in_freelist_before - expected_wordsize_decrease);
284
285
return result;
286
287
} // end: merge_chunk_with_checks
288
289
public:
290
291
VirtualSpaceNodeTest(size_t vs_word_size, size_t commit_limit) :
292
_counter_reserved_words(),
293
_counter_committed_words(),
294
_commit_limiter(commit_limit),
295
_node(NULL),
296
_vs_word_size(vs_word_size),
297
_commit_limit(commit_limit)
298
{
299
{
300
MutexLocker fcl(Metaspace_lock, Mutex::_no_safepoint_check_flag);
301
_node = VirtualSpaceNode::create_node(vs_word_size, &_commit_limiter,
302
&_counter_reserved_words, &_counter_committed_words);
303
EXPECT_EQ(_node->word_size(), vs_word_size);
304
}
305
EXPECT_TRUE(_commit_limiter.possible_expansion_words() == _commit_limit);
306
verify();
307
}
308
309
~VirtualSpaceNodeTest() {
310
{
311
MutexLocker fcl(Metaspace_lock, Mutex::_no_safepoint_check_flag);
312
delete _node;
313
}
314
// After the node is deleted, counters should be back to zero
315
// (we cannot use ASSERT/EXPECT here in the destructor)
316
assert(_counter_reserved_words.get() == 0, "Sanity");
317
assert(_counter_committed_words.get() == 0, "Sanity");
318
assert(_commit_limiter.committed_words() == 0, "Sanity");
319
}
320
321
void test_simple() {
322
Metachunk* c = alloc_root_chunk();
323
commit_root_chunk(c, Settings::commit_granule_words());
324
commit_root_chunk(c, c->word_size());
325
uncommit_chunk(c);
326
}
327
328
void test_exhaust_node() {
329
Metachunk* c = NULL;
330
bool rc = true;
331
do {
332
c = alloc_root_chunk();
333
if (c != NULL) {
334
rc = commit_root_chunk(c, c->word_size());
335
}
336
} while (c != NULL && rc);
337
}
338
339
void test_arbitrary_commits() {
340
341
assert(_commit_limit >= _vs_word_size, "For this test no commit limit.");
342
343
// Get a root chunk to have a committable region
344
Metachunk* c = alloc_root_chunk();
345
ASSERT_NOT_NULL(c);
346
347
if (c->committed_words() > 0) {
348
c->uncommit();
349
}
350
351
ASSERT_EQ(_node->committed_words(), (size_t)0);
352
ASSERT_EQ(_counter_committed_words.get(), (size_t)0);
353
354
TestMap testmap(c->word_size());
355
assert(testmap.get_num_set() == 0, "Sanity");
356
357
for (int run = 0; run < 1000; run++) {
358
359
const size_t committed_words_before = testmap.get_num_set();
360
ASSERT_EQ(_commit_limiter.committed_words(), committed_words_before);
361
ASSERT_EQ(_counter_committed_words.get(), committed_words_before);
362
363
// A random range
364
SizeRange r = SizeRange(c->word_size()).random_aligned_subrange(Settings::commit_granule_words());
365
366
const size_t committed_words_in_range_before =
367
testmap.get_num_set(r.start(), r.end());
368
369
const bool do_commit = IntRange(100).random_value() >= 50;
370
if (do_commit) {
371
372
//LOG("c " SIZE_FORMAT "," SIZE_FORMAT, r.start(), r.end());
373
374
bool rc = false;
375
{
376
MutexLocker fcl(Metaspace_lock, Mutex::_no_safepoint_check_flag);
377
rc = _node->ensure_range_is_committed(c->base() + r.start(), r.size());
378
}
379
380
// Test-zap
381
zap_range(c->base() + r.start(), r.size());
382
383
// We should never reach commit limit since it is as large as the whole area.
384
ASSERT_TRUE(rc);
385
386
testmap.set_range(r.start(), r.end());
387
388
} else {
389
390
//LOG("u " SIZE_FORMAT "," SIZE_FORMAT, r.start(), r.end());
391
392
{
393
MutexLocker fcl(Metaspace_lock, Mutex::_no_safepoint_check_flag);
394
_node->uncommit_range(c->base() + r.start(), r.size());
395
}
396
397
testmap.clear_range(r.start(), r.end());
398
399
}
400
401
const size_t committed_words_after = testmap.get_num_set();
402
403
ASSERT_EQ(_commit_limiter.committed_words(), committed_words_after);
404
ASSERT_EQ(_counter_committed_words.get(), committed_words_after);
405
406
verify();
407
}
408
}
409
410
// Helper function for test_splitting_chunks_1
411
static void check_chunk_is_committed_at_least_up_to(const Metachunk* c, size_t& word_size) {
412
if (word_size >= c->word_size()) {
413
EXPECT_TRUE(c->is_fully_committed());
414
word_size -= c->word_size();
415
} else {
416
EXPECT_EQ(c->committed_words(), word_size);
417
word_size = 0; // clear remaining size if there is.
418
}
419
}
420
421
void test_split_and_merge_chunks() {
422
423
assert(_commit_limit >= _vs_word_size, "No commit limit here pls");
424
425
// Allocate a root chunk and commit a random part of it. Then repeatedly split
426
// it and merge it back together; observe the committed regions of the split chunks.
427
428
Metachunk* c = alloc_root_chunk();
429
430
if (c->committed_words() > 0) {
431
c->uncommit();
432
}
433
434
// To capture split-off chunks. Note: it is okay to use this here as a temp object.
435
FreeChunkListVector freelist;
436
437
const int granules_per_root_chunk = (int)(c->word_size() / Settings::commit_granule_words());
438
439
for (int granules_to_commit = 0; granules_to_commit < granules_per_root_chunk; granules_to_commit++) {
440
441
const size_t words_to_commit = Settings::commit_granule_words() * granules_to_commit;
442
443
c->ensure_committed(words_to_commit);
444
445
ASSERT_EQ(c->committed_words(), words_to_commit);
446
ASSERT_EQ(_counter_committed_words.get(), words_to_commit);
447
ASSERT_EQ(_commit_limiter.committed_words(), words_to_commit);
448
449
const size_t committed_words_before = c->committed_words();
450
451
verify();
452
453
for (chunklevel_t target_level = LOWEST_CHUNK_LEVEL + 1;
454
target_level <= HIGHEST_CHUNK_LEVEL; target_level++) {
455
456
// Split:
457
Metachunk* c2 = split_chunk_with_checks(c, target_level, &freelist);
458
c2->set_in_use();
459
460
// Split smallest leftover chunk.
461
if (c2->level() < HIGHEST_CHUNK_LEVEL) {
462
463
Metachunk* c3 = freelist.remove_first(c2->level());
464
ASSERT_NOT_NULL(c3); // Must exist since c2 must have a splinter buddy now.
465
466
Metachunk* c4 = split_chunk_with_checks(c3, HIGHEST_CHUNK_LEVEL, &freelist);
467
c4->set_in_use();
468
469
// Merge it back. We expect this to merge up to c2's level, since c2 is in use.
470
c4->set_free();
471
Metachunk* c5 = merge_chunk_with_checks(c4, c2->level(), &freelist);
472
ASSERT_NOT_NULL(c5);
473
freelist.add(c5);
474
475
}
476
477
// Merge c2 back.
478
c2->set_free();
479
merge_chunk_with_checks(c2, LOWEST_CHUNK_LEVEL, &freelist);
480
481
// After all this splitting and combining committed size should not have changed.
482
ASSERT_EQ(c2->committed_words(), committed_words_before);
483
484
}
485
486
}
487
488
} // end: test_splitting_chunks
489
490
};
491
492
TEST_VM(metaspace, virtual_space_node_test_basics) {
493
494
MutexLocker fcl(Metaspace_lock, Mutex::_no_safepoint_check_flag);
495
496
const size_t word_size = metaspace::chunklevel::MAX_CHUNK_WORD_SIZE * 10;
497
498
SizeCounter scomm;
499
SizeCounter sres;
500
CommitLimiter cl (word_size * 2); // basically, no commit limiter.
501
502
VirtualSpaceNode* node = VirtualSpaceNode::create_node(word_size, &cl, &sres, &scomm);
503
ASSERT_NOT_NULL(node);
504
ASSERT_EQ(node->committed_words(), (size_t)0);
505
ASSERT_EQ(node->committed_words(), scomm.get());
506
DEBUG_ONLY(node->verify_locked();)
507
508
bool b = node->ensure_range_is_committed(node->base(), node->word_size());
509
ASSERT_TRUE(b);
510
ASSERT_EQ(node->committed_words(), word_size);
511
ASSERT_EQ(node->committed_words(), scomm.get());
512
DEBUG_ONLY(node->verify_locked();)
513
zap_range(node->base(), node->word_size());
514
515
node->uncommit_range(node->base(), node->word_size());
516
ASSERT_EQ(node->committed_words(), (size_t)0);
517
ASSERT_EQ(node->committed_words(), scomm.get());
518
DEBUG_ONLY(node->verify_locked();)
519
520
const int num_granules = (int)(word_size / Settings::commit_granule_words());
521
for (int i = 1; i < num_granules; i += 4) {
522
b = node->ensure_range_is_committed(node->base(), i * Settings::commit_granule_words());
523
ASSERT_TRUE(b);
524
ASSERT_EQ(node->committed_words(), i * Settings::commit_granule_words());
525
ASSERT_EQ(node->committed_words(), scomm.get());
526
DEBUG_ONLY(node->verify_locked();)
527
zap_range(node->base(), i * Settings::commit_granule_words());
528
}
529
530
node->uncommit_range(node->base(), node->word_size());
531
ASSERT_EQ(node->committed_words(), (size_t)0);
532
ASSERT_EQ(node->committed_words(), scomm.get());
533
DEBUG_ONLY(node->verify_locked();)
534
535
}
536
537
// Note: we unfortunately need TEST_VM even though the system tested
538
// should be pretty independent since we need things like os::vm_page_size()
539
// which in turn need OS layer initialization.
540
TEST_VM(metaspace, virtual_space_node_test_1) {
541
VirtualSpaceNodeTest test(metaspace::chunklevel::MAX_CHUNK_WORD_SIZE,
542
metaspace::chunklevel::MAX_CHUNK_WORD_SIZE);
543
test.test_simple();
544
}
545
546
TEST_VM(metaspace, virtual_space_node_test_2) {
547
// Should not hit commit limit
548
VirtualSpaceNodeTest test(3 * metaspace::chunklevel::MAX_CHUNK_WORD_SIZE,
549
3 * metaspace::chunklevel::MAX_CHUNK_WORD_SIZE);
550
test.test_simple();
551
test.test_exhaust_node();
552
}
553
554
TEST_VM(metaspace, virtual_space_node_test_3) {
555
double d = os::elapsedTime();
556
// Test committing uncommitting arbitrary ranges
557
for (int run = 0; run < 100; run++) {
558
VirtualSpaceNodeTest test(metaspace::chunklevel::MAX_CHUNK_WORD_SIZE,
559
metaspace::chunklevel::MAX_CHUNK_WORD_SIZE);
560
test.test_split_and_merge_chunks();
561
}
562
double d2 = os::elapsedTime();
563
LOG("%f", (d2-d));
564
}
565
566
TEST_VM(metaspace, virtual_space_node_test_4) {
567
// Should hit commit limit
568
VirtualSpaceNodeTest test(10 * metaspace::chunklevel::MAX_CHUNK_WORD_SIZE,
569
3 * metaspace::chunklevel::MAX_CHUNK_WORD_SIZE);
570
test.test_exhaust_node();
571
}
572
573
TEST_VM(metaspace, virtual_space_node_test_5) {
574
// Test committing uncommitting arbitrary ranges
575
VirtualSpaceNodeTest test(metaspace::chunklevel::MAX_CHUNK_WORD_SIZE,
576
metaspace::chunklevel::MAX_CHUNK_WORD_SIZE);
577
test.test_arbitrary_commits();
578
}
579
580
TEST_VM(metaspace, virtual_space_node_test_7) {
581
// Test large allocation and freeing.
582
{
583
VirtualSpaceNodeTest test(metaspace::chunklevel::MAX_CHUNK_WORD_SIZE * 100,
584
metaspace::chunklevel::MAX_CHUNK_WORD_SIZE * 100);
585
test.test_exhaust_node();
586
}
587
{
588
VirtualSpaceNodeTest test(metaspace::chunklevel::MAX_CHUNK_WORD_SIZE * 100,
589
metaspace::chunklevel::MAX_CHUNK_WORD_SIZE * 100);
590
test.test_exhaust_node();
591
}
592
593
}
594
595