1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "common_runtime_test.h" 18 #include "gc/collector/immune_spaces.h" 19 #include "gc/space/image_space.h" 20 #include "gc/space/space-inl.h" 21 #include "oat_file.h" 22 #include "thread-inl.h" 23 24 namespace art { 25 namespace mirror { 26 class Object; 27 } // namespace mirror 28 namespace gc { 29 namespace collector { 30 31 class DummyOatFile : public OatFile { 32 public: 33 DummyOatFile(uint8_t* begin, uint8_t* end) : OatFile("Location", /*is_executable*/ false) { 34 begin_ = begin; 35 end_ = end; 36 } 37 }; 38 39 class DummyImageSpace : public space::ImageSpace { 40 public: 41 DummyImageSpace(MemMap* map, 42 accounting::ContinuousSpaceBitmap* live_bitmap, 43 std::unique_ptr<DummyOatFile>&& oat_file, 44 std::unique_ptr<MemMap>&& oat_map) 45 : ImageSpace("DummyImageSpace", 46 /*image_location*/"", 47 map, 48 live_bitmap, 49 map->End()), 50 oat_map_(std::move(oat_map)) { 51 oat_file_ = std::move(oat_file); 52 oat_file_non_owned_ = oat_file_.get(); 53 } 54 55 private: 56 std::unique_ptr<MemMap> oat_map_; 57 }; 58 59 class ImmuneSpacesTest : public CommonRuntimeTest { 60 static constexpr size_t kMaxBitmaps = 10; 61 62 public: 63 ImmuneSpacesTest() {} 64 65 void ReserveBitmaps() { 66 // Create a bunch of dummy bitmaps since these are required to create image spaces. The bitmaps 67 // do not need to cover the image spaces though. 68 for (size_t i = 0; i < kMaxBitmaps; ++i) { 69 std::unique_ptr<accounting::ContinuousSpaceBitmap> bitmap( 70 accounting::ContinuousSpaceBitmap::Create("bitmap", 71 reinterpret_cast<uint8_t*>(kPageSize), 72 kPageSize)); 73 CHECK(bitmap != nullptr); 74 live_bitmaps_.push_back(std::move(bitmap)); 75 } 76 } 77 78 // Create an image space, the oat file is optional. 79 DummyImageSpace* CreateImageSpace(uint8_t* image_begin, 80 size_t image_size, 81 uint8_t* oat_begin, 82 size_t oat_size) { 83 std::string error_str; 84 std::unique_ptr<MemMap> map(MemMap::MapAnonymous("DummyImageSpace", 85 image_begin, 86 image_size, 87 PROT_READ | PROT_WRITE, 88 /*low_4gb*/true, 89 /*reuse*/false, 90 &error_str)); 91 if (map == nullptr) { 92 LOG(ERROR) << error_str; 93 return nullptr; 94 } 95 CHECK(!live_bitmaps_.empty()); 96 std::unique_ptr<accounting::ContinuousSpaceBitmap> live_bitmap(std::move(live_bitmaps_.back())); 97 live_bitmaps_.pop_back(); 98 std::unique_ptr<MemMap> oat_map(MemMap::MapAnonymous("OatMap", 99 oat_begin, 100 oat_size, 101 PROT_READ | PROT_WRITE, 102 /*low_4gb*/true, 103 /*reuse*/false, 104 &error_str)); 105 if (oat_map == nullptr) { 106 LOG(ERROR) << error_str; 107 return nullptr; 108 } 109 std::unique_ptr<DummyOatFile> oat_file(new DummyOatFile(oat_map->Begin(), oat_map->End())); 110 // Create image header. 111 ImageSection sections[ImageHeader::kSectionCount]; 112 new (map->Begin()) ImageHeader( 113 /*image_begin*/PointerToLowMemUInt32(map->Begin()), 114 /*image_size*/map->Size(), 115 sections, 116 /*image_roots*/PointerToLowMemUInt32(map->Begin()) + 1, 117 /*oat_checksum*/0u, 118 // The oat file data in the header is always right after the image space. 119 /*oat_file_begin*/PointerToLowMemUInt32(oat_begin), 120 /*oat_data_begin*/PointerToLowMemUInt32(oat_begin), 121 /*oat_data_end*/PointerToLowMemUInt32(oat_begin + oat_size), 122 /*oat_file_end*/PointerToLowMemUInt32(oat_begin + oat_size), 123 /*boot_image_begin*/0u, 124 /*boot_image_size*/0u, 125 /*boot_oat_begin*/0u, 126 /*boot_oat_size*/0u, 127 /*pointer_size*/sizeof(void*), 128 /*compile_pic*/false, 129 /*is_pic*/false, 130 ImageHeader::kStorageModeUncompressed, 131 /*storage_size*/0u); 132 return new DummyImageSpace(map.release(), 133 live_bitmap.release(), 134 std::move(oat_file), 135 std::move(oat_map)); 136 } 137 138 // Does not reserve the memory, the caller needs to be sure no other threads will map at the 139 // returned address. 140 static uint8_t* GetContinuousMemoryRegion(size_t size) { 141 std::string error_str; 142 std::unique_ptr<MemMap> map(MemMap::MapAnonymous("reserve", 143 nullptr, 144 size, 145 PROT_READ | PROT_WRITE, 146 /*low_4gb*/true, 147 /*reuse*/false, 148 &error_str)); 149 if (map == nullptr) { 150 LOG(ERROR) << "Failed to allocate memory region " << error_str; 151 return nullptr; 152 } 153 return map->Begin(); 154 } 155 156 private: 157 // Bitmap pool for pre-allocated dummy bitmaps. We need to pre-allocate them since we don't want 158 // them to randomly get placed somewhere where we want an image space. 159 std::vector<std::unique_ptr<accounting::ContinuousSpaceBitmap>> live_bitmaps_; 160 }; 161 162 class DummySpace : public space::ContinuousSpace { 163 public: 164 DummySpace(uint8_t* begin, uint8_t* end) 165 : ContinuousSpace("DummySpace", 166 space::kGcRetentionPolicyNeverCollect, 167 begin, 168 end, 169 /*limit*/end) {} 170 171 space::SpaceType GetType() const OVERRIDE { 172 return space::kSpaceTypeMallocSpace; 173 } 174 175 bool CanMoveObjects() const OVERRIDE { 176 return false; 177 } 178 179 accounting::ContinuousSpaceBitmap* GetLiveBitmap() const OVERRIDE { 180 return nullptr; 181 } 182 183 accounting::ContinuousSpaceBitmap* GetMarkBitmap() const OVERRIDE { 184 return nullptr; 185 } 186 }; 187 188 TEST_F(ImmuneSpacesTest, AppendBasic) { 189 ImmuneSpaces spaces; 190 uint8_t* const base = reinterpret_cast<uint8_t*>(0x1000); 191 DummySpace a(base, base + 45 * KB); 192 DummySpace b(a.Limit(), a.Limit() + 813 * KB); 193 { 194 WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); 195 spaces.AddSpace(&a); 196 spaces.AddSpace(&b); 197 } 198 EXPECT_TRUE(spaces.ContainsSpace(&a)); 199 EXPECT_TRUE(spaces.ContainsSpace(&b)); 200 EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().Begin()), a.Begin()); 201 EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().End()), b.Limit()); 202 } 203 204 // Tests [image][oat][space] producing a single large immune region. 205 TEST_F(ImmuneSpacesTest, AppendAfterImage) { 206 ReserveBitmaps(); 207 ImmuneSpaces spaces; 208 constexpr size_t kImageSize = 123 * kPageSize; 209 constexpr size_t kImageOatSize = 321 * kPageSize; 210 constexpr size_t kOtherSpaceSize= 100 * kPageSize; 211 212 uint8_t* memory = GetContinuousMemoryRegion(kImageSize + kImageOatSize + kOtherSpaceSize); 213 214 std::unique_ptr<DummyImageSpace> image_space(CreateImageSpace(memory, 215 kImageSize, 216 memory + kImageSize, 217 kImageOatSize)); 218 ASSERT_TRUE(image_space != nullptr); 219 const ImageHeader& image_header = image_space->GetImageHeader(); 220 DummySpace space(image_header.GetOatFileEnd(), image_header.GetOatFileEnd() + kOtherSpaceSize); 221 222 EXPECT_EQ(image_header.GetImageSize(), kImageSize); 223 EXPECT_EQ(static_cast<size_t>(image_header.GetOatFileEnd() - image_header.GetOatFileBegin()), 224 kImageOatSize); 225 EXPECT_EQ(image_space->GetOatFile()->Size(), kImageOatSize); 226 // Check that we do not include the oat if there is no space after. 227 { 228 WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); 229 spaces.AddSpace(image_space.get()); 230 } 231 EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().Begin()), 232 image_space->Begin()); 233 EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().End()), 234 image_space->Limit()); 235 // Add another space and ensure it gets appended. 236 EXPECT_NE(image_space->Limit(), space.Begin()); 237 { 238 WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); 239 spaces.AddSpace(&space); 240 } 241 EXPECT_TRUE(spaces.ContainsSpace(image_space.get())); 242 EXPECT_TRUE(spaces.ContainsSpace(&space)); 243 // CreateLargestImmuneRegion should have coalesced the two spaces since the oat code after the 244 // image prevents gaps. 245 // Check that we have a continuous region. 246 EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().Begin()), 247 image_space->Begin()); 248 EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().End()), space.Limit()); 249 } 250 251 // Test [image1][image2][image1 oat][image2 oat][image3] producing a single large immune region. 252 TEST_F(ImmuneSpacesTest, MultiImage) { 253 ReserveBitmaps(); 254 // Image 2 needs to be smaller or else it may be chosen for immune region. 255 constexpr size_t kImage1Size = kPageSize * 17; 256 constexpr size_t kImage2Size = kPageSize * 13; 257 constexpr size_t kImage3Size = kPageSize * 3; 258 constexpr size_t kImage1OatSize = kPageSize * 5; 259 constexpr size_t kImage2OatSize = kPageSize * 8; 260 constexpr size_t kImage3OatSize = kPageSize; 261 constexpr size_t kImageBytes = kImage1Size + kImage2Size + kImage3Size; 262 constexpr size_t kMemorySize = kImageBytes + kImage1OatSize + kImage2OatSize + kImage3OatSize; 263 uint8_t* memory = GetContinuousMemoryRegion(kMemorySize); 264 uint8_t* space1_begin = memory; 265 memory += kImage1Size; 266 uint8_t* space2_begin = memory; 267 memory += kImage2Size; 268 uint8_t* space1_oat_begin = memory; 269 memory += kImage1OatSize; 270 uint8_t* space2_oat_begin = memory; 271 memory += kImage2OatSize; 272 uint8_t* space3_begin = memory; 273 274 std::unique_ptr<DummyImageSpace> space1(CreateImageSpace(space1_begin, 275 kImage1Size, 276 space1_oat_begin, 277 kImage1OatSize)); 278 ASSERT_TRUE(space1 != nullptr); 279 280 281 std::unique_ptr<DummyImageSpace> space2(CreateImageSpace(space2_begin, 282 kImage2Size, 283 space2_oat_begin, 284 kImage2OatSize)); 285 ASSERT_TRUE(space2 != nullptr); 286 287 // Finally put a 3rd image space. 288 std::unique_ptr<DummyImageSpace> space3(CreateImageSpace(space3_begin, 289 kImage3Size, 290 space3_begin + kImage3Size, 291 kImage3OatSize)); 292 ASSERT_TRUE(space3 != nullptr); 293 294 // Check that we do not include the oat if there is no space after. 295 ImmuneSpaces spaces; 296 { 297 WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); 298 LOG(INFO) << "Adding space1 " << reinterpret_cast<const void*>(space1->Begin()); 299 spaces.AddSpace(space1.get()); 300 LOG(INFO) << "Adding space2 " << reinterpret_cast<const void*>(space2->Begin()); 301 spaces.AddSpace(space2.get()); 302 } 303 // There are no more heap bytes, the immune region should only be the first 2 image spaces and 304 // should exclude the image oat files. 305 EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().Begin()), 306 space1->Begin()); 307 EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().End()), 308 space2->Limit()); 309 310 // Add another space after the oat files, now it should contain the entire memory region. 311 { 312 WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); 313 LOG(INFO) << "Adding space3 " << reinterpret_cast<const void*>(space3->Begin()); 314 spaces.AddSpace(space3.get()); 315 } 316 EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().Begin()), 317 space1->Begin()); 318 EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().End()), 319 space3->Limit()); 320 321 // Add a smaller non-adjacent space and ensure it does not become part of the immune region. 322 // Image size is kImageBytes - kPageSize 323 // Oat size is kPageSize. 324 // Guard pages to ensure it is not adjacent to an existing immune region. 325 // Layout: [guard page][image][oat][guard page] 326 constexpr size_t kGuardSize = kPageSize; 327 constexpr size_t kImage4Size = kImageBytes - kPageSize; 328 constexpr size_t kImage4OatSize = kPageSize; 329 uint8_t* memory2 = GetContinuousMemoryRegion(kImage4Size + kImage4OatSize + kGuardSize * 2); 330 std::unique_ptr<DummyImageSpace> space4(CreateImageSpace(memory2 + kGuardSize, 331 kImage4Size, 332 memory2 + kGuardSize + kImage4Size, 333 kImage4OatSize)); 334 ASSERT_TRUE(space4 != nullptr); 335 { 336 WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); 337 LOG(INFO) << "Adding space4 " << reinterpret_cast<const void*>(space4->Begin()); 338 spaces.AddSpace(space4.get()); 339 } 340 EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().Begin()), 341 space1->Begin()); 342 EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().End()), 343 space3->Limit()); 344 345 // Add a larger non-adjacent space and ensure it becomes the new largest immune region. 346 // Image size is kImageBytes + kPageSize 347 // Oat size is kPageSize. 348 // Guard pages to ensure it is not adjacent to an existing immune region. 349 // Layout: [guard page][image][oat][guard page] 350 constexpr size_t kImage5Size = kImageBytes + kPageSize; 351 constexpr size_t kImage5OatSize = kPageSize; 352 uint8_t* memory3 = GetContinuousMemoryRegion(kImage5Size + kImage5OatSize + kGuardSize * 2); 353 std::unique_ptr<DummyImageSpace> space5(CreateImageSpace(memory3 + kGuardSize, 354 kImage5Size, 355 memory3 + kGuardSize + kImage5Size, 356 kImage5OatSize)); 357 ASSERT_TRUE(space5 != nullptr); 358 { 359 WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); 360 LOG(INFO) << "Adding space5 " << reinterpret_cast<const void*>(space5->Begin()); 361 spaces.AddSpace(space5.get()); 362 } 363 EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().Begin()), space5->Begin()); 364 EXPECT_EQ(reinterpret_cast<uint8_t*>(spaces.GetLargestImmuneRegion().End()), space5->Limit()); 365 } 366 367 } // namespace collector 368 } // namespace gc 369 } // namespace art 370