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