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