/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "card_table-inl.h" #include #include "base/atomic.h" #include "base/utils.h" #include "common_runtime_test.h" #include "handle_scope-inl.h" #include "mirror/class-inl.h" #include "mirror/string-inl.h" // Strings are easiest to allocate #include "scoped_thread_state_change-inl.h" #include "thread_pool.h" namespace art { namespace mirror { class Object; } // namespace mirror namespace gc { namespace accounting { class CardTableTest : public CommonRuntimeTest { public: std::unique_ptr card_table_; void CommonSetup() { if (card_table_.get() == nullptr) { card_table_.reset(CardTable::Create(heap_begin_, heap_size_)); EXPECT_TRUE(card_table_.get() != nullptr); } else { ClearCardTable(); } } // Default values for the test, not random to avoid undeterministic behaviour. CardTableTest() : heap_begin_(reinterpret_cast(0x2000000)), heap_size_(2 * MB) { } void ClearCardTable() { card_table_->ClearCardTable(); } uint8_t* HeapBegin() const { return heap_begin_; } uint8_t* HeapLimit() const { return HeapBegin() + heap_size_; } // Return a non-zero pseudo random card for an address. uint8_t PseudoRandomCard(const uint8_t* addr) const { size_t offset = RoundDown(addr - heap_begin_, CardTable::kCardSize); return 1 + offset % 254; } void FillRandom() { for (const uint8_t* addr = HeapBegin(); addr != HeapLimit(); addr += CardTable::kCardSize) { EXPECT_TRUE(card_table_->AddrIsInCardTable(addr)); uint8_t* card = card_table_->CardFromAddr(addr); *card = PseudoRandomCard(addr); } } private: uint8_t* const heap_begin_; const size_t heap_size_; }; TEST_F(CardTableTest, TestMarkCard) { CommonSetup(); for (const uint8_t* addr = HeapBegin(); addr < HeapLimit(); addr += kObjectAlignment) { auto obj = reinterpret_cast(addr); EXPECT_EQ(card_table_->GetCard(obj), CardTable::kCardClean); EXPECT_TRUE(!card_table_->IsDirty(obj)); card_table_->MarkCard(addr); EXPECT_TRUE(card_table_->IsDirty(obj)); EXPECT_EQ(card_table_->GetCard(obj), CardTable::kCardDirty); uint8_t* card_addr = card_table_->CardFromAddr(addr); EXPECT_EQ(*card_addr, CardTable::kCardDirty); *card_addr = CardTable::kCardClean; EXPECT_EQ(*card_addr, CardTable::kCardClean); } } class UpdateVisitor { public: uint8_t operator()(uint8_t c) const { // Must map zero to zero. Never applied to zero. return c == 0 ? 0 : c * 93 + 123; } void operator()(uint8_t* /*card*/, uint8_t /*expected_value*/, uint8_t /*new_value*/) const { } }; TEST_F(CardTableTest, TestModifyCardsAtomic) { CommonSetup(); FillRandom(); const size_t delta = std::min(static_cast(HeapLimit() - HeapBegin()), 8U * CardTable::kCardSize); UpdateVisitor visitor; size_t start_offset = 0; for (uint8_t* cstart = HeapBegin(); cstart < HeapBegin() + delta; cstart += CardTable::kCardSize) { start_offset = (start_offset + kObjectAlignment) % CardTable::kCardSize; size_t end_offset = 0; for (uint8_t* cend = HeapLimit() - delta; cend < HeapLimit(); cend += CardTable::kCardSize) { // Don't always start at a card boundary. uint8_t* start = cstart + start_offset; uint8_t* end = cend - end_offset; end_offset = (end_offset + kObjectAlignment) % CardTable::kCardSize; // Modify cards. card_table_->ModifyCardsAtomic(start, end, visitor, visitor); // Check adjacent cards not modified. for (uint8_t* cur = start - CardTable::kCardSize; cur >= HeapBegin(); cur -= CardTable::kCardSize) { EXPECT_EQ(card_table_->GetCard(reinterpret_cast(cur)), PseudoRandomCard(cur)); } for (uint8_t* cur = end + CardTable::kCardSize; cur < HeapLimit(); cur += CardTable::kCardSize) { EXPECT_EQ(card_table_->GetCard(reinterpret_cast(cur)), PseudoRandomCard(cur)); } // Verify Range. for (uint8_t* cur = start; cur < AlignUp(end, CardTable::kCardSize); cur += CardTable::kCardSize) { uint8_t* card = card_table_->CardFromAddr(cur); uint8_t value = PseudoRandomCard(cur); EXPECT_EQ(visitor(value), *card); // Restore for next iteration. *card = value; } } } } // TODO: Add test for CardTable::Scan. } // namespace accounting } // namespace gc } // namespace art