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
26namespace art {
27namespace mirror {
28class Object;
29}  // namespace mirror
30namespace gc {
31namespace collector {
32
33class DummyOatFile : public OatFile {
34 public:
35  DummyOatFile(uint8_t* begin, uint8_t* end) : OatFile("Location", /*is_executable*/ false) {
36    begin_ = begin;
37    end_ = end;
38  }
39};
40
41class DummyImageSpace : public space::ImageSpace {
42 public:
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
61class ImmuneSpacesTest : public CommonRuntimeTest {
62  static constexpr size_t kMaxBitmaps = 10;
63
64 public:
65  ImmuneSpacesTest() {}
66
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.
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.
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
164class DummySpace : public space::ContinuousSpace {
165 public:
166  DummySpace(uint8_t* begin, uint8_t* end)
167      : ContinuousSpace("DummySpace",
168                        space::kGcRetentionPolicyNeverCollect,
169                        begin,
170                        end,
171                        /*limit*/end) {}
172
173  space::SpaceType GetType() const OVERRIDE {
174    return space::kSpaceTypeMallocSpace;
175  }
176
177  bool CanMoveObjects() const OVERRIDE {
178    return false;
179  }
180
181  accounting::ContinuousSpaceBitmap* GetLiveBitmap() const OVERRIDE {
182    return nullptr;
183  }
184
185  accounting::ContinuousSpaceBitmap* GetMarkBitmap() const OVERRIDE {
186    return nullptr;
187  }
188};
189
190TEST_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.
207TEST_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.
254TEST_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