1 /**
2 * Copyright (c) 2021-2022 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 <chrono>
17 #include <cstdlib>
18 #include <ctime>
19 #include <random>
20
21 #include "gtest/gtest.h"
22 #include "runtime/mem/gc/card_table-inl.h"
23 #include "runtime/mem/mem_stats_additional_info.h"
24 #include "runtime/mem/mem_stats_default.h"
25 #include "runtime/mem/internal_allocator-inl.h"
26 #include "runtime/include/runtime_options.h"
27 #include "runtime/include/runtime.h"
28 #include "runtime/include/panda_vm.h"
29
30 namespace panda::mem::test {
31
32 class CardTableTest : public testing::Test {
33 protected:
34 // static constexpr size_t kHeapSize = 0xffffffff;
35 static constexpr size_t kAllocCount = 1000;
36 // static constexpr size_t maxCardIndex = kHeapSize / ::panda::mem::CardTable::GetCardSize();
37 std::mt19937 gen;
38 std::uniform_int_distribution<uintptr_t> addrDis;
39 std::uniform_int_distribution<size_t> cardIndexDis;
40 mem::MemStatsType *mem_stats;
41
CardTableTest()42 CardTableTest()
43 {
44 #ifdef PANDA_NIGHTLY_TEST_ON
45 seed_ = std::time(NULL);
46 #else
47 seed_ = 123456;
48 #endif
49 RuntimeOptions options;
50 options.SetHeapSizeLimit(64_MB);
51 options.SetShouldLoadBootPandaFiles(false);
52 options.SetShouldInitializeIntrinsics(false);
53 options.SetGcType("epsilon");
54 Runtime::Create(options);
55 thread_ = panda::MTManagedThread::GetCurrent();
56 thread_->ManagedCodeBegin();
57
58 internal_allocator_ = thread_->GetVM()->GetHeapManager()->GetInternalAllocator();
59 addrDis = std::uniform_int_distribution<uintptr_t>(0, GetPoolSize() - 1);
60 ASSERT(GetPoolSize() % CardTable::GetCardSize() == 0);
61 cardIndexDis = std::uniform_int_distribution<size_t>(0, GetPoolSize() / CardTable::GetCardSize() - 1);
62 card_table_ = std::make_unique<CardTable>(internal_allocator_, GetMinAddress(), GetPoolSize());
63 card_table_->Initialize();
64 }
65
~CardTableTest()66 ~CardTableTest()
67 {
68 card_table_.reset(nullptr);
69 thread_->ManagedCodeEnd();
70 Runtime::Destroy();
71 }
72
SetUp()73 void SetUp() override
74 {
75 gen.seed(seed_);
76 }
77
TearDown()78 void TearDown() override
79 {
80 const ::testing::TestInfo *const test_info = ::testing::UnitTest::GetInstance()->current_test_info();
81 if (test_info->result()->Failed()) {
82 std::cout << "CartTableTest seed = " << seed_ << std::endl;
83 }
84 }
85
GetMinAddress()86 uintptr_t GetMinAddress()
87 {
88 return PoolManager::GetMmapMemPool()->GetMinObjectAddress();
89 }
90
GetPoolSize()91 size_t GetPoolSize()
92 {
93 return (PoolManager::GetMmapMemPool()->GetMaxObjectAddress() -
94 PoolManager::GetMmapMemPool()->GetMinObjectAddress());
95 }
96
GetRandomAddress()97 uintptr_t GetRandomAddress()
98 {
99 return PoolManager::GetMmapMemPool()->GetMinObjectAddress() + addrDis(gen) % GetPoolSize();
100 }
101
GetRandomCardIndex()102 size_t GetRandomCardIndex()
103 {
104 return cardIndexDis(gen) % GetPoolSize();
105 }
106
107 // generate address at the begining of the card
GetRandomCardAddress()108 uintptr_t GetRandomCardAddress()
109 {
110 return PoolManager::GetMmapMemPool()->GetMinObjectAddress() + GetRandomCardIndex() * CardTable::GetCardSize();
111 }
112
113 InternalAllocatorPtr internal_allocator_;
114 std::unique_ptr<CardTable> card_table_;
115 unsigned int seed_;
116 panda::MTManagedThread *thread_;
117 };
118
TEST_F(CardTableTest,MarkTest)119 TEST_F(CardTableTest, MarkTest)
120 {
121 size_t markedCnt = 0;
122
123 for (size_t i = 0; i < kAllocCount; i++) {
124 uintptr_t addr = GetRandomAddress();
125 if (!card_table_->IsMarked(addr)) {
126 ++markedCnt;
127 card_table_->MarkCard(addr);
128 }
129 }
130
131 for (auto card : *card_table_) {
132 if (card->IsMarked()) {
133 markedCnt--;
134 }
135 }
136 ASSERT_EQ(markedCnt, 0);
137 }
138
TEST_F(CardTableTest,MarkAndClearAllTest)139 TEST_F(CardTableTest, MarkAndClearAllTest)
140 {
141 // std::set<uintptr_t> addrSet;
142
143 size_t cnt = 0;
144 for (auto card : *card_table_) {
145 card->Mark();
146 cnt++;
147 }
148 ASSERT_EQ(cnt, card_table_->GetCardsCount());
149
150 size_t cnt_cleared = 0;
151 for (auto card : *card_table_) {
152 card->Clear();
153 cnt_cleared++;
154 }
155 ASSERT_EQ(cnt_cleared, card_table_->GetCardsCount());
156 }
157
TEST_F(CardTableTest,ClearTest)158 TEST_F(CardTableTest, ClearTest)
159 {
160 std::set<uintptr_t> addrSet;
161
162 // Mark some cards not more than once
163 while (addrSet.size() <= kAllocCount) {
164 uintptr_t addr = GetRandomCardAddress();
165 if (!addrSet.insert(addr).second) {
166 continue;
167 }
168 card_table_->MarkCard(addr);
169 }
170
171 size_t cleared_cnt = 0;
172 // clear all marked and count them
173 for (auto card : *card_table_) {
174 if (card->IsMarked()) {
175 card->Clear();
176 cleared_cnt++;
177 }
178 }
179
180 ASSERT_EQ(addrSet.size(), cleared_cnt);
181 // check that there are no marked
182 for (auto card : *card_table_) {
183 ASSERT_EQ(card->IsMarked(), false);
184 }
185 }
186
TEST_F(CardTableTest,ClearAllTest)187 TEST_F(CardTableTest, ClearAllTest)
188 {
189 std::set<uintptr_t> addrSet;
190
191 // Mark some cards not more than once
192 while (addrSet.size() < kAllocCount) {
193 uintptr_t addr = GetRandomCardAddress();
194 if (!addrSet.insert(addr).second) {
195 continue;
196 }
197 card_table_->MarkCard(addr);
198 }
199
200 card_table_->ClearAll();
201 for (auto card : *card_table_) {
202 ASSERT_EQ(card->IsMarked(), false);
203 }
204 }
205
TEST_F(CardTableTest,double_initialization)206 TEST_F(CardTableTest, double_initialization)
207 {
208 EXPECT_DEATH(card_table_->Initialize(), ".*");
209 }
210
TEST_F(CardTableTest,corner_cases)211 TEST_F(CardTableTest, corner_cases)
212 {
213 // Mark 1st byte in the heap
214 ASSERT_EQ((*card_table_->begin())->IsMarked(), false);
215 card_table_->MarkCard(GetMinAddress());
216 ASSERT_EQ((*card_table_->begin())->IsMarked(), true);
217 // Mark last byte in the heap
218 uintptr_t last = GetMinAddress() + GetPoolSize() - 1;
219 ASSERT_EQ(card_table_->IsMarked(last), false);
220 card_table_->MarkCard(last);
221 ASSERT_EQ(card_table_->IsMarked(last), true);
222 // Mark last byte of second card
223 uintptr_t secondLast = GetMinAddress() + 2 * card_table_->GetCardSize() - 1;
224 ASSERT_EQ(card_table_->IsMarked(secondLast), false);
225 card_table_->MarkCard(secondLast);
226 ASSERT_EQ(((*card_table_->begin()) + 1)->IsMarked(), true);
227 }
228
TEST_F(CardTableTest,VisitMarked)229 TEST_F(CardTableTest, VisitMarked)
230 {
231 size_t markedCnt = 0;
232
233 while (markedCnt < kAllocCount) {
234 uintptr_t addr = GetRandomAddress();
235 if (!card_table_->IsMarked(addr)) {
236 ++markedCnt;
237 card_table_->MarkCard(addr);
238 }
239 }
240
241 PandaVector<MemRange> mem_ranges;
242 card_table_->VisitMarked([&mem_ranges](MemRange mem_range) { mem_ranges.emplace_back(mem_range); },
243 CardTableProcessedFlag::VISIT_MARKED);
244
245 // Got ranges one by one
246 PandaVector<MemRange> expected_ranges;
247 for (auto card : *card_table_) {
248 if (card->IsMarked()) {
249 expected_ranges.emplace_back(card_table_->GetMemoryRange(card));
250 }
251 }
252
253 ASSERT_EQ(expected_ranges.size(), mem_ranges.size());
254 for (size_t i = 0; i < expected_ranges.size(); i++) {
255 ASSERT(mem_ranges[i].GetStartAddress() == expected_ranges[i].GetStartAddress());
256 ASSERT(mem_ranges[i].GetEndAddress() == expected_ranges[i].GetEndAddress());
257 }
258 }
259
260 } // namespace panda::mem::test
261