| /* |
| * Copyright (C) 2015 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 "common_runtime_test.h" |
| #include "gc/collector/immune_spaces.h" |
| #include "gc/space/image_space.h" |
| #include "gc/space/space-inl.h" |
| #include "oat_file.h" |
| #include "thread-inl.h" |
| |
| namespace art { |
| namespace mirror { |
| class Object; |
| } // namespace mirror |
| namespace gc { |
| namespace collector { |
| |
| class ImmuneSpacesTest : public CommonRuntimeTest {}; |
| |
| class DummySpace : public space::ContinuousSpace { |
| public: |
| DummySpace(uint8_t* begin, uint8_t* end) |
| : ContinuousSpace("DummySpace", |
| space::kGcRetentionPolicyNeverCollect, |
| begin, |
| end, |
| /*limit*/end) {} |
| |
| space::SpaceType GetType() const OVERRIDE { |
| return space::kSpaceTypeMallocSpace; |
| } |
| |
| bool CanMoveObjects() const OVERRIDE { |
| return false; |
| } |
| |
| accounting::ContinuousSpaceBitmap* GetLiveBitmap() const OVERRIDE { |
| return nullptr; |
| } |
| |
| accounting::ContinuousSpaceBitmap* GetMarkBitmap() const OVERRIDE { |
| return nullptr; |
| } |
| }; |
| |
| TEST_F(ImmuneSpacesTest, AppendBasic) { |
| ImmuneSpaces spaces; |
| uint8_t* const base = reinterpret_cast<uint8_t*>(0x1000); |
| DummySpace a(base, base + 45 * KB); |
| DummySpace b(a.Limit(), a.Limit() + 813 * KB); |
| { |
| WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); |
| spaces.AddSpace(&a); |
| spaces.AddSpace(&b); |
| } |
| EXPECT_TRUE(spaces.ContainsSpace(&a)); |
| EXPECT_TRUE(spaces.ContainsSpace(&b)); |
| EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().Begin()), a.Begin()); |
| EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().End()), b.Limit()); |
| } |
| |
| class DummyImageSpace : public space::ImageSpace { |
| public: |
| DummyImageSpace(MemMap* map, accounting::ContinuousSpaceBitmap* live_bitmap) |
| : ImageSpace("DummyImageSpace", |
| /*image_location*/"", |
| map, |
| live_bitmap, |
| map->End()) {} |
| |
| // OatSize is how large the oat file is after the image. |
| static DummyImageSpace* Create(size_t size, size_t oat_size) { |
| std::string error_str; |
| std::unique_ptr<MemMap> map(MemMap::MapAnonymous("DummyImageSpace", |
| nullptr, |
| size, |
| PROT_READ | PROT_WRITE, |
| /*low_4gb*/true, |
| /*reuse*/false, |
| &error_str)); |
| if (map == nullptr) { |
| LOG(ERROR) << error_str; |
| return nullptr; |
| } |
| std::unique_ptr<accounting::ContinuousSpaceBitmap> live_bitmap( |
| accounting::ContinuousSpaceBitmap::Create("bitmap", map->Begin(), map->Size())); |
| if (live_bitmap == nullptr) { |
| return nullptr; |
| } |
| // Create image header. |
| ImageSection sections[ImageHeader::kSectionCount]; |
| new (map->Begin()) ImageHeader( |
| /*image_begin*/PointerToLowMemUInt32(map->Begin()), |
| /*image_size*/map->Size(), |
| sections, |
| /*image_roots*/PointerToLowMemUInt32(map->Begin()) + 1, |
| /*oat_checksum*/0u, |
| /*oat_file_begin*/PointerToLowMemUInt32(map->End()), |
| /*oat_data_begin*/PointerToLowMemUInt32(map->End()), |
| /*oat_data_end*/PointerToLowMemUInt32(map->End() + oat_size), |
| /*oat_file_end*/PointerToLowMemUInt32(map->End() + oat_size), |
| /*boot_image_begin*/0u, |
| /*boot_image_size*/0u, |
| /*boot_oat_begin*/0u, |
| /*boot_oat_size*/0u, |
| /*pointer_size*/sizeof(void*), |
| /*compile_pic*/false, |
| /*is_pic*/false, |
| ImageHeader::kStorageModeUncompressed, |
| /*storage_size*/0u); |
| return new DummyImageSpace(map.release(), live_bitmap.release()); |
| } |
| }; |
| |
| TEST_F(ImmuneSpacesTest, AppendAfterImage) { |
| ImmuneSpaces spaces; |
| constexpr size_t image_size = 123 * kPageSize; |
| constexpr size_t image_oat_size = 321 * kPageSize; |
| std::unique_ptr<DummyImageSpace> image_space(DummyImageSpace::Create(image_size, image_oat_size)); |
| ASSERT_TRUE(image_space != nullptr); |
| const ImageHeader& image_header = image_space->GetImageHeader(); |
| EXPECT_EQ(image_header.GetImageSize(), image_size); |
| EXPECT_EQ(static_cast<size_t>(image_header.GetOatFileEnd() - image_header.GetOatFileBegin()), |
| image_oat_size); |
| DummySpace space(image_header.GetOatFileEnd(), image_header.GetOatFileEnd() + 813 * kPageSize); |
| EXPECT_NE(image_space->Limit(), space.Begin()); |
| { |
| WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); |
| spaces.AddSpace(image_space.get()); |
| spaces.AddSpace(&space); |
| } |
| EXPECT_TRUE(spaces.ContainsSpace(image_space.get())); |
| EXPECT_TRUE(spaces.ContainsSpace(&space)); |
| // CreateLargestImmuneRegion should have coalesced the two spaces since the oat code after the |
| // image prevents gaps. |
| // Check that we have a continuous region. |
| EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().Begin()), |
| image_space->Begin()); |
| EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().End()), space.Limit()); |
| } |
| |
| } // namespace collector |
| } // namespace gc |
| } // namespace art |