1 /**
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <random>
17
18 #include "gtest/gtest.h"
19 #include "runtime/include/managed_thread.h"
20 #include "runtime/mark_word.cpp"
21
22 namespace ark {
23
24 class MarkWordTest : public testing::Test {
25 public:
26 MarkWordTest() = default;
27
28 protected:
SetUp()29 void SetUp() override
30 {
31 // Logger::InitializeStdLogging(Logger::Level::DEBUG, Logger::Component::ALL);
32 }
33
TearDown()34 void TearDown() override
35 {
36 // Logger::Destroy();
37 }
38
39 enum MarkWordFieldsMaxValues : MarkWord::MarkWordSize {
40 MAX_THREAD_ID = (1UL << MarkWord::MarkWordRepresentation::LIGHT_LOCK_THREADID_SIZE) - 1UL,
41 MAX_LOCK_COUNT = (1UL << MarkWord::MarkWordRepresentation::LIGHT_LOCK_LOCK_COUNT_SIZE) - 1UL,
42 MAX_MONITOR_ID = (1UL << MarkWord::MarkWordRepresentation::MONITOR_POINTER_SIZE) - 1UL,
43 MAX_HASH = (1UL << MarkWord::MarkWordRepresentation::HASH_SIZE) - 1UL,
44 MAX_FORWARDING_ADDRESS = std::numeric_limits<MarkWord::MarkWordSize>::max() &
45 MarkWord::MarkWordRepresentation::FORWARDING_ADDRESS_MASK_IN_PLACE,
46 };
47
48 class RandomTestValuesGetter {
49 public:
50 using MarkWordDistribution = std::uniform_int_distribution<MarkWord::MarkWordSize>;
51
52 // NOLINTNEXTLINE(cert-msc51-cpp)
RandomTestValuesGetter()53 RandomTestValuesGetter()
54 {
55 #ifdef PANDA_NIGHTLY_TEST_ON
56 seed_ = std::random_device()();
57 #else
58 // NOLINTNEXTLINE(readability-magic-numbers)
59 seed_ = 0xC0E67D50;
60 #endif
61 gen_ = std::mt19937(seed_);
62
63 threadIdRange_ = MarkWordDistribution(0, MAX_THREAD_ID);
64 lockCountRange_ = MarkWordDistribution(0, MAX_LOCK_COUNT);
65 monitorIdRange_ = MarkWordDistribution(0, MAX_MONITOR_ID);
66 hashRange_ = MarkWordDistribution(0, MAX_HASH);
67 forwardingAddressRange_ = MarkWordDistribution(0, MAX_FORWARDING_ADDRESS);
68 }
69
GetThreadId()70 ManagedThread::ThreadId GetThreadId()
71 {
72 return threadIdRange_(gen_);
73 }
74
GetLockCount()75 uint32_t GetLockCount()
76 {
77 return lockCountRange_(gen_);
78 }
79
GetMonitorId()80 Monitor::MonitorId GetMonitorId()
81 {
82 return monitorIdRange_(gen_);
83 }
84
GetHash()85 uint32_t GetHash()
86 {
87 return hashRange_(gen_);
88 }
89
GetForwardingAddress()90 MarkWord::MarkWordSize GetForwardingAddress()
91 {
92 return forwardingAddressRange_(gen_) & MarkWord::MarkWordRepresentation::FORWARDING_ADDRESS_MASK_IN_PLACE;
93 }
94
GetSeed()95 uint32_t GetSeed()
96 {
97 return seed_;
98 }
99
100 private:
101 uint32_t seed_;
102 std::mt19937 gen_;
103 MarkWordDistribution threadIdRange_;
104 MarkWordDistribution lockCountRange_;
105 MarkWordDistribution monitorIdRange_;
106 MarkWordDistribution hashRange_;
107 MarkWordDistribution forwardingAddressRange_;
108 };
109
110 class MaxTestValuesGetter {
111 public:
GetThreadId() const112 ManagedThread::ThreadId GetThreadId() const
113 {
114 return MAX_THREAD_ID;
115 }
116
GetLockCount() const117 uint32_t GetLockCount() const
118 {
119 return MAX_LOCK_COUNT;
120 }
121
GetMonitorId() const122 Monitor::MonitorId GetMonitorId() const
123 {
124 return static_cast<Monitor::MonitorId>(MAX_MONITOR_ID);
125 }
126
GetHash() const127 uint32_t GetHash() const
128 {
129 return static_cast<uint32_t>(MAX_HASH);
130 }
131
GetForwardingAddress() const132 MarkWord::MarkWordSize GetForwardingAddress() const
133 {
134 return MAX_FORWARDING_ADDRESS;
135 }
136
GetSeed() const137 uint32_t GetSeed() const
138 {
139 // We don't have a seed for this case
140 return 0;
141 }
142 };
143
144 template <class Getter>
145 class MarkWordWrapper {
146 public:
MarkWordWrapper(bool isMarkedForGc=false,bool isReadBarrierSet=false)147 explicit MarkWordWrapper(bool isMarkedForGc = false, bool isReadBarrierSet = false)
148 {
149 if (isMarkedForGc) {
150 mw_ = mw_.SetMarkedForGC();
151 }
152 if (isReadBarrierSet) {
153 mw_ = mw_.SetReadBarrier();
154 }
155 };
156
CheckUnlocked(bool isMarkedForGc=false,bool isReadBarrierSet=false)157 void CheckUnlocked(bool isMarkedForGc = false, bool isReadBarrierSet = false)
158 {
159 ASSERT_EQ(mw_.GetState(), MarkWord::ObjectState::STATE_UNLOCKED) << " seed = " << paramGetter_.GetSeed();
160 ASSERT_EQ(mw_.IsMarkedForGC(), isMarkedForGc) << " seed = " << paramGetter_.GetSeed();
161 ASSERT_EQ(mw_.IsReadBarrierSet(), isReadBarrierSet) << " seed = " << paramGetter_.GetSeed();
162 }
163
CheckLightweightLock(const ManagedThread::ThreadId tId,const uint32_t lockCount,bool isMarkedForGc,bool isReadBarrierSet=false)164 void CheckLightweightLock(const ManagedThread::ThreadId tId, const uint32_t lockCount, bool isMarkedForGc,
165 bool isReadBarrierSet = false)
166 {
167 ASSERT_EQ(mw_.GetState(), MarkWord::ObjectState::STATE_LIGHT_LOCKED)
168 << " seed = " << paramGetter_.GetSeed();
169 ASSERT_EQ(mw_.GetThreadId(), tId) << " seed = " << paramGetter_.GetSeed();
170 ASSERT_EQ(mw_.GetLockCount(), lockCount) << " seed = " << paramGetter_.GetSeed();
171 ASSERT_EQ(mw_.IsMarkedForGC(), isMarkedForGc) << " seed = " << paramGetter_.GetSeed();
172 ASSERT_EQ(mw_.IsReadBarrierSet(), isReadBarrierSet) << " seed = " << paramGetter_.GetSeed();
173 }
174
CheckHeavyweightLock(const Monitor::MonitorId mId,bool isMarkedForGc,bool isReadBarrierSet=false)175 void CheckHeavyweightLock(const Monitor::MonitorId mId, bool isMarkedForGc, bool isReadBarrierSet = false)
176 {
177 ASSERT_EQ(mw_.GetState(), MarkWord::ObjectState::STATE_HEAVY_LOCKED)
178 << " seed = " << paramGetter_.GetSeed();
179 ASSERT_EQ(mw_.GetMonitorId(), mId) << " seed = " << paramGetter_.GetSeed();
180 ASSERT_EQ(mw_.IsMarkedForGC(), isMarkedForGc) << " seed = " << paramGetter_.GetSeed();
181 ASSERT_EQ(mw_.IsReadBarrierSet(), isReadBarrierSet) << " seed = " << paramGetter_.GetSeed();
182 }
183
CheckHashed(uint32_t hash,bool isMarkedForGc,bool isReadBarrierSet=false)184 void CheckHashed(uint32_t hash, bool isMarkedForGc, bool isReadBarrierSet = false)
185 {
186 if (mw_.CONFIG_IS_HASH_IN_OBJ_HEADER) {
187 ASSERT_EQ(mw_.GetState(), MarkWord::ObjectState::STATE_HASHED) << " seed = " << paramGetter_.GetSeed();
188 ASSERT_EQ(mw_.GetHash(), hash) << " seed = " << paramGetter_.GetSeed();
189 ASSERT_EQ(mw_.IsMarkedForGC(), isMarkedForGc) << " seed = " << paramGetter_.GetSeed();
190 ASSERT_EQ(mw_.IsReadBarrierSet(), isReadBarrierSet) << " seed = " << paramGetter_.GetSeed();
191 }
192 }
193
CheckGC(MarkWord::MarkWordSize forwardingAddress)194 void CheckGC(MarkWord::MarkWordSize forwardingAddress)
195 {
196 ASSERT_EQ(mw_.GetState(), MarkWord::ObjectState::STATE_GC) << " seed = " << paramGetter_.GetSeed();
197 ASSERT_EQ(mw_.GetForwardingAddress(), forwardingAddress) << " seed = " << paramGetter_.GetSeed();
198 }
199
DecodeLightLock(ManagedThread::ThreadId tId,uint32_t lCount)200 void DecodeLightLock(ManagedThread::ThreadId tId, uint32_t lCount)
201 {
202 mw_ = mw_.DecodeFromLightLock(tId, lCount);
203 }
204
DecodeHeavyLock(Monitor::MonitorId mId)205 void DecodeHeavyLock(Monitor::MonitorId mId)
206 {
207 mw_ = mw_.DecodeFromMonitor(mId);
208 }
209
DecodeHash(uint32_t hash)210 void DecodeHash(uint32_t hash)
211 {
212 mw_ = mw_.DecodeFromHash(hash);
213 }
214
DecodeForwardingAddress(MarkWord::MarkWordSize fAddress)215 void DecodeForwardingAddress(MarkWord::MarkWordSize fAddress)
216 {
217 mw_ = mw_.DecodeFromForwardingAddress(fAddress);
218 }
219
DecodeAndCheckLightLock(bool isMarkedForGc=false,bool isReadBarrierSet=false)220 void DecodeAndCheckLightLock(bool isMarkedForGc = false, bool isReadBarrierSet = false)
221 {
222 auto tId = paramGetter_.GetThreadId();
223 auto lCount = paramGetter_.GetLockCount();
224 DecodeLightLock(tId, lCount);
225 CheckLightweightLock(tId, lCount, isMarkedForGc, isReadBarrierSet);
226 }
227
DecodeAndCheckHeavyLock(bool isMarkedForGc=false,bool isReadBarrierSet=false)228 void DecodeAndCheckHeavyLock(bool isMarkedForGc = false, bool isReadBarrierSet = false)
229 {
230 auto mId = paramGetter_.GetMonitorId();
231 DecodeHeavyLock(mId);
232 CheckHeavyweightLock(mId, isMarkedForGc, isReadBarrierSet);
233 }
234
DecodeAndCheckHashed(bool isMarkedForGc=false,bool isReadBarrierSet=false)235 void DecodeAndCheckHashed(bool isMarkedForGc = false, bool isReadBarrierSet = false)
236 {
237 auto hash = paramGetter_.GetHash();
238 DecodeHash(hash);
239 CheckHashed(hash, isMarkedForGc, isReadBarrierSet);
240 }
241
DecodeAndCheckGC()242 void DecodeAndCheckGC()
243 {
244 auto fAddress = paramGetter_.GetForwardingAddress();
245 DecodeForwardingAddress(fAddress);
246 CheckGC(fAddress);
247 }
248
SetMarkedForGC()249 void SetMarkedForGC()
250 {
251 mw_ = mw_.SetMarkedForGC();
252 }
253
SetReadBarrier()254 void SetReadBarrier()
255 {
256 mw_ = mw_.SetReadBarrier();
257 }
258
259 private:
260 MarkWord mw_;
261 Getter paramGetter_;
262 };
263
264 template <class Getter>
265 void CheckMakeHashed(bool isMarkedForGc, bool isReadBarrierSet);
266
267 template <class Getter>
268 void CheckMakeLightweightLock(bool isMarkedForGc, bool isReadBarrierSet);
269
270 template <class Getter>
271 void CheckMakeHeavyweightLock(bool isMarkedForGc, bool isReadBarrierSet);
272
273 template <class Getter>
274 void CheckMakeGC();
275
276 template <class Getter>
277 void CheckMarkingWithGC();
278
279 template <class Getter>
280 void CheckReadBarrierSet();
281 };
282
283 template <class Getter>
CheckMakeHashed(bool isMarkedForGc,bool isReadBarrierSet)284 void MarkWordTest::CheckMakeHashed(bool isMarkedForGc, bool isReadBarrierSet)
285 {
286 // nothing, gc = markedForGC, rb = readBarrierSet, state = unlocked
287 MarkWordWrapper<Getter> wrapper(isMarkedForGc, isReadBarrierSet);
288
289 // check new hash
290 wrapper.DecodeAndCheckHashed(isMarkedForGc, isReadBarrierSet);
291 wrapper.DecodeAndCheckHashed(isMarkedForGc, isReadBarrierSet);
292
293 // check after lightweight lock
294 wrapper.DecodeAndCheckLightLock(isMarkedForGc, isReadBarrierSet);
295 wrapper.DecodeAndCheckHashed(isMarkedForGc, isReadBarrierSet);
296
297 // check after heavyweight lock
298 wrapper.DecodeAndCheckHeavyLock(isMarkedForGc, isReadBarrierSet);
299 wrapper.DecodeAndCheckHashed(isMarkedForGc, isReadBarrierSet);
300 }
301
TEST_F(MarkWordTest,CreateHashedWithRandValues)302 TEST_F(MarkWordTest, CreateHashedWithRandValues)
303 {
304 CheckMakeHashed<RandomTestValuesGetter>(false, false);
305 CheckMakeHashed<RandomTestValuesGetter>(false, true);
306 CheckMakeHashed<RandomTestValuesGetter>(true, false);
307 CheckMakeHashed<RandomTestValuesGetter>(true, true);
308 }
309
TEST_F(MarkWordTest,CreateHashedWithMaxValues)310 TEST_F(MarkWordTest, CreateHashedWithMaxValues)
311 {
312 CheckMakeHashed<MaxTestValuesGetter>(false, false);
313 CheckMakeHashed<MaxTestValuesGetter>(false, true);
314 CheckMakeHashed<MaxTestValuesGetter>(true, false);
315 CheckMakeHashed<MaxTestValuesGetter>(true, true);
316 }
317
318 template <class Getter>
CheckMakeLightweightLock(bool isMarkedForGc,bool isReadBarrierSet)319 void MarkWordTest::CheckMakeLightweightLock(bool isMarkedForGc, bool isReadBarrierSet)
320 {
321 // nothing, gc = markedForGC, rb = readBarrierSet, state = unlocked
322 MarkWordWrapper<Getter> wrapper(isMarkedForGc, isReadBarrierSet);
323
324 // check new lightweight lock
325 wrapper.DecodeAndCheckLightLock(isMarkedForGc, isReadBarrierSet);
326 wrapper.DecodeAndCheckLightLock(isMarkedForGc, isReadBarrierSet);
327
328 // check after hash
329 wrapper.DecodeAndCheckHashed(isMarkedForGc, isReadBarrierSet);
330 wrapper.DecodeAndCheckLightLock(isMarkedForGc, isReadBarrierSet);
331
332 // check after heavyweight lock
333 wrapper.DecodeAndCheckHeavyLock(isMarkedForGc, isReadBarrierSet);
334 wrapper.DecodeAndCheckLightLock(isMarkedForGc, isReadBarrierSet);
335 }
336
TEST_F(MarkWordTest,CreateLightweightLockWithRandValues)337 TEST_F(MarkWordTest, CreateLightweightLockWithRandValues)
338 {
339 CheckMakeLightweightLock<RandomTestValuesGetter>(false, false);
340 CheckMakeLightweightLock<RandomTestValuesGetter>(false, true);
341 CheckMakeLightweightLock<RandomTestValuesGetter>(true, false);
342 CheckMakeLightweightLock<RandomTestValuesGetter>(true, true);
343 }
344
TEST_F(MarkWordTest,CreateLightweightLockWithMaxValues)345 TEST_F(MarkWordTest, CreateLightweightLockWithMaxValues)
346 {
347 CheckMakeLightweightLock<MaxTestValuesGetter>(false, false);
348 CheckMakeLightweightLock<MaxTestValuesGetter>(false, true);
349 CheckMakeLightweightLock<MaxTestValuesGetter>(true, false);
350 CheckMakeLightweightLock<MaxTestValuesGetter>(true, true);
351 }
352
353 template <class Getter>
CheckMakeHeavyweightLock(bool isMarkedForGc,bool isReadBarrierSet)354 void MarkWordTest::CheckMakeHeavyweightLock(bool isMarkedForGc, bool isReadBarrierSet)
355 {
356 // nothing, gc = markedForGC, rb = readBarrierSet, state = unlocked
357 MarkWordWrapper<Getter> wrapper(isMarkedForGc, isReadBarrierSet);
358
359 // check new heavyweight lock
360 wrapper.DecodeAndCheckHeavyLock(isMarkedForGc, isReadBarrierSet);
361 wrapper.DecodeAndCheckHeavyLock(isMarkedForGc, isReadBarrierSet);
362
363 // check after hash
364 wrapper.DecodeAndCheckHashed(isMarkedForGc, isReadBarrierSet);
365 wrapper.DecodeAndCheckHeavyLock(isMarkedForGc, isReadBarrierSet);
366
367 // check after lightweight lock
368 wrapper.DecodeAndCheckLightLock(isMarkedForGc, isReadBarrierSet);
369 wrapper.DecodeAndCheckHeavyLock(isMarkedForGc, isReadBarrierSet);
370 }
371
TEST_F(MarkWordTest,CreateHeavyweightLockWithRandValues)372 TEST_F(MarkWordTest, CreateHeavyweightLockWithRandValues)
373 {
374 CheckMakeHeavyweightLock<RandomTestValuesGetter>(false, false);
375 CheckMakeHeavyweightLock<RandomTestValuesGetter>(false, true);
376 CheckMakeHeavyweightLock<RandomTestValuesGetter>(true, false);
377 CheckMakeHeavyweightLock<RandomTestValuesGetter>(true, true);
378 }
379
TEST_F(MarkWordTest,CreateHeavyweightLockWithMaxValues)380 TEST_F(MarkWordTest, CreateHeavyweightLockWithMaxValues)
381 {
382 CheckMakeHeavyweightLock<MaxTestValuesGetter>(false, false);
383 CheckMakeHeavyweightLock<MaxTestValuesGetter>(false, true);
384 CheckMakeHeavyweightLock<MaxTestValuesGetter>(true, false);
385 CheckMakeHeavyweightLock<MaxTestValuesGetter>(true, true);
386 }
387
388 template <class Getter>
CheckMakeGC()389 void MarkWordTest::CheckMakeGC()
390 {
391 // check new gc
392 {
393 MarkWordWrapper<Getter> wrapper;
394 wrapper.DecodeAndCheckGC();
395 wrapper.DecodeAndCheckGC();
396 }
397
398 // check after hash
399 {
400 MarkWordWrapper<Getter> wrapper;
401 wrapper.DecodeAndCheckHashed();
402 wrapper.DecodeAndCheckGC();
403 }
404
405 // check after lightweight lock
406 {
407 MarkWordWrapper<Getter> wrapper;
408 wrapper.DecodeAndCheckLightLock();
409 wrapper.DecodeAndCheckGC();
410 }
411
412 // check after heavyweight lock
413 {
414 MarkWordWrapper<Getter> wrapper;
415 wrapper.DecodeAndCheckHeavyLock();
416 wrapper.DecodeAndCheckGC();
417 }
418 }
419
TEST_F(MarkWordTest,CreateGCWithRandomValues)420 TEST_F(MarkWordTest, CreateGCWithRandomValues)
421 {
422 CheckMakeGC<RandomTestValuesGetter>();
423 }
424
TEST_F(MarkWordTest,CreateGCWithMaxValues)425 TEST_F(MarkWordTest, CreateGCWithMaxValues)
426 {
427 CheckMakeGC<MaxTestValuesGetter>();
428 }
429
430 template <class Getter>
CheckMarkingWithGC()431 void MarkWordTest::CheckMarkingWithGC()
432 {
433 Getter paramGetter;
434
435 // with unlocked
436 {
437 MarkWordWrapper<Getter> wrapper;
438
439 wrapper.SetMarkedForGC();
440 wrapper.CheckUnlocked(true);
441 }
442
443 // with lightweight locked
444 {
445 MarkWordWrapper<Getter> wrapper;
446 auto tId = paramGetter.GetThreadId();
447 auto lCount = paramGetter.GetLockCount();
448 wrapper.DecodeLightLock(tId, lCount);
449
450 wrapper.SetMarkedForGC();
451 wrapper.CheckLightweightLock(tId, lCount, true);
452 }
453
454 // with heavyweight locked
455 {
456 MarkWordWrapper<Getter> wrapper;
457 auto mId = paramGetter.GetMonitorId();
458 wrapper.DecodeHeavyLock(mId);
459
460 wrapper.SetMarkedForGC();
461 wrapper.CheckHeavyweightLock(mId, true);
462 }
463
464 // with hashed
465 {
466 MarkWordWrapper<Getter> wrapper;
467 auto hash = paramGetter.GetHash();
468 wrapper.DecodeHash(hash);
469
470 wrapper.SetMarkedForGC();
471 wrapper.CheckHashed(hash, true);
472 }
473 }
474
TEST_F(MarkWordTest,MarkWithGCWithRandValues)475 TEST_F(MarkWordTest, MarkWithGCWithRandValues)
476 {
477 CheckMarkingWithGC<RandomTestValuesGetter>();
478 }
479
TEST_F(MarkWordTest,MarkWithGCWithMaxValues)480 TEST_F(MarkWordTest, MarkWithGCWithMaxValues)
481 {
482 CheckMarkingWithGC<MaxTestValuesGetter>();
483 }
484
485 template <class Getter>
CheckReadBarrierSet()486 void MarkWordTest::CheckReadBarrierSet()
487 {
488 Getter paramGetter;
489
490 // with unlocked
491 {
492 MarkWordWrapper<Getter> wrapper;
493
494 wrapper.SetReadBarrier();
495 wrapper.CheckUnlocked(false, true);
496 }
497
498 // with lightweight locked
499 {
500 MarkWordWrapper<Getter> wrapper;
501 auto tId = paramGetter.GetThreadId();
502 auto lCount = paramGetter.GetLockCount();
503 wrapper.DecodeLightLock(tId, lCount);
504
505 wrapper.SetReadBarrier();
506 wrapper.CheckLightweightLock(tId, lCount, false, true);
507 }
508
509 // with heavyweight locked
510 {
511 MarkWordWrapper<Getter> wrapper;
512 auto mId = paramGetter.GetMonitorId();
513 wrapper.DecodeHeavyLock(mId);
514
515 wrapper.SetReadBarrier();
516 wrapper.CheckHeavyweightLock(mId, false, true);
517 }
518
519 // with hashed
520 {
521 MarkWordWrapper<Getter> wrapper;
522 auto hash = paramGetter.GetHash();
523 wrapper.DecodeHash(hash);
524
525 wrapper.SetReadBarrier();
526 wrapper.CheckHashed(hash, false, true);
527 }
528 }
529
TEST_F(MarkWordTest,ReadBarrierSetWithRandValues)530 TEST_F(MarkWordTest, ReadBarrierSetWithRandValues)
531 {
532 CheckReadBarrierSet<RandomTestValuesGetter>();
533 }
534
TEST_F(MarkWordTest,ReadBarrierSetWithMaxValues)535 TEST_F(MarkWordTest, ReadBarrierSetWithMaxValues)
536 {
537 CheckReadBarrierSet<MaxTestValuesGetter>();
538 }
539
540 } // namespace ark
541